Skip to content

Commit

Permalink
tag release the experiment
Browse files Browse the repository at this point in the history
  • Loading branch information
HuiSF committed May 8, 2024
1 parent ae7942c commit 72feab7
Show file tree
Hide file tree
Showing 33 changed files with 767 additions and 8 deletions.
7 changes: 7 additions & 0 deletions packages/adapter-nextjs/client/package.json
@@ -0,0 +1,7 @@
{
"name": "@aws-amplify/adapter-nextjs/client",
"main": "../dist/cjs/client/index.js",
"browser": "../dist/esm/client/index.mjs",
"module": "../dist/esm/client/index.mjs",
"typings": "../dist/esm/client/index.d.ts"
}
6 changes: 6 additions & 0 deletions packages/adapter-nextjs/package.json
Expand Up @@ -8,6 +8,7 @@
"next": ">=14.2.3 <15.0.0"
},
"dependencies": {
"client-only": "0.0.1",
"cookie": "0.5.0"
},
"devDependencies": {
Expand Down Expand Up @@ -42,6 +43,11 @@
"import": "./dist/esm/api/index.mjs",
"require": "./dist/cjs/api/index.js"
},
"./client": {
"types": "./dist/esm/client/index.d.ts",
"import": "./dist/esm/client/index.mjs",
"require": "./dist/cjs/client/index.js"
},
"./package.json": "./package.json"
},
"files": [
Expand Down
8 changes: 7 additions & 1 deletion packages/adapter-nextjs/src/api/createServerRunnerForAPI.ts
Expand Up @@ -9,7 +9,13 @@ import { NextServer } from '../types';

export const createServerRunnerForAPI = ({
config,
}: NextServer.CreateServerRunnerInput): NextServer.CreateServerRunnerOutput & {
}: Omit<NextServer.CreateServerRunnerInput, 'origin'>): Omit<
NextServer.CreateServerRunnerOutput,
| 'createOAuthRouteHandler'
| 'getOAuthInitiationRoute'
| 'createTokenExchangeRouteHandler'
| 'origin'
> & {
resourcesConfig: ResourcesConfig;
} => {
const amplifyConfig = parseAmplifyConfig(config);
Expand Down
@@ -0,0 +1,63 @@
import { NextRequest } from 'next/server';
import { cookies } from 'next/headers';
import { fetchAuthSession } from 'aws-amplify/auth/server';

import { createRunWithAmplifyServerContext } from '../utils';

import {
CreateTokenExchangeRouteHandlerFactory,
CreateTokenExchangeRouteHandlerInput,
} from './types';

export const createTokenExchangeRouteHandlerFactory: CreateTokenExchangeRouteHandlerFactory =
input => {
const runWithAmplifyServerContext =
createRunWithAmplifyServerContext(input);
const { origin } = input;

const handleRequest = async (
_: NextRequest,
__: CreateTokenExchangeRouteHandlerInput,
) => {
const userSession = await runWithAmplifyServerContext({
nextServerContext: { cookies },
operation: contextSpec => fetchAuthSession(contextSpec),
});

const clockDrift = cookies()
.getAll()
.find(cookie => cookie.name.endsWith('.clockDrift'))?.value;

return new Response(
JSON.stringify({
...userSession,
tokens: {
accessToken: userSession.tokens?.accessToken.toString(),
idToken: userSession.tokens?.idToken?.toString(),
},
username: userSession.tokens?.accessToken.payload.username,
clockDrift,
userSession,
}),
{
headers: {
'content-type': 'application/json',
'Access-Control-Allow-Origin': origin,
'Access-Control-Allow-Methods': 'POST',
},
},
);
};

return handlerInput => ({
async POST(request) {
try {
return await handleRequest(request, handlerInput);
} catch (error) {
const { onError } = handlerInput;

onError(error as Error);
}
},
});
};
@@ -0,0 +1,57 @@
import { LibraryOptions, sharedInMemoryStorage } from '@aws-amplify/core';
import { runInBrowserContext } from '@aws-amplify/core/internals/utils';
import {
cognitoCredentialsProvider,
cognitoUserPoolsTokenProvider,
} from 'aws-amplify/auth/cognito';

export const createHttpOnlyCookieBasedAuthProviders = ({
authTokenExchangeRoute,
}: {
authTokenExchangeRoute: string;
}): LibraryOptions['Auth'] => {
cognitoUserPoolsTokenProvider.setKeyValueStorage(sharedInMemoryStorage);

runInBrowserContext(() => {
refreshSession({
authTokenExchangeRoute,
tokenProvider: cognitoUserPoolsTokenProvider,
credentialsProvider: cognitoCredentialsProvider,
});
});

return {
tokenProvider: cognitoUserPoolsTokenProvider,
credentialsProvider: cognitoCredentialsProvider,
};
};

const refreshSession = async ({
authTokenExchangeRoute,
tokenProvider,
credentialsProvider,
}: {
authTokenExchangeRoute: string;
tokenProvider: typeof cognitoUserPoolsTokenProvider;
credentialsProvider: typeof cognitoCredentialsProvider;
}) => {
const response = await fetch(authTokenExchangeRoute, { method: 'POST' });
const session = await response.json();

tokenProvider.tokenOrchestrator.setTokens({
tokens: {
accessToken: session.tokens.accessToken,
idToken: session.tokens.idToken,
clockDrift: session.clockDrift,
username: session.username,
},
});

credentialsProvider.setIdentityIdCredentials(
{
credentials: session.credentials,
identityId: session.identityId,
},
session.tokens.idToken,
);
};
@@ -0,0 +1,3 @@
import 'client-only';

export { createHttpOnlyCookieBasedAuthProviders } from './createHttpOnlyCookieBasedAuthProviders';
26 changes: 26 additions & 0 deletions packages/adapter-nextjs/src/auth/types.ts
@@ -0,0 +1,26 @@
import { ResourcesConfig } from 'aws-amplify';
import { NextRequest } from 'next/server';

import { NextServer } from '../types';

interface CreateTokenExchangeRouteHandlerFactoryInput {
config: ResourcesConfig;
origin: string;
setAuthCookieOptions?: NextServer.SetCookieOptions;
}

interface CreateOAuthRouteHandlerOutput {
POST(request: NextRequest): Promise<Response | void>;
}

export interface CreateTokenExchangeRouteHandlerInput {
onError(error: Error): void;
}

export type CreateTokenExchangeRouteHandler = (
input: CreateTokenExchangeRouteHandlerInput,
) => CreateOAuthRouteHandlerOutput;

export type CreateTokenExchangeRouteHandlerFactory = (
input: CreateTokenExchangeRouteHandlerFactoryInput,
) => CreateTokenExchangeRouteHandler;
1 change: 1 addition & 0 deletions packages/adapter-nextjs/src/client/index.ts
@@ -0,0 +1 @@
export { createHttpOnlyCookieBasedAuthProviders } from '../auth/httpOnlyCookieBasedAuthProviders';
19 changes: 19 additions & 0 deletions packages/adapter-nextjs/src/createServerRunner.ts
Expand Up @@ -6,6 +6,9 @@ import { parseAmplifyConfig } from '@aws-amplify/core/internals/utils';

import { createRunWithAmplifyServerContext } from './utils';
import { NextServer } from './types';
import { createOAuthRouteHandlerFactory } from './oauth';
import { createTokenExchangeRouteHandlerFactory } from './auth/createTokenExchangeRouteHandlerFactory';
import { createGetOAuthInitiationRouteFactory } from './oauth/createGetOAuthInitiationRouteFactory';

/**
* Creates the `runWithAmplifyServerContext` function to run Amplify server side APIs in an isolated request context.
Expand All @@ -27,12 +30,28 @@ import { NextServer } from './types';
*/
export const createServerRunner: NextServer.CreateServerRunner = ({
config,
origin,
setAuthCookieOptions,
}) => {
const amplifyConfig = parseAmplifyConfig(config);

return {
runWithAmplifyServerContext: createRunWithAmplifyServerContext({
config: amplifyConfig,
setAuthCookieOptions,
}),
createOAuthRouteHandler: createOAuthRouteHandlerFactory({
config: amplifyConfig,
setAuthCookieOptions,
}),
getOAuthInitiationRoute: createGetOAuthInitiationRouteFactory({
config: amplifyConfig,
origin,
}),
createTokenExchangeRouteHandler: createTokenExchangeRouteHandlerFactory({
config: amplifyConfig,
origin,
setAuthCookieOptions,
}),
};
};
@@ -0,0 +1,30 @@
import {
assertOAuthConfig,
assertTokenProviderConfig,
} from '@aws-amplify/core/internals/utils';

import {
CreateGetOAuthInitiationRouteFactory,
GetOAuthInitiationRoute,
} from './types';
import { getRedirectUrl } from './utils/getRedirectUrl';

export const createGetOAuthInitiationRouteFactory: CreateGetOAuthInitiationRouteFactory =
({ config: resourcesConfig, origin }) => {
assertTokenProviderConfig(resourcesConfig.Auth?.Cognito);
assertOAuthConfig(resourcesConfig.Auth.Cognito);

const { Cognito: cognitoUserPoolConfig } = resourcesConfig.Auth;
const redirectUrl = getRedirectUrl(
origin,
cognitoUserPoolConfig.loginWith.oauth,
);

const getOAuthInitiationRoute: GetOAuthInitiationRoute = input => {
const { provider } = input;

return `${redirectUrl}?init=true&provider=${provider}`;
};

return getOAuthInitiationRoute;
};
@@ -0,0 +1,74 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

import {
assertOAuthConfig,
assertTokenProviderConfig,
} from '@aws-amplify/core/internals/utils';
import { NextRequest } from 'next/server';
import { AuthError } from '@aws-amplify/auth';

import {
CreateOAuthRouteHandler,
CreateOAuthRouteHandlerFactory,
CreateOAuthRouteHandlerInput,
} from './types';
import { initOAuthFlow } from './utils/initOAuthFlow';
import { completeOAuthFlow } from './utils/completeOAuthFlow';

export const createOAuthRouteHandlerFactory: CreateOAuthRouteHandlerFactory = ({
config: resourcesConfig,
setAuthCookieOptions,
}): CreateOAuthRouteHandler => {
assertTokenProviderConfig(resourcesConfig.Auth?.Cognito);
assertOAuthConfig(resourcesConfig.Auth.Cognito);

const { Cognito: cognitoUserPoolConfig } = resourcesConfig.Auth;

const handleRequest = async (
request: NextRequest,
{
customState,
redirectOnAuthComplete,
onError,
}: CreateOAuthRouteHandlerInput,
): Promise<Response | void> => {
const { searchParams } = request.nextUrl;

// when request url has `init` query param - initiate oauth flow
if (searchParams.has('init')) {
return initOAuthFlow({
setAuthCookieOptions,
request,
customState,
cognitoUserPoolConfig,
oAuthConfig: cognitoUserPoolConfig.loginWith.oauth,
});
}

if (searchParams.has('code') && searchParams.has('state')) {
return completeOAuthFlow({
request,
redirectOnComplete: redirectOnAuthComplete,
setAuthCookieOptions,
customState,
cognitoUserPoolConfig,
oAuthConfig: cognitoUserPoolConfig.loginWith.oauth,
});
}

onError(new Error('Invalid point (update me)'));
};

return handlerInput => ({
async GET(request) {
try {
return await handleRequest(request, handlerInput);
} catch (error) {
const { onError } = handlerInput;

onError(error as AuthError);
}
},
});
};
4 changes: 4 additions & 0 deletions packages/adapter-nextjs/src/oauth/index.ts
@@ -0,0 +1,4 @@
// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
// SPDX-License-Identifier: Apache-2.0

export { createOAuthRouteHandlerFactory } from './createOAuthRouteHandlerFactory';

0 comments on commit 72feab7

Please sign in to comment.