import { StateService } from "../abstractions/state.service" ; import { TokenService as TokenServiceAbstraction } from "../abstractions/token.service" ; import { Utils } from "../misc/utils" ; export class TokenService implements TokenServiceAbstraction { constructor ( private stateService ) { } async setTokens ( accessToken , refreshToken , clientIdClientSecret ) { await this . setToken ( accessToken ) ; await this . setRefreshToken ( refreshToken ) ; if ( clientIdClientSecret != null ) { await this . setClientId ( clientIdClientSecret [ 0 ] ) ; await this . setClientSecret ( clientIdClientSecret [ 1 ] ) ; } } async setClientId ( clientId ) { if ( ( await this . skipTokenStorage ( ) ) || clientId == null ) { return ; } return await this . stateService . setApiKeyClientId ( clientId ) ; } async getClientId ( ) { return await this . stateService . getApiKeyClientId ( ) ; } async setClientSecret ( clientSecret ) { if ( ( await this . skipTokenStorage ( ) ) || clientSecret == null ) { return ; } return await this . stateService . setApiKeyClientSecret ( clientSecret ) ; } async getClientSecret ( ) { return await this . stateService . getApiKeyClientSecret ( ) ; } async setToken ( token ) { await this . stateService . setAccessToken ( token ) ; } async getToken ( ) { return await this . stateService . getAccessToken ( ) ; } async setRefreshToken ( refreshToken ) { if ( await this . skipTokenStorage ( ) ) { return ; } return await this . stateService . setRefreshToken ( refreshToken ) ; } async getRefreshToken ( ) { return await this . stateService . getRefreshToken ( ) ; } async toggleTokens ( ) { const token = await this . getToken ( ) ; const refreshToken = await this . getRefreshToken ( ) ; const clientId = await this . getClientId ( ) ; const clientSecret = await this . getClientSecret ( ) ; const timeout = await this . stateService . getVaultTimeout ( ) ; const action = await this . stateService . getVaultTimeoutAction ( ) ; if ( ( timeout != null || timeout === 0 ) && action === "logOut" ) { await this . clearToken ( ) ; } await this . setToken ( token ) ; await this . setRefreshToken ( refreshToken ) ; await this . setClientId ( clientId ) ; await this . setClientSecret ( clientSecret ) ; } async setTwoFactorToken ( token ) { return await this . stateService . setTwoFactorToken ( token ) ; } async getTwoFactorToken ( ) { return await this . stateService . getTwoFactorToken ( ) ; } async clearTwoFactorToken ( ) { return await this . stateService . setTwoFactorToken ( null ) ; } async clearToken ( userId ? ) { 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 } ) ; } async decodeToken ( token ? ) { 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 ( ) { const decoded = await this . decodeToken ( ) ; if ( typeof decoded . exp === "undefined" ) { return null ; } const d = new Date ( 0 ) ; d . setUTCSeconds ( decoded . exp ) ; return d ; } async tokenSecondsRemaining ( offsetSeconds = 0 ) { 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 = 5 ) { const sRemaining = await this . tokenSecondsRemaining ( ) ; return sRemaining < 60 * minutes ; } async getUserId ( ) { const decoded = await this . decodeToken ( ) ; if ( typeof decoded . sub === "undefined" ) { throw new Error ( "No user id found" ) ; } return decoded . sub as string ; } async getEmail ( ) { const decoded = await this . decodeToken ( ) ; if ( typeof decoded . email === "undefined" ) { throw new Error ( "No email found" ) ; } return decoded . email as string ; } async getEmailVerified ( ) { 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 ( ) { const decoded = await this . decodeToken ( ) ; if ( typeof decoded . name === "undefined" ) { return null ; } return decoded . name as string ; } async getPremium ( ) { const decoded = await this . decodeToken ( ) ; if ( typeof decoded . premium === "undefined" ) { return false ; } return decoded . premium as boolean ; } async getIssuer ( ) { const decoded = await this . decodeToken ( ) ; if ( typeof decoded . iss === "undefined" ) { throw new Error ( "No issuer found" ) ; } return decoded . iss as string ; } async getIsExternal ( ) { const decoded = await this . decodeToken ( ) ; return Array . isArray ( decoded . amr ) && decoded . amr . includes ( "external" ) ; } private async skipTokenStorage ( ) { const timeout = await this . stateService . getVaultTimeout ( ) ; const action = await this . stateService . getVaultTimeoutAction ( ) ; return timeout != null && action === "logOut" ; } }