All files / sn-client-js/src/Authentication TokenStore.ts

100% Statements 54/54
91.11% Branches 41/45
100% Functions 12/12
100% Lines 54/54
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150      1x                     1x                   30x 30x 30x 30x 30x 30x 30x 30x   30x 26x 4x 2x   2x             30x               186x       8x 8x 4x 8x 4x 4x 4x     4x       4x 4x 2x   4x                 157x 157x 157x   141x   4x   4x     8x         113x                 29x 29x 29x   21x 21x   2x 2x   2x 2x   2x 2x   2x 2x               115x     11x             42x     9x      
/**
 * @module Authentication
 */ /** */
import { Token, TokenPersist, TokenStoreType } from './';
 
/**
 * Indicates the type of the token
 */
export type TokenType = 'access' | 'refresh';
 
 
/**
 * This class is intended to store token data in LocalStorage or in-memory storage.
 */
export class TokenStore {
 
    /**
    * @param {strnig} baseUrl The Base URL to the related site
    * @param {string} keyTemplate The template to use when generating keys in the local/session storage or for a cookie. ${siteName} and ${tokenName} will be replaced. Example: 'sn-${siteName}-${tokenName}'
    * @param {TokenPersist} tokenPersist Setting that indicates if the token should be persisted per session (browser close) or per Token expiration (based on the token `exp` property)
    * @param {Partial<Document>} documentRef The Document reference (used by unit tests)
    * @param {Storage} localStorageRef The localStorage reference (used by unit tests)
    * @param {Storage} sessionStorageRef The sessionStorage reference (used by unit tests)
     */
    constructor(private readonly baseUrl: string,
        private readonly keyTemplate: string,
        private readonly tokenPersist: TokenPersist,
        private documentRef = (typeof document === 'object') ? document : undefined,
        private localStorageRef = (typeof localStorage === 'object') ? localStorage : undefined,
        private sessionStorageRef = (typeof sessionStorage === 'object') ? sessionStorage : undefined) {
        let storesAvailable = (typeof this.localStorageRef !== 'undefined' && typeof this.sessionStorageRef !== 'undefined');
        let cookieAvailable = (typeof this.documentRef !== 'undefined' && typeof this.documentRef.cookie !== 'undefined');
 
        if (!storesAvailable && !cookieAvailable) {
            this.TokenStoreType = TokenStoreType.InMemory;
        } else if (this.tokenPersist === TokenPersist.Expiration) {
            storesAvailable ? this.TokenStoreType = TokenStoreType.LocalStorage : this.TokenStoreType = TokenStoreType.ExpirationCookie;
        } else {
            storesAvailable ? this.TokenStoreType = TokenStoreType.SessionStorage : this.TokenStoreType = TokenStoreType.SessionCookie;
        }
    }
 
    /**
     * If localStorage is not available, stores the token data in this in-memory array
     */
    private innerStore: string[] = [];
 
    /**
     * The type of the generated Token Store
     */
    public readonly TokenStoreType: TokenStoreType;
 
    private getStoreKey(key: TokenType) {
        return this.keyTemplate.replace('${siteName}', this.baseUrl).replace('${tokenName}', key);
    }
 
    private getTokenFromCookie(key: string, document: Document): Token {
        const prefix = key + '=';
        if (document && document.cookie){
            const cookieVal = document.cookie.split(';')
                .map(v => v.trim())
                .find(v => v.trim().indexOf(prefix) === 0);
            Eif (cookieVal){
                return Token.FromHeadAndPayload(cookieVal.substring(prefix.length));
            }
        }
        return Token.CreateEmpty();
    }
 
    private setTokenToCookie(key: string, Token: Token, persist: TokenPersist, doc: Document): void {
        let cookie = `${key}=${Token.toString()}`;
        if (persist === TokenPersist.Expiration) {
            cookie += `; expires=${Token.ExpirationTime.toUTCString()};`
        }
        doc.cookie = cookie;
    }
 
    /**
     * Gets the specified token
     * @param key {TokenType} The key for the token
     * @returns {Token} The requested token, or Token.Empty in case of error
     */
    public GetToken(key: TokenType): Token {
        const storeKey = this.getStoreKey(key);
        try {
            switch (this.TokenStoreType) {
                case TokenStoreType.InMemory:
                    return Token.FromHeadAndPayload(this.innerStore[storeKey as any]);
                case TokenStoreType.LocalStorage:
                    return Token.FromHeadAndPayload((this.localStorageRef as any).getItem(storeKey));
                case TokenStoreType.SessionStorage:
                    return Token.FromHeadAndPayload((this.sessionStorageRef as any).getItem(storeKey));
                case TokenStoreType.ExpirationCookie:
                case TokenStoreType.SessionCookie:
                    return this.getTokenFromCookie(storeKey, this.documentRef as Document);
            }
        } catch (err) {
            // 
        }
        return Token.CreateEmpty();
    }
 
    /**
     * Sets the token with the specified key to the specified value
     * @param key {TokenType} The key for the token to set
     * @param token {Token} The token to set with the specified key
     */
    public SetToken(key: TokenType, token: Token) {
        const storeKey = this.getStoreKey(key);
        let dtaString = token.toString();
        switch (this.TokenStoreType) {
            case TokenStoreType.InMemory:
                this.innerStore[storeKey as any] = dtaString;
                break;
            case TokenStoreType.LocalStorage:
                this.localStorageRef && this.localStorageRef.setItem(storeKey, dtaString);
                break;
            case TokenStoreType.SessionStorage:
                this.sessionStorageRef && this.sessionStorageRef.setItem(storeKey, dtaString);
                break;
            case TokenStoreType.ExpirationCookie:
                this.setTokenToCookie(storeKey, token, TokenPersist.Expiration, this.documentRef as Document);
                break;
            case TokenStoreType.SessionCookie:
                this.setTokenToCookie(storeKey, token, TokenPersist.Session, this.documentRef as Document);
                break;
        }
    }
 
    /**
     * The current Access Token
     */
    public get AccessToken() {
        return this.GetToken('access');
    }
    public set AccessToken(value: Token) {
        this.SetToken('access', value);
    }
 
    /**
     * The current Refresh Token
     */
    public get RefreshToken() {
        return this.GetToken('refresh');
    }
    public set RefreshToken(value: Token) {
        this.SetToken('refresh', value);
    }
 
}