UNPKG

@pecureo/anvil-stream

Version:

Stream-Based Authorization framework for Anvil Connect

128 lines (108 loc) 3.2 kB
import { Observable, interval } from 'rxjs'; import { distinctUntilChanged, map, shareReplay, startWith, tap } from 'rxjs/operators'; import { Anvil } from './anvil'; import { IAuthorizationOptions, Issuer } from './issuer'; import { Scopes } from './scopes'; interface ICommonClaims { aud: string; exp: number; iat: number; iss: string; sub: string; } export interface IAccessClaims extends ICommonClaims { jti: string; scope: string; } export interface IIdClaims extends ICommonClaims { at_hash: string; nonce: string; } export interface ISessionData { access_claims: IAccessClaims; access_token: string; id_claims: IIdClaims; id_token: string; expires_in: string; token_type: string; options?: IAuthorizationOptions; session_state?: string; state?: string; user_info?: Object; } export class Session { public access_token: string; public expiration_date: Date; public expired$: Observable<boolean>; public scopes: Scopes; public subject: string; constructor(private issuer: Issuer, private session_data?: ISessionData) { if(session_data) { this.setTokenData( session_data.access_token, session_data.id_claims.exp, session_data.access_claims.scope, session_data.access_claims.sub ); } else { this.setTokenData('', 0, '', ''); } this.expired$ = interval(0.5).pipe( map(() => this.expired), distinctUntilChanged(), startWith(this.expired), // This is only acceptable as long as the `distinctUntilChanged` call is // in this chain! If it is removed `abort` will keep being called every // emission of the `Observable` once the `Session` expires tap(expired => { if(expired) { this.abort(); } }), shareReplay(1) ); } abort() { if(this.session_data === Anvil.session) { if(this.session_data.options.force_path) { // As the regular reset method only removes the cookie containing the // secret for the current path, the one for the forced path has to be // removed manually let expires = new Date(0).toUTCString(); let path = decodeURIComponent(this.session_data.options.force_path); document.cookie = `anvil.connect=; expires=${expires}; path=${path}`; } Anvil.reset(); } // Force the session to be 'expired' as it's no longer valid when aborted this.setTokenData('', 0, '', ''); this.issuer.removeSession(this); } get expired(): boolean { return this.expiration_date <= new Date(); } get persisted(): boolean { return this.issuer.persisted_session === this; } get user_info() { if(this.session_data) { return this.session_data.user_info; } else { return null; } } private setTokenData( access_token: string, expiration_date_s: number, scopes: string, subject: string ) { this.access_token = access_token; // Token expirations are in seconds, JavaScript operates using milliseconds this.expiration_date = new Date(expiration_date_s * 1000); this.scopes = new Scopes(scopes); this.subject = subject; } }