-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: use OidcBroker for sign-out functionality
- Loading branch information
Showing
7 changed files
with
183 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/* eslint-disable no-undef */ | ||
|
||
import {Auth} from '../../../src/libs/auth/auth'; | ||
import {Config} from '../../../src/libs/config'; | ||
import {GoogleIS} from '../../../src/libs/googleIS'; | ||
import {Storage} from '../../../src/libs/storage'; | ||
|
||
describe('Auth', function () { | ||
it('Sign Out Clears the session when called', async function () { | ||
cy.stub(Config, 'getGoogleClientId').returns('12345'); | ||
cy.stub(GoogleIS, 'revokeAccessToken'); | ||
await Auth.initialize(); | ||
Storage.setUserIsLogged(true); | ||
expect(Storage.userIsLogged()).to.be.true; | ||
await Auth.signOut(); | ||
expect(Storage.userIsLogged()).to.be.false; | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
/* eslint-disable no-undef */ | ||
|
||
import {Config} from '../../../src/libs/config'; | ||
import {GoogleIS} from '../../../src/libs/googleIS'; | ||
import {OidcBroker} from '../../../src/libs/auth/oidcBroker'; | ||
|
||
describe('OidcBroker', function () { | ||
it('Sign Out calls Oidc UserManager sign-out functions', async function () { | ||
cy.stub(Config, 'getGoogleClientId').returns('12345'); | ||
cy.stub(GoogleIS, 'revokeAccessToken'); | ||
await OidcBroker.initialize(); | ||
const um = OidcBroker.getUserManager(); | ||
cy.spy(um, 'removeUser').as('removeUser'); | ||
cy.spy(um, 'clearStaleState').as('clearStaleState'); | ||
await OidcBroker.signOut(); | ||
expect(um.removeUser).to.be.called; | ||
expect(um.clearStaleState).to.be.called; | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
import axios from 'axios'; | ||
import {Config} from '../config'; | ||
|
||
export interface OAuthConfig { | ||
clientId: string; | ||
authorityEndpoint: string; | ||
} | ||
|
||
export const OAuth2 = { | ||
getConfig: async (): Promise<OAuthConfig> => getConfig(), | ||
}; | ||
|
||
const getConfig = async (): Promise<OAuthConfig> => { | ||
const configUrl = `${await Config.getApiUrl()}/oauth2/configuration`; | ||
return (await axios.get(configUrl)).data; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/* | ||
This file should abstract out the oidcBroker actions | ||
and implement DUOS specific auth login (signIn, signOut, etc.) | ||
*/ | ||
import {OidcBroker, OidcUser} from './oidcBroker'; | ||
import {Storage} from './../storage'; | ||
import { UserManager } from 'oidc-client-ts'; | ||
|
||
export const Auth = { | ||
initialize: async (): Promise<void> => { | ||
await OidcBroker.initialize(); | ||
const oidcUser: OidcUser | null = await OidcBroker.getUser(); | ||
const um: UserManager = OidcBroker.getUserManager(); | ||
// UserManager events. | ||
// For details of each event, see https://authts.github.io/oidc-client-ts/classes/UserManagerEvents.html | ||
// eslint-disable-next-line no-unused-vars | ||
um.events.addUserLoaded((_: OidcUser) => { | ||
//TODO: DUOS-3072 Add metrics for user loaded | ||
}); | ||
um.events.addAccessTokenExpiring((): void => { | ||
//TODO: DUOS-3082 Add an alert that session will expire soon | ||
}); | ||
um.events.addAccessTokenExpired((): void => { | ||
Auth.signOut(); | ||
//TODO: DUOS-3082 Add an alert that session has expired | ||
}); | ||
if (oidcUser !== null) { | ||
Storage.setUserIsLogged(true); | ||
} else { | ||
await Auth.signOut(); | ||
} | ||
}, | ||
signOut: async () => { | ||
Storage.clearStorage(); | ||
Storage.setUserIsLogged(false); | ||
await OidcBroker.signOut(); | ||
}, | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
import { | ||
IdTokenClaims, | ||
OidcMetadata, | ||
User, | ||
UserManager, | ||
UserManagerSettings, | ||
WebStorageStateStore | ||
} from 'oidc-client-ts'; | ||
|
||
import {Config} from '../config'; | ||
import {OAuth2, OAuthConfig} from '../ajax/OAuth2'; | ||
import {GoogleIS} from '../googleIS'; | ||
|
||
export interface B2cIdTokenClaims extends IdTokenClaims { | ||
email_verified?: boolean; | ||
idp?: string; | ||
idp_access_token?: string; | ||
tid?: string; | ||
ver?: string; | ||
} | ||
|
||
export interface OidcUser extends User { | ||
profile: B2cIdTokenClaims; | ||
} | ||
|
||
type OidcUserManager = UserManager; | ||
|
||
let config: OAuthConfig | null = null; | ||
let userManagerSettings: UserManagerSettings | null = null; | ||
let userManager: UserManager | null = null; | ||
|
||
const generateOidcUserManagerSettings = async ( | ||
config: OAuthConfig | ||
): Promise<UserManagerSettings> => { | ||
const metadata: Partial<OidcMetadata> = { | ||
authorization_endpoint: `${await Config.getApiUrl()}/oauth2/authorize`, | ||
token_endpoint: `${await Config.getApiUrl()}/oauth2/token`, | ||
}; | ||
return { | ||
authority: config.authorityEndpoint, | ||
client_id: config.clientId, | ||
popup_redirect_uri: `${window.origin}/redirect-from-oauth`, | ||
silent_redirect_uri: `${window.origin}/redirect-from-oauth-silent`, | ||
metadata, | ||
prompt: 'consent login', | ||
scope: 'openid email profile', | ||
stateStore: new WebStorageStateStore({store: window.localStorage}), | ||
userStore: new WebStorageStateStore({store: window.localStorage}), | ||
automaticSilentRenew: true, | ||
// Time before access token expires when access token expiring event is fired | ||
accessTokenExpiringNotificationTimeInSeconds: 330, | ||
includeIdTokenInSilentRenew: true, | ||
extraQueryParams: {access_type: 'offline'}, | ||
redirect_uri: '', // this field is not being used currently, but is expected from UserManager | ||
}; | ||
}; | ||
|
||
export const OidcBroker = { | ||
initialize: async (): Promise<void> => { | ||
config = await OAuth2.getConfig(); | ||
userManagerSettings = await generateOidcUserManagerSettings(config); | ||
userManager = new UserManager(userManagerSettings); | ||
}, | ||
getUserManager: (): UserManager => { | ||
if (userManager === null) { | ||
throw new Error('Cannot retrieve userManager before OidcBroker is initialized'); | ||
} | ||
return userManager; | ||
}, | ||
getUserManagerSettings: (): UserManagerSettings => { | ||
if (userManagerSettings === null) { | ||
throw new Error('Cannot retrieve userManagerSettings before OidcBroker is initialized'); | ||
} | ||
return userManagerSettings; | ||
}, | ||
getUser: async (): Promise<OidcUser | null> => { | ||
const userManager: OidcUserManager = new UserManager(OidcBroker.getUserManagerSettings()); | ||
return await userManager.getUser(); | ||
}, | ||
signOut: async (): Promise<void> => { | ||
// TODO: When sign-in is migrated to OIDC, we can remove GoogleIS functionality | ||
const clientId = await Config.getGoogleClientId(); | ||
await GoogleIS.revokeAccessToken(clientId); | ||
const um: UserManager = OidcBroker.getUserManager(); | ||
await um.removeUser(); | ||
await um.clearStaleState(); | ||
} | ||
}; |