fix jwt handler to return proper error codes
This commit is contained in:
@@ -64,6 +64,12 @@ public class SecurityConfig {
|
|||||||
.requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
|
.requestMatchers("/swagger-ui/**", "/api-docs/**").permitAll()
|
||||||
.requestMatchers(HttpMethod.GET, "/api/sites/**").authenticated()
|
.requestMatchers(HttpMethod.GET, "/api/sites/**").authenticated()
|
||||||
.anyRequest().authenticated()
|
.anyRequest().authenticated()
|
||||||
|
)
|
||||||
|
.exceptionHandling(ex -> ex
|
||||||
|
.authenticationEntryPoint((request, response, authException) ->
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Unauthorized"))
|
||||||
|
.accessDeniedHandler((request, response, accessDeniedException) ->
|
||||||
|
response.sendError(HttpServletResponse.SC_FORBIDDEN, "Forbidden"))
|
||||||
);
|
);
|
||||||
|
|
||||||
if (isOAuth2Configured()) {
|
if (isOAuth2Configured()) {
|
||||||
@@ -128,6 +134,9 @@ public class SecurityConfig {
|
|||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
org.springframework.security.core.context.SecurityContextHolder
|
org.springframework.security.core.context.SecurityContextHolder
|
||||||
.clearContext();
|
.clearContext();
|
||||||
|
response.sendError(HttpServletResponse.SC_UNAUTHORIZED,
|
||||||
|
"Invalid or expired token");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,11 +1,21 @@
|
|||||||
import { Injectable } from '@angular/core';
|
import { Injectable, inject } from '@angular/core';
|
||||||
import { HttpInterceptor, HttpRequest, HttpHandler, HttpEvent } from '@angular/common/http';
|
import {
|
||||||
import { Observable } from 'rxjs';
|
HttpInterceptor,
|
||||||
|
HttpRequest,
|
||||||
|
HttpHandler,
|
||||||
|
HttpEvent,
|
||||||
|
HttpErrorResponse,
|
||||||
|
} from '@angular/common/http';
|
||||||
|
import { BehaviorSubject, Observable, catchError, filter, switchMap, take, throwError } from 'rxjs';
|
||||||
import { AuthService } from '../services/auth.service';
|
import { AuthService } from '../services/auth.service';
|
||||||
|
import { Router } from '@angular/router';
|
||||||
|
|
||||||
@Injectable()
|
@Injectable()
|
||||||
export class JwtInterceptor implements HttpInterceptor {
|
export class JwtInterceptor implements HttpInterceptor {
|
||||||
constructor(private authService: AuthService) {}
|
private authService = inject(AuthService);
|
||||||
|
private router = inject(Router);
|
||||||
|
private isRefreshing = false;
|
||||||
|
private refreshTokenSubject = new BehaviorSubject<string | null>(null);
|
||||||
|
|
||||||
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
intercept(req: HttpRequest<unknown>, next: HttpHandler): Observable<HttpEvent<unknown>> {
|
||||||
const token = this.authService.getAccessToken();
|
const token = this.authService.getAccessToken();
|
||||||
@@ -14,6 +24,65 @@ export class JwtInterceptor implements HttpInterceptor {
|
|||||||
setHeaders: { Authorization: `Bearer ${token}` },
|
setHeaders: { Authorization: `Bearer ${token}` },
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
return next.handle(req);
|
|
||||||
|
return next.handle(req).pipe(
|
||||||
|
catchError((error) => {
|
||||||
|
if (
|
||||||
|
error instanceof HttpErrorResponse &&
|
||||||
|
error.status === 401 &&
|
||||||
|
!req.url.includes('/api/auth/refresh')
|
||||||
|
) {
|
||||||
|
return this.handle401Error(req, next);
|
||||||
|
}
|
||||||
|
return throwError(() => error);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
private handle401Error(
|
||||||
|
req: HttpRequest<unknown>,
|
||||||
|
next: HttpHandler,
|
||||||
|
): Observable<HttpEvent<unknown>> {
|
||||||
|
if (!this.isRefreshing) {
|
||||||
|
this.isRefreshing = true;
|
||||||
|
this.refreshTokenSubject.next(null);
|
||||||
|
|
||||||
|
const refreshToken = this.authService.getRefreshToken();
|
||||||
|
if (!refreshToken) {
|
||||||
|
this.authService.clearSessionForLogout();
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
return throwError(() => new Error('No refresh token available'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return this.authService.refreshToken(refreshToken).pipe(
|
||||||
|
switchMap((res) => {
|
||||||
|
this.isRefreshing = false;
|
||||||
|
this.refreshTokenSubject.next(res.accessToken);
|
||||||
|
return next.handle(
|
||||||
|
req.clone({
|
||||||
|
setHeaders: { Authorization: `Bearer ${res.accessToken}` },
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
catchError((err) => {
|
||||||
|
this.isRefreshing = false;
|
||||||
|
this.authService.clearSessionForLogout();
|
||||||
|
this.router.navigate(['/login']);
|
||||||
|
return throwError(() => err);
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return this.refreshTokenSubject.pipe(
|
||||||
|
filter((token): token is string => token !== null),
|
||||||
|
take(1),
|
||||||
|
switchMap((token) =>
|
||||||
|
next.handle(
|
||||||
|
req.clone({
|
||||||
|
setHeaders: { Authorization: `Bearer ${token}` },
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user