import { StateService } from "../abstractions/state.service"; import { TokenService as TokenServiceAbstraction } from "../abstractions/token.service"; import { Utils } from "../misc/utils"; import { IdentityTokenResponse } from "../models/response/identityTokenResponse"; export class TokenService implements TokenServiceAbstraction { constructor(private stateService: StateService) {} async setTokens( accessToken: string, refreshToken: string, clientIdClientSecret: [string, string] ): Promise { await this.setToken(accessToken); await this.setRefreshToken(refreshToken); if (clientIdClientSecret != null) { await this.setClientId(clientIdClientSecret[0]); await this.setClientSecret(clientIdClientSecret[1]); } } async setClientId(clientId: string): Promise { return await this.stateService.setApiKeyClientId(clientId); } async getClientId(): Promise { return await this.stateService.getApiKeyClientId(); } async setClientSecret(clientSecret: string): Promise { return await this.stateService.setApiKeyClientSecret(clientSecret); } async getClientSecret(): Promise { return await this.stateService.getApiKeyClientSecret(); } async setToken(token: string): Promise { await this.stateService.setAccessToken(token); } async getToken(): Promise { return await this.stateService.getAccessToken(); } async setRefreshToken(refreshToken: string): Promise { return await this.stateService.setRefreshToken(refreshToken); } async getRefreshToken(): Promise { return await this.stateService.getRefreshToken(); } async setTwoFactorToken(tokenResponse: IdentityTokenResponse): Promise { return await this.stateService.setTwoFactorToken(tokenResponse.twoFactorToken); } async getTwoFactorToken(): Promise { return await this.stateService.getTwoFactorToken(); } async clearTwoFactorToken(): Promise { return await this.stateService.setTwoFactorToken(null); } async clearToken(userId?: string): Promise { await this.stateService.setAccessToken(null, { userId: userId }); await this.stateService.setRefreshToken(null, { userId: userId }); await this.stateService.setApiKeyClientId(null, { userId: userId }); await this.stateService.setApiKeyClientSecret(null, { userId: userId }); } // jwthelper methods // ref https://github.com/auth0/angular-jwt/blob/master/src/angularJwt/services/jwt.js async decodeToken(token?: string): Promise { const storedToken = await this.stateService.getDecodedToken(); if (token === null && storedToken != null) { return storedToken; } token = token ?? (await this.stateService.getAccessToken()); if (token == null) { throw new Error("Token not found."); } const parts = token.split("."); if (parts.length !== 3) { throw new Error("JWT must have 3 parts"); } const decoded = Utils.fromUrlB64ToUtf8(parts[1]); if (decoded == null) { throw new Error("Cannot decode the token"); } const decodedToken = JSON.parse(decoded); return decodedToken; } async getTokenExpirationDate(): Promise { const decoded = await this.decodeToken(); if (typeof decoded.exp === "undefined") { return null; } const d = new Date(0); // The 0 here is the key, which sets the date to the epoch d.setUTCSeconds(decoded.exp); return d; } async tokenSecondsRemaining(offsetSeconds: number = 0): Promise { const d = await this.getTokenExpirationDate(); if (d == null) { return 0; } const msRemaining = d.valueOf() - (new Date().valueOf() + offsetSeconds * 1000); return Math.round(msRemaining / 1000); } async tokenNeedsRefresh(minutes: number = 5): Promise { const sRemaining = await this.tokenSecondsRemaining(); return sRemaining < 60 * minutes; } async getUserId(): Promise { const decoded = await this.decodeToken(); if (typeof decoded.sub === "undefined") { throw new Error("No user id found"); } return decoded.sub as string; } async getEmail(): Promise { const decoded = await this.decodeToken(); if (typeof decoded.email === "undefined") { throw new Error("No email found"); } return decoded.email as string; } async getEmailVerified(): Promise { const decoded = await this.decodeToken(); if (typeof decoded.email_verified === "undefined") { throw new Error("No email verification found"); } return decoded.email_verified as boolean; } async getName(): Promise { const decoded = await this.decodeToken(); if (typeof decoded.name === "undefined") { return null; } return decoded.name as string; } async getPremium(): Promise { const decoded = await this.decodeToken(); if (typeof decoded.premium === "undefined") { return false; } return decoded.premium as boolean; } async getIssuer(): Promise { const decoded = await this.decodeToken(); if (typeof decoded.iss === "undefined") { throw new Error("No issuer found"); } return decoded.iss as string; } async getIsExternal(): Promise { const decoded = await this.decodeToken(); return Array.isArray(decoded.amr) && decoded.amr.includes("external"); } }