@aws-amplify/adapter-nextjs 1.1.6 → 1.1.7-s-auth.30d0cd2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/cjs/auth/createTokenExchangeRouteHandlerFactory.js +52 -0
  2. package/dist/cjs/auth/createTokenExchangeRouteHandlerFactory.js.map +1 -0
  3. package/dist/cjs/auth/httpOnlyCookieBasedAuthProviders/createHttpOnlyCookieBasedAuthProviders.js +39 -0
  4. package/dist/cjs/auth/httpOnlyCookieBasedAuthProviders/createHttpOnlyCookieBasedAuthProviders.js.map +1 -0
  5. package/dist/cjs/auth/httpOnlyCookieBasedAuthProviders/index.js +8 -0
  6. package/dist/cjs/auth/httpOnlyCookieBasedAuthProviders/index.js.map +1 -0
  7. package/dist/cjs/auth/types.js +4 -0
  8. package/dist/cjs/auth/types.js.map +1 -0
  9. package/dist/cjs/client/index.js +7 -0
  10. package/dist/cjs/client/index.js.map +1 -0
  11. package/dist/cjs/createServerRunner.js +18 -1
  12. package/dist/cjs/createServerRunner.js.map +1 -1
  13. package/dist/cjs/oauth/createGetOAuthInitiationRouteFactory.js +22 -0
  14. package/dist/cjs/oauth/createGetOAuthInitiationRouteFactory.js.map +1 -0
  15. package/dist/cjs/oauth/createOAuthRouteHandlerFactory.js +55 -0
  16. package/dist/cjs/oauth/createOAuthRouteHandlerFactory.js.map +1 -0
  17. package/dist/cjs/oauth/index.js +9 -0
  18. package/dist/cjs/oauth/index.js.map +1 -0
  19. package/dist/cjs/oauth/types.js +6 -0
  20. package/dist/cjs/oauth/types.js.map +1 -0
  21. package/dist/cjs/oauth/utils/completeOAuthFlow.js +101 -0
  22. package/dist/cjs/oauth/utils/completeOAuthFlow.js.map +1 -0
  23. package/dist/cjs/oauth/utils/getRedirectUrl.js +20 -0
  24. package/dist/cjs/oauth/utils/getRedirectUrl.js.map +1 -0
  25. package/dist/cjs/oauth/utils/initOAuthFlow.js +70 -0
  26. package/dist/cjs/oauth/utils/initOAuthFlow.js.map +1 -0
  27. package/dist/cjs/utils/createRunWithAmplifyServerContext.js +2 -2
  28. package/dist/cjs/utils/createRunWithAmplifyServerContext.js.map +1 -1
  29. package/dist/esm/api/createServerRunnerForAPI.d.ts +1 -1
  30. package/dist/esm/auth/createTokenExchangeRouteHandlerFactory.d.ts +2 -0
  31. package/dist/esm/auth/createTokenExchangeRouteHandlerFactory.mjs +50 -0
  32. package/dist/esm/auth/createTokenExchangeRouteHandlerFactory.mjs.map +1 -0
  33. package/dist/esm/auth/httpOnlyCookieBasedAuthProviders/createHttpOnlyCookieBasedAuthProviders.d.ts +4 -0
  34. package/dist/esm/auth/httpOnlyCookieBasedAuthProviders/createHttpOnlyCookieBasedAuthProviders.mjs +37 -0
  35. package/dist/esm/auth/httpOnlyCookieBasedAuthProviders/createHttpOnlyCookieBasedAuthProviders.mjs.map +1 -0
  36. package/dist/esm/auth/httpOnlyCookieBasedAuthProviders/index.d.ts +2 -0
  37. package/dist/esm/auth/httpOnlyCookieBasedAuthProviders/index.mjs +3 -0
  38. package/dist/esm/auth/httpOnlyCookieBasedAuthProviders/index.mjs.map +1 -0
  39. package/dist/esm/auth/types.d.ts +17 -0
  40. package/dist/esm/auth/types.mjs +2 -0
  41. package/dist/esm/auth/types.mjs.map +1 -0
  42. package/dist/esm/client/index.d.ts +1 -0
  43. package/dist/esm/client/index.mjs +3 -0
  44. package/dist/esm/client/index.mjs.map +1 -0
  45. package/dist/esm/createServerRunner.mjs +18 -1
  46. package/dist/esm/createServerRunner.mjs.map +1 -1
  47. package/dist/esm/oauth/createGetOAuthInitiationRouteFactory.d.ts +2 -0
  48. package/dist/esm/oauth/createGetOAuthInitiationRouteFactory.mjs +20 -0
  49. package/dist/esm/oauth/createGetOAuthInitiationRouteFactory.mjs.map +1 -0
  50. package/dist/esm/oauth/createOAuthRouteHandlerFactory.d.ts +2 -0
  51. package/dist/esm/oauth/createOAuthRouteHandlerFactory.mjs +53 -0
  52. package/dist/esm/oauth/createOAuthRouteHandlerFactory.mjs.map +1 -0
  53. package/dist/esm/oauth/index.d.ts +1 -0
  54. package/dist/esm/oauth/index.mjs +2 -0
  55. package/dist/esm/oauth/index.mjs.map +1 -0
  56. package/dist/esm/oauth/types.d.ts +39 -0
  57. package/dist/esm/oauth/types.mjs +2 -0
  58. package/dist/esm/oauth/types.mjs.map +1 -0
  59. package/dist/esm/oauth/utils/completeOAuthFlow.d.ts +12 -0
  60. package/dist/esm/oauth/utils/completeOAuthFlow.mjs +99 -0
  61. package/dist/esm/oauth/utils/completeOAuthFlow.mjs.map +1 -0
  62. package/dist/esm/oauth/utils/getRedirectUrl.d.ts +2 -0
  63. package/dist/esm/oauth/utils/getRedirectUrl.mjs +18 -0
  64. package/dist/esm/oauth/utils/getRedirectUrl.mjs.map +1 -0
  65. package/dist/esm/oauth/utils/initOAuthFlow.d.ts +11 -0
  66. package/dist/esm/oauth/utils/initOAuthFlow.mjs +68 -0
  67. package/dist/esm/oauth/utils/initOAuthFlow.mjs.map +1 -0
  68. package/dist/esm/types/NextServer.d.ts +19 -1
  69. package/dist/esm/utils/createRunWithAmplifyServerContext.d.ts +2 -1
  70. package/dist/esm/utils/createRunWithAmplifyServerContext.mjs +2 -2
  71. package/dist/esm/utils/createRunWithAmplifyServerContext.mjs.map +1 -1
  72. package/package.json +78 -72
  73. package/src/api/createServerRunnerForAPI.ts +7 -1
  74. package/src/auth/createTokenExchangeRouteHandlerFactory.ts +70 -0
  75. package/src/auth/httpOnlyCookieBasedAuthProviders/createHttpOnlyCookieBasedAuthProviders.ts +57 -0
  76. package/src/auth/httpOnlyCookieBasedAuthProviders/index.ts +3 -0
  77. package/src/auth/types.ts +26 -0
  78. package/src/client/index.ts +1 -0
  79. package/src/createServerRunner.ts +19 -0
  80. package/src/oauth/createGetOAuthInitiationRouteFactory.ts +35 -0
  81. package/src/oauth/createOAuthRouteHandlerFactory.ts +77 -0
  82. package/src/oauth/index.ts +4 -0
  83. package/src/oauth/types.ts +60 -0
  84. package/src/oauth/utils/completeOAuthFlow.ts +176 -0
  85. package/src/oauth/utils/getRedirectUrl.ts +23 -0
  86. package/src/oauth/utils/initOAuthFlow.ts +109 -0
  87. package/src/types/NextServer.ts +27 -1
  88. package/src/utils/createRunWithAmplifyServerContext.ts +3 -0
@@ -0,0 +1,176 @@
1
+ import {
2
+ CognitoUserPoolConfig,
3
+ OAuthConfig,
4
+ decodeJWT,
5
+ } from '@aws-amplify/core';
6
+ import { NextRequest, NextResponse } from 'next/server.js';
7
+ import {
8
+ createKeyValueStorageFromCookieStorageAdapter,
9
+ validateState,
10
+ } from 'aws-amplify/adapter-core';
11
+ import {
12
+ AuthenticationResultType,
13
+ CognitoAuthSignInDetails,
14
+ DefaultOAuthStore,
15
+ DefaultTokenStore,
16
+ DeviceMetadata,
17
+ TokenOrchestrator,
18
+ } from '@aws-amplify/auth/cognito';
19
+
20
+ import { NextServer } from '../../types';
21
+ import { createCookieStorageAdapterFromNextServerContext } from '../../utils/createCookieStorageAdapterFromNextServerContext';
22
+
23
+ import { getRedirectUrl } from './getRedirectUrl';
24
+
25
+ export const completeOAuthFlow = async ({
26
+ origin,
27
+ request,
28
+ redirectOnComplete,
29
+ cognitoUserPoolConfig,
30
+ oAuthConfig,
31
+ setAuthCookieOptions,
32
+ }: {
33
+ origin: string;
34
+ request: NextRequest;
35
+ customState: string | undefined;
36
+ redirectOnComplete: string;
37
+ cognitoUserPoolConfig: CognitoUserPoolConfig;
38
+ oAuthConfig: OAuthConfig;
39
+ setAuthCookieOptions?: NextServer.SetCookieOptions;
40
+ }): Promise<Response> => {
41
+ const { searchParams } = request.nextUrl;
42
+ const code = searchParams.get('code')!;
43
+ const state = searchParams.get('state')!;
44
+
45
+ const oAuthTokenEndpoint = `https://${oAuthConfig.domain}/oauth2/token`;
46
+
47
+ const response = NextResponse.redirect(
48
+ new URL(redirectOnComplete, request.url),
49
+ );
50
+
51
+ const keyValueStorage = createKeyValueStorageFromCookieStorageAdapter(
52
+ createCookieStorageAdapterFromNextServerContext({
53
+ request,
54
+ response,
55
+ }),
56
+ setAuthCookieOptions,
57
+ );
58
+ const oAuthStore = new DefaultOAuthStore(keyValueStorage);
59
+ oAuthStore.setAuthConfig(cognitoUserPoolConfig);
60
+
61
+ await validateState(oAuthStore, state);
62
+
63
+ const authTokenStore = new DefaultTokenStore();
64
+ authTokenStore.setAuthConfig({ Cognito: cognitoUserPoolConfig });
65
+ authTokenStore.setKeyValueStorage(keyValueStorage);
66
+ const tokenOrchestrator = new TokenOrchestrator();
67
+ tokenOrchestrator.setAuthConfig({ Cognito: cognitoUserPoolConfig });
68
+ tokenOrchestrator.setAuthTokenStore(authTokenStore);
69
+
70
+ const codeVerifier = await oAuthStore.loadPKCE();
71
+
72
+ const oAuthTokenBody = {
73
+ grant_type: 'authorization_code',
74
+ code,
75
+ client_id: cognitoUserPoolConfig.userPoolClientId,
76
+ // TODO(Hui): request.nextUrl.origin should be generic and not use Next specifics
77
+ redirect_uri: getRedirectUrl(origin, oAuthConfig),
78
+ ...(codeVerifier ? { code_verifier: codeVerifier } : {}),
79
+ };
80
+
81
+ const body = Object.entries(oAuthTokenBody)
82
+ .map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`)
83
+ .join('&');
84
+
85
+ const tokenExchangeResponse = await fetch(oAuthTokenEndpoint, {
86
+ method: 'POST',
87
+ headers: {
88
+ 'Content-Type': 'application/x-www-form-urlencoded',
89
+ },
90
+ body,
91
+ });
92
+
93
+ const {
94
+ access_token,
95
+ refresh_token: refreshToken,
96
+ id_token,
97
+ error,
98
+ error_message: errorMessage,
99
+ token_type,
100
+ expires_in,
101
+ } = await tokenExchangeResponse.json();
102
+
103
+ if (error) {
104
+ throw new Error(errorMessage ?? error);
105
+ }
106
+
107
+ const username =
108
+ (access_token && decodeJWT(access_token).payload.username) ?? 'username';
109
+
110
+ await writeTokensToStorage(
111
+ {
112
+ username,
113
+ AccessToken: access_token,
114
+ IdToken: id_token,
115
+ RefreshToken: refreshToken,
116
+ TokenType: token_type,
117
+ ExpiresIn: expires_in,
118
+ },
119
+ tokenOrchestrator,
120
+ );
121
+
122
+ await oAuthStore.clearOAuthData();
123
+
124
+ return response;
125
+ };
126
+
127
+ const writeTokensToStorage = async (
128
+ payload: AuthenticationResultType & {
129
+ NewDeviceMetadata?: DeviceMetadata;
130
+ username: string;
131
+ signInDetails?: CognitoAuthSignInDetails;
132
+ },
133
+ tokenOrchestrator: TokenOrchestrator,
134
+ ) => {
135
+ if (!payload.AccessToken) {
136
+ return;
137
+ }
138
+
139
+ const accessToken = decodeJWT(payload.AccessToken);
140
+ const accessTokenIssuedAtInMillis = (accessToken.payload.iat || 0) * 1000;
141
+ const currentTime = new Date().getTime();
142
+ const clockDrift =
143
+ accessTokenIssuedAtInMillis > 0
144
+ ? accessTokenIssuedAtInMillis - currentTime
145
+ : 0;
146
+ let idToken;
147
+ let refreshToken: string | undefined;
148
+ let deviceMetadata;
149
+
150
+ if (payload.RefreshToken) {
151
+ refreshToken = payload.RefreshToken;
152
+ }
153
+
154
+ if (payload.IdToken) {
155
+ idToken = decodeJWT(payload.IdToken);
156
+ }
157
+
158
+ if (payload?.NewDeviceMetadata) {
159
+ deviceMetadata = payload.NewDeviceMetadata;
160
+ }
161
+
162
+ const tokens: any = {
163
+ accessToken,
164
+ idToken,
165
+ refreshToken,
166
+ clockDrift,
167
+ deviceMetadata,
168
+ username: payload.username,
169
+ };
170
+
171
+ if (payload?.signInDetails) {
172
+ tokens.signInDetails = payload.signInDetails;
173
+ }
174
+
175
+ await tokenOrchestrator.setTokens({ tokens });
176
+ };
@@ -0,0 +1,23 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import { AuthError } from '@aws-amplify/auth';
5
+ import { OAuthConfig } from '@aws-amplify/core';
6
+
7
+ export const getRedirectUrl = (origin: string, oAuthConfig: OAuthConfig) => {
8
+ const redirectUrl = oAuthConfig.redirectSignIn.find(url =>
9
+ url.startsWith(origin),
10
+ );
11
+
12
+ if (!redirectUrl) {
13
+ throw new AuthError({
14
+ name: 'InvalidRedirectException',
15
+ message:
16
+ 'signInRedirect or signOutRedirect had an invalid format or was not found.',
17
+ recoverySuggestion:
18
+ 'Please make sure the signIn/Out redirect in your oauth config is valid.',
19
+ });
20
+ }
21
+
22
+ return redirectUrl;
23
+ };
@@ -0,0 +1,109 @@
1
+ // Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
2
+ // SPDX-License-Identifier: Apache-2.0
3
+
4
+ import {
5
+ AuthProvider,
6
+ cognitoHostedUIIdentityProviderMap,
7
+ createKeyValueStorageFromCookieStorageAdapter,
8
+ generateCodeVerifier,
9
+ generateState,
10
+ } from 'aws-amplify/adapter-core';
11
+ import { NextRequest, NextResponse } from 'next/server.js';
12
+ import { urlSafeEncode } from '@aws-amplify/core/internals/utils';
13
+ import { CognitoUserPoolConfig, OAuthConfig } from '@aws-amplify/core';
14
+ import { DefaultOAuthStore } from '@aws-amplify/auth/cognito';
15
+
16
+ import { createCookieStorageAdapterFromNextServerContext } from '../../utils/createCookieStorageAdapterFromNextServerContext';
17
+ import { NextServer } from '../../types';
18
+
19
+ import { getRedirectUrl } from './getRedirectUrl';
20
+
21
+ export const initOAuthFlow = async ({
22
+ request,
23
+ customState,
24
+ cognitoUserPoolConfig,
25
+ oAuthConfig,
26
+ setAuthCookieOptions,
27
+ }: {
28
+ origin: string;
29
+ request: NextRequest;
30
+ customState: string | undefined;
31
+ cognitoUserPoolConfig: CognitoUserPoolConfig;
32
+ oAuthConfig: OAuthConfig;
33
+ setAuthCookieOptions?: NextServer.SetCookieOptions;
34
+ }): Promise<Response> => {
35
+ const { searchParams } = request.nextUrl;
36
+ const specifiedProvider = searchParams.get('provider');
37
+ const provider = getProvider(specifiedProvider);
38
+ const randomState = generateState();
39
+ const state = customState
40
+ ? `${randomState}-${urlSafeEncode(customState)}`
41
+ : randomState;
42
+ const scope = oAuthConfig.scopes.join(' ');
43
+
44
+ const redirectUrlSearchParams = new URLSearchParams({
45
+ redirect_uri: getRedirectUrl(origin, oAuthConfig),
46
+ response_type: oAuthConfig.responseType,
47
+ client_id: cognitoUserPoolConfig.userPoolClientId!,
48
+ identity_provider: provider,
49
+ scope,
50
+ state,
51
+ });
52
+
53
+ let peckKey: string | undefined;
54
+
55
+ if (oAuthConfig.responseType === 'code') {
56
+ const { value, method, toCodeChallenge } = generateCodeVerifier(128);
57
+
58
+ peckKey = value;
59
+ redirectUrlSearchParams.append('code_challenge', toCodeChallenge());
60
+ redirectUrlSearchParams.append('code_challenge_method', method);
61
+ }
62
+
63
+ const redirectUrl = new URL(
64
+ `https://${
65
+ oAuthConfig.domain
66
+ }/oauth2/authorize?${redirectUrlSearchParams.toString()}`,
67
+ );
68
+
69
+ const response = NextResponse.redirect(redirectUrl);
70
+ const keyValueStorage = createKeyValueStorageFromCookieStorageAdapter(
71
+ createCookieStorageAdapterFromNextServerContext({
72
+ request,
73
+ response,
74
+ }),
75
+ setAuthCookieOptions,
76
+ );
77
+ const oauthStore = new DefaultOAuthStore(keyValueStorage);
78
+ oauthStore.setAuthConfig(cognitoUserPoolConfig);
79
+ oauthStore.storeOAuthState(state);
80
+ peckKey && oauthStore.storePKCE(peckKey);
81
+
82
+ return response;
83
+ };
84
+
85
+ const getProvider = (provider: string | null): string => {
86
+ if (typeof provider === 'string') {
87
+ return resolveProvider(provider);
88
+ }
89
+
90
+ return 'COGNITO';
91
+ };
92
+
93
+ const resolveProvider = (provider: string): string => {
94
+ try {
95
+ assertAuthProvider(provider);
96
+
97
+ return cognitoHostedUIIdentityProviderMap[provider];
98
+ } catch (_) {
99
+ return provider;
100
+ }
101
+ };
102
+
103
+ function assertAuthProvider(
104
+ provider: string,
105
+ ): asserts provider is AuthProvider {
106
+ if (!['Amazon', 'Apple', 'Facebook', 'Google'].includes(provider)) {
107
+ throw new Error('No valid provider specified.');
108
+ }
109
+ }
@@ -4,11 +4,22 @@
4
4
  import { GetServerSidePropsContext as NextGetServerSidePropsContext } from 'next';
5
5
  import { NextRequest, NextResponse } from 'next/server.js';
6
6
  import { cookies } from 'next/headers.js';
7
- import { AmplifyOutputs, LegacyConfig } from 'aws-amplify/adapter-core';
7
+ import {
8
+ AmplifyOutputs,
9
+ CookieStorage,
10
+ LegacyConfig,
11
+ } from 'aws-amplify/adapter-core';
8
12
  import { AmplifyServer } from '@aws-amplify/core/internals/adapter-core';
9
13
  import { ResourcesConfig } from '@aws-amplify/core';
10
14
 
15
+ import {
16
+ CreateOAuthRouteHandler,
17
+ GetOAuthInitiationRoute,
18
+ } from '../oauth/types';
19
+ import { CreateTokenExchangeRouteHandler } from '../auth/types';
20
+
11
21
  export declare namespace NextServer {
22
+ export type SetCookieOptions = CookieStorage.SetCookieOptions;
12
23
  /**
13
24
  * This context is normally available in the following:
14
25
  * - Next App Router [middleware](https://nextjs.org/docs/app/building-your-application/routing/middleware)
@@ -74,11 +85,26 @@ export declare namespace NextServer {
74
85
  ) => Promise<OperationResult>;
75
86
 
76
87
  export interface CreateServerRunnerInput {
88
+ /**
89
+ * The Amplify resources config. Typically imported from `amplify_outputs.json` (Gen2)
90
+ * or `amplifyconfiguration.json` (Gen1).
91
+ */
77
92
  config: ResourcesConfig | LegacyConfig | AmplifyOutputs;
93
+ /**
94
+ * The origin of your Next app.
95
+ */
96
+ origin?: string;
97
+ /**
98
+ * Configures attributes of Set-Cookie.
99
+ */
100
+ setAuthCookieOptions?: SetCookieOptions;
78
101
  }
79
102
 
80
103
  export interface CreateServerRunnerOutput {
81
104
  runWithAmplifyServerContext: RunOperationWithContext;
105
+ createOAuthRouteHandler: CreateOAuthRouteHandler;
106
+ getOAuthInitiationRoute: GetOAuthInitiationRoute;
107
+ createTokenExchangeRouteHandler: CreateTokenExchangeRouteHandler;
82
108
  }
83
109
 
84
110
  export type CreateServerRunner = (
@@ -15,8 +15,10 @@ import { createCookieStorageAdapterFromNextServerContext } from './createCookieS
15
15
 
16
16
  export const createRunWithAmplifyServerContext = ({
17
17
  config: resourcesConfig,
18
+ setAuthCookieOptions,
18
19
  }: {
19
20
  config: ResourcesConfig;
21
+ setAuthCookieOptions?: NextServer.SetCookieOptions;
20
22
  }) => {
21
23
  const runWithAmplifyServerContext: NextServer.RunOperationWithContext =
22
24
  async ({ nextServerContext, operation }) => {
@@ -34,6 +36,7 @@ export const createRunWithAmplifyServerContext = ({
34
36
  createCookieStorageAdapterFromNextServerContext(
35
37
  nextServerContext,
36
38
  ),
39
+ setAuthCookieOptions,
37
40
  );
38
41
  const credentialsProvider = createAWSCredentialsAndIdentityIdProvider(
39
42
  resourcesConfig.Auth,