First implementation of refresh token.
This commit is contained in:
@@ -2,7 +2,11 @@ application:
|
|||||||
pictures:
|
pictures:
|
||||||
path: /home/florian/Developpement/codiki-hexagonal/backend/pictures-folder/
|
path: /home/florian/Developpement/codiki-hexagonal/backend/pictures-folder/
|
||||||
temp-path : /home/florian/Developpement/codiki-hexagonal/backend/pictures-folder/temp/
|
temp-path : /home/florian/Developpement/codiki-hexagonal/backend/pictures-folder/temp/
|
||||||
|
security:
|
||||||
|
jwt:
|
||||||
|
expirationDelayInMinutes: 1
|
||||||
|
refreshToken:
|
||||||
|
expirationDelayInDays: 7
|
||||||
server:
|
server:
|
||||||
port: 8987
|
port: 8987
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
meta {
|
||||||
|
name: Refresh token
|
||||||
|
type: http
|
||||||
|
seq: 4
|
||||||
|
}
|
||||||
|
|
||||||
|
post {
|
||||||
|
url: {{url}}/api/users/refresh-token
|
||||||
|
body: none
|
||||||
|
auth: none
|
||||||
|
}
|
||||||
@@ -8,6 +8,10 @@ export class JwtInterceptor implements HttpInterceptor {
|
|||||||
private readonly authenticationService = inject(AuthenticationService);
|
private readonly authenticationService = inject(AuthenticationService);
|
||||||
|
|
||||||
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
|
||||||
|
if (this.authenticationService.isTokenExpired()) {
|
||||||
|
this.authenticationService.renewToken();
|
||||||
|
}
|
||||||
|
|
||||||
const jwt = this.authenticationService.getToken();
|
const jwt = this.authenticationService.getToken();
|
||||||
|
|
||||||
if (jwt) {
|
if (jwt) {
|
||||||
@@ -16,6 +20,8 @@ export class JwtInterceptor implements HttpInterceptor {
|
|||||||
});
|
});
|
||||||
|
|
||||||
return next.handle(cloned);
|
return next.handle(cloned);
|
||||||
|
} else {
|
||||||
|
this.authenticationService.unauthenticate();
|
||||||
}
|
}
|
||||||
|
|
||||||
return next.handle(request);
|
return next.handle(request);
|
||||||
|
|||||||
@@ -0,0 +1,3 @@
|
|||||||
|
export interface RefreshTokenRequest {
|
||||||
|
refreshTokenValue: string;
|
||||||
|
}
|
||||||
@@ -3,6 +3,7 @@ import { Injectable, inject } from "@angular/core";
|
|||||||
import { LoginRequest, LoginResponse } from "./model/login.model";
|
import { LoginRequest, LoginResponse } from "./model/login.model";
|
||||||
import { lastValueFrom } from "rxjs";
|
import { lastValueFrom } from "rxjs";
|
||||||
import { SigninRequest } from "./model/signin.model";
|
import { SigninRequest } from "./model/signin.model";
|
||||||
|
import { RefreshTokenRequest } from "./model/refresh-token.model";
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
@@ -17,4 +18,8 @@ export class UserRestService {
|
|||||||
signin(request: SigninRequest): Promise<void> {
|
signin(request: SigninRequest): Promise<void> {
|
||||||
return lastValueFrom(this.httpClient.post<void>('/api/users', request));
|
return lastValueFrom(this.httpClient.post<void>('/api/users', request));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
refreshToken(request: RefreshTokenRequest): Promise<LoginResponse> {
|
||||||
|
return lastValueFrom(this.httpClient.post<LoginResponse>('/api/users/refresh-token', request));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,7 +1,10 @@
|
|||||||
import { Injectable } from "@angular/core";
|
import { inject, Injectable } from "@angular/core";
|
||||||
import { User } from "../model/User";
|
import { User } from "../model/User";
|
||||||
|
import { UserRestService } from "../rest-services/user/user.rest-service";
|
||||||
|
import { RefreshTokenRequest } from "../rest-services/user/model/refresh-token.model";
|
||||||
|
|
||||||
const JWT_PARAM = 'jwt';
|
const JWT_PARAM = 'jwt';
|
||||||
|
const REFRESH_TOKEN_PARAM = 'refresh-token';
|
||||||
|
|
||||||
interface UserDetails {
|
interface UserDetails {
|
||||||
sub: string;
|
sub: string;
|
||||||
@@ -15,8 +18,11 @@ interface UserDetails {
|
|||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class AuthenticationService {
|
export class AuthenticationService {
|
||||||
authenticate(token: string): void {
|
private userRestService = inject(UserRestService);
|
||||||
|
|
||||||
|
authenticate(token: string, refreshToken: string): void {
|
||||||
localStorage.setItem(JWT_PARAM, token);
|
localStorage.setItem(JWT_PARAM, token);
|
||||||
|
localStorage.setItem(REFRESH_TOKEN_PARAM, refreshToken);
|
||||||
}
|
}
|
||||||
|
|
||||||
unauthenticate(): void {
|
unauthenticate(): void {
|
||||||
@@ -44,6 +50,34 @@ export class AuthenticationService {
|
|||||||
return localStorage.getItem(JWT_PARAM) ?? undefined;
|
return localStorage.getItem(JWT_PARAM) ?? undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
isTokenExpired(): boolean {
|
||||||
|
let result = false;
|
||||||
|
|
||||||
|
const userDetails = this.extractUserDetails();
|
||||||
|
|
||||||
|
if (userDetails) {
|
||||||
|
const expirationDate = new Date(userDetails.exp * 1000);
|
||||||
|
const now = new Date();
|
||||||
|
|
||||||
|
result = expirationDate < now;
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
renewToken(): void {
|
||||||
|
const refreshToken = localStorage.getItem(REFRESH_TOKEN_PARAM);
|
||||||
|
if (refreshToken) {
|
||||||
|
const request: RefreshTokenRequest = {
|
||||||
|
refreshTokenValue: refreshToken
|
||||||
|
};
|
||||||
|
this.userRestService.refreshToken(request)
|
||||||
|
.then(refreshTokenResponse => {
|
||||||
|
this.authenticate(refreshTokenResponse.accessToken, refreshTokenResponse.refreshToken);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private extractUserFromLocalStorage(): User | undefined {
|
private extractUserFromLocalStorage(): User | undefined {
|
||||||
let result: User | undefined = undefined;
|
let result: User | undefined = undefined;
|
||||||
|
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ export class LoginService {
|
|||||||
this.userRestService
|
this.userRestService
|
||||||
.login(state.request)
|
.login(state.request)
|
||||||
.then((response) => {
|
.then((response) => {
|
||||||
this.authenticationService.authenticate(response.accessToken);
|
this.authenticationService.authenticate(response.accessToken, response.refreshToken);
|
||||||
this.snackBar.open('Authentication succeeded!', 'Close', {
|
this.snackBar.open('Authentication succeeded!', 'Close', {
|
||||||
duration: 5000,
|
duration: 5000,
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user