From db33dbc259cb262016757b2be955ca78dd7d7d70 Mon Sep 17 00:00:00 2001 From: rushtong Date: Mon, 9 Sep 2024 11:40:47 -0400 Subject: [PATCH 01/27] feat: sign-in via OIDC --- src/App.jsx | 9 +- src/components/DuosHeader.jsx | 7 +- src/components/SignInButton.tsx | 151 +++++++++++++------------------- src/index.tsx | 4 + src/libs/ajax/Metrics.js | 7 +- src/libs/auth/auth.ts | 18 +++- src/libs/auth/oidcBroker.ts | 18 +++- src/libs/config.js | 6 +- src/libs/storage.js | 9 +- src/pages/BackgroundSignIn.jsx | 2 +- 10 files changed, 113 insertions(+), 118 deletions(-) diff --git a/src/App.jsx b/src/App.jsx index 376d06fba..455066f3a 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -65,18 +65,13 @@ function App() { setUserIsLogged(); }); - const signIn = async () => { - await Storage.setUserIsLogged(true); - await setIsLoggedIn(true); - }; - return (
- + - +
diff --git a/src/components/DuosHeader.jsx b/src/components/DuosHeader.jsx index a8f487a29..2596cbbed 100644 --- a/src/components/DuosHeader.jsx +++ b/src/components/DuosHeader.jsx @@ -140,7 +140,6 @@ export const headerTabsConfig = [ const NavigationTabsComponent = (props) => { const { - onSignIn, history, orientation, makeNotifications, @@ -230,7 +229,6 @@ const NavigationTabsComponent = (props) => { } @@ -250,7 +248,6 @@ const NavigationTabsComponent = (props) => {
} @@ -338,7 +335,7 @@ const navbarDuosText = { }; const DuosHeader = (props) => { - const {location, classes, onSignIn, history} = props; + const {location, classes, history} = props; const [state, setState] = useState({ showSupportRequestModal: false, hover: false, @@ -506,7 +503,6 @@ const DuosHeader = (props) => {
{/* Standard navbar for medium sized displays and higher (pre-existing navbar) */} { onClose={() => toggleDrawer(false)} > { - const [clientId, setClientId] = useState(''); const [errorDisplay, setErrorDisplay] = useState({}); - const { onSignIn, history, customStyle } = props; - - useEffect(() => { - // Using `isSubscribed` resolves the - // "To fix, cancel all subscriptions and asynchronous tasks in a useEffect cleanup function." warning - let isSubscribed = true; - const init = async () => { - if (isSubscribed) { - const googleClientId = await Config.getGoogleClientId(); - setClientId(googleClientId); - if (window.google !== undefined && GoogleIS.client === null) { - await GoogleIS.initTokenClient(googleClientId, onSuccess, onFailure); - } - } - ReactTooltip.rebuild(); - }; - init(); - return () => { - isSubscribed = false; - }; - }); + const {onSignIn, history} = props; + const [isLoading, setIsLoading] = useState(false); // Utility function called in the normal success case and in the undocumented 409 case - // Check for ToS Acceptance - redirect user if not set. + // Check for ToS Acceptance - sign out and redirect user if not set. const checkToSAndRedirect = async (redirectPath: string | null) => { // Check if the user has accepted ToS yet or not: - const user = await User.getMe(); - if (!user.roles) { - await StackdriverReporter.report('roles not found for user: ' + user.email); - } - setUserRoleStatuses(user, Storage); - await onSignIn(); const userStatus = await ToS.getStatus(); - const { tosAccepted } = userStatus; + const {tosAccepted} = userStatus; if (!isEmpty(userStatus) && !tosAccepted) { await Auth.signOut(); if (isNil(redirectPath)) { @@ -90,22 +56,31 @@ export const SignInButton = (props: SignInButtonProps) => { } } else { if (isNil(redirectPath)) { - Navigation.back(user, history); + Navigation.back(Storage.getCurrentUser(), history); } else { history.push(redirectPath); } } }; - const onSuccess = async (response: GoogleSuccessPayload) => { - Storage.setGoogleData(response); + // eslint-disable-next-line no-unused-vars + const onSuccess = async (_: OidcUser) => { + const duosUser: DuosUserResponse = await User.getMe(); + Storage.setCurrentUser(duosUser); + setUserRoleStatuses(duosUser, Storage); + if (!duosUser.roles) { + await StackdriverReporter.report('roles not found for user: ' + duosUser.email); + } const redirectTo = getRedirectTo(); const shouldRedirect = shouldRedirectTo(redirectTo); Storage.setAnonymousId(); + await Metrics.identify(Storage.getAnonymousId()); + await Metrics.syncProfile(); + await Metrics.captureEvent(eventList.userSignIn); try { - await attemptSignInCheckToSAndRedirect(redirectTo, shouldRedirect); + await checkToSAndRedirect(shouldRedirect ? redirectTo : null); } catch (error) { await handleRegistration(redirectTo, shouldRedirect); } @@ -118,13 +93,6 @@ export const SignInButton = (props: SignInButtonProps) => { const shouldRedirectTo = (page: string): boolean => page !== '/' && page !== '/home'; - const attemptSignInCheckToSAndRedirect = async (redirectTo:string, shouldRedirect: boolean) => { - await checkToSAndRedirect(shouldRedirect ? redirectTo : null); - Metrics.identify(Storage.getAnonymousId()); - Metrics.syncProfile(); - Metrics.captureEvent(eventList.userSignIn); - }; - const handleRegistration = async (redirectTo: string, shouldRedirect: boolean) => { try { await registerAndRedirectNewUser(redirectTo, shouldRedirect); @@ -137,9 +105,6 @@ export const SignInButton = (props: SignInButtonProps) => { const registeredUser = await User.registerUser(); setUserRoleStatuses(registeredUser, Storage); await onSignIn(); - Metrics.identify(Storage.getAnonymousId()); - Metrics.syncProfile(); - Metrics.captureEvent(eventList.userRegister); history.push(`/tos_acceptance${shouldRedirect ? `?redirectTo=${redirectTo}` : ''}`); }; @@ -148,13 +113,13 @@ export const SignInButton = (props: SignInButtonProps) => { switch (status) { case 400: - setErrorDisplay({ show: true, title: 'Error', msg: JSON.stringify(error) }); + setErrorDisplay({show: true, title: 'Error', msg: JSON.stringify(error)}); break; case 409: await handleConflictError(redirectTo, shouldRedirect); break; default: - setErrorDisplay({ show: true, title: 'Error', msg: 'Unexpected error, please try again' }); + setErrorDisplay({show: true, title: 'Error', msg: 'Unexpected error, please try again'}); break; } }; @@ -173,46 +138,56 @@ export const SignInButton = (props: SignInButtonProps) => { setErrorDisplay( Sign-in cancelled ... - + {'loading'}/ ); setTimeout(() => { setErrorDisplay({}); }, 2000); } else { - setErrorDisplay({ title: response.error, description: response.details }); + setErrorDisplay({title: response.error, description: response.details}); } }; - const spinnerOrSignInButton = () => { - return (clientId === '' - ? Spinner() - : (
- {isNil(customStyle) - ? GoogleIS.signInButton(clientId, onSuccess, onFailure) - : } - {isNil(customStyle) && + const loadingElement = (): React.JSX.Element => { + return ( + + {'loading'}/ + + ); + }; + + const signInElement = (): React.JSX.Element => { + return ( +
+ - } - -
)); + +
+ ); }; return (
{isEmpty(errorDisplay) ?
- {spinnerOrSignInButton()} + {signInElement()}
:
{ unregister(); await Auth.initialize(); + if (window.location.pathname.startsWith('/redirect-from-oauth')) { + await OidcBroker.getUserManager().signinPopupCallback(window.location.href); + } const container = document.getElementById('root'); const root = createRoot(container!); root.render(); diff --git a/src/libs/ajax/Metrics.js b/src/libs/ajax/Metrics.js index 678b09446..3838e08d3 100644 --- a/src/libs/ajax/Metrics.js +++ b/src/libs/ajax/Metrics.js @@ -3,6 +3,7 @@ import { getDefaultProperties } from '@databiosphere/bard-client'; import { Storage } from '../storage'; import { getBardApiUrl } from '../ajax'; +import {Token} from '../config'; export const Metrics = { captureEvent: (event, details, signal) => captureEventFn(event, details, signal).catch(() => { }), @@ -42,7 +43,7 @@ const captureEventFn = async (event, details = {}, signal) => { method: 'POST', url: `${await getBardApiUrl()}/api/event`, data: body, - headers: isRegistered ? { Authorization: `Bearer ${Storage.getGoogleData()?.accessToken}` } : undefined, + headers: isRegistered ? { Authorization: `Bearer ${Token.getToken()}` } : undefined, signal, }; @@ -59,7 +60,7 @@ const syncProfile = async (signal) => { const config = { method: 'POST', url: `${await getBardApiUrl()}/api/syncProfile`, - headers: { Authorization: `Bearer ${Storage.getGoogleData()?.accessToken}` }, + headers: { Authorization: `Bearer ${Token.getToken()}` }, signal, }; @@ -80,7 +81,7 @@ const identify = async (anonId, signal) => { method: 'POST', url: `${await getBardApiUrl()}/api/identify`, data: body, - headers: { Authorization: `Bearer ${Storage.getGoogleData()?.accessToken}` }, + headers: { Authorization: `Bearer ${Token.getToken()}` }, signal, }; diff --git a/src/libs/auth/auth.ts b/src/libs/auth/auth.ts index d4fb87870..1db14b3bc 100644 --- a/src/libs/auth/auth.ts +++ b/src/libs/auth/auth.ts @@ -4,9 +4,16 @@ */ import {OidcBroker, OidcUser} from './oidcBroker'; import {Storage} from './../storage'; -import { UserManager } from 'oidc-client-ts'; +import {UserManager} from 'oidc-client-ts'; export const Auth = { + getToken: (): string => { + // In a future ticket, it would be better to make get Token an async function and call + // the UserManager.getUser to get the token. Since authOpts depends on getToken being synchonous + // it would be a lot of places to change the uses of authOpts. + const oidcUser: OidcUser | null = OidcBroker.getUserSync(); + return oidcUser !== null ? oidcUser.access_token : 'token'; + }, initialize: async (): Promise => { await OidcBroker.initialize(); const oidcUser: OidcUser | null = await OidcBroker.getUser(); @@ -30,6 +37,15 @@ export const Auth = { await Auth.signOut(); } }, + signIn: async (popup: boolean): Promise => { + const user: OidcUser | null = await OidcBroker.signIn(popup); + if (user === null) { + throw new Error('signInSilent called before signInPopup'); + } + Storage.setOidcUser(user); + Storage.setUserIsLogged(true); + return user; + }, signOut: async () => { Storage.clearStorage(); Storage.setUserIsLogged(false); diff --git a/src/libs/auth/oidcBroker.ts b/src/libs/auth/oidcBroker.ts index a9deb7055..1c6a7282c 100644 --- a/src/libs/auth/oidcBroker.ts +++ b/src/libs/auth/oidcBroker.ts @@ -1,6 +1,8 @@ import { IdTokenClaims, OidcMetadata, + SigninPopupArgs, + SigninSilentArgs, User, UserManager, UserManagerSettings, @@ -9,7 +11,6 @@ import { import {Config} from '../config'; import {OAuth2, OAuthConfig} from '../ajax/OAuth2'; -import {GoogleIS} from '../googleIS'; export interface B2cIdTokenClaims extends IdTokenClaims { email_verified?: boolean; @@ -77,10 +78,19 @@ export const OidcBroker = { const userManager: OidcUserManager = new UserManager(OidcBroker.getUserManagerSettings()); return await userManager.getUser(); }, + getUserSync: (): OidcUser | null => { + const settings: UserManagerSettings = + OidcBroker.getUserManagerSettings(); + const oidcStorage: string | null = localStorage.getItem( + `oidc.user:${settings.authority}:${settings.client_id}` + ); + return oidcStorage !== null ? User.fromStorageString(oidcStorage) : null; + }, + signIn: async (popup: boolean, args?: SigninPopupArgs | SigninSilentArgs): Promise => { + const um: UserManager = OidcBroker.getUserManager(); + return popup ? await um.signinPopup(args) : await um.signinSilent(args); + }, signOut: async (): Promise => { - // 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(); diff --git a/src/libs/config.js b/src/libs/config.js index 995dc1f7c..ec273db70 100644 --- a/src/libs/config.js +++ b/src/libs/config.js @@ -90,11 +90,9 @@ export const Config = { }), }; -const Token = { +export const Token = { getToken: () => { - return Storage.getGoogleData() !== null ? - Storage.getGoogleData().accessToken : - 'token'; + return Storage.getOidcUser()?.id_token; }, }; diff --git a/src/libs/storage.js b/src/libs/storage.js index 86129781e..20e730f62 100644 --- a/src/libs/storage.js +++ b/src/libs/storage.js @@ -4,6 +4,7 @@ import { v4 as uuid } from 'uuid'; // Storage Variables const CurrentUser = 'CurrentUser'; // System user const GoogleUser = 'Gapi'; // Google user info, including token +const OidcUser = 'OidcUser'; // Google user info, including token const UserIsLogged = 'isLogged'; // User log status flag const UserSettings = 'UserSettings'; // Different user settings for saving statuses in the app const anonymousId = 'anonymousId'; @@ -50,12 +51,12 @@ export const Storage = { sessionStorage.setItem(UserSettings, JSON.stringify(userSettings)); }, - setGoogleData: data => { - sessionStorage.setItem(GoogleUser, JSON.stringify(data)); + setOidcUser: oidcUser => { + sessionStorage.setItem(OidcUser, JSON.stringify(oidcUser)); }, - getGoogleData: () => { - return sessionStorage.getItem(GoogleUser) ? JSON.parse(sessionStorage.getItem(GoogleUser)) : null; + getOidcUser: () => { + return sessionStorage.getItem(OidcUser) ? JSON.parse(sessionStorage.getItem(OidcUser)) : null; }, userIsLogged: () => { diff --git a/src/pages/BackgroundSignIn.jsx b/src/pages/BackgroundSignIn.jsx index ba835ead7..fee15f8fc 100644 --- a/src/pages/BackgroundSignIn.jsx +++ b/src/pages/BackgroundSignIn.jsx @@ -36,7 +36,7 @@ export default function BackgroundSignIn(props) { const performLogin = () => { setLoading(true); - Storage.setGoogleData({ accessToken: accessToken }); + Storage.setOidcUser({ od_token: accessToken }); getUser().then( user => { user = Object.assign(user, setUserRoleStatuses(user, Storage)); From 386d0f2bda54cb1e76b31e2f0e0807919014bd89 Mon Sep 17 00:00:00 2001 From: rushtong Date: Mon, 9 Sep 2024 14:22:36 -0400 Subject: [PATCH 02/27] feat: re-add register metrics --- src/components/SignInButton.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/components/SignInButton.tsx b/src/components/SignInButton.tsx index bebb8b30a..5dace9f55 100644 --- a/src/components/SignInButton.tsx +++ b/src/components/SignInButton.tsx @@ -105,6 +105,9 @@ export const SignInButton = (props: SignInButtonProps) => { const registeredUser = await User.registerUser(); setUserRoleStatuses(registeredUser, Storage); await onSignIn(); + await Metrics.identify(Storage.getAnonymousId()); + await Metrics.syncProfile(); + await Metrics.captureEvent(eventList.userRegister); history.push(`/tos_acceptance${shouldRedirect ? `?redirectTo=${redirectTo}` : ''}`); }; From 456cb7f26815a87920034bc25cff2eff36f31054 Mon Sep 17 00:00:00 2001 From: rushtong Date: Mon, 9 Sep 2024 14:23:22 -0400 Subject: [PATCH 03/27] feat: rm unused --- cypress/component/SignIn/google_is.spec.js | 48 ---------- src/libs/auth/auth.ts | 7 -- src/libs/auth/oidcBroker.ts | 12 +-- src/libs/googleIS.js | 102 --------------------- 4 files changed, 1 insertion(+), 168 deletions(-) delete mode 100644 cypress/component/SignIn/google_is.spec.js delete mode 100644 src/libs/googleIS.js diff --git a/cypress/component/SignIn/google_is.spec.js b/cypress/component/SignIn/google_is.spec.js deleted file mode 100644 index a6d589ea4..000000000 --- a/cypress/component/SignIn/google_is.spec.js +++ /dev/null @@ -1,48 +0,0 @@ -/* eslint-disable no-undef */ - -import {GoogleIS} from '../../../src/libs/googleIS'; - -describe('Google IS Utility', function () { - it('GoogleIS.initTokenClient works with a valid client id', function () { - cy.viewport(600, 300); - cy.readFile('config/alpha.json').then((config) => { - const clientId = config.clientId; - GoogleIS.initTokenClient( - clientId, - () => {}, - () => {}) - .then(() => { - expect(GoogleIS.client).to.not.equal(null); - }); - }); - }); - - it('GoogleIS.requestAccessToken: Cannot be tested as it would trigger an OAuth flow', function () { - expect(true).to.equal(true); - }); - - it('GoogleIS.revokeAccessToken does not fail with empty client or access token', function () { - cy.viewport(600, 300); - cy.readFile('config/alpha.json').then((config) => { - const clientId = config.clientId; - GoogleIS.initTokenClient( - clientId, - () => {}, - () => {}) - .then(() => { - expect(GoogleIS.client).to.not.equal(null); - GoogleIS.revokeAccessToken(clientId).then(() => { - expect(GoogleIS.client).to.equal(null); - expect(GoogleIS.accessToken).to.equal(null); - }); - }); - }); - }); - - it('GoogleIS.signInButton renders a button', function() { - cy.viewport(600, 300); - const button = GoogleIS.signInButton('', () => {}, () => {}); - expect(button).to.not.equal(null); - expect(button.type).to.equal('button'); - }); -}); diff --git a/src/libs/auth/auth.ts b/src/libs/auth/auth.ts index 1db14b3bc..03b1a0f18 100644 --- a/src/libs/auth/auth.ts +++ b/src/libs/auth/auth.ts @@ -7,13 +7,6 @@ import {Storage} from './../storage'; import {UserManager} from 'oidc-client-ts'; export const Auth = { - getToken: (): string => { - // In a future ticket, it would be better to make get Token an async function and call - // the UserManager.getUser to get the token. Since authOpts depends on getToken being synchonous - // it would be a lot of places to change the uses of authOpts. - const oidcUser: OidcUser | null = OidcBroker.getUserSync(); - return oidcUser !== null ? oidcUser.access_token : 'token'; - }, initialize: async (): Promise => { await OidcBroker.initialize(); const oidcUser: OidcUser | null = await OidcBroker.getUser(); diff --git a/src/libs/auth/oidcBroker.ts b/src/libs/auth/oidcBroker.ts index 1c6a7282c..6fc535b8e 100644 --- a/src/libs/auth/oidcBroker.ts +++ b/src/libs/auth/oidcBroker.ts @@ -2,7 +2,6 @@ import { IdTokenClaims, OidcMetadata, SigninPopupArgs, - SigninSilentArgs, User, UserManager, UserManagerSettings, @@ -41,7 +40,6 @@ const generateOidcUserManagerSettings = async ( 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', @@ -78,15 +76,7 @@ export const OidcBroker = { const userManager: OidcUserManager = new UserManager(OidcBroker.getUserManagerSettings()); return await userManager.getUser(); }, - getUserSync: (): OidcUser | null => { - const settings: UserManagerSettings = - OidcBroker.getUserManagerSettings(); - const oidcStorage: string | null = localStorage.getItem( - `oidc.user:${settings.authority}:${settings.client_id}` - ); - return oidcStorage !== null ? User.fromStorageString(oidcStorage) : null; - }, - signIn: async (popup: boolean, args?: SigninPopupArgs | SigninSilentArgs): Promise => { + signIn: async (popup: boolean, args?: SigninPopupArgs): Promise => { const um: UserManager = OidcBroker.getUserManager(); return popup ? await um.signinPopup(args) : await um.signinSilent(args); }, diff --git a/src/libs/googleIS.js b/src/libs/googleIS.js deleted file mode 100644 index 44208759c..000000000 --- a/src/libs/googleIS.js +++ /dev/null @@ -1,102 +0,0 @@ -import React from 'react'; - -/** - * This utility is a wrapper around Google's Identity Services API - * https://developers.google.com/identity/oauth2/web/guides/migration-to-gis#gis-only - * For the purposes of DUOS authentication, our primary need is for an - * Access Token that we can use to communicate with any back end system. - */ - -const SCOPES = ['openid', - 'https://www.googleapis.com/auth/userinfo.profile', - 'https://www.googleapis.com/auth/userinfo.email'].join(' '); - -export const GoogleIS = { - - client: null, - - accessToken: null, - - initTokenClient: async (clientId, onSuccess, onFailure) => { - GoogleIS.client = await window.google.accounts.oauth2.initTokenClient({ - client_id: clientId, - scope: SCOPES, - callback: (tokenResponse) => { - try { - GoogleIS.accessToken = tokenResponse.access_token; - onSuccess({accessToken: GoogleIS.accessToken}); - } catch (e) { - onFailure(e); - } - }, - }); - }, - - requestAccessToken: async (clientId, onSuccess, onFailure) => { - try { - if (GoogleIS.client === null) { - await GoogleIS.initTokenClient(clientId, onSuccess, onFailure); - } - GoogleIS.accessToken = await GoogleIS.client.requestAccessToken(); - } catch (e) { - onFailure(e); - } - }, - - revokeAccessToken: async (clientId) => { - if (GoogleIS.accessToken !== null) { - if (GoogleIS.client === null) { - // We're initializing a client here purely for logout purposes. - await GoogleIS.initTokenClient(clientId, () => {}, () => {}); - } - await window.google.accounts.oauth2.revoke(GoogleIS.accessToken, () => { - // Reset internal state - GoogleIS.client = null; - GoogleIS.accessToken = null; - }); - } - }, - - signInButton: (clientId, onSuccess, onFailure) => - -}; From 9bf40f324799bf03db57e8305fd894d66fa2f7c7 Mon Sep 17 00:00:00 2001 From: rushtong Date: Mon, 9 Sep 2024 14:25:58 -0400 Subject: [PATCH 04/27] feat: fix typo --- src/pages/BackgroundSignIn.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/BackgroundSignIn.jsx b/src/pages/BackgroundSignIn.jsx index fee15f8fc..eb389d409 100644 --- a/src/pages/BackgroundSignIn.jsx +++ b/src/pages/BackgroundSignIn.jsx @@ -36,7 +36,7 @@ export default function BackgroundSignIn(props) { const performLogin = () => { setLoading(true); - Storage.setOidcUser({ od_token: accessToken }); + Storage.setOidcUser({ id_token: accessToken }); getUser().then( user => { user = Object.assign(user, setUserRoleStatuses(user, Storage)); From d79a57efc32baef6d48564dae9f90b1cb7064620 Mon Sep 17 00:00:00 2001 From: rushtong Date: Mon, 9 Sep 2024 14:45:50 -0400 Subject: [PATCH 05/27] feat: configs --- config/alpha.json | 1 - config/base_config.json | 1 - 2 files changed, 2 deletions(-) diff --git a/config/alpha.json b/config/alpha.json index 23cf9da0c..5d91b79ae 100644 --- a/config/alpha.json +++ b/config/alpha.json @@ -5,7 +5,6 @@ "hash": "alpha", "apiUrl": "https://consent.dsde-alpha.broadinstitute.org", "ontologyApiUrl": "https://consent-ontology.dsde-alpha.broadinstitute.org/", - "clientId": "1020846292598-hd801vsmmbhh97vaf6aar17lu0q2evfj.apps.googleusercontent.com", "errorApiKey": "1234567890abcdefghijklmnop", "gaId": "", "profileUrl": "https://profile-dot-broad-shibboleth-prod.appspot.com/dev", diff --git a/config/base_config.json b/config/base_config.json index ad3bb1429..2310c252a 100644 --- a/config/base_config.json +++ b/config/base_config.json @@ -6,7 +6,6 @@ "ontologyApiUrl": "", "terraUrl": "", "tdrApiUrl": "", - "clientId": "", "errorApiKey": "", "nihUrl": "", "gaId": "", From 924b8ee3bd0ffb605ce3e0667fcb03b0526ac369 Mon Sep 17 00:00:00 2001 From: rushtong Date: Mon, 9 Sep 2024 14:46:01 -0400 Subject: [PATCH 06/27] feat: update test --- .../component/SignIn/sign_in_button.spec.js | 18 +++--------------- 1 file changed, 3 insertions(+), 15 deletions(-) diff --git a/cypress/component/SignIn/sign_in_button.spec.js b/cypress/component/SignIn/sign_in_button.spec.js index 7994c0131..48b5b1e6d 100644 --- a/cypress/component/SignIn/sign_in_button.spec.js +++ b/cypress/component/SignIn/sign_in_button.spec.js @@ -3,9 +3,8 @@ import React from 'react'; import { mount } from 'cypress/react'; import SignInButton from '../../../src/components/SignInButton'; -import { Config } from '../../../src/libs/config'; -const signInText = 'Sign-in'; +const signInText = 'Sign in'; // Note that we do not want to click the signin button // in tests as that would trigger an auth-flow we cannot @@ -13,18 +12,7 @@ const signInText = 'Sign-in'; describe('Sign In Component', function() { it('Sign In Button Loads when client id is valid', function () { cy.viewport(600, 300); - // Load the client id from perf so we can have a valid button - cy.readFile('config/alpha.json').then((config) => { - const clientId = config.clientId; - cy.stub(Config, 'getGoogleClientId').returns(clientId); - mount(); - cy.contains(signInText).should('exist'); - }); - }); - it('Spinner loads when client id is empty', function () { - cy.viewport(600, 300); - cy.stub(Config, 'getGoogleClientId').returns(''); - mount(); - cy.contains(signInText).should('not.exist'); + mount( {}}/>); + cy.contains(signInText).should('exist'); }); }); From becd2c8e87ba0fd3b9b37acfd3c0e6dc68352e83 Mon Sep 17 00:00:00 2001 From: rushtong Date: Mon, 9 Sep 2024 14:46:45 -0400 Subject: [PATCH 07/27] feat: styling --- src/components/DuosHeader.jsx | 2 -- src/components/SignInButton.tsx | 13 +++++++++---- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/components/DuosHeader.jsx b/src/components/DuosHeader.jsx index 2596cbbed..fe7a2533a 100644 --- a/src/components/DuosHeader.jsx +++ b/src/components/DuosHeader.jsx @@ -227,7 +227,6 @@ const NavigationTabsComponent = (props) => { {/* Sign-in button location when window is narrow and menu is vertical */} {!isLogged && orientation === 'vertical' &&
  • } @@ -246,7 +245,6 @@ const NavigationTabsComponent = (props) => { flexDirection: orientation === 'vertical' ? 'column' : 'row' }}>
    diff --git a/src/components/SignInButton.tsx b/src/components/SignInButton.tsx index 5dace9f55..5ca78a90f 100644 --- a/src/components/SignInButton.tsx +++ b/src/components/SignInButton.tsx @@ -12,13 +12,11 @@ import ReactTooltip from 'react-tooltip'; import eventList from '../libs/events'; import {StackdriverReporter} from '../libs/stackdriverReporter'; import {History} from 'history'; -import CSS from 'csstype'; import {OidcUser} from '../libs/auth/oidcBroker'; import {DuosUserResponse} from '../types/responseTypes'; interface SignInButtonProps { - customStyle: CSS.Properties | undefined; history: History; onSignIn: () => Promise; } @@ -162,9 +160,16 @@ export const SignInButton = (props: SignInButtonProps) => { const signInElement = (): React.JSX.Element => { return ( -
    +