Skip to content

Commit

Permalink
feat(adapter-nextjs): add runtimeOptions.cookies to createServerRunner (
Browse files Browse the repository at this point in the history
#13788)

* feat(aws-amplify|adapter-nextjs): add runtimeOptions.cookies to createServerRunner

* chore: resolve comments

* chore(adapter-nextjs): adapt the latest impl. changes
  • Loading branch information
HuiSF committed Jan 27, 2025
1 parent 8b489d1 commit 903e512
Show file tree
Hide file tree
Showing 6 changed files with 135 additions and 19 deletions.
94 changes: 77 additions & 17 deletions packages/adapter-nextjs/__tests__/createServerRunner.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,12 +23,22 @@ const mockAmplifyConfig: ResourcesConfig = {
jest.mock(
'../src/utils/createCookieStorageAdapterFromNextServerContext',
() => ({
createCookieStorageAdapterFromNextServerContext: jest.fn(),
createCookieStorageAdapterFromNextServerContext: jest.fn(() => ({
get: jest.fn(),
set: jest.fn(),
delete: jest.fn(),
getAll: jest.fn(),
})),
}),
);

jest.mock('../src/utils/createTokenValidator', () => ({
createTokenValidator: jest.fn(() => ({
getItem: jest.fn(),
})),
}));
describe('createServerRunner', () => {
let createServerRunner: any;
let createServerRunner: NextServer.CreateServerRunner;
let createRunWithAmplifyServerContextSpy: any;

const mockParseAmplifyConfig = jest.fn();
Expand Down Expand Up @@ -139,26 +149,28 @@ describe('createServerRunner', () => {
});

describe('when nextServerContext is not null', () => {
const mockNextServerContext = {
req: {
headers: {
cookie: 'cookie',
},
},
res: {
setHeader: jest.fn(),
},
};
const mockCookieStorageAdapter = {
get: jest.fn(),
set: jest.fn(),
remove: jest.fn(),
};

it('should create auth providers with cookie storage adapter', async () => {
const operation = jest.fn();
const mockCookieStorageAdapter = {
get: jest.fn(),
set: jest.fn(),
remove: jest.fn(),
};

mockCreateKeyValueStorageFromCookieStorageAdapter.mockReturnValueOnce(
mockCookieStorageAdapter,
);
const mockNextServerContext = {
req: {
headers: {
cookie: 'cookie',
},
},
res: {
setHeader: jest.fn(),
},
};
const { runWithAmplifyServerContext } = createServerRunner({
config: mockAmplifyConfig,
});
Expand All @@ -184,6 +196,54 @@ describe('createServerRunner', () => {
}),
});
});

it('should call createKeyValueStorageFromCookieStorageAdapter with specified runtimeOptions.cookies', async () => {
const testCookiesOptions: NextServer.CreateServerRunnerRuntimeOptions['cookies'] =
{
domain: '.example.com',
sameSite: 'lax',
expires: new Date('2024-09-05'),
};
mockCreateKeyValueStorageFromCookieStorageAdapter.mockReturnValueOnce(
mockCookieStorageAdapter,
);

const { runWithAmplifyServerContext } = createServerRunner({
config: mockAmplifyConfig,
runtimeOptions: {
cookies: testCookiesOptions,
},
});

await runWithAmplifyServerContext({
nextServerContext:
mockNextServerContext as unknown as NextServer.Context,
operation: jest.fn(),
});

expect(
mockCreateKeyValueStorageFromCookieStorageAdapter,
).toHaveBeenCalledWith(
expect.any(Object),
expect.any(Object),
testCookiesOptions,
);

// modify by reference should not affect the original configuration
testCookiesOptions.sameSite = 'strict';
runWithAmplifyServerContext({
nextServerContext:
mockNextServerContext as unknown as NextServer.Context,
operation: jest.fn(),
});

expect(
mockCreateKeyValueStorageFromCookieStorageAdapter,
).toHaveBeenCalledWith(expect.any(Object), expect.any(Object), {
...testCookiesOptions,
sameSite: 'lax',
});
});
});
});
});
Expand Down
2 changes: 2 additions & 0 deletions packages/adapter-nextjs/src/createServerRunner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import { createTokenValidator } from './utils/createTokenValidator';
*/
export const createServerRunner: NextServer.CreateServerRunner = ({
config,
runtimeOptions,
}) => {
const amplifyConfig = parseAmplifyConfig(config);

Expand All @@ -45,6 +46,7 @@ export const createServerRunner: NextServer.CreateServerRunner = ({
runWithAmplifyServerContext: createRunWithAmplifyServerContext({
config: amplifyConfig,
tokenValidator,
runtimeOptions,
}),
};
};
13 changes: 12 additions & 1 deletion packages/adapter-nextjs/src/types/NextServer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,10 @@ import { GetServerSidePropsContext as NextGetServerSidePropsContext } from 'next
import { NextRequest, NextResponse } from 'next/server.js';
import { cookies } from 'next/headers.js';
import { AmplifyOutputs, LegacyConfig } from 'aws-amplify/adapter-core';
import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core';
import {
AmplifyServer,
CookieStorage,
} from '@aws-amplify/core/internals/adapter-core';
import { ResourcesConfig } from '@aws-amplify/core';

export declare namespace NextServer {
Expand Down Expand Up @@ -73,8 +76,16 @@ export declare namespace NextServer {
input: RunWithContextInput<OperationResult>,
) => Promise<OperationResult>;

export interface CreateServerRunnerRuntimeOptions {
cookies?: Pick<
CookieStorage.SetCookieOptions,
'domain' | 'expires' | 'sameSite'
>;
}

export interface CreateServerRunnerInput {
config: ResourcesConfig | LegacyConfig | AmplifyOutputs;
runtimeOptions?: CreateServerRunnerRuntimeOptions;
}

export interface CreateServerRunnerOutput {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,15 @@ import { createCookieStorageAdapterFromNextServerContext } from './createCookieS
export const createRunWithAmplifyServerContext = ({
config: resourcesConfig,
tokenValidator,
runtimeOptions = {},
}: {
config: ResourcesConfig;
tokenValidator?: KeyValueStorageMethodValidator;
runtimeOptions?: NextServer.CreateServerRunnerRuntimeOptions;
}) => {
const setCookieOptions = {
...runtimeOptions.cookies,
};
const runWithAmplifyServerContext: NextServer.RunOperationWithContext =
async ({ nextServerContext, operation }) => {
// When the Auth config is presented, attempt to create a Amplify server
Expand All @@ -38,6 +43,7 @@ export const createRunWithAmplifyServerContext = ({
nextServerContext,
),
tokenValidator,
setCookieOptions,
);
const credentialsProvider = createAWSCredentialsAndIdentityIdProvider(
resourcesConfig.Auth,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import { createKeyValueStorageFromCookieStorageAdapter } from '../../../src/adapter-core';
import {
CookieStorage,
createKeyValueStorageFromCookieStorageAdapter,
} from '../../../src/adapter-core';
import { defaultSetCookieOptions } from '../../../src/adapter-core/storageFactories/createKeyValueStorageFromCookieStorageAdapter';

const mockCookiesStorageAdapter = {
Expand All @@ -12,6 +15,13 @@ const mockCookiesStorageAdapter = {
};

describe('keyValueStorage', () => {
afterEach(() => {
mockCookiesStorageAdapter.delete.mockClear();
mockCookiesStorageAdapter.get.mockClear();
mockCookiesStorageAdapter.set.mockClear();
mockCookiesStorageAdapter.getAll.mockClear();
});

describe('createKeyValueStorageFromCookiesStorageAdapter', () => {
it('should return a key value storage', () => {
const keyValueStorage = createKeyValueStorageFromCookieStorageAdapter(
Expand Down Expand Up @@ -100,6 +110,31 @@ describe('keyValueStorage', () => {
});
});

describe('passing setCookieOptions parameter', () => {
const testSetCookieOptions: CookieStorage.SetCookieOptions = {
httpOnly: true,
sameSite: 'strict',
expires: new Date('2024-09-05'),
};
const keyValueStorage = createKeyValueStorageFromCookieStorageAdapter(
mockCookiesStorageAdapter,
undefined,
testSetCookieOptions,
);

it('sets item with specified setCookieOptions', async () => {
keyValueStorage.setItem('testKey', 'testValue');
expect(mockCookiesStorageAdapter.set).toHaveBeenCalledWith(
'testKey',
'testValue',
{
...defaultSetCookieOptions,
...testSetCookieOptions,
},
);
});
});

describe('in conjunction with token validator', () => {
const testKey = 'testKey';
const testValue = 'testValue';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ const ONE_YEAR_IN_MS = 365 * 24 * 60 * 60 * 1000;
export const createKeyValueStorageFromCookieStorageAdapter = (
cookieStorageAdapter: CookieStorage.Adapter,
validator?: KeyValueStorageMethodValidator,
setCookieOptions: CookieStorage.SetCookieOptions = {},
): KeyValueStorageInterface => {
return {
setItem(key, value) {
Expand All @@ -36,6 +37,7 @@ export const createKeyValueStorageFromCookieStorageAdapter = (
cookieStorageAdapter.set(key, value, {
...defaultSetCookieOptions,
expires: new Date(Date.now() + ONE_YEAR_IN_MS),
...setCookieOptions,
});

return Promise.resolve();
Expand Down

0 comments on commit 903e512

Please sign in to comment.