From 8eaff3894df357fad3cd848efff7fdd2a97a99f1 Mon Sep 17 00:00:00 2001 From: Simon Kirsten Date: Sun, 10 Jul 2022 20:20:55 +0200 Subject: [PATCH] feat(core): add allowedAccountTokens option --- docs/docs/adapters/models.md | 2 +- docs/docs/configuration/options.md | 29 ++++++++++++++++++- docs/docs/providers/azure-ad-b2c.md | 2 +- .../next-auth/src/core/lib/oauth/callback.ts | 24 +++++++++++++-- packages/next-auth/src/core/types.ts | 10 +++++++ 5 files changed, 61 insertions(+), 6 deletions(-) diff --git a/docs/docs/adapters/models.md b/docs/docs/adapters/models.md index 3efd665b1f..9982d84192 100644 --- a/docs/docs/adapters/models.md +++ b/docs/docs/adapters/models.md @@ -72,7 +72,7 @@ User creation in the database is automatic, and happens when the user is logging The Account model is for information about OAuth accounts associated with a User. It will usually contain `access_token`, `id_token` and other OAuth specific data. [`TokenSet`](https://github.com/panva/node-openid-client/blob/main/docs/README.md#new-tokensetinput) from `openid-client` might give you an idea of all the fields. :::note -In case of an OAuth 1.0 provider (like Twitter), you will have to look for `oauth_token` and `oauth_token_secret` string fields. GitHub also has an extra `refresh_token_expires_in` integer field. You have to make sure that your database schema includes these fields. +In case of an OAuth 1.0 provider (like Twitter), you will have to look for `oauth_token` and `oauth_token_secret` string fields. GitHub also has an extra `refresh_token_expires_in` integer field. You have to make sure that your database schema includes these fields or configure the [`allowedAccountTokens`](/configuration/options#allowedaccounttokens) option. ::: A single User can have multiple Accounts, but each Account can only have one User. diff --git a/docs/docs/configuration/options.md b/docs/docs/configuration/options.md index b9925806bc..1fb1f5d9bf 100644 --- a/docs/docs/configuration/options.md +++ b/docs/docs/configuration/options.md @@ -29,7 +29,6 @@ Using [System Environment Variables](https://vercel.com/docs/concepts/projects/e Used to encrypt the NextAuth.js JWT, and to hash [email verification tokens](/adapters/models#verification-token). This is the default value for the `secret` option in [NextAuth](/configuration/options#secret) and [Middleware](/configuration/nextjs#secret). - ### NEXTAUTH_URL_INTERNAL If provided, server-side calls will use this instead of `NEXTAUTH_URL`. Useful in environments when the server doesn't have access to the canonical URL of your site. Defaults to `NEXTAUTH_URL`. @@ -308,6 +307,34 @@ By default NextAuth.js does not include an adapter any longer. If you would like --- +### allowedAccountTokens + +- **Default value**: All tokens are allowed +- **Required**: _No_ + +#### Description + +By default NextAuth.js stores all information about an OAuth account gathered by the provider on the [Account Model](/adapters/models#account). +These tokens can include fields that are not defined on the database schema. With this option the tokens can be filtered before being stored on the Account. + +_For example:_ + +```js +allowedAccountTokens: [ + "refresh_token", + "access_token", + "expires_at", + "token_type", + "scope", + "id_token", + "session_state", +], +``` + +ensures only the fields that are supported by the [default Prisma schema](/adapters/prisma) are stored. + +--- + ### debug - **Default value**: `false` diff --git a/docs/docs/providers/azure-ad-b2c.md b/docs/docs/providers/azure-ad-b2c.md index a19748ab3a..0b32da53d7 100644 --- a/docs/docs/providers/azure-ad-b2c.md +++ b/docs/docs/providers/azure-ad-b2c.md @@ -11,7 +11,7 @@ Azure AD B2C returns the following fields on `Account`: - `id_token_expires_in` (number) - `profile_info` (string). -See their [docs](https://docs.microsoft.com/en-us/azure/active-directory-b2c/access-tokens). Remember to add these fields to your database schema, in case if you are using an [Adapter](/adapters/overview). +See their [docs](https://docs.microsoft.com/en-us/azure/active-directory-b2c/access-tokens). Remember to add these fields to your database schema, in case if you are using an [Adapter](/adapters/overview) or configure the [`allowedAccountTokens`](/configuration/options#allowedaccounttokens) option. ::: ## Documentation diff --git a/packages/next-auth/src/core/lib/oauth/callback.ts b/packages/next-auth/src/core/lib/oauth/callback.ts index 333ed3d887..4550ff9a60 100644 --- a/packages/next-auth/src/core/lib/oauth/callback.ts +++ b/packages/next-auth/src/core/lib/oauth/callback.ts @@ -20,7 +20,7 @@ export default async function oAuthCallback(params: { cookies: RequestInternal["cookies"] }): Promise { const { options, query, body, method, cookies } = params - const { logger, provider } = options + const { logger, provider, allowedAccountTokens } = options const errorMessage = body?.error ?? query?.error if (errorMessage) { @@ -57,7 +57,13 @@ export default async function oAuthCallback(params: { profile = JSON.parse(profile) } - return await getProfile({ profile, tokens, provider, logger }) + return await getProfile({ + profile, + tokens, + provider, + allowedAccountTokens, + logger, + }) } catch (error) { logger.error("OAUTH_V1_GET_ACCESS_TOKEN_ERROR", error as Error) throw error @@ -142,6 +148,7 @@ export default async function oAuthCallback(params: { profile, provider, tokens, + allowedAccountTokens, logger, }) return { ...profileResult, cookies: resCookies } @@ -158,6 +165,7 @@ export interface GetProfileParams { profile: Profile tokens: TokenSet provider: OAuthConfig + allowedAccountTokens?: string[] logger: LoggerInstance } @@ -173,6 +181,7 @@ async function getProfile({ profile: OAuthProfile, tokens, provider, + allowedAccountTokens, logger, }: GetProfileParams): Promise { try { @@ -180,6 +189,15 @@ async function getProfile({ // @ts-expect-error const profile = await provider.profile(OAuthProfile, tokens) profile.email = profile.email?.toLowerCase() + + let accountTokens = { ...tokens } + if (allowedAccountTokens) { + accountTokens = Object.fromEntries( + Object.entries(accountTokens).filter(([key]) => + allowedAccountTokens.includes(key) + ) + ) + } // Return profile, raw profile and auth provider details return { profile, @@ -187,7 +205,7 @@ async function getProfile({ provider: provider.id, type: provider.type, providerAccountId: profile.id.toString(), - ...tokens, + ...accountTokens, }, OAuthProfile, } diff --git a/packages/next-auth/src/core/types.ts b/packages/next-auth/src/core/types.ts index f75fcf8b6b..b16ae2994b 100644 --- a/packages/next-auth/src/core/types.ts +++ b/packages/next-auth/src/core/types.ts @@ -120,6 +120,15 @@ export interface NextAuthOptions { * [Adapters Overview](https://next-auth.js.org/adapters/overview) */ adapter?: Adapter + /** + * Filter the tokens from the provider before storing it in the Account. + * + * * **Default value**: All tokens are allowed + * * **Required**: *No* + * + * [Documentation](https://next-auth.js.org/configuration/options#allowedaccounttokens) | + */ + allowedAccountTokens?: string[] /** * Set debug to true to enable debug messages for authentication and database operations. * * **Default value**: `false` @@ -536,6 +545,7 @@ export interface InternalOptions { jwt: JWTOptions events: Partial adapter?: Adapter + allowedAccountTokens?: string[] callbacks: CallbacksOptions cookies: CookiesOptions callbackUrl: string