abstract tokenstore to support different storage methods of tokenstore
This commit is contained in:
		
							
								
								
									
										11
									
								
								src/App.tsx
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								src/App.tsx
									
									
									
									
									
								
							@@ -9,7 +9,7 @@ import style from './App.module.css';
 | 
			
		||||
 | 
			
		||||
import SettingsPage from './pages/SettingsPage/SettingsPage';
 | 
			
		||||
import CategoryPage from './pages/CategoryPage/CategoryPage';
 | 
			
		||||
import {APINode, apiTokenValid, callAPI, refreshAPIToken} from './utils/Api';
 | 
			
		||||
import {APINode, callAPI, token} from './utils/Api';
 | 
			
		||||
 | 
			
		||||
import {BrowserRouter as Router, NavLink, Route, Switch} from 'react-router-dom';
 | 
			
		||||
import Player from './pages/Player/Player';
 | 
			
		||||
@@ -19,6 +19,7 @@ import {SettingsTypes} from './types/ApiTypes';
 | 
			
		||||
import AuthenticationPage from './pages/AuthenticationPage/AuthenticationPage';
 | 
			
		||||
import TVShowPage from './pages/TVShowPage/TVShowPage';
 | 
			
		||||
import TVPlayer from './pages/TVShowPage/TVPlayer';
 | 
			
		||||
import {CookieTokenStore} from './utils/TokenStore/CookieTokenStore';
 | 
			
		||||
 | 
			
		||||
interface state {
 | 
			
		||||
    password: boolean | null; // null if uninitialized - true if pwd needed false if not needed
 | 
			
		||||
@@ -32,12 +33,14 @@ class App extends React.Component<{}, state> {
 | 
			
		||||
    constructor(props: {}) {
 | 
			
		||||
        super(props);
 | 
			
		||||
 | 
			
		||||
        token.setTokenStore(new CookieTokenStore());
 | 
			
		||||
 | 
			
		||||
        let pwdneeded: boolean | null = null;
 | 
			
		||||
 | 
			
		||||
        if (apiTokenValid()) {
 | 
			
		||||
        if (token.apiTokenValid()) {
 | 
			
		||||
            pwdneeded = false;
 | 
			
		||||
        } else {
 | 
			
		||||
            refreshAPIToken((err) => {
 | 
			
		||||
            token.refreshAPIToken((err) => {
 | 
			
		||||
                if (err === 'invalid_client') {
 | 
			
		||||
                    this.setState({password: true});
 | 
			
		||||
                } else if (err === '') {
 | 
			
		||||
@@ -61,7 +64,7 @@ class App extends React.Component<{}, state> {
 | 
			
		||||
        // set the hook to load passwordfield on global func call
 | 
			
		||||
        GlobalInfos.loadPasswordPage = (callback?: () => void): void => {
 | 
			
		||||
            // try refreshing the token
 | 
			
		||||
            refreshAPIToken((err) => {
 | 
			
		||||
            token.refreshAPIToken((err) => {
 | 
			
		||||
                if (err !== '') {
 | 
			
		||||
                    this.setState({password: true});
 | 
			
		||||
                } else {
 | 
			
		||||
 
 | 
			
		||||
@@ -1,6 +1,7 @@
 | 
			
		||||
import React from 'react';
 | 
			
		||||
import AuthenticationPage from './AuthenticationPage';
 | 
			
		||||
import {shallow} from 'enzyme';
 | 
			
		||||
import {token} from "../../utils/Api";
 | 
			
		||||
 | 
			
		||||
describe('<AuthenticationPage/>', function () {
 | 
			
		||||
    it('renders without crashing ', function () {
 | 
			
		||||
@@ -25,8 +26,7 @@ describe('<AuthenticationPage/>', function () {
 | 
			
		||||
    it('test fail authenticate', function () {
 | 
			
		||||
        const events = mockKeyPress();
 | 
			
		||||
 | 
			
		||||
        const helpers = require('../../utils/Api');
 | 
			
		||||
        helpers.refreshAPIToken = jest.fn().mockImplementation((callback, force, pwd) => {
 | 
			
		||||
        token.refreshAPIToken = jest.fn().mockImplementation((callback, force, pwd) => {
 | 
			
		||||
            callback('there was an error')
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
@@ -41,8 +41,7 @@ describe('<AuthenticationPage/>', function () {
 | 
			
		||||
        const events = mockKeyPress();
 | 
			
		||||
        const func = jest.fn()
 | 
			
		||||
 | 
			
		||||
        const helpers = require('../../utils/Api');
 | 
			
		||||
        helpers.refreshAPIToken = jest.fn().mockImplementation((callback, force, pwd) => {
 | 
			
		||||
        token.refreshAPIToken = jest.fn().mockImplementation((callback, force, pwd) => {
 | 
			
		||||
            callback('')
 | 
			
		||||
        });
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
@@ -2,7 +2,7 @@ import React from 'react';
 | 
			
		||||
import {Button} from '../../elements/GPElements/Button';
 | 
			
		||||
import style from './AuthenticationPage.module.css';
 | 
			
		||||
import {addKeyHandler, removeKeyHandler} from '../../utils/ShortkeyHandler';
 | 
			
		||||
import {refreshAPIToken} from '../../utils/Api';
 | 
			
		||||
import {token} from '../../utils/Api';
 | 
			
		||||
import {faTimes} from '@fortawesome/free-solid-svg-icons';
 | 
			
		||||
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
 | 
			
		||||
 | 
			
		||||
@@ -76,7 +76,7 @@ class AuthenticationPage extends React.Component<Props, state> {
 | 
			
		||||
     * request a new token and check if pwd was valid
 | 
			
		||||
     */
 | 
			
		||||
    authenticate(): void {
 | 
			
		||||
        refreshAPIToken(
 | 
			
		||||
        token.refreshAPIToken(
 | 
			
		||||
            (error) => {
 | 
			
		||||
                if (error !== '') {
 | 
			
		||||
                    this.setState({wrongPWDInfo: true});
 | 
			
		||||
 
 | 
			
		||||
@@ -7,6 +7,8 @@ import '@testing-library/jest-dom/extend-expect';
 | 
			
		||||
import {configure} from 'enzyme';
 | 
			
		||||
import Adapter from 'enzyme-adapter-react-16';
 | 
			
		||||
import GlobalInfos from './utils/GlobalInfos';
 | 
			
		||||
import {CookieTokenStore} from "./utils/TokenStore/CookieTokenStore";
 | 
			
		||||
import {token} from "./utils/Api";
 | 
			
		||||
 | 
			
		||||
configure({adapter: new Adapter()});
 | 
			
		||||
 | 
			
		||||
@@ -44,6 +46,7 @@ global.callAPIMock = (resonse) => {
 | 
			
		||||
global.beforeEach(() => {
 | 
			
		||||
    // empty fetch response implementation for each test
 | 
			
		||||
    global.fetch = prepareFetchApi({});
 | 
			
		||||
    token.setTokenStore(new CookieTokenStore());
 | 
			
		||||
    // todo with callAPIMock
 | 
			
		||||
});
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										261
									
								
								src/utils/Api.ts
									
									
									
									
									
								
							
							
						
						
									
										261
									
								
								src/utils/Api.ts
									
									
									
									
									
								
							@@ -1,4 +1,5 @@
 | 
			
		||||
import GlobalInfos from './GlobalInfos';
 | 
			
		||||
import {TokenStore} from './TokenStore/TokenStore';
 | 
			
		||||
 | 
			
		||||
const APIPREFIX: string = '/api/';
 | 
			
		||||
 | 
			
		||||
@@ -11,165 +12,127 @@ interface ApiBaseRequest {
 | 
			
		||||
    [_: string]: string | number | boolean | object;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// store api token - empty if not set
 | 
			
		||||
let apiToken = '';
 | 
			
		||||
export namespace token {
 | 
			
		||||
    // store api token - empty if not set
 | 
			
		||||
    let apiToken = '';
 | 
			
		||||
 | 
			
		||||
// a callback que to be called after api token refresh
 | 
			
		||||
let callQue: ((error: string) => void)[] = [];
 | 
			
		||||
// flag to check wheter a api refresh is currently pending
 | 
			
		||||
let refreshInProcess = false;
 | 
			
		||||
// store the expire seconds of token
 | 
			
		||||
let expireSeconds = -1;
 | 
			
		||||
    // a callback que to be called after api token refresh
 | 
			
		||||
    let callQue: ((error: string) => void)[] = [];
 | 
			
		||||
    // flag to check wheter a api refresh is currently pending
 | 
			
		||||
    let refreshInProcess = false;
 | 
			
		||||
    // store the expire seconds of token
 | 
			
		||||
    let expireSeconds = -1;
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * refresh the api token or use that one in cookie if still valid
 | 
			
		||||
 * @param callback to be called after successful refresh
 | 
			
		||||
 * @param password
 | 
			
		||||
 * @param force
 | 
			
		||||
 */
 | 
			
		||||
export function refreshAPIToken(callback: (error: string) => void, force?: boolean, password?: string): void {
 | 
			
		||||
    callQue.push(callback);
 | 
			
		||||
    let tokenStore: TokenStore;
 | 
			
		||||
 | 
			
		||||
    // check if already is a token refresh is in process
 | 
			
		||||
    if (refreshInProcess) {
 | 
			
		||||
        // if yes return
 | 
			
		||||
        return;
 | 
			
		||||
    } else {
 | 
			
		||||
        // if not set flat
 | 
			
		||||
        refreshInProcess = true;
 | 
			
		||||
    export function setTokenStore(ts: TokenStore): void {
 | 
			
		||||
        tokenStore = ts;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    if (apiTokenValid() && !force) {
 | 
			
		||||
        console.log('token still valid...');
 | 
			
		||||
        callFuncQue('');
 | 
			
		||||
        return;
 | 
			
		||||
    }
 | 
			
		||||
    /**
 | 
			
		||||
     * refresh the api token or use that one in cookie if still valid
 | 
			
		||||
     * @param callback to be called after successful refresh
 | 
			
		||||
     * @param password
 | 
			
		||||
     * @param force
 | 
			
		||||
     */
 | 
			
		||||
    export function refreshAPIToken(callback: (error: string) => void, force?: boolean, password?: string): void {
 | 
			
		||||
        callQue.push(callback);
 | 
			
		||||
 | 
			
		||||
    const formData = new FormData();
 | 
			
		||||
    formData.append('grant_type', 'client_credentials');
 | 
			
		||||
    formData.append('client_id', 'openmediacenter');
 | 
			
		||||
    formData.append('client_secret', password ? password : 'openmediacenter');
 | 
			
		||||
    formData.append('scope', 'all');
 | 
			
		||||
        // check if already is a token refresh is in process
 | 
			
		||||
        if (refreshInProcess) {
 | 
			
		||||
            // if yes return
 | 
			
		||||
            return;
 | 
			
		||||
        } else {
 | 
			
		||||
            // if not set flat
 | 
			
		||||
            refreshInProcess = true;
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    interface APIToken {
 | 
			
		||||
        error?: string;
 | 
			
		||||
        // eslint-disable-next-line camelcase
 | 
			
		||||
        access_token: string; // no camel case allowed because of backendlib
 | 
			
		||||
        // eslint-disable-next-line camelcase
 | 
			
		||||
        expires_in: number; // no camel case allowed because of backendlib
 | 
			
		||||
        scope: string;
 | 
			
		||||
        // eslint-disable-next-line camelcase
 | 
			
		||||
        token_type: string; // no camel case allowed because of backendlib
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    fetch('/token', {method: 'POST', body: formData}).then((response) =>
 | 
			
		||||
        response.json().then((result: APIToken) => {
 | 
			
		||||
            if (result.error) {
 | 
			
		||||
                callFuncQue(result.error);
 | 
			
		||||
                return;
 | 
			
		||||
            }
 | 
			
		||||
            // set api token
 | 
			
		||||
            apiToken = result.access_token;
 | 
			
		||||
            // set expire time
 | 
			
		||||
            expireSeconds = new Date().getTime() / 1000 + result.expires_in;
 | 
			
		||||
            setTokenCookie(apiToken, expireSeconds);
 | 
			
		||||
            // call all handlers and release flag
 | 
			
		||||
        if (apiTokenValid() && !force) {
 | 
			
		||||
            console.log('token still valid...');
 | 
			
		||||
            callFuncQue('');
 | 
			
		||||
        })
 | 
			
		||||
    );
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export function apiTokenValid(): boolean {
 | 
			
		||||
    // check if a cookie with token is available
 | 
			
		||||
    const token = getTokenCookie();
 | 
			
		||||
    if (token !== null) {
 | 
			
		||||
        // check if token is at least valid for the next minute
 | 
			
		||||
        if (token.expire > new Date().getTime() / 1000 + 60) {
 | 
			
		||||
            apiToken = token.token;
 | 
			
		||||
            expireSeconds = token.expire;
 | 
			
		||||
 | 
			
		||||
            return true;
 | 
			
		||||
            return;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return false;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * call all qued callbacks
 | 
			
		||||
 */
 | 
			
		||||
function callFuncQue(error: string): void {
 | 
			
		||||
    // call all pending handlers
 | 
			
		||||
    callQue.map((func) => {
 | 
			
		||||
        return func(error);
 | 
			
		||||
    });
 | 
			
		||||
    // reset pending que
 | 
			
		||||
    callQue = [];
 | 
			
		||||
    // release flag to be able to start new refresh
 | 
			
		||||
    refreshInProcess = false;
 | 
			
		||||
}
 | 
			
		||||
        const formData = new FormData();
 | 
			
		||||
        formData.append('grant_type', 'client_credentials');
 | 
			
		||||
        formData.append('client_id', 'openmediacenter');
 | 
			
		||||
        formData.append('client_secret', password ? password : 'openmediacenter');
 | 
			
		||||
        formData.append('scope', 'all');
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * set the cookie for the currently gotten token
 | 
			
		||||
 * @param token token string
 | 
			
		||||
 * @param validSec second time when the token will be invalid
 | 
			
		||||
 */
 | 
			
		||||
function setTokenCookie(token: string, validSec: number): void {
 | 
			
		||||
    let d = new Date();
 | 
			
		||||
    d.setTime(validSec * 1000);
 | 
			
		||||
    console.log('token set' + d.toUTCString());
 | 
			
		||||
    let expires = 'expires=' + d.toUTCString();
 | 
			
		||||
    document.cookie = 'token=' + token + ';' + expires + ';path=/';
 | 
			
		||||
    document.cookie = 'token_expire=' + validSec + ';' + expires + ';path=/';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * get all required cookies for the token
 | 
			
		||||
 */
 | 
			
		||||
function getTokenCookie(): {token: string; expire: number} | null {
 | 
			
		||||
    const token = decodeCookie('token');
 | 
			
		||||
    const expireInString = decodeCookie('token_expire');
 | 
			
		||||
    const expireIn = parseInt(expireInString, 10);
 | 
			
		||||
 | 
			
		||||
    if (expireIn !== 0 && token !== '') {
 | 
			
		||||
        return {token: token, expire: expireIn};
 | 
			
		||||
    } else {
 | 
			
		||||
        return null;
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * decode a simple cookie with key specified
 | 
			
		||||
 * @param key cookie key
 | 
			
		||||
 */
 | 
			
		||||
function decodeCookie(key: string): string {
 | 
			
		||||
    let name = key + '=';
 | 
			
		||||
    let decodedCookie = decodeURIComponent(document.cookie);
 | 
			
		||||
    let ca = decodedCookie.split(';');
 | 
			
		||||
    for (let i = 0; i < ca.length; i++) {
 | 
			
		||||
        let c = ca[i];
 | 
			
		||||
        while (c.charAt(0) === ' ') {
 | 
			
		||||
            c = c.substring(1);
 | 
			
		||||
        interface APIToken {
 | 
			
		||||
            error?: string;
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            access_token: string; // no camel case allowed because of backendlib
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            expires_in: number; // no camel case allowed because of backendlib
 | 
			
		||||
            scope: string;
 | 
			
		||||
            // eslint-disable-next-line camelcase
 | 
			
		||||
            token_type: string; // no camel case allowed because of backendlib
 | 
			
		||||
        }
 | 
			
		||||
        if (c.indexOf(name) === 0) {
 | 
			
		||||
            return c.substring(name.length, c.length);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
    return '';
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
/**
 | 
			
		||||
 * check if api token is valid -- if not request new one
 | 
			
		||||
 * when finished call callback
 | 
			
		||||
 * @param callback function to be called afterwards
 | 
			
		||||
 */
 | 
			
		||||
function checkAPITokenValid(callback: () => void): void {
 | 
			
		||||
    // check if token is valid and set
 | 
			
		||||
    if (apiToken === '' || expireSeconds <= new Date().getTime() / 1000) {
 | 
			
		||||
        refreshAPIToken(() => {
 | 
			
		||||
            callback();
 | 
			
		||||
        fetch('/token', {method: 'POST', body: formData}).then((response) =>
 | 
			
		||||
            response.json().then((result: APIToken) => {
 | 
			
		||||
                if (result.error) {
 | 
			
		||||
                    callFuncQue(result.error);
 | 
			
		||||
                    return;
 | 
			
		||||
                }
 | 
			
		||||
                // set api token
 | 
			
		||||
                apiToken = result.access_token;
 | 
			
		||||
                // set expire time
 | 
			
		||||
                expireSeconds = new Date().getTime() / 1000 + result.expires_in;
 | 
			
		||||
                // setTokenCookie(apiToken, expireSeconds);
 | 
			
		||||
                tokenStore.setToken({accessToken: apiToken, expireTime: expireSeconds, tokenType: '', scope: ''});
 | 
			
		||||
                // call all handlers and release flag
 | 
			
		||||
                callFuncQue('');
 | 
			
		||||
            })
 | 
			
		||||
        );
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    export function apiTokenValid(): boolean {
 | 
			
		||||
        // check if a cookie with token is available
 | 
			
		||||
        // const token = getTokenCookie();
 | 
			
		||||
        const tmptoken = tokenStore.loadToken();
 | 
			
		||||
        if (tmptoken !== null) {
 | 
			
		||||
            // check if token is at least valid for the next minute
 | 
			
		||||
            if (tmptoken.expireTime > new Date().getTime() / 1000 + 60) {
 | 
			
		||||
                apiToken = tmptoken.accessToken;
 | 
			
		||||
                expireSeconds = tmptoken.expireTime;
 | 
			
		||||
 | 
			
		||||
                return true;
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * call all qued callbacks
 | 
			
		||||
     */
 | 
			
		||||
    function callFuncQue(error: string): void {
 | 
			
		||||
        // call all pending handlers
 | 
			
		||||
        callQue.map((func) => {
 | 
			
		||||
            return func(error);
 | 
			
		||||
        });
 | 
			
		||||
    } else {
 | 
			
		||||
        callback();
 | 
			
		||||
        // reset pending que
 | 
			
		||||
        callQue = [];
 | 
			
		||||
        // release flag to be able to start new refresh
 | 
			
		||||
        refreshInProcess = false;
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * check if api token is valid -- if not request new one
 | 
			
		||||
     * when finished call callback
 | 
			
		||||
     * @param callback function to be called afterwards
 | 
			
		||||
     */
 | 
			
		||||
    export function checkAPITokenValid(callback: (mytoken: string) => void): void {
 | 
			
		||||
        // check if token is valid and set
 | 
			
		||||
        if (apiToken === '' || expireSeconds <= new Date().getTime() / 1000) {
 | 
			
		||||
            console.log('token not valid...');
 | 
			
		||||
            refreshAPIToken(() => {
 | 
			
		||||
                callback(apiToken);
 | 
			
		||||
            });
 | 
			
		||||
        } else {
 | 
			
		||||
            callback(apiToken);
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -186,13 +149,13 @@ export function callAPI<T>(
 | 
			
		||||
    callback: (_: T) => void,
 | 
			
		||||
    errorcallback: (_: string) => void = (_: string): void => {}
 | 
			
		||||
): void {
 | 
			
		||||
    checkAPITokenValid(() => {
 | 
			
		||||
    token.checkAPITokenValid((mytoken) => {
 | 
			
		||||
        fetch(APIPREFIX + apinode, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            body: JSON.stringify(fd),
 | 
			
		||||
            headers: new Headers({
 | 
			
		||||
                'Content-Type': 'application/json',
 | 
			
		||||
                Authorization: 'Bearer ' + apiToken
 | 
			
		||||
                Authorization: 'Bearer ' + mytoken
 | 
			
		||||
            })
 | 
			
		||||
        })
 | 
			
		||||
            .then((response) => {
 | 
			
		||||
@@ -252,13 +215,13 @@ export function callApiUnsafe<T>(
 | 
			
		||||
 * @param callback the callback with PLAIN text reply from backend
 | 
			
		||||
 */
 | 
			
		||||
export function callAPIPlain(apinode: APINode, fd: ApiBaseRequest, callback: (_: string) => void): void {
 | 
			
		||||
    checkAPITokenValid(() => {
 | 
			
		||||
    token.checkAPITokenValid((mytoken) => {
 | 
			
		||||
        fetch(APIPREFIX + apinode, {
 | 
			
		||||
            method: 'POST',
 | 
			
		||||
            body: JSON.stringify(fd),
 | 
			
		||||
            headers: new Headers({
 | 
			
		||||
                'Content-Type': 'application/json',
 | 
			
		||||
                Authorization: 'Bearer ' + apiToken
 | 
			
		||||
                Authorization: 'Bearer ' + mytoken
 | 
			
		||||
            })
 | 
			
		||||
        }).then((response) =>
 | 
			
		||||
            response.text().then((result) => {
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										48
									
								
								src/utils/TokenStore/CookieTokenStore.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								src/utils/TokenStore/CookieTokenStore.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,48 @@
 | 
			
		||||
import {Token, TokenStore} from './TokenStore';
 | 
			
		||||
 | 
			
		||||
export class CookieTokenStore extends TokenStore {
 | 
			
		||||
    loadToken(): Token | null {
 | 
			
		||||
        const token = this.decodeCookie('token');
 | 
			
		||||
        const expireInString = this.decodeCookie('token_expire');
 | 
			
		||||
        const expireIn = parseInt(expireInString, 10);
 | 
			
		||||
 | 
			
		||||
        if (expireIn !== 0 && token !== '') {
 | 
			
		||||
            return {accessToken: token, expireTime: expireIn, scope: '', tokenType: ''};
 | 
			
		||||
        } else {
 | 
			
		||||
            return null;
 | 
			
		||||
        }
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * set the cookie for the currently gotten token
 | 
			
		||||
     * @param token the token to set
 | 
			
		||||
     */
 | 
			
		||||
    setToken(token: Token): void {
 | 
			
		||||
        let d = new Date();
 | 
			
		||||
        d.setTime(token.expireTime * 1000);
 | 
			
		||||
        console.log('token set' + d.toUTCString());
 | 
			
		||||
        let expires = 'expires=' + d.toUTCString();
 | 
			
		||||
        document.cookie = 'token=' + token.accessToken + ';' + expires + ';path=/';
 | 
			
		||||
        document.cookie = 'token_expire=' + token.expireTime + ';' + expires + ';path=/';
 | 
			
		||||
    }
 | 
			
		||||
 | 
			
		||||
    /**
 | 
			
		||||
     * decode a simple cookie with key specified
 | 
			
		||||
     * @param key cookie key
 | 
			
		||||
     */
 | 
			
		||||
    decodeCookie(key: string): string {
 | 
			
		||||
        let name = key + '=';
 | 
			
		||||
        let decodedCookie = decodeURIComponent(document.cookie);
 | 
			
		||||
        let ca = decodedCookie.split(';');
 | 
			
		||||
        for (let i = 0; i < ca.length; i++) {
 | 
			
		||||
            let c = ca[i];
 | 
			
		||||
            while (c.charAt(0) === ' ') {
 | 
			
		||||
                c = c.substring(1);
 | 
			
		||||
            }
 | 
			
		||||
            if (c.indexOf(name) === 0) {
 | 
			
		||||
                return c.substring(name.length, c.length);
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        return '';
 | 
			
		||||
    }
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										11
									
								
								src/utils/TokenStore/TokenStore.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/utils/TokenStore/TokenStore.ts
									
									
									
									
									
										Normal file
									
								
							@@ -0,0 +1,11 @@
 | 
			
		||||
export interface Token {
 | 
			
		||||
    accessToken: string;
 | 
			
		||||
    expireTime: number; // second time when token will be invalidated
 | 
			
		||||
    scope: string;
 | 
			
		||||
    tokenType: string;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export abstract class TokenStore {
 | 
			
		||||
    abstract loadToken(): Token | null;
 | 
			
		||||
    abstract setToken(token: Token): void;
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user