Skip to content
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

feat(adapter-nextjs): add user has signed in check before initiating sign-in and sign-up #13839

Open
wants to merge 1 commit into
base: hui/feat/adapter-nextjs/3-auth-api-handlers-impl
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 @@ -62,6 +62,8 @@ const mockIsNextRequest = jest.mocked(isNextRequest);
const mockIsAuthRoutesHandlersContext = jest.mocked(
isAuthRoutesHandlersContext,
);
const mockRunWithAmplifyServerContext =
jest.fn() as jest.MockedFunction<NextServer.RunOperationWithContext>;

describe('createAuthRoutesHandlersFactory', () => {
const existingProcessEnvVars = { ...process.env };
Expand All @@ -84,6 +86,7 @@ describe('createAuthRoutesHandlersFactory', () => {
createAuthRouteHandlersFactory({
config: mockAmplifyConfig,
runtimeOptions: mockRuntimeOptions,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
}),
).toThrow('Could not find the AMPLIFY_APP_ORIGIN environment variable.');
});
Expand All @@ -92,6 +95,7 @@ describe('createAuthRoutesHandlersFactory', () => {
createAuthRouteHandlersFactory({
config: mockAmplifyConfig,
runtimeOptions: mockRuntimeOptions,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockAssertTokenProviderConfig).toHaveBeenCalledWith(
Expand All @@ -106,6 +110,7 @@ describe('createAuthRoutesHandlersFactory', () => {
const testCreateAuthRoutesHandlersFactoryInput = {
config: mockAmplifyConfig,
runtimeOptions: mockRuntimeOptions,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
};
const testCreateAuthRoutesHandlersInput: CreateAuthRoutesHandlersInput = {
customState: 'random-state',
Expand Down Expand Up @@ -151,6 +156,7 @@ describe('createAuthRoutesHandlersFactory', () => {
setCookieOptions: mockRuntimeOptions.cookies,
origin: 'https://example.com',
userPoolClientId: 'def',
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});
});

Expand All @@ -172,6 +178,7 @@ describe('createAuthRoutesHandlersFactory', () => {
setCookieOptions: mockRuntimeOptions.cookies,
origin: 'https://example.com',
userPoolClientId: 'def',
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});
});

Expand All @@ -192,6 +199,7 @@ describe('createAuthRoutesHandlersFactory', () => {
const createAuthRoutesHandlers = createAuthRouteHandlersFactory({
config: mockAmplifyConfig,
runtimeOptions: undefined,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});
const handlerWithDefaultParamValues =
createAuthRoutesHandlers(/* undefined */);
Expand All @@ -214,6 +222,7 @@ describe('createAuthRoutesHandlersFactory', () => {
setCookieOptions: {},
origin: 'https://example.com',
userPoolClientId: 'def',
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});
});
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,14 @@ import {
handleSignOutCallbackRequest,
handleSignOutRequest,
} from '../../src/auth/handlers';
import { NextServer } from '../../src';
import {
hasUserSignedInWithAppRouter,
isSupportedAuthApiRoutePath,
} from '../../src/auth/utils';

jest.mock('../../src/auth/handlers');
jest.mock('../../src/auth/utils');

const mockHandleSignInSignUpRequest = jest.mocked(handleSignInSignUpRequest);
const mockHandleSignOutRequest = jest.mocked(handleSignOutRequest);
Expand All @@ -23,6 +29,14 @@ const mockHandleSignInCallbackRequest = jest.mocked(
const mockHandleSignOutCallbackRequest = jest.mocked(
handleSignOutCallbackRequest,
);
const mockHasUserSignedInWithAppRouter = jest.mocked(
hasUserSignedInWithAppRouter,
);
const mockIsSupportedAuthApiRoutePath = jest.mocked(
isSupportedAuthApiRoutePath,
);
const mockRunWithAmplifyServerContext =
jest.fn() as jest.MockedFunction<NextServer.RunOperationWithContext>;

describe('handleAuthApiRouteRequestForAppRouter', () => {
const testOrigin = 'https://example.com';
Expand All @@ -40,6 +54,11 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
};
const _ = handleAuthApiRouteRequestForAppRouter;

beforeAll(() => {
mockHasUserSignedInWithAppRouter.mockResolvedValue(false);
mockIsSupportedAuthApiRoutePath.mockReturnValue(true);
});

it('returns a 405 response when input.request has an unsupported method', async () => {
const request = new NextRequest(
new URL('https://example.com/api/auth/sign-in'),
Expand All @@ -55,6 +74,7 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response.status).toBe(405);
Expand All @@ -75,6 +95,7 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response.status).toBe(400);
Expand All @@ -87,6 +108,9 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
method: 'GET',
},
);

mockIsSupportedAuthApiRoutePath.mockReturnValueOnce(false);

const response = await handleAuthApiRouteRequestForAppRouter({
request,
handlerContext: { params: { slug: 'exchange-token' } },
Expand All @@ -95,6 +119,7 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response.status).toBe(404);
Expand Down Expand Up @@ -124,6 +149,7 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response).toBe(mockResponse);
Expand All @@ -139,6 +165,36 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
},
);

test.each([['sign-in'], ['sign-up']])(
`calls hasUserSignedInWithAppRouter with correct params when handlerContext.params.slug is %s, and when it returns true, the handler returns a 302 response`,
async slug => {
mockHasUserSignedInWithAppRouter.mockResolvedValueOnce(true);
const mockRequest = new NextRequest(
new URL('https://example.com/api/auth/sign-in'),
{
method: 'GET',
},
);

const response = await handleAuthApiRouteRequestForAppRouter({
request: mockRequest,
handlerContext: { params: { slug } },
handlerInput: {
...testHandlerInput,
redirectOnSignInComplete: undefined,
},
userPoolClientId: 'userPoolClientId',
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response.status).toBe(302);
expect(response.headers.get('Location')).toBe('/');
},
);

it('calls handleSignOutRequest with correct params when handlerContext.params.slug is sign-out', async () => {
const mockRequest = new NextRequest(
new URL('https://example.com/api/auth/sign-out'),
Expand All @@ -158,6 +214,7 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response).toBe(mockResponse);
Expand Down Expand Up @@ -188,6 +245,7 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response).toBe(mockResponse);
Expand Down Expand Up @@ -220,6 +278,7 @@ describe('handleAuthApiRouteRequestForAppRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(response).toBe(mockResponse);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,16 @@ import {
handleSignOutCallbackRequestForPagesRouter,
handleSignOutRequestForPagesRouter,
} from '../../src/auth/handlers';
import { NextServer } from '../../src';
import {
hasUserSignedInWithPagesRouter,
isSupportedAuthApiRoutePath,
} from '../../src/auth/utils';

import { createMockNextApiResponse } from './testUtils';

jest.mock('../../src/auth/handlers');
jest.mock('../../src/auth/utils');

const mockHandleSignInSignUpRequestForPagesRouter = jest.mocked(
handleSignInSignUpRequestForPagesRouter,
Expand All @@ -26,6 +32,14 @@ const mockHandleSignInCallbackRequestForPagesRouter = jest.mocked(
const mockHandleSignOutCallbackRequestForPagesRouter = jest.mocked(
handleSignOutCallbackRequestForPagesRouter,
);
const mockIsSupportedAuthApiRoutePath = jest.mocked(
isSupportedAuthApiRoutePath,
);
const mockHasUserSignedInWithPagesRouter = jest.mocked(
hasUserSignedInWithPagesRouter,
);
const mockRunWithAmplifyServerContext =
jest.fn() as jest.MockedFunction<NextServer.RunOperationWithContext>;

describe('handleAuthApiRouteRequestForPagesRouter', () => {
const testOrigin = 'https://example.com';
Expand All @@ -50,6 +64,11 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
mockResponse,
} = createMockNextApiResponse();

beforeAll(() => {
mockHasUserSignedInWithPagesRouter.mockResolvedValue(false);
mockIsSupportedAuthApiRoutePath.mockReturnValue(true);
});

afterEach(() => {
mockResponseAppendHeader.mockClear();
mockResponseEnd.mockClear();
Expand All @@ -69,6 +88,7 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: testSetCookieOptions,
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockResponseStatus).toHaveBeenCalledWith(405);
Expand All @@ -86,6 +106,7 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: testSetCookieOptions,
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockResponseStatus).toHaveBeenCalledWith(400);
Expand All @@ -98,6 +119,8 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
query: { slug: 'exchange-token' },
} as any;

mockIsSupportedAuthApiRoutePath.mockReturnValueOnce(false);

handleAuthApiRouteRequestForPagesRouter({
request: mockRequest,
response: mockResponse,
Expand All @@ -106,6 +129,7 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: testSetCookieOptions,
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockResponseStatus).toHaveBeenCalledWith(404);
Expand All @@ -132,6 +156,7 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockHandleSignInSignUpRequestForPagesRouter).toHaveBeenCalledWith({
Expand All @@ -147,6 +172,34 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
},
);

test.each([['sign-in'], ['sign-up']])(
`calls hasUserSignedInWithPagesRouter with correct params when handlerContext.params.slug is %s, and when it returns true, the handler returns a 302 response`,
async slug => {
mockHasUserSignedInWithPagesRouter.mockResolvedValueOnce(true);
const mockRequest = {
url: 'https://example.com/api/auth/sign-in',
method: 'GET',
query: { slug },
} as unknown as NextApiRequest;

await handleAuthApiRouteRequestForPagesRouter({
request: mockRequest,
response: mockResponse,
handlerInput: {
...testHandlerInput,
redirectOnSignInComplete: undefined,
},
userPoolClientId: 'userPoolClientId',
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockResponseRedirect).toHaveBeenCalledWith(302, '/');
},
);

it('calls handleSignOutRequest with correct params when handlerContext.params.slug is sign-out', async () => {
const mockRequest = {
url: 'https://example.com/api/auth/sign-in',
Expand All @@ -162,6 +215,7 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockHandleSignOutRequestForPagesRouter).toHaveBeenCalledWith({
Expand All @@ -188,6 +242,7 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockHandleSignInCallbackRequestForPagesRouter).toHaveBeenCalledWith({
Expand Down Expand Up @@ -216,6 +271,7 @@ describe('handleAuthApiRouteRequestForPagesRouter', () => {
oAuthConfig: testOAuthConfig,
setCookieOptions: {},
origin: testOrigin,
runWithAmplifyServerContext: mockRunWithAmplifyServerContext,
});

expect(mockHandleSignOutCallbackRequestForPagesRouter).toHaveBeenCalledWith(
Expand Down
Loading
Loading