diff --git a/cypress/e2e/app.cy.ts b/cypress/e2e/app.cy.ts index 9394030fc..4ac44217a 100644 --- a/cypress/e2e/app.cy.ts +++ b/cypress/e2e/app.cy.ts @@ -1,5 +1,5 @@ const getUser = () => - ({ email: `${crypto.randomUUID()}@local.host`, password: 'Loc@l.h0st' } as const); + ({ email: `${crypto.randomUUID()}@local.host`, password: 'Loc@l.h0st' }) as const; Cypress.on('uncaught:exception', (_err, _runnable) => { return false; @@ -82,3 +82,11 @@ it('oidc login for organization', () => { cy.get('[data-cy="organization-picker-current"]').contains('Bubatzbieber'); }); }); + +it('oidc login for invalid url shows correct error message', () => { + cy.clearAllCookies(); + cy.clearAllLocalStorage(); + cy.clearAllSessionStorage(); + cy.visit('/auth/oidc?id=invalid'); + cy.get('div.text-red-500').contains('Could not find OIDC integration.'); +}); diff --git a/packages/services/api/package.json b/packages/services/api/package.json index e6bd13ae9..c7b2b3968 100644 --- a/packages/services/api/package.json +++ b/packages/services/api/package.json @@ -56,7 +56,7 @@ "param-case": "3.0.4", "prom-client": "15.0.0", "redlock": "5.0.0-beta.2", - "supertokens-node": "14.1.3", + "supertokens-node": "15.2.1", "tslib": "2.6.2", "zod": "3.22.4", "zod-validation-error": "2.1.0" diff --git a/packages/web/app/package.json b/packages/web/app/package.json index 7a2a61fd8..8ac4d274f 100644 --- a/packages/web/app/package.json +++ b/packages/web/app/package.json @@ -84,10 +84,10 @@ "react-window": "1.8.9", "regenerator-runtime": "0.14.0", "snarkdown": "2.0.0", - "supertokens-auth-react": "0.33.1", + "supertokens-auth-react": "0.35.6", "supertokens-js-override": "0.0.4", - "supertokens-node": "14.1.3", - "supertokens-web-js": "0.6.0", + "supertokens-node": "15.2.1", + "supertokens-web-js": "0.8.0", "tailwind-merge": "2.0.0", "tslib": "2.6.2", "urql": "4.0.5", diff --git a/packages/web/app/src/config/supertokens/backend.ts b/packages/web/app/src/config/supertokens/backend.ts index a09d10ff6..5c549094e 100644 --- a/packages/web/app/src/config/supertokens/backend.ts +++ b/packages/web/app/src/config/supertokens/backend.ts @@ -2,7 +2,7 @@ import * as crypto from 'node:crypto'; import { OverrideableBuilder } from 'supertokens-js-override/lib/build'; import EmailVerification from 'supertokens-node/recipe/emailverification'; import SessionNode from 'supertokens-node/recipe/session'; -import { TypeProvider } from 'supertokens-node/recipe/thirdparty/types'; +import type { ProviderInput } from 'supertokens-node/recipe/thirdparty/types'; import ThirdPartyEmailPasswordNode from 'supertokens-node/recipe/thirdpartyemailpassword'; import { TypeInput as ThirdPartEmailPasswordTypeInput } from 'supertokens-node/recipe/thirdpartyemailpassword/types'; import { TypeInput } from 'supertokens-node/types'; @@ -10,14 +10,12 @@ import zod from 'zod'; import { env } from '@/env/backend'; import { appInfo } from '@/lib/supertokens/app-info'; import { - createOIDCSuperTokensNoopProvider, - getOIDCThirdPartyEmailPasswordNodeOverrides, + createOIDCSuperTokensProvider, + getOIDCSuperTokensOverrides, } from '@/lib/supertokens/third-party-email-password-node-oidc-provider'; import { createThirdPartyEmailPasswordNodeOktaProvider } from '@/lib/supertokens/third-party-email-password-node-okta-provider'; -// eslint-disable-next-line import/no-extraneous-dependencies -- TODO: should we move to "dependencies"? -import { EmailsApi } from '@hive/emails'; -// eslint-disable-next-line import/no-extraneous-dependencies -- TODO: should we move to "dependencies"? -import { type InternalApi } from '@hive/server'; +import type { EmailsApi } from '@hive/emails'; +import type { InternalApi } from '@hive/server'; import { createTRPCProxyClient, CreateTRPCProxyClient, httpLink } from '@trpc/client'; import { fetch } from '@whatwg-node/fetch'; @@ -28,29 +26,39 @@ export const backendConfig = (): TypeInput => { const internalApi = createTRPCProxyClient({ links: [httpLink({ url: `${env.serverEndpoint}/trpc` })], }); - const providers: TypeProvider[] = []; + const providers: ProviderInput[] = []; if (env.auth.github) { - providers.push( - ThirdPartyEmailPasswordNode.Github({ - clientId: env.auth.github.clientId, - clientSecret: env.auth.github.clientSecret, - scope: ['read:user', 'user:email'], - }), - ); + providers.push({ + config: { + thirdPartyId: 'github', + clients: [ + { + scope: ['read:user', 'user:email'], + clientId: env.auth.github.clientId, + clientSecret: env.auth.github.clientSecret, + }, + ], + }, + }); } if (env.auth.google) { - providers.push( - ThirdPartyEmailPasswordNode.Google({ - clientId: env.auth.google.clientId, - clientSecret: env.auth.google.clientSecret, - scope: [ - 'https://www.googleapis.com/auth/userinfo.email', - 'https://www.googleapis.com/auth/userinfo.profile', - 'openid', + providers.push({ + config: { + thirdPartyId: 'google', + clients: [ + { + clientId: env.auth.google.clientId, + clientSecret: env.auth.google.clientSecret, + scope: [ + 'https://www.googleapis.com/auth/userinfo.email', + 'https://www.googleapis.com/auth/userinfo.profile', + 'openid', + ], + }, ], - }), - ); + }, + }); } if (env.auth.okta) { @@ -58,7 +66,11 @@ export const backendConfig = (): TypeInput => { } if (env.auth.organizationOIDC) { - providers.push(createOIDCSuperTokensNoopProvider()); + providers.push( + createOIDCSuperTokensProvider({ + internalApi, + }), + ); } return { @@ -91,9 +103,7 @@ export const backendConfig = (): TypeInput => { }, override: composeSuperTokensOverrides([ getEnsureUserOverrides(internalApi), - env.auth.organizationOIDC - ? getOIDCThirdPartyEmailPasswordNodeOverrides({ internalApi }) - : null, + env.auth.organizationOIDC ? getOIDCSuperTokensOverrides() : null, /** * These overrides are only relevant for the legacy Auth0 -> SuperTokens migration (period). */ @@ -206,14 +216,24 @@ const getEnsureUserOverrides = ( throw Error('Should never come here'); } + function extractOidcId(args: typeof input) { + if (input.provider.id === 'oidc') { + // eslint-disable-next-line prefer-destructuring + const oidcId: unknown = args.userContext['oidcId']; + if (typeof oidcId === 'string') { + return oidcId; + } + } + return null; + } + const response = await originalImplementation.thirdPartySignInUpPOST(input); if (response.status === 'OK') { await internalApi.ensureUser.mutate({ superTokensUserId: response.user.id, email: response.user.email, - // This is provided via `getOIDCThirdPartyEmailPasswordNodeOverrides` if it is enabled. - oidcIntegrationId: input.userContext['oidcIntegrationId'] ?? null, + oidcIntegrationId: extractOidcId(input), }); } @@ -295,7 +315,7 @@ const getAuth0Overrides = (config: Exclude) = if (email) { // We first use the existing implementation for looking for users within supertokens. - const users = await ThirdPartyEmailPasswordNode.getUsersByEmail(email); + const users = await ThirdPartyEmailPasswordNode.getUsersByEmail('public', email); // If there is no email/password SuperTokens user yet, we need to check if there is an Auth0 user for this email. if (!users.some(user => user.thirdParty == null)) { @@ -308,6 +328,7 @@ const getAuth0Overrides = (config: Exclude) = if (dbUser) { // If we have this user within our database we create our new supertokens user const newUserResult = await ThirdPartyEmailPasswordNode.emailPasswordSignUp( + 'public', dbUser.email, await generateRandomPassword(), ); @@ -337,6 +358,7 @@ const getAuth0Overrides = (config: Exclude) = if (await doesUserExistInAuth0(config, input.email)) { // check if user exists in SuperTokens const superTokensUsers = await this.getUsersByEmail({ + tenantId: 'public', email: input.email, userContext: input.userContext, }); diff --git a/packages/web/app/src/config/supertokens/frontend.ts b/packages/web/app/src/config/supertokens/frontend.ts index c69c4737b..f2592462a 100644 --- a/packages/web/app/src/config/supertokens/frontend.ts +++ b/packages/web/app/src/config/supertokens/frontend.ts @@ -9,7 +9,6 @@ import { createThirdPartyEmailPasswordReactOIDCProvider, getOIDCOverrides, } from '@/lib/supertokens/third-party-email-password-react-oidc-provider'; -import { createThirdPartyEmailPasswordReactOktaProvider } from '@/lib/supertokens/third-party-email-password-react-okta-provider'; export const frontendConfig = () => { const providers: Array = []; @@ -32,7 +31,7 @@ export const frontendConfig = () => { // Only show Okta via query parameter new URLSearchParams(globalThis.window?.location.search ?? '').get('show_okta') === '1')) ) { - providers.push(createThirdPartyEmailPasswordReactOktaProvider()); + providers.push(ThirdPartyEmailPasswordReact.Okta.init()); } const url = new URL(globalThis.window.location.toString()); diff --git a/packages/web/app/src/lib/supertokens/start-auth-flow-for-provider.ts b/packages/web/app/src/lib/supertokens/start-auth-flow-for-provider.ts index d1cfb5189..3258b5e2d 100644 --- a/packages/web/app/src/lib/supertokens/start-auth-flow-for-provider.ts +++ b/packages/web/app/src/lib/supertokens/start-auth-flow-for-provider.ts @@ -4,10 +4,10 @@ import { env } from '@/env/frontend'; /** * utility for starting the login flow manually without clicking a button */ -export const startAuthFlowForProvider = async (providerId: 'google' | 'okta' | 'github') => { +export const startAuthFlowForProvider = async (thirdPartyId: 'google' | 'okta' | 'github') => { const authUrl = await getAuthorisationURLWithQueryParamsAndSetState({ - providerId, - authorisationURL: `${env.appBaseUrl}/auth/callback/${providerId}`, + thirdPartyId, + frontendRedirectURI: `${env.appBaseUrl}/auth/callback/${thirdPartyId}`, }); window.location.assign(authUrl); diff --git a/packages/web/app/src/lib/supertokens/third-party-email-password-node-oidc-provider.ts b/packages/web/app/src/lib/supertokens/third-party-email-password-node-oidc-provider.ts index 8df0ac239..1fb37337f 100644 --- a/packages/web/app/src/lib/supertokens/third-party-email-password-node-oidc-provider.ts +++ b/packages/web/app/src/lib/supertokens/third-party-email-password-node-oidc-provider.ts @@ -1,12 +1,143 @@ -import { ExpressRequest } from 'supertokens-node/lib/build/framework/express/framework'; -import ThirdPartyEmailPasswordNode from 'supertokens-node/recipe/thirdpartyemailpassword'; -import { TypeInput as ThirdPartEmailPasswordTypeInput } from 'supertokens-node/recipe/thirdpartyemailpassword/types'; +import type { ExpressRequest } from 'supertokens-node/lib/build/framework/express/framework'; +import type { ProviderInput } from 'supertokens-node/recipe/thirdparty/types'; +import type { TypeInput as ThirdPartEmailPasswordTypeInput } from 'supertokens-node/recipe/thirdpartyemailpassword/types'; import zod from 'zod'; -import { env } from '@/env/backend'; import { getLogger } from '@/server-logger'; -// eslint-disable-next-line import/no-extraneous-dependencies -- TODO: should we move to "dependencies"? -import { type InternalApi } from '@hive/server'; -import { CreateTRPCProxyClient } from '@trpc/client'; +import type { InternalApi } from '@hive/server'; +import type { CreateTRPCProxyClient } from '@trpc/client'; + +const couldNotResolveOidcIntegrationSymbol = Symbol('could_not_resolve_oidc_integration'); + +export const getOIDCSuperTokensOverrides = (): ThirdPartEmailPasswordTypeInput['override'] => ({ + apis(originalImplementation) { + return { + ...originalImplementation, + async authorisationUrlGET(input) { + if (input.userContext?.[couldNotResolveOidcIntegrationSymbol] === true) { + return { + status: 'GENERAL_ERROR', + message: 'Could not find OIDC integration.', + }; + } + + return originalImplementation.authorisationUrlGET!(input); + }, + }; + }, +}); + +export const createOIDCSuperTokensProvider = (args: { + internalApi: CreateTRPCProxyClient; +}): ProviderInput => ({ + config: { + thirdPartyId: 'oidc', + }, + override(originalImplementation) { + const logger = getLogger(); + return { + ...originalImplementation, + + async getConfigForClientType(input) { + logger.info('resolve config for OIDC provider.'); + const config = await getOIDCConfigFromInput(args.internalApi, input); + if (!config) { + // In the next step the override `authorisationUrlGET` from `getOIDCSuperTokensOverrides` is called. + // We use the user context to return a `GENERAL_ERROR` with a human readable message. + // We cannot return an error here (except an "Unexpected error"), so we also need to return fake dat + input.userContext[couldNotResolveOidcIntegrationSymbol] = true; + + return { + thirdPartyId: 'oidc', + get clientId(): string { + throw new Error('Noop value accessed.'); + }, + }; + } + + return { + thirdPartyId: 'oidc', + clientId: config.clientId, + clientSecret: config.clientSecret, + authorizationEndpoint: config.authorizationEndpoint, + userInfoEndpoint: config.userinfoEndpoint, + tokenEndpoint: config.tokenEndpoint, + scope: ['openid', 'email'], + }; + }, + + async getAuthorisationRedirectURL(input) { + logger.info('resolve config for OIDC provider.'); + const oidcConfig = await getOIDCConfigFromInput(args.internalApi, input); + + if (!oidcConfig) { + // This case should never be reached (guarded by getConfigForClientType). + // We still have it for security reasons. + throw new Error('Could not find OIDC integration.'); + } + + const authorizationRedirectUrl = + await originalImplementation.getAuthorisationRedirectURL(input); + + const url = new URL(authorizationRedirectUrl.urlWithQueryParams); + url.searchParams.set('state', oidcConfig.id); + + return { + ...authorizationRedirectUrl, + urlWithQueryParams: url.toString(), + }; + }, + + async getUserInfo(input) { + logger.info('retrieve profile info from OIDC provider'); + const config = await getOIDCConfigFromInput(args.internalApi, input); + if (!config) { + // This case should never be reached (guarded by getConfigForClientType). + // We still have it for security reasons. + throw new Error('Could not find OIDC integration.'); + } + + logger.info('fetch info for OIDC provider (oidcId=%s)', config.id); + + const tokenResponse = OIDCTokenSchema.parse(input.oAuthTokens); + const rawData: unknown = await fetch(config.userinfoEndpoint, { + headers: { + authorization: `Bearer ${tokenResponse.access_token}`, + accept: 'application/json', + 'content-type': 'application/json', + }, + }).then(res => res.json()); + + logger.info('retrieved profile info for provider (oidcId=%s)', config.id); + + const data = OIDCProfileInfoSchema.parse(rawData); + + // Set the oidcId to the user context so it can be used in `thirdPartySignInUpPOST` for linking the user account to the OIDC integration. + input.userContext.oidcId = config.id; + + return { + thirdPartyUserId: `${config.id}-${data.sub}`, + email: { + id: data.email, + isVerified: true, + }, + rawUserInfoFromProvider: { + fromIdTokenPayload: undefined, + fromUserInfoAPI: undefined, + }, + }; + }, + }; + }, +}); + +type OIDCCOnfig = { + id: string; + clientId: string; + clientSecret: string; + tokenEndpoint: string; + userinfoEndpoint: string; + authorizationEndpoint: string; +}; const OIDCProfileInfoSchema = zod.object({ sub: zod.string(), @@ -15,69 +146,6 @@ const OIDCProfileInfoSchema = zod.object({ const OIDCTokenSchema = zod.object({ access_token: zod.string() }); -const createOIDCSuperTokensProvider = (oidcConfig: { - id: string; - clientId: string; - clientSecret: string; - tokenEndpoint: string; - userinfoEndpoint: string; - authorizationEndpoint: string; -}): ThirdPartyEmailPasswordNode.TypeProvider => ({ - id: 'oidc', - get: (redirectURI, authCodeFromRequest) => ({ - getClientId: () => oidcConfig.clientId, - getProfileInfo: async (rawTokenAPIResponse: unknown) => { - const tokenResponse = OIDCTokenSchema.parse(rawTokenAPIResponse); - const rawData: unknown = await fetch(oidcConfig.userinfoEndpoint, { - headers: { - authorization: `Bearer ${tokenResponse.access_token}`, - accept: 'application/json', - 'content-type': 'application/json', - }, - }).then(res => res.json()); - - const logger = getLogger(); - logger.info( - `getProfileInfo: fetched OIDC (${ - oidcConfig.userinfoEndpoint - }) profile info: ${JSON.stringify(rawData)}`, - ); - - const data = OIDCProfileInfoSchema.parse(rawData); - - return { - // We scope the user id to the oidc config id to avoid potential collisions - id: `${oidcConfig.id}-${data.sub}`, - email: { - id: data.email, - isVerified: true, - }, - }; - }, - accessTokenAPI: { - url: oidcConfig.tokenEndpoint, - params: { - client_id: oidcConfig.clientId, - client_secret: oidcConfig.clientSecret, - grant_type: 'authorization_code', - redirect_uri: redirectURI ?? '', - code: authCodeFromRequest ?? '', - }, - }, - authorisationRedirect: { - // this contains info about forming the authorisation redirect URL without the state params and without the redirect_uri param - url: oidcConfig.authorizationEndpoint, - params: { - client_id: oidcConfig.clientId, - scope: 'openid email', - response_type: 'code', - redirect_uri: `${env.appBaseUrl}/auth/callback/oidc`, - state: oidcConfig.id, - }, - }, - }), -}); - const getOIDCIdFromInput = (input: { userContext: any }): string => { const expressRequest = input.userContext._default.request as ExpressRequest; const originalUrl = 'http://localhost' + expressRequest.getOriginalURL(); @@ -92,68 +160,29 @@ const getOIDCIdFromInput = (input: { userContext: any }): string => { return oidcId; }; -export const getOIDCThirdPartyEmailPasswordNodeOverrides = (args: { - internalApi: CreateTRPCProxyClient; -}): ThirdPartEmailPasswordTypeInput['override'] => ({ - apis: originalImplementation => ({ - ...originalImplementation, - thirdPartySignInUpPOST: async input => { - if (input.provider.id !== 'oidc') { - return originalImplementation.thirdPartySignInUpPOST!(input); - } +const configCache = new WeakMap(); - const oidcId = getOIDCIdFromInput(input); - const config = await fetchOIDCConfig(args.internalApi, oidcId); +/** + * Get cached OIDC config from the supertokens input. + */ +async function getOIDCConfigFromInput( + internalApi: CreateTRPCProxyClient, + input: { userContext: any }, +) { + const expressRequest = input.userContext._default.request as ExpressRequest; + if (configCache.has(expressRequest)) { + return configCache.get(expressRequest) ?? null; + } - if (config === null) { - return { - status: 'GENERAL_ERROR', - message: 'Could not find OIDC integration.', - }; - } - - return originalImplementation.thirdPartySignInUpPOST!({ - ...input, - provider: createOIDCSuperTokensProvider(config), - userContext: { - ...input.userContext, - oidcIntegrationId: oidcId, - }, - }); - }, - authorisationUrlGET: async input => { - if (input.provider.id !== 'oidc') { - return originalImplementation.authorisationUrlGET!(input); - } - - const oidcId = getOIDCIdFromInput(input); - const config = await fetchOIDCConfig(args.internalApi, oidcId); - - if (config === null) { - return { - status: 'GENERAL_ERROR', - message: 'Could not find OIDC integration.', - }; - } - - const result = originalImplementation.authorisationUrlGET!({ - ...input, - provider: createOIDCSuperTokensProvider(config), - }); - - return result; - }, - }), -}); - -export const createOIDCSuperTokensNoopProvider = () => ({ - id: 'oidc', - get() { + const oidcId = getOIDCIdFromInput(input); + const config = await fetchOIDCConfig(internalApi, oidcId); + configCache.set(expressRequest, config); + if (!config) { const logger = getLogger(); - logger.error('OIDC provider implementation was not provided via overrides.'); - throw new Error('Provider implementation was not provided via overrides.'); - }, -}); + logger.error('Could not find OIDC integration (oidcId: %s)', oidcId); + } + return config; +} const fetchOIDCConfig = async ( internalApi: CreateTRPCProxyClient, @@ -169,7 +198,7 @@ const fetchOIDCConfig = async ( const result = await internalApi.getOIDCIntegrationById.query({ oidcIntegrationId }); if (result === null) { const logger = getLogger(); - logger.error('OIDC integration not found: %s', oidcIntegrationId); + logger.error('OIDC integration not found. (oidcId=%s)', oidcIntegrationId); return null; } return result; diff --git a/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts b/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts index 55c148551..64a3bb09d 100644 --- a/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts +++ b/packages/web/app/src/lib/supertokens/third-party-email-password-node-okta-provider.ts @@ -1,4 +1,4 @@ -import { TypeProvider } from 'supertokens-node/recipe/thirdpartyemailpassword'; +import type { ProviderInput } from 'supertokens-node/recipe/thirdparty/types'; import zod from 'zod'; import { env } from '@/env/backend'; @@ -7,46 +7,39 @@ type OktaConfig = Exclude<(typeof env)['auth']['okta'], null>; /** * Custom (server) provider for SuperTokens in order to allow Okta users to sign in. */ -export const createThirdPartyEmailPasswordNodeOktaProvider = (config: OktaConfig): TypeProvider => { +export const createThirdPartyEmailPasswordNodeOktaProvider = ( + config: OktaConfig, +): ProviderInput => { return { - id: 'okta', - get(redirectURI, authCodeFromRequest) { + config: { + thirdPartyId: 'okta', + clients: [ + { + clientId: config.clientId, + clientSecret: config.clientSecret, + scope: ['openid', 'email', 'profile', 'okta.users.read.self'], + }, + ], + authorizationEndpoint: `${config.endpoint}/oauth2/v1/authorize`, + tokenEndpoint: `${config.endpoint}/oauth2/v1/token`, + }, + override(originalImplementation) { return { - accessTokenAPI: { - // this contains info about the token endpoint which exchanges the auth code with the access token and profile info. - url: `${config.endpoint}/oauth2/v1/token`, - params: { - // example post params - client_id: config.clientId, - client_secret: config.clientSecret, - grant_type: 'authorization_code', - redirect_uri: redirectURI || '', - code: authCodeFromRequest || '', - }, - }, - authorisationRedirect: { - // this contains info about forming the authorisation redirect URL without the state params and without the redirect_uri param - url: `${config.endpoint}/oauth2/v1/authorize`, - params: { - client_id: config.clientId, - scope: 'openid email profile okta.users.read.self', - response_type: 'code', - redirect_uri: `${env.appBaseUrl}/auth/callback/okta`, - }, - }, - getClientId: () => { - return config.clientId; - }, - getProfileInfo: async (accessTokenAPIResponse: unknown) => { - const data = OktaAccessTokenResponseModel.parse(accessTokenAPIResponse); + ...originalImplementation, + async getUserInfo(input) { + const data = OktaAccessTokenResponseModel.parse(input.oAuthTokens); const userData = await fetchOktaProfile(config, data.access_token); return { - id: userData.id, + thirdPartyUserId: userData.id, email: { id: userData.profile.email, isVerified: true, }, + rawUserInfoFromProvider: { + fromIdTokenPayload: undefined, + fromUserInfoAPI: undefined, + }, }; }, }; diff --git a/packages/web/app/src/lib/supertokens/third-party-email-password-react-oidc-provider.ts b/packages/web/app/src/lib/supertokens/third-party-email-password-react-oidc-provider.ts index be04d59af..8152c24bf 100644 --- a/packages/web/app/src/lib/supertokens/third-party-email-password-react-oidc-provider.ts +++ b/packages/web/app/src/lib/supertokens/third-party-email-password-react-oidc-provider.ts @@ -14,7 +14,7 @@ export const getOIDCOverrides = (): UserInput['override'] => ({ ...originalImplementation, generateStateToSendToOAuthProvider(input) { const hash = originalImplementation.generateStateToSendToOAuthProvider(input); - const { oidcId } = input.userContext; + const oidcId = input?.userContext?.['oidcId']; if (typeof oidcId === 'string') { return `${hash}${delimiter}${oidcId}`; @@ -71,8 +71,8 @@ export const getOIDCOverrides = (): UserInput['override'] => ({ export const startAuthFlowForOIDCProvider = async (oidcId: string) => { const authUrl = await getAuthorisationURLWithQueryParamsAndSetState({ - providerId: 'oidc', - authorisationURL: `${env.appBaseUrl}/auth/callback/oidc`, + thirdPartyId: 'oidc', + frontendRedirectURI: `${env.appBaseUrl}/auth/callback/oidc`, // The user context is very important - we store the OIDC ID so we can use it later on. userContext: { oidcId, diff --git a/packages/web/app/src/lib/supertokens/third-party-email-password-react-okta-provider.tsx b/packages/web/app/src/lib/supertokens/third-party-email-password-react-okta-provider.tsx deleted file mode 100644 index b71187a98..000000000 --- a/packages/web/app/src/lib/supertokens/third-party-email-password-react-okta-provider.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { CustomProviderConfig } from 'supertokens-auth-react/lib/build/recipe/thirdparty/providers/types'; - -export const createThirdPartyEmailPasswordReactOktaProvider = (): CustomProviderConfig => ({ - id: 'okta', - name: 'Okta', - buttonComponent: ( -
- Login with Okta -
- ), -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2e01e4751..93ee9f5cd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -671,8 +671,8 @@ importers: specifier: 5.0.0-beta.2 version: 5.0.0-beta.2 supertokens-node: - specifier: 14.1.3 - version: 14.1.3 + specifier: 15.2.1 + version: 15.2.1 tslib: specifier: 2.6.2 version: 2.6.2 @@ -1665,17 +1665,17 @@ importers: specifier: 2.0.0 version: 2.0.0 supertokens-auth-react: - specifier: 0.33.1 - version: 0.33.1(react-dom@18.2.0)(react@18.2.0)(supertokens-web-js@0.6.0) + specifier: 0.35.6 + version: 0.35.6(react-dom@18.2.0)(react@18.2.0)(supertokens-web-js@0.8.0) supertokens-js-override: specifier: 0.0.4 version: 0.0.4 supertokens-node: - specifier: 14.1.3 - version: 14.1.3 + specifier: 15.2.1 + version: 15.2.1 supertokens-web-js: - specifier: 0.6.0 - version: 0.6.0 + specifier: 0.8.0 + version: 0.8.0 tailwind-merge: specifier: 2.0.0 version: 2.0.0 @@ -16548,6 +16548,9 @@ packages: randombytes: 2.1.0 randomfill: 1.0.4 + /crypto-js@4.2.0: + resolution: {integrity: sha512-KALDyEYgpY+Rlob/iriUtjV6d5Eq+Y191A5g4UqLAi8CyGP9N1+FdVbkc1SxKc2r4YAYqG8JzO2KGL+AizD70Q==} + /crypto-random-string@2.0.0: resolution: {integrity: sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==} engines: {node: '>=8'} @@ -24901,14 +24904,9 @@ packages: nodemailer-fetch: 1.6.0 dev: true - /nodemailer@6.9.3: - resolution: {integrity: sha512-fy9v3NgTzBngrMFkDsKEj0r02U7jm6XfC3b52eoNV+GCrGj+s8pt5OqhiJdWKuw51zCTdiNR/IUD1z33LIIGpg==} - engines: {node: '>=6.0.0'} - /nodemailer@6.9.7: resolution: {integrity: sha512-rUtR77ksqex/eZRLmQ21LKVH5nAAsVicAtAYudK7JgwenEDZ0UIQ1adUGqErz7sMkWYxWTTU1aeP2Jga6WQyJw==} engines: {node: '>=6.0.0'} - dev: true /noms@0.0.0: resolution: {integrity: sha512-lNDU9VJaOPxUmXcLb+HQFeUgQQPtMI24Gt6hgfuMHRJgMRHMF/qZ4HJD3GDru4sSw9IQl2jPjAYnQrdIeLbwow==} @@ -26165,6 +26163,11 @@ packages: engines: {node: '>= 6'} dev: true + /pkce-challenge@3.1.0: + resolution: {integrity: sha512-bQ/0XPZZ7eX+cdAkd61uYWpfMhakH3NeteUF1R8GNa+LMqX8QFAkbCLqq+AYAns1/ueACBu/BMWhrlKGrdvGZg==} + dependencies: + crypto-js: 4.2.0 + /pkg-dir@3.0.0: resolution: {integrity: sha512-/E57AYkoeQ25qkxMj5PBOVgF8Kiu/h7cYS30Z5+R7WaiCCBfLq58ZI/dSeaEKb9WVJV5n/03QwrN3IeWIFllvw==} engines: {node: '>=6'} @@ -27176,15 +27179,6 @@ packages: iconv-lite: 0.4.24 unpipe: 1.0.0 - /raw-body@2.5.2: - resolution: {integrity: sha512-8zGqypfENjCIqGhgXToC8aB2r7YrBX+AQAfIPs/Mlk+BtPTztOvTS01NRW/3Eh60J+a48lt8qsCzirQ6loCVfA==} - engines: {node: '>= 0.8'} - dependencies: - bytes: 3.1.2 - http-errors: 2.0.0 - iconv-lite: 0.4.24 - unpipe: 1.0.0 - /react-avatar@5.0.3(@babel/runtime@7.23.1)(core-js-pure@3.28.0)(prop-types@15.8.1)(react@18.2.0): resolution: {integrity: sha512-DNc+qkWH9QehSEZqHBhqpXWsPY+rU9W7kD68QFHfu8Atfsvx/3ML0DzAePgTUd96nCXQQ3KZMcC3LKYT8FiBIg==} peerDependencies: @@ -29441,27 +29435,27 @@ packages: ts-interface-checker: 0.1.13 dev: true - /supertokens-auth-react@0.33.1(react-dom@18.2.0)(react@18.2.0)(supertokens-web-js@0.6.0): - resolution: {integrity: sha512-TEzwW7k+ACZ4kkmRvOm6U8VAub0ykcCamBL0n43zOvjo+K+1NIhRycbpFdAh1sadk9EWzTXRM7wOEq6TRtfVEg==} + /supertokens-auth-react@0.35.6(react-dom@18.2.0)(react@18.2.0)(supertokens-web-js@0.8.0): + resolution: {integrity: sha512-Bt+rYunzqP6g331Ks5Q66obiIU/t2k6siG6dtpJNY2DZqAnjgJlDr8S/UAU8I72f4qWwlYv8FKUTa8+O/0z3zA==} engines: {node: '>=16.0.0', npm: '>=8'} peerDependencies: react: '>=16.8.0' react-dom: '>=16.8.0' - supertokens-web-js: ^0.6.0 + supertokens-web-js: ^0.8.0 dependencies: intl-tel-input: 17.0.19 prop-types: 15.8.1 react: 18.2.0 react-dom: 18.2.0(react@18.2.0) supertokens-js-override: 0.0.4 - supertokens-web-js: 0.6.0 + supertokens-web-js: 0.8.0 dev: false /supertokens-js-override@0.0.4: resolution: {integrity: sha512-r0JFBjkMIdep3Lbk3JA+MpnpuOtw4RSyrlRAbrzMcxwiYco3GFWl/daimQZ5b1forOiUODpOlXbSOljP/oyurg==} - /supertokens-node@14.1.3: - resolution: {integrity: sha512-cwnzmJMHRQvqiztdRITlkK0o2psAw2wAYP+tJVYyIk4fm/93Yb0e9QMekuCJnhZAP0KXaZi+PGjA6F5FEE123Q==} + /supertokens-node@15.2.1: + resolution: {integrity: sha512-3zJ2EsiHYJHnYwAzDQI5Alp+4x/KcwEOBgeoPN5bWglZY0Xw0AzcZvd8S3N71vjLGvab0J3XxWmHeEHqSz5dbg==} dependencies: content-type: 1.0.5 cookie: 0.4.0 @@ -29470,24 +29464,24 @@ packages: inflation: 2.0.0 jose: 4.14.4 libphonenumber-js: 1.10.14 - nodemailer: 6.9.3 + nodemailer: 6.9.7 + pkce-challenge: 3.1.0 psl: 1.8.0 - raw-body: 2.5.2 supertokens-js-override: 0.0.4 twilio: 4.7.2(debug@4.3.4) transitivePeerDependencies: - encoding - supports-color - /supertokens-web-js@0.6.0: - resolution: {integrity: sha512-2KOgWNGanh+/zpdaFQgiA9dQa6T9wA6LcoB6m1BTZ7JIqR3mYmpLD5uu8ERtISNAGUjcXziON/Fp5ZnlBByCcw==} + /supertokens-web-js@0.8.0: + resolution: {integrity: sha512-kkvuPbdy1I0e7nejVJAhpPJhR5h7EUHdn7Fh1YqfSjZfSJh46J3JU2qWGCgSDVZuD1hSuUKb6skF9CQnltHWrQ==} dependencies: supertokens-js-override: 0.0.4 - supertokens-website: 17.0.1 + supertokens-website: 17.0.4 dev: false - /supertokens-website@17.0.1: - resolution: {integrity: sha512-RCBPIj4A5mVdiYEL6ArvFrZcQ8ByENe3FJEpBXIP0SB++kLuV54zU8lfL99kHcc9kbOHTdUjpWxrwEpvlTTPUQ==} + /supertokens-website@17.0.4: + resolution: {integrity: sha512-ayWhEFvspUe26YhM1bq11ssEpnFCZIsoHZtJwJHgHsoflfMUKdgrzOix/bboI0PWJeNTUphHyZebw0ApctaS1Q==} dependencies: browser-tabs-lock: 1.3.0 supertokens-js-override: 0.0.4