Skip to content

Commit c7069e2

Browse files
feat(passport): Redirect based login (#2543)
1 parent 816ddf0 commit c7069e2

File tree

18 files changed

+132
-14
lines changed

18 files changed

+132
-14
lines changed

packages/passport/sdk-sample-app/.env.development

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
NEXT_PUBLIC_ENV=dev
2-
NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/login/callback
2+
NEXT_PUBLIC_POPUP_REDIRECT_URI=http://localhost:3000/login/callback
3+
NEXT_PUBLIC_REDIRECT_URI=http://localhost:3000/login/redirect-callback
34
NEXT_PUBLIC_LOGOUT_REDIRECT_URI=http://localhost:3000
45
# Logout modes: redirect, silent
56
NEXT_PUBLIC_LOGOUT_MODE=silent

packages/passport/sdk-sample-app/.env.production

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
NEXT_PUBLIC_ENV=prod
2-
NEXT_PUBLIC_REDIRECT_URI=https://passport.immutable.com/sdk-sample-app/login/callback
2+
NEXT_PUBLIC_POPUP_REDIRECT_URI=https://passport.immutable.com/sdk-sample-app/login/callback
3+
NEXT_PUBLIC_REDIRECT_URI=http://passport.immutable.com/sdk-sample-app/login/redirect-callback
34
NEXT_PUBLIC_LOGOUT_REDIRECT_URI=https://passport.immutable.com/sdk-sample-app
45
# Logout modes: redirect, silent
56
NEXT_PUBLIC_LOGOUT_MODE=silent

packages/passport/sdk-sample-app/src/components/PassportMethods.tsx

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ function PassportMethods() {
1212
const {
1313
logout,
1414
login,
15+
popupRedirect,
1516
getIdToken,
1617
getAccessToken,
1718
getUserInfo,
@@ -43,6 +44,12 @@ function PassportMethods() {
4344
return (
4445
<CardStack title="Passport Methods">
4546
<Stack direction="horizontal" style={{ flexWrap: 'wrap' }} gap={3}>
47+
<WorkflowButton
48+
disabled={isLoading}
49+
onClick={popupRedirect}
50+
>
51+
Popup Login
52+
</WorkflowButton>
4653
<WorkflowButton
4754
disabled={isLoading}
4855
onClick={login}

packages/passport/sdk-sample-app/src/config/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
export const SCOPE = 'openid offline_access profile email transact';
22
export const AUDIENCE = 'platform_api';
3+
export const POPUP_REDIRECT_URI = process.env.NEXT_PUBLIC_POPUP_REDIRECT_URI
4+
|| process.env.NEXT_PUBLIC_REDIRECT_URI || '';
35
export const REDIRECT_URI = process.env.NEXT_PUBLIC_REDIRECT_URI || '';
46
export const LOGOUT_REDIRECT_URI = process.env.NEXT_PUBLIC_LOGOUT_REDIRECT_URI || '';
57
export const LOGOUT_MODE = process.env.NEXT_PUBLIC_LOGOUT_MODE as 'redirect' | 'silent' | undefined;

packages/passport/sdk-sample-app/src/context/ImmutableProvider.tsx

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,8 +12,9 @@ import { Passport, PassportModuleConfiguration } from '@imtbl/passport';
1212
import { Environment, ImmutableConfiguration, ModuleConfiguration } from '@imtbl/config';
1313
import {
1414
AUDIENCE,
15-
LOGOUT_REDIRECT_URI,
15+
POPUP_REDIRECT_URI,
1616
REDIRECT_URI,
17+
LOGOUT_REDIRECT_URI,
1718
SILENT_LOGOUT_REDIRECT_URI,
1819
LOGOUT_MODE,
1920
SCOPE,
@@ -89,6 +90,7 @@ const getPassportConfig = (environment: EnvironmentNames): PassportModuleConfigu
8990
const sharedConfigurationValues = {
9091
scope: SCOPE,
9192
audience: AUDIENCE,
93+
popupRedirectUri: POPUP_REDIRECT_URI,
9294
redirectUri: REDIRECT_URI,
9395
logoutMode: LOGOUT_MODE,
9496
logoutRedirectUri: LOGOUT_MODE === 'silent'

packages/passport/sdk-sample-app/src/context/PassportProvider.tsx

Lines changed: 20 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ const PassportContext = createContext<{
1515
connectZkEvm: () => void;
1616
logout: () => void;
1717
login: () => void;
18+
popupRedirect: () => void;
1819
getIdToken: () => Promise<string | undefined>;
1920
getAccessToken: () => Promise<string | undefined>;
2021
getUserInfo: () => Promise<UserProfile | undefined>;
@@ -27,6 +28,7 @@ const PassportContext = createContext<{
2728
connectZkEvm: () => undefined,
2829
logout: () => undefined,
2930
login: () => Promise.resolve(undefined),
31+
popupRedirect: () => Promise.resolve(undefined),
3032
getIdToken: () => Promise.resolve(undefined),
3133
getAccessToken: () => Promise.resolve(undefined),
3234
getUserInfo: () => Promise.resolve(undefined),
@@ -135,7 +137,7 @@ export function PassportProvider({
135137
}
136138
}, [addMessage, passportClient, setIsLoading]);
137139

138-
const login = useCallback(async () => {
140+
const popupRedirect = useCallback(async () => {
139141
try {
140142
setIsLoading(true);
141143
const userProfile = await passportClient.login();
@@ -148,13 +150,27 @@ export function PassportProvider({
148150
}
149151
}, [addMessage, passportClient, setIsLoading]);
150152

153+
const login = useCallback(async () => {
154+
try {
155+
setIsLoading(true);
156+
const userProfile = await passportClient.login({ useRedirectFlow: true });
157+
addMessage('Login Redirect', userProfile);
158+
} catch (err) {
159+
addMessage('Login Redirect', err);
160+
console.error(err);
161+
} finally {
162+
setIsLoading(false);
163+
}
164+
}, [addMessage, passportClient, setIsLoading]);
165+
151166
const providerValues = useMemo(() => ({
152167
imxProvider,
153168
zkEvmProvider,
154169
connectImx,
155170
connectZkEvm,
156171
logout,
157172
login,
173+
popupRedirect,
158174
getIdToken,
159175
getAccessToken,
160176
getUserInfo,
@@ -167,6 +183,7 @@ export function PassportProvider({
167183
connectZkEvm,
168184
logout,
169185
login,
186+
popupRedirect,
170187
getIdToken,
171188
getAccessToken,
172189
getUserInfo,
@@ -188,6 +205,7 @@ export function usePassportProvider() {
188205
connectImx,
189206
connectZkEvm,
190207
login,
208+
popupRedirect,
191209
logout,
192210
getIdToken,
193211
getAccessToken,
@@ -201,6 +219,7 @@ export function usePassportProvider() {
201219
connectImx,
202220
connectZkEvm,
203221
login,
222+
popupRedirect,
204223
logout,
205224
getIdToken,
206225
getAccessToken,

packages/passport/sdk-sample-app/src/pages/login/callback.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,6 @@ export default function HandleCallback() {
55
const { passportClient } = useImmutableProvider();
66

77
useEffect(() => {
8-
passportClient.loginCallback();
8+
passportClient.loginCallback().catch(console.error);
99
}, [passportClient]);
1010
}
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import { useEffect, useState } from 'react';
2+
import { useImmutableProvider } from '@/context/ImmutableProvider';
3+
4+
export default function HandleCallback() {
5+
const { passportClient } = useImmutableProvider();
6+
const [shouldCallCallback, setShouldCallCallback] = useState(false);
7+
8+
useEffect(() => {
9+
if (!passportClient || !shouldCallCallback) {
10+
setTimeout(() => {
11+
setShouldCallCallback(true);
12+
}, 1000);
13+
return;
14+
}
15+
16+
const handleCallback = async () => {
17+
await passportClient.loginCallback();
18+
window.location.href = window.location.origin;
19+
};
20+
21+
handleCallback().catch(console.error);
22+
}, [passportClient, shouldCallCallback]);
23+
24+
return null;
25+
}

packages/passport/sdk/src/Passport.int.test.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ jest.mock('@imtbl/x-client');
2626

2727
const authenticationDomain = 'example.com';
2828
const redirectUri = 'example.com';
29+
const popupRedirectUri = 'example.com';
2930
const logoutRedirectUri = 'example.com';
3031
const clientId = 'clientId123';
3132
const mockOidcUser = {
@@ -53,6 +54,7 @@ const mockOidcUserZkevm = {
5354
const oidcConfiguration: OidcConfiguration = {
5455
clientId,
5556
redirectUri,
57+
popupRedirectUri,
5658
logoutRedirectUri,
5759
};
5860

@@ -64,6 +66,7 @@ const getZkEvmProvider = async () => {
6466
audience: 'platform_api',
6567
clientId,
6668
redirectUri,
69+
popupRedirectUri,
6770
logoutRedirectUri,
6871
scope: 'openid offline_access profile email transact',
6972
});
@@ -325,6 +328,7 @@ describe('Passport', () => {
325328
audience: 'platform_api',
326329
clientId,
327330
redirectUri,
331+
popupRedirectUri,
328332
logoutRedirectUri,
329333
scope: 'openid offline_access profile email transact',
330334
});

packages/passport/sdk/src/Passport.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ jest.mock('@imtbl/metrics');
3232
const oidcConfiguration: OidcConfiguration = {
3333
clientId: '11111',
3434
redirectUri: 'https://test.com',
35+
popupRedirectUri: 'https://test.com',
3536
logoutRedirectUri: 'https://test.com',
3637
};
3738

@@ -71,6 +72,7 @@ describe('Passport', () => {
7172
forceUserRefreshMock = jest.fn();
7273
(AuthManager as unknown as jest.Mock).mockReturnValue({
7374
login: authLoginMock,
75+
loginWithRedirect: authLoginMock,
7476
loginCallback: loginCallbackMock,
7577
logout: logoutMock,
7678
removeUser: removeUserMock,
@@ -573,6 +575,14 @@ describe('Passport', () => {
573575
expect(forceUserRefreshMock).toBeCalledTimes(1);
574576
expect(authLoginMock).toBeCalledTimes(0);
575577
});
578+
579+
it('should call loginWithRedirect', async () => {
580+
getUserMock.mockReturnValue(null);
581+
await passport.login({ useRedirectFlow: true });
582+
583+
expect(getUserMock).toBeCalledTimes(1);
584+
expect(authLoginMock).toBeCalledTimes(1);
585+
});
576586
});
577587

578588
describe('linkExternalWallet', () => {

0 commit comments

Comments
 (0)