Skip to content

Commit 76548ca

Browse files
feat(auth): add prompt parameter to signInWithRedirect (#14464)
* feat(auth): add prompt parameter to signInWithRedirect
1 parent f8d52cf commit 76548ca

File tree

3 files changed

+69
-2
lines changed

3 files changed

+69
-2
lines changed

packages/auth/__tests__/providers/cognito/signInWithRedirect.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@ import { createOAuthError } from '../../../src/providers/cognito/utils/oauth/cre
2424
import { signInWithRedirect } from '../../../src/providers/cognito/apis/signInWithRedirect';
2525
import type { OAuthStore } from '../../../src/providers/cognito/utils/types';
2626
import { mockAuthConfigWithOAuth } from '../../mockData';
27+
import { type AuthPrompt } from '../../../src/types/inputs';
2728

2829
jest.mock('@aws-amplify/core/internals/utils', () => ({
2930
...jest.requireActual('@aws-amplify/core/internals/utils'),
@@ -107,6 +108,13 @@ describe('signInWithRedirect', () => {
107108
const mockCodeChallenge = 'code_challenge';
108109
const mockToCodeChallenge = jest.fn(() => mockCodeChallenge);
109110

111+
const promptTypes = [
112+
'NONE',
113+
'LOGIN',
114+
'CONSENT',
115+
'SELECT_ACCOUNT',
116+
] as const satisfies readonly AuthPrompt[];
117+
110118
beforeAll(() => {
111119
mockGenerateState.mockReturnValue(mockState);
112120
mockGenerateCodeVerifier.mockReturnValue({
@@ -189,6 +197,42 @@ describe('signInWithRedirect', () => {
189197
expect(mockUrlSafeEncode).toHaveBeenCalledWith(expectedCustomState);
190198
});
191199

200+
it('includes prompt parameter in authorization URL', async () => {
201+
for (const prompt of promptTypes) {
202+
const expectedCustomProvider = 'PieAuth';
203+
await signInWithRedirect({
204+
provider: { custom: expectedCustomProvider },
205+
options: { prompt },
206+
});
207+
const [oauthUrl] = mockOpenAuthSession.mock.calls[0];
208+
const cognitoPrompt = prompt.toLowerCase();
209+
expect(oauthUrl).toStrictEqual(
210+
`https://oauth.domain.com/oauth2/authorize?redirect_uri=http%3A%2F%2Flocalhost%3A3000%2F&response_type=code&client_id=userPoolClientId&identity_provider=${expectedCustomProvider}&scope=phone%20email%20openid%20profile%20aws.cognito.signin.user.admin&prompt=${cognitoPrompt}&state=oauth_state&code_challenge=code_challenge&code_challenge_method=S256`,
211+
);
212+
mockOpenAuthSession.mockClear();
213+
}
214+
});
215+
216+
it('calls assertUserNotAuthenticated based on prompt value', async () => {
217+
for (const prompt of promptTypes) {
218+
const expectedCustomProvider = 'PieAuth';
219+
await signInWithRedirect({
220+
provider: { custom: expectedCustomProvider },
221+
options: { prompt },
222+
});
223+
224+
expect(mockAssertUserNotAuthenticated).not.toHaveBeenCalled();
225+
226+
mockAssertUserNotAuthenticated.mockClear();
227+
mockOpenAuthSession.mockClear();
228+
}
229+
230+
// Test no options at all
231+
await signInWithRedirect();
232+
expect(mockAssertUserNotAuthenticated).toHaveBeenCalled();
233+
mockAssertUserNotAuthenticated.mockClear();
234+
});
235+
192236
describe('specifications on Web', () => {
193237
describe('side effect', () => {
194238
it('attaches oauth listener to the Amplify singleton', async () => {

packages/auth/src/providers/cognito/apis/signInWithRedirect.ts

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,10 @@ export async function signInWithRedirect(
4141
assertTokenProviderConfig(authConfig);
4242
assertOAuthConfig(authConfig);
4343
oAuthStore.setAuthConfig(authConfig);
44-
await assertUserNotAuthenticated();
44+
45+
if (!input?.options?.prompt) {
46+
await assertUserNotAuthenticated();
47+
}
4548

4649
let provider = 'COGNITO'; // Default
4750

@@ -61,6 +64,7 @@ export async function signInWithRedirect(
6164
loginHint: input?.options?.loginHint,
6265
lang: input?.options?.lang,
6366
nonce: input?.options?.nonce,
67+
prompt: input?.options?.prompt,
6468
},
6569
});
6670
}
@@ -81,7 +85,7 @@ const oauthSignIn = async ({
8185
options?: SignInWithRedirectInput['options'];
8286
}) => {
8387
const { domain, redirectSignIn, responseType, scopes } = oauthConfig;
84-
const { loginHint, lang, nonce } = options ?? {};
88+
const { loginHint, lang, nonce, prompt } = options ?? {};
8589
const randomState = generateState();
8690

8791
/* encodeURIComponent is not URL safe, use urlSafeEncode instead. Cognito
@@ -111,6 +115,7 @@ const oauthSignIn = async ({
111115
...(loginHint && { login_hint: loginHint }),
112116
...(lang && { lang }),
113117
...(nonce && { nonce }),
118+
...(prompt && { prompt: prompt.toLowerCase() }), // Cognito expects lowercase prompt values
114119
state,
115120
...(responseType === 'code' && {
116121
code_challenge: toCodeChallenge(),

packages/auth/src/types/inputs.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,18 @@ export interface AuthSignOutInput {
5454

5555
export type AuthProvider = 'Amazon' | 'Apple' | 'Facebook' | 'Google';
5656

57+
/**
58+
* OIDC prompt parameter that specifies whether the Authorization Server prompts the End-User for reauthentication and consent.
59+
*
60+
* - `'NONE'` - No authentication or consent UI will be displayed
61+
* - `'LOGIN'` - Force user to re-authenticate even if they have a valid session
62+
* - `'CONSENT'` - Force user to consent to sharing information with the client
63+
* - `'SELECT_ACCOUNT'` - Prompt user to select among multiple authenticated accounts
64+
*
65+
* @see https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
66+
*/
67+
export type AuthPrompt = 'NONE' | 'LOGIN' | 'CONSENT' | 'SELECT_ACCOUNT';
68+
5769
export interface AuthSignInWithRedirectInput {
5870
provider?: AuthProvider | { custom: string };
5971
customState?: string;
@@ -95,6 +107,12 @@ export interface AuthSignInWithRedirectInput {
95107
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
96108
*/
97109
nonce?: string;
110+
111+
/**
112+
* An OIDC parameter that controls authentication behavior for existing sessions. It can be used by the Client to make sure that the End-User is still present for the current session or to bring attention to the request. Available in the managed login branding version only, not in the classic hosted UI.
113+
* @see https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html
114+
*/
115+
prompt?: AuthPrompt;
98116
};
99117
}
100118

0 commit comments

Comments
 (0)