Skip to content

feat: add url opener option to oauth sign in with redirect flow #14333

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import { signInWithRedirect } from '../../../src/providers/cognito/apis/signInWi
import type { OAuthStore } from '../../../src/providers/cognito/utils/types';
import { mockAuthConfigWithOAuth } from '../../mockData';

const mockUrlOpener = jest.fn();
jest.mock('@aws-amplify/core/internals/utils', () => ({
...jest.requireActual('@aws-amplify/core/internals/utils'),
assertOAuthConfig: jest.fn(),
Expand All @@ -39,7 +40,22 @@ jest.mock('@aws-amplify/core', () => {

return {
Amplify: {
getConfig: jest.fn(() => mockAuthConfigWithOAuth),
getConfig: jest.fn(() => ({
...mockAuthConfigWithOAuth,
Auth: {
...mockAuthConfigWithOAuth.Auth,
Cognito: {
...mockAuthConfigWithOAuth.Auth?.Cognito,
loginWith: {
...mockAuthConfigWithOAuth.Auth?.Cognito?.loginWith,
oauth: {
...mockAuthConfigWithOAuth.Auth?.Cognito?.loginWith?.oauth,
urlOpener: mockUrlOpener,
},
},
},
},
})),
[ACTUAL_ADD_OAUTH_LISTENER]: jest.fn(),
},
ConsoleLogger: jest.fn().mockImplementation(() => {
Expand Down Expand Up @@ -129,6 +145,7 @@ describe('signInWithRedirect', () => {
mockToCodeChallenge.mockClear();
mockHandleFailure.mockClear();
mockCompleteOAuthFlow.mockClear();
mockUrlOpener.mockClear();

(oAuthStore.setAuthConfig as jest.Mock).mockClear();
(oAuthStore.storeOAuthInFlight as jest.Mock).mockClear();
Expand Down Expand Up @@ -189,6 +206,11 @@ describe('signInWithRedirect', () => {
expect(mockUrlSafeEncode).toHaveBeenCalledWith(expectedCustomState);
});

it('uses custom url opener if specified', async () => {
await signInWithRedirect();
expect(mockUrlOpener).toHaveBeenCalled();
});

describe('specifications on Web', () => {
describe('side effect', () => {
it('attaches oauth listener to the Amplify singleton', async () => {
Expand Down
11 changes: 8 additions & 3 deletions packages/auth/src/providers/cognito/apis/signInWithRedirect.ts
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,8 @@ const oauthSignIn = async ({
preferPrivateSession?: boolean;
options?: SignInWithRedirectInput['options'];
}) => {
const { domain, redirectSignIn, responseType, scopes } = oauthConfig;
const { domain, redirectSignIn, responseType, scopes, urlOpener } =
oauthConfig;
const { loginHint, lang, nonce } = options ?? {};
const randomState = generateState();

Expand Down Expand Up @@ -130,8 +131,12 @@ const oauthSignIn = async ({

// the following is effective only in react-native as openAuthSession resolves only in react-native
const { type, error, url } =
(await openAuthSession(oAuthUrl, redirectSignIn, preferPrivateSession)) ??
{};
(await openAuthSession(
oAuthUrl,
redirectSignIn,
preferPrivateSession,
urlOpener,
)) ?? {};

try {
if (type === 'error') {
Expand Down
15 changes: 13 additions & 2 deletions packages/auth/src/utils/openAuthSession.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,21 @@

import { OpenAuthSession } from './types';

export const openAuthSession: OpenAuthSession = async (url: string) => {
export const openAuthSession: OpenAuthSession = async (
url: string,
_redirectUrls: string[],
_preferPrivateSession?: boolean,
urlOpener: (_url: string) => Promise<void> = async (_url: string) => {
window.location.href = _url;
},
) => {
if (!window?.location) {
return;
}

// enforce HTTPS
window.location.href = url.replace('http://', 'https://');
const secureUrl = url.replace('http://', 'https://');

// Call the provided or default urlOpener
await urlOpener(secureUrl);
};
1 change: 1 addition & 0 deletions packages/auth/src/utils/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ export type OpenAuthSession = (
url: string,
redirectUrls: string[],
preferPrivateSession?: boolean,
urlOpener?: (url: string) => Promise<void>,
) => Promise<OpenAuthSessionResult | void>;

type OpenAuthSessionResultType = 'canceled' | 'success' | 'error';
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/singleton/Auth/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ export interface OAuthConfig {
redirectSignOut: string[];
responseType: 'code' | 'token';
providers?: (OAuthProvider | CustomProvider)[];
urlOpener?: (url: string) => Promise<void>
}

export type OAuthProvider = 'Google' | 'Facebook' | 'Amazon' | 'Apple';
Expand Down