import Router from 'vue-router' ; import Vue from 'vue' ; import { UserManagerSettings , Log , Logger , User , UserManager , Profile , WebStorageStateStore , UserManagerEvents } from 'oidc-client' ; /**
 * Indicates the sign in behavior.
 */ export enum SignInType { /**
     * Uses the main browser window to do sign-in.
     */ Window , /**
     * Uses a popup window to do sign-in.
     */ Popup } /**
 * Logging level values used by createOidcAuth().
 */ export enum LogLevel { /**
     * No logs messages.
     */ None = 0 , /**
     * Only error messages.
     */ Error = 1 , /**
     * Error and warning messages.
     */ Warn = 2 , /**
     * Error, warning, and info messages.
     */ Info = 3 , /**
     * Everything.
     */ Debug = 4 } /**
 * A wrapper on oidc-client with vue support.
 */ export interface OidcAuth { /**
     * Original app url used to create this instance.
     */ readonly appUrl : string ; /**
     * Name of this oidc authentication instance.
     * Use in a route's meta:{authName} property to protect that route.
     */ readonly authName : string ; /**
     * Gets whether the user is authenticated.
     */ readonly isAuthenticated : boolean ; /**
     * Gets the API access token if applicable.
     */ readonly accessToken : string ; /**
     * Gets the user claims if authenticated.
     */ readonly userProfile : Profile ; /**
     * Gets the auth events provided by oidc-client.
     */ readonly events : UserManagerEvents ; /**
     * Required call before all the properties are reliably initialized.
     * Should be called and waited on before starting the root Vue instance.
     */ startup ( ) : Promise < boolean > ; param router useRouter ( router ) : void ; param args signIn ( args ? ) : Promise < User | void > ; param args signOut ( args ? ) : Promise < void > ; /**
     * Enables silent renew.
     */ startSilentRenew ( ) : void ; /**
     * Disables silent renew.
     */ stopSilentRenew ( ) : void ; } param authName param defaultSignInType param appUrl param oidcConfig param logger param logLevel export function createOidcAuth ( authName , defaultSignInType , appUrl , oidcConfig , logger ? , logLevel ? ) { if ( ! authName ) { throw new Error ( 'Auth name is required.' ) ; } if ( defaultSignInType !== SignInType . Window && defaultSignInType !== SignInType . Popup ) { throw new Error ( 'Only window or popup are valid default signin types.' ) ; } if ( ! appUrl ) { throw new Error ( 'App base url is required.' ) ; } if ( ! oidcConfig ) { throw new Error ( 'No config provided to oidc auth.' ) ; } Log . logger = logger || console ; Log . level = logLevel || LogLevel . Error ; const nameSlug = slugify ( authName ) ; const config = { response_type : 'id_token' , scope : 'openid profile' , automaticSilentRenew : true , userStore : new WebStorageStateStore ( { store : sessionStorage } ) , post_logout_redirect_uri : appUrl , redirect_uri : `${ appUrl }auth/signinwin/${ nameSlug }` , popup_post_logout_redirect_uri : `${ appUrl }auth/signoutpop/${ nameSlug }` , popup_redirect_uri : `${ appUrl }auth/signinpop/${ nameSlug }` , silent_redirect_uri : `${ appUrl }auth/signinsilent/${ nameSlug }` , ... oidcConfig } ; Log . debug ( `Creating new oidc auth as ${ authName }` ) ; const mgr = new UserManager ( config ) ; let _inited = false ; const auth = new Vue ( { data ( ) { return { user : null as User | null , myRouter : null as Router | null } ; } , computed : { appUrl ( ) { return appUrl ; } , authName ( ) { return authName ; } , isAuthenticated ( ) { return ! ! this . user && ! this . user . expired ; } , accessToken ( ) { return ! ! this . user && ! this . user . expired ? this . user . access_token : '' ; } , userProfile ( ) { return ! ! this . user && ! this . user . expired ? this . user . profile : { iss : '' , sub : '' , aud : '' , exp : 0 , iat : 0 } ; } , events ( ) { return mgr . events ; } } , methods : { startup ( ) { let isCB = false ; if ( matchesPath ( config . popup_redirect_uri ) ) { Log . debug ( `${ authName } Popup signin callback` ) ; mgr . signinPopupCallback ( ) ; isCB = true ; } else if ( matchesPath ( config . silent_redirect_uri ) ) { Log . debug ( `${ authName } Silent signin callback` ) ; mgr . signinSilentCallback ( ) ; isCB = true ; } else if ( matchesPath ( config . popup_post_logout_redirect_uri ) ) { Log . debug ( `${ authName } Popup logout callback` ) ; mgr . signoutPopupCallback ( ) ; isCB = true ; } if ( isCB ) return Promise . resolve ( false ) ; if ( _inited ) { return Promise . resolve ( true ) ; } else { return mgr . getUser ( ) . then ( test => { _inited = true ; if ( test && ! test . expired ) { this . user = test ; } return true ; } ) . catch ( err => { Log . warn ( `Auth startup err = ${ err }` ) ; return false ; } ) ; } } , useRouter ( router ) { this . myRouter = router ; router . beforeEach ( ( to , from , next ) => { if ( to . matched . some ( record => record . meta . authName === this . authName ) ) { if ( this . isAuthenticated ) { Log . debug ( `${ authName } auth authenticated user entering protected route ${ to . fullPath }` ) ; next ( ) ; } else { Log . debug ( `${ authName } auth anon user entering protected route ${ to . fullPath }` ) ; signInReal ( defaultSignInType , { state : { to } } ) . then ( ( ) => { if ( defaultSignInType === SignInType . Window ) { next ( false ) ; } else { next ( ) ; } } ) . catch ( ( ) => next ( false ) ) ; } } else { next ( ) ; } } ) ; if ( config . redirect_uri ) { const vroutePath = '/' + getUrlPath ( config . redirect_uri ) . substring ( ( router . options . base || '/' ) . length ) ; router . addRoute ( { path : vroutePath , name : `signinwin-${ nameSlug }` , component : { render : h => h ( 'div' ) , created ( ) { mgr . signinRedirectCallback ( ) . then ( data => { Log . debug ( `${ authName } Window signin callback success` , data ) ; const redirect = data . state ? data . state . to : null ; if ( router ) router . replace ( redirect || '/' ) ; else window . location . href = appUrl ; } ) . catch ( err => { Log . error ( `${ authName } Window signin callback error` , err ) ; if ( router ) router . replace ( '/' ) ; else window . location . href = appUrl ; } ) ; } } } ) ; } } , signIn ( args ? ) { return signInReal ( defaultSignInType , args ) ; } , signOut ( args ? ) { if ( defaultSignInType === SignInType . Popup ) { const router = this . myRouter ; return mgr . signoutPopup ( args ) . then ( ( ) => { redirectAfterSignout ( router ) ; } ) . catch ( ( ) => { redirectAfterSignout ( router ) ; } ) ; } return mgr . signoutRedirect ( args ) ; } , startSilentRenew ( ) { mgr . startSilentRenew ( ) ; } , stopSilentRenew ( ) { mgr . stopSilentRenew ( ) ; } } } ) ; function signInIfNecessary ( ) { if ( auth . myRouter ) { const current = auth . myRouter . currentRoute ; if ( current && current . meta . authName === authName ) { Log . debug ( `${ authName } auth page re-signin with ${ defaultSignInType }` ) ; signInReal ( defaultSignInType , { state : { current } } ) . then ( ( ) => { } ) . catch ( ( ) => { setTimeout ( signInIfNecessary , 5000 ) ; } ) ; } } } function signInReal ( type , args ? ) { switch ( type ) { case SignInType . Popup : return mgr . signinPopup ( args ) ; } return mgr . signinRedirect ( args ) ; } function redirectAfterSignout ( router ) { if ( router ) { const current = router . currentRoute ; if ( current && current . meta . authName === authName ) { router . replace ( '/' ) ; return ; } } if ( appUrl ) window . location . href = appUrl ; } /**
     * Translates user manager events to vue events and perform default actions
     * if necessary.
     */ function handleManagerEvents ( ) { mgr . events . addUserLoaded ( user => { auth . user = user ; } ) ; mgr . events . addUserUnloaded ( ( ) => { auth . user = null ; Log . debug ( `${ auth . authName } auth user unloaded` ) ; signInIfNecessary ( ) ; } ) ; mgr . events . addAccessTokenExpired ( ( ) => { Log . debug ( `${ auth . authName } auth token expired, user is authenticated=${ auth . isAuthenticated }` ) ; auth . user = null ; signInIfNecessary ( ) ; } ) ; mgr . events . addSilentRenewError ( e => { Log . debug ( `${ auth . authName } auth silent renew error ${ e }` ) ; if ( auth . isAuthenticated ) { setTimeout ( ( ) => { Log . debug ( `${ auth . authName } auth silent renew retry` ) ; mgr . signinSilent ( ) ; } , 5000 ) ; } else { signInIfNecessary ( ) ; } } ) ; mgr . events . addUserSignedOut ( ( ) => { Log . debug ( `${ auth . authName } auth user signed out` ) ; auth . user = null ; signInIfNecessary ( ) ; } ) ; } handleManagerEvents ( ) ; return auth ; } param url returns function getUrlPath ( url ) { const a = document . createElement ( 'a' ) ; a . href = url ; let p = a . pathname ; if ( p [ 0 ] !== '/' ) p = '/' + p ; return p ; } param { String } testUrl function matchesPath ( testUrl ) { return ( window . location . pathname . toLocaleLowerCase ( ) === getUrlPath ( testUrl ) . toLocaleLowerCase ( ) ) ; } function slugify ( str ) { str = str . replace ( /^\s+|\s+$/g , '' ) ; str = str . toLowerCase ( ) ; const from = 'ãàáäâáº½èéëêìíïîõòóöôùúüûñç·/_,:;' ; const to = 'aaaaaeeeeeiiiiooooouuuunc------' ; for ( let i = 0 , l = from . length ; i < l ; i ++ ) { str = str . replace ( new RegExp ( from . charAt ( i ) , 'g' ) , to . charAt ( i ) ) ; } str = str . replace ( /[^a-z0-9 -]/g , '' ) . replace ( /\s+/g , '-' ) . replace ( /-+/g , '-' ) ; return str ; }