|
| 1 | +import {Injectable} from "@angular/core" |
| 2 | +import {BehaviorSubject, finalize, Observable} from "rxjs" |
| 3 | +import jwtDecode from "jwt-decode" |
| 4 | +import {Data} from "./jwt.models" |
| 5 | +import * as moment from "moment" |
| 6 | +import {HttpClient, HttpResponse} from "@angular/common/http" |
| 7 | + |
| 8 | +@Injectable({ |
| 9 | + providedIn: "root" |
| 10 | +}) |
| 11 | +export class JwtService { |
| 12 | + static readonly UPDATE_URL = "/api/user/updateToken" |
| 13 | + static readonly REFRESH_TOKEN_NAME = "refresh_token" |
| 14 | + static readonly ACCESS_TOKEN_NAME = "access_token" |
| 15 | + static readonly SECOND_TO_NEED_UPDATE = 30 |
| 16 | + |
| 17 | + get access() { |
| 18 | + return localStorage.getItem(JwtService.ACCESS_TOKEN_NAME) ?? "" |
| 19 | + } |
| 20 | + |
| 21 | + set access(value: string) { |
| 22 | + localStorage.setItem(JwtService.ACCESS_TOKEN_NAME, value) |
| 23 | + this._data = this.decode() |
| 24 | + } |
| 25 | + |
| 26 | + get refresh() { |
| 27 | + return localStorage.getItem(JwtService.REFRESH_TOKEN_NAME) ?? "" |
| 28 | + } |
| 29 | + |
| 30 | + set refresh(value: string) { |
| 31 | + localStorage.setItem(JwtService.REFRESH_TOKEN_NAME, value) |
| 32 | + } |
| 33 | + |
| 34 | + get tokens() { |
| 35 | + return [this.access, this.refresh] |
| 36 | + } |
| 37 | + |
| 38 | + get updatingChanges() { |
| 39 | + return this._updating$.asObservable() |
| 40 | + } |
| 41 | + |
| 42 | + get isUpdating() { |
| 43 | + return this._updating$.value |
| 44 | + } |
| 45 | + |
| 46 | + get data(): Data | null { |
| 47 | + return this._data |
| 48 | + } |
| 49 | + |
| 50 | + constructor(private http: HttpClient) { |
| 51 | + this._data = this.decode() |
| 52 | + } |
| 53 | + |
| 54 | + removeTokens(): void { |
| 55 | + localStorage.removeItem(JwtService.REFRESH_TOKEN_NAME) |
| 56 | + localStorage.removeItem(JwtService.ACCESS_TOKEN_NAME) |
| 57 | + } |
| 58 | + |
| 59 | + isNeedUpdate(): boolean { |
| 60 | + //if null -> return false |
| 61 | + //null will be if res < JwtService.SECOND_TO_NEED_UPDATE, |
| 62 | + //so JwtService.SECOND_TO_NEED_UPDATE < JwtService.SECOND_TO_NEED_UPDATE = false |
| 63 | + return (this.expireDifferenceBetweenNow() ?? JwtService.SECOND_TO_NEED_UPDATE) < JwtService.SECOND_TO_NEED_UPDATE |
| 64 | + } |
| 65 | + |
| 66 | + isMustUpdate(): boolean { |
| 67 | + //if null -> return false |
| 68 | + //null will be if res <= 0, |
| 69 | + //so 1 <= 0 = false |
| 70 | + return (this.expireDifferenceBetweenNow() ?? 1) <= 0 |
| 71 | + } |
| 72 | + |
| 73 | + expireDifferenceBetweenNow(): number | null { |
| 74 | + return this.data?.exp.diff(moment.now(), "second") ?? null |
| 75 | + } |
| 76 | + |
| 77 | + update(): Observable<HttpResponse<void>> { |
| 78 | + this._updating$.next(true) |
| 79 | + return this.http |
| 80 | + .put<void>(JwtService.UPDATE_URL, `"${this.refresh}"`, {observe: "response"}) |
| 81 | + .pipe(finalize(() => this._updating$.next(false))) |
| 82 | + } |
| 83 | + |
| 84 | + private _updating$ = new BehaviorSubject<boolean>(false) |
| 85 | + private _data: Data | null |
| 86 | + |
| 87 | + private decode(): Data | null { |
| 88 | + try { |
| 89 | + const data = jwtDecode<Data>(this.access) |
| 90 | + data.exp = moment(data.exp as any * 1000) |
| 91 | + return data |
| 92 | + } catch (error) { |
| 93 | + return null |
| 94 | + } |
| 95 | + } |
| 96 | +} |
0 commit comments