Skip to content

Commit

Permalink
feat(apps): show specific app registration complete page for some users
Browse files Browse the repository at this point in the history
  • Loading branch information
coldlink committed Feb 20, 2024
1 parent d05daea commit c61f6a7
Show file tree
Hide file tree
Showing 11 changed files with 113 additions and 9 deletions.
5 changes: 4 additions & 1 deletion cypress/integration/ete-okta/registration_1.2.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,10 @@ describe('Registration flow - Split 1/2', () => {
cy.get('input[name="secondName"]').type('Last Name');
cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();
cy.url().should('contain', 'https://m.code.dev-theguardian.com/');
cy.url().should('contain', '/welcome/app/complete');
cy.url().should('contain', '/welcome/app/complete');
cy.contains(unregisteredEmail);
cy.contains('Guardian app');

// test the registration platform is set correctly
cy.getTestOktaUser(unregisteredEmail).then((oktaUser) => {
Expand Down
7 changes: 5 additions & 2 deletions cypress/integration/ete-okta/registration_2.6.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,14 +669,17 @@ describe('Registration flow - Split 2/2', () => {
/welcome\/([^"]*)/,
).then(({ body, token }) => {
expect(body).to.have.string('Complete registration');
const appClientId = Cypress.env('OKTA_ANDROID_CLIENT_ID');
// manually adding the app prefix to the token
cy.visit(`/welcome/al_${token}`);
cy.visit(`/welcome/al_${token}&appClientId=${appClientId}`);
cy.contains('Save and continue');

cy.get('input[name="password"]').type(randomPassword());
cy.get('button[type="submit"]').click();

cy.url().should('contain', 'https://m.code.dev-theguardian.com/');
cy.url().should('contain', '/welcome/app/complete');
cy.contains(unregisteredEmail);
cy.contains('Guardian app');
});
});
});
Expand Down
20 changes: 20 additions & 0 deletions src/client/pages/ReturnToApp.stories.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import React from 'react';
import { Meta } from '@storybook/react';

import { ReturnToApp } from './ReturnToApp';

export default {
title: 'Pages/ReturnToApp',
component: ReturnToApp,
parameters: { layout: 'fullscreen' },
} as Meta;

export const Default = () => (
<ReturnToApp email="[email protected]" appName="Guardian" />
);

export const NoEmail = () => <ReturnToApp appName="Guardian" />;

export const NoApp = () => <ReturnToApp email="[email protected]" />;

export const NoEmailOrApp = () => <ReturnToApp />;
28 changes: 28 additions & 0 deletions src/client/pages/ReturnToApp.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
import React from 'react';
import { MainLayout } from '@/client/layouts/Main';
import { MainBodyText } from '@/client/components/MainBodyText';

type ReturnToAppProps = {
email?: string;
appName?: string;
};

export const ReturnToApp = ({ email, appName: app }: ReturnToAppProps) => (
<MainLayout pageHeader="Account Created">
<MainBodyText>
You have finished creating your Guardian account
{email ? (
<>
: <b>{email}</b>
</>
) : (
''
)}
.
</MainBodyText>
<MainBodyText>
Open the <b>{app ? app : 'Guardian'}</b> app and sign in with your new
account.
</MainBodyText>
</MainLayout>
);
11 changes: 11 additions & 0 deletions src/client/pages/ReturnToAppPage.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import React from 'react';
import useClientState from '@/client/lib/hooks/useClientState';
import { ReturnToApp } from '@/client/pages/ReturnToApp';

export const ReturnToAppPage = () => {
const clientState = useClientState();
const { pageData = {} } = clientState;
const { email, appName } = pageData;

return <ReturnToApp email={email} appName={appName} />;
};
5 changes: 5 additions & 0 deletions src/client/routes.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ import { DeleteAccountEmailPasswordValidationPage } from './pages/DeleteAccountE
import { DeleteAccountCompletePage } from '@/client/pages/DeleteAccountCompletePage';
import { RegisterWithEmailPage } from './pages/RegisterWithEmailPage';
import { WelcomeSocialPage } from './pages/WelcomeSocialPage';
import { ReturnToAppPage } from './pages/ReturnToAppPage';

export type RoutingConfig = {
clientState: ClientState;
Expand Down Expand Up @@ -257,6 +258,10 @@ const routes: Array<{
path: '/delete/email-sent',
element: <EmailSentPage />,
},
{
path: '/welcome/app/complete',
element: <ReturnToAppPage />,
},
];

interface Props {
Expand Down
16 changes: 16 additions & 0 deletions src/server/lib/middleware/requestState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,10 @@ const getRequestState = async (
// eslint-disable-next-line functional/no-let
let isNativeApp: IsNativeApp;

// it is also useful to know the app name
// eslint-disable-next-line functional/no-let
let appName;

try {
if (!!queryParams.appClientId) {
const app = await getApp(queryParams.appClientId);
Expand All @@ -61,6 +65,17 @@ const getRequestState = async (
} else if (label.startsWith('ios_')) {
isNativeApp = 'ios';
}

switch (label) {
case 'android_live_app':
case 'ios_live_app':
appName = 'Guardian';
appName = 'Guardian';
break;
case 'ios_feast_app':
appName = 'Guardian Feast';
break;
}
}
} catch (error) {
logger.error('Error getting app info in request state', error, {
Expand All @@ -74,6 +89,7 @@ const getRequestState = async (
geolocation: getGeolocationRegion(req),
returnUrl: queryParams.returnUrl,
isNativeApp,
appName,
},
globalMessage: {},
csrf: {
Expand Down
10 changes: 4 additions & 6 deletions src/server/routes/oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -317,17 +317,15 @@ const authenticationHandler = async (

// temporary fix: if the user registered on the app (the token will be prefixed),
// and instead of ending up back in the app but in a mobile browser instead,
// where we don't want to show the onboarding,
// for now we simply redirect them to the default return url when app prefix is set,
// and the confirmation page is one of the consent pages, then we redirect to the default return url,
// which is normally the guardian home page, by setting authState.confirmationPage to undefined
// this will be fixed when we either use a deep link with a custom scheme, or passwordless OTP flow
// where we don't want to show the onboarding flow.
// We simply redirect them to a page telling them to return to app, when app prefix is set.
// This will be fixed when we either use the passcode registration flow.
if (
authState.data?.hasAppPrefix &&
consentPages.some((page) => page.path === authState.confirmationPage)
) {
// eslint-disable-next-line functional/immutable-data
authState.confirmationPage = undefined;
authState.confirmationPage = '/welcome/app/complete';
}

const returnUrl = authState.confirmationPage
Expand Down
18 changes: 18 additions & 0 deletions src/server/routes/welcome.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,24 @@ import { updateRegistrationPlatform } from '../lib/registrationPlatform';

const { okta } = getConfiguration();

// temp return to app page for app users who get stuck in browser
router.get(
'/welcome/app/complete',
loginMiddlewareOAuth,
(req: Request, res: ResponseWithRequestState) => {
const html = renderer('/welcome/app/complete', {
pageTitle: 'Welcome',
requestState: mergeRequestState(res.locals, {
pageData: {
// email is type unknown, but we know it's a string
email: res.locals.oauthState.idToken?.claims.email as string,
},
}),
});
return res.type('html').send(html);
},
);

// consent page for post social registration - google
router.get(
'/welcome/google',
Expand Down
1 change: 1 addition & 0 deletions src/shared/model/ClientState.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ export interface PageData {
browserName?: string;
isNativeApp?: IsNativeApp;
accountManagementUrl?: string;
appName?: string;

// token
token?: string;
Expand Down
1 change: 1 addition & 0 deletions src/shared/model/Routes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ export const ValidRoutePathsArray = [
'/verify-email', //this can be removed once Jobs has been migrated
'/welcome',
'/welcome/:token',
'/welcome/app/complete',
'/welcome/complete',
'/welcome/email-sent',
'/welcome/expired',
Expand Down

0 comments on commit c61f6a7

Please sign in to comment.