feat: upgrade supertokens SDK (#3328)

This commit is contained in:
Laurin Quast 2023-11-10 09:25:06 +01:00 committed by GitHub
parent fce33b23e6
commit 2bcfdf8a1d
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
11 changed files with 287 additions and 263 deletions

View file

@ -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.');
});

View file

@ -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"

View file

@ -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",

View file

@ -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<InternalApi>({
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<typeof env.auth.legacyAuth0, null>) =
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<typeof env.auth.legacyAuth0, null>) =
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<typeof env.auth.legacyAuth0, null>) =
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,
});

View file

@ -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<Provider | CustomProviderConfig> = [];
@ -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());

View file

@ -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);

View file

@ -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<InternalApi>;
}): 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<InternalApi>;
}): ThirdPartEmailPasswordTypeInput['override'] => ({
apis: originalImplementation => ({
...originalImplementation,
thirdPartySignInUpPOST: async input => {
if (input.provider.id !== 'oidc') {
return originalImplementation.thirdPartySignInUpPOST!(input);
}
const configCache = new WeakMap<ExpressRequest, OIDCCOnfig | null>();
const oidcId = getOIDCIdFromInput(input);
const config = await fetchOIDCConfig(args.internalApi, oidcId);
/**
* Get cached OIDC config from the supertokens input.
*/
async function getOIDCConfigFromInput(
internalApi: CreateTRPCProxyClient<InternalApi>,
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<InternalApi>,
@ -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;

View file

@ -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,
},
};
},
};

View file

@ -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,

View file

@ -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: (
<div
style={{
cursor: 'pointer',
border: '1',
paddingTop: '5px',
paddingBottom: '5px',
borderRadius: '5px',
borderStyle: 'solid',
background: '#00297A',
}}
>
Login with Okta
</div>
),
});

View file

@ -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