diff --git a/apps/api/graphql/package.json b/apps/api/graphql/package.json index 2508c6af2ff..b0a9ffc10ee 100644 --- a/apps/api/graphql/package.json +++ b/apps/api/graphql/package.json @@ -64,7 +64,8 @@ "src": [ "@webiny/api-tenant-manager", "@webiny/api-theme-manager", - "@webiny/api-security-okta" + "@webiny/api-security-okta", + "@webiny/api-security-auth0" ] } } diff --git a/apps/api/graphql/src/security.auth0.ts b/apps/api/graphql/src/security.auth0.ts new file mode 100644 index 00000000000..607974066d4 --- /dev/null +++ b/apps/api/graphql/src/security.auth0.ts @@ -0,0 +1,108 @@ +import { DynamoDBDocument } from "@webiny/aws-sdk/client-dynamodb"; +import { createTenancyContext, createTenancyGraphQL } from "@webiny/api-tenancy"; +import { createStorageOperations as tenancyStorageOperations } from "@webiny/api-tenancy-so-ddb"; +import { createSecurityContext, createSecurityGraphQL } from "@webiny/api-security"; +import { createStorageOperations as securityStorageOperations } from "@webiny/api-security-so-ddb"; +import { authenticateUsingHttpHeader } from "@webiny/api-security/plugins/authenticateUsingHttpHeader"; +import apiKeyAuthentication from "@webiny/api-security/plugins/apiKeyAuthentication"; +import apiKeyAuthorization from "@webiny/api-security/plugins/apiKeyAuthorization"; +import { createAuth0 } from "@webiny/api-security-auth0"; +import anonymousAuthorization from "@webiny/api-security/plugins/anonymousAuthorization"; +import tenantLinkAuthorization from "@webiny/api-security/plugins/tenantLinkAuthorization"; +import createAdminUsersApp from "@webiny/api-admin-users"; +import { createStorageOperations as createAdminUsersStorageOperations } from "@webiny/api-admin-users-so-ddb"; + +export default ({ documentClient }: { documentClient: DynamoDBDocument }) => [ + /** + * Create Tenancy app in the `context`. + */ + createTenancyContext({ + storageOperations: tenancyStorageOperations({ documentClient }) + }), + + /** + * Expose tenancy GraphQL schema. + */ + createTenancyGraphQL(), + + /** + * Create Security app in the `context`. + */ + createSecurityContext({ + storageOperations: securityStorageOperations({ documentClient }) + }), + + /** + * Expose security GraphQL schema. + */ + createSecurityGraphQL({ + async getDefaultTenant(context) { + return context.tenancy.getRootTenant(); + } + }), + + /** + * Create Admin Users app. + */ + createAdminUsersApp({ + storageOperations: createAdminUsersStorageOperations({ documentClient }) + }), + + /** + * Perform authentication using the common "Authorization" HTTP header. + * This will fetch the value of the header, and execute the authentication process. + */ + authenticateUsingHttpHeader(), + + /** + * API Key authenticator. + * API Keys are a standalone entity, and are not connected to users in any way. + * They identify a project, a 3rd party client, not a particular user. + * They are used for programmatic API access, CMS data import/export, etc. + */ + apiKeyAuthentication({ identityType: "api-key" }), + + /** + * Configure Auth0 authentication and authorization. + */ + createAuth0({ + /** + * `domain` is required for token verification. + */ + domain: String(process.env.AUTH0_DOMAIN), + /** + * Construct the identity object and map token claims to arbitrary identity properties. + */ + getIdentity({ token }) { + return { + id: token["sub"], + type: "admin", + displayName: token["name"], + group: token["webiny_group"] + }; + } + }), + + /** + * Authorization plugin to fetch permissions for a verified API key. + * The "identityType" must match the authentication plugin used to load the identity. + */ + apiKeyAuthorization({ identityType: "api-key" }), + + /** + * Authorization plugin to fetch permissions from a security role or team associated with the identity. + */ + tenantLinkAuthorization({ identityType: "admin" }), + + /** + * Authorization plugin to fetch permissions from the parent tenant. + */ + tenantLinkAuthorization({ identityType: "admin", parent: true }), + + /** + * Authorization plugin to load permissions for anonymous requests. + * This allows you to control which API resources can be accessed publicly. + * The authorization is performed by loading permissions from the "anonymous" user group. + */ + anonymousAuthorization() +]; diff --git a/packages/api-headless-cms-tasks/tsconfig.build.json b/packages/api-headless-cms-tasks/tsconfig.build.json index a28bb951997..9da4431c8d4 100644 --- a/packages/api-headless-cms-tasks/tsconfig.build.json +++ b/packages/api-headless-cms-tasks/tsconfig.build.json @@ -2,18 +2,18 @@ "extends": "../../tsconfig.build.json", "include": ["src"], "references": [ + { "path": "../api-headless-cms/tsconfig.build.json" }, + { "path": "../handler/tsconfig.build.json" }, + { "path": "../handler-aws/tsconfig.build.json" }, + { "path": "../tasks/tsconfig.build.json" }, { "path": "../api/tsconfig.build.json" }, { "path": "../api-admin-users/tsconfig.build.json" }, - { "path": "../api-headless-cms/tsconfig.build.json" }, { "path": "../api-i18n/tsconfig.build.json" }, { "path": "../api-security/tsconfig.build.json" }, { "path": "../api-tenancy/tsconfig.build.json" }, { "path": "../api-wcp/tsconfig.build.json" }, - { "path": "../handler-aws/tsconfig.build.json" }, { "path": "../handler-graphql/tsconfig.build.json" }, - { "path": "../handler/tsconfig.build.json" }, { "path": "../plugins/tsconfig.build.json" }, - { "path": "../tasks/tsconfig.build.json" }, { "path": "../wcp/tsconfig.build.json" } ], "compilerOptions": { diff --git a/packages/api-headless-cms-tasks/tsconfig.json b/packages/api-headless-cms-tasks/tsconfig.json index 8d091441523..650a13e104c 100644 --- a/packages/api-headless-cms-tasks/tsconfig.json +++ b/packages/api-headless-cms-tasks/tsconfig.json @@ -2,18 +2,18 @@ "extends": "../../tsconfig.json", "include": ["src", "__tests__"], "references": [ + { "path": "../api-headless-cms" }, + { "path": "../handler" }, + { "path": "../handler-aws" }, + { "path": "../tasks" }, { "path": "../api" }, { "path": "../api-admin-users" }, - { "path": "../api-headless-cms" }, { "path": "../api-i18n" }, { "path": "../api-security" }, { "path": "../api-tenancy" }, { "path": "../api-wcp" }, - { "path": "../handler" }, - { "path": "../handler-aws" }, { "path": "../handler-graphql" }, { "path": "../plugins" }, - { "path": "../tasks" }, { "path": "../wcp" } ], "compilerOptions": { @@ -23,32 +23,32 @@ "paths": { "~/*": ["./src/*"], "~tests/*": ["./__tests__/*"], - "@webiny/api": ["../api/src"], + "@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"], + "@webiny/api-headless-cms": ["../api-headless-cms/src"], + "@webiny/handler/*": ["../handler/src/*"], + "@webiny/handler": ["../handler/src"], + "@webiny/handler-aws/*": ["../handler-aws/src/*"], + "@webiny/handler-aws": ["../handler-aws/src"], + "@webiny/tasks/*": ["../tasks/src/*"], + "@webiny/tasks": ["../tasks/src"], "@webiny/api/*": ["../api/src/*"], - "@webiny/api-admin-users": ["../api-admin-users/src"], + "@webiny/api": ["../api/src"], "@webiny/api-admin-users/*": ["../api-admin-users/src/*"], - "@webiny/api-headless-cms": ["../api-headless-cms/src"], - "@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"], - "@webiny/api-i18n": ["../api-i18n/src"], + "@webiny/api-admin-users": ["../api-admin-users/src"], "@webiny/api-i18n/*": ["../api-i18n/src/*"], - "@webiny/api-security": ["../api-security/src"], + "@webiny/api-i18n": ["../api-i18n/src"], "@webiny/api-security/*": ["../api-security/src/*"], - "@webiny/api-tenancy": ["../api-tenancy/src"], + "@webiny/api-security": ["../api-security/src"], "@webiny/api-tenancy/*": ["../api-tenancy/src/*"], - "@webiny/api-wcp": ["../api-wcp/src"], + "@webiny/api-tenancy": ["../api-tenancy/src"], "@webiny/api-wcp/*": ["../api-wcp/src/*"], - "@webiny/handler": ["../handler/src"], - "@webiny/handler-aws": ["../handler-aws/src"], - "@webiny/handler-aws/*": ["../handler-aws/src/*"], - "@webiny/handler-graphql": ["../handler-graphql/src"], + "@webiny/api-wcp": ["../api-wcp/src"], "@webiny/handler-graphql/*": ["../handler-graphql/src/*"], - "@webiny/handler/*": ["../handler/src/*"], - "@webiny/plugins": ["../plugins/src"], + "@webiny/handler-graphql": ["../handler-graphql/src"], "@webiny/plugins/*": ["../plugins/src/*"], - "@webiny/tasks": ["../tasks/src"], - "@webiny/tasks/*": ["../tasks/src/*"], - "@webiny/wcp": ["../wcp/src"], - "@webiny/wcp/*": ["../wcp/src/*"] + "@webiny/plugins": ["../plugins/src"], + "@webiny/wcp/*": ["../wcp/src/*"], + "@webiny/wcp": ["../wcp/src"] }, "baseUrl": "." } diff --git a/packages/api-serverless-cms/tsconfig.build.json b/packages/api-serverless-cms/tsconfig.build.json index 1f153853c01..1be9444ed46 100644 --- a/packages/api-serverless-cms/tsconfig.build.json +++ b/packages/api-serverless-cms/tsconfig.build.json @@ -6,7 +6,6 @@ { "path": "../api-aco/tsconfig.build.json" }, { "path": "../api-file-manager/tsconfig.build.json" }, { "path": "../api-form-builder/tsconfig.build.json" }, - { "path": "../handler-graphql/tsconfig.build.json" }, { "path": "../api-headless-cms/tsconfig.build.json" }, { "path": "../api-i18n/tsconfig.build.json" }, { "path": "../api-i18n-content/tsconfig.build.json" }, @@ -15,7 +14,8 @@ { "path": "../api-prerendering-service/tsconfig.build.json" }, { "path": "../api-security/tsconfig.build.json" }, { "path": "../api-tenancy/tsconfig.build.json" }, - { "path": "../handler-client/tsconfig.build.json" } + { "path": "../handler-client/tsconfig.build.json" }, + { "path": "../handler-graphql/tsconfig.build.json" } ], "compilerOptions": { "rootDir": "./src", diff --git a/packages/api-serverless-cms/tsconfig.json b/packages/api-serverless-cms/tsconfig.json index 07dcf392adf..5e4cb06d313 100644 --- a/packages/api-serverless-cms/tsconfig.json +++ b/packages/api-serverless-cms/tsconfig.json @@ -6,7 +6,6 @@ { "path": "../api-aco" }, { "path": "../api-file-manager" }, { "path": "../api-form-builder" }, - { "path": "../handler-graphql" }, { "path": "../api-headless-cms" }, { "path": "../api-i18n" }, { "path": "../api-i18n-content" }, @@ -15,7 +14,8 @@ { "path": "../api-prerendering-service" }, { "path": "../api-security" }, { "path": "../api-tenancy" }, - { "path": "../handler-client" } + { "path": "../handler-client" }, + { "path": "../handler-graphql" } ], "compilerOptions": { "rootDirs": ["./src", "./__tests__"], @@ -32,8 +32,6 @@ "@webiny/api-file-manager": ["../api-file-manager/src"], "@webiny/api-form-builder/*": ["../api-form-builder/src/*"], "@webiny/api-form-builder": ["../api-form-builder/src"], - "@webiny/handler-graphql/*": ["../handler-graphql/src/*"], - "@webiny/handler-graphql": ["../handler-graphql/src"], "@webiny/api-headless-cms/*": ["../api-headless-cms/src/*"], "@webiny/api-headless-cms": ["../api-headless-cms/src"], "@webiny/api-i18n/*": ["../api-i18n/src/*"], @@ -51,7 +49,9 @@ "@webiny/api-tenancy/*": ["../api-tenancy/src/*"], "@webiny/api-tenancy": ["../api-tenancy/src"], "@webiny/handler-client/*": ["../handler-client/src/*"], - "@webiny/handler-client": ["../handler-client/src"] + "@webiny/handler-client": ["../handler-client/src"], + "@webiny/handler-graphql/*": ["../handler-graphql/src/*"], + "@webiny/handler-graphql": ["../handler-graphql/src"] }, "baseUrl": "." } diff --git a/packages/app-admin-auth0/package.json b/packages/app-admin-auth0/package.json index ac1c9039112..44de2a3d2b7 100644 --- a/packages/app-admin-auth0/package.json +++ b/packages/app-admin-auth0/package.json @@ -11,7 +11,7 @@ "license": "Webiny Enterprise", "dependencies": { "@apollo/react-hooks": "^3.1.5", - "@auth0/auth0-react": "^1.10.1", + "@auth0/auth0-react": "^2.2.4", "@emotion/styled": "^11.10.6", "@webiny/app": "0.0.0", "@webiny/app-admin": "0.0.0", diff --git a/packages/app-admin-auth0/src/Auth0.tsx b/packages/app-admin-auth0/src/Auth0.tsx index f1ee2b9eefc..93aeadd64bb 100644 --- a/packages/app-admin-auth0/src/Auth0.tsx +++ b/packages/app-admin-auth0/src/Auth0.tsx @@ -6,7 +6,8 @@ import { LoginScreenRenderer, useTenancy, useTags } from "@webiny/app-serverless import { createAuthentication, Auth0Options, - CreateAuthenticationConfig + CreateAuthenticationConfig, + OnLogout } from "./createAuthentication"; import { UserMenuModule } from "~/modules/userMenu"; import { AppClientModule } from "~/modules/appClient"; @@ -16,6 +17,7 @@ interface AppClientIdLoaderProps { auth0: Auth0Options; rootAppClientId: string; children: React.ReactNode; + onLogout?: OnLogout; onError?: CreateAuthenticationConfig["onError"]; } @@ -30,6 +32,7 @@ const GET_CLIENT_ID = gql` const AppClientIdLoader = ({ auth0, rootAppClientId, + onLogout, onError, children }: AppClientIdLoaderProps) => { @@ -50,6 +53,7 @@ const AppClientIdLoader = ({ if (tenantId === "root") { console.info(`Configuring Auth0 with App Client Id "${rootAppClientId}"`); authRef.current = createAuthentication({ + onLogout, onError, auth0: { ...auth0, @@ -114,6 +118,7 @@ const createLoginScreenPlugin = (params: Auth0Props) => { export interface Auth0Props { auth0: Auth0Options; rootAppClientId: string; + onLogout?: OnLogout; children?: React.ReactNode; } diff --git a/packages/app-admin-auth0/src/createAuthentication.tsx b/packages/app-admin-auth0/src/createAuthentication.tsx index f4ca4c421c3..450564b28c6 100644 --- a/packages/app-admin-auth0/src/createAuthentication.tsx +++ b/packages/app-admin-auth0/src/createAuthentication.tsx @@ -3,7 +3,7 @@ import { setContext } from "apollo-link-context"; import ApolloClient from "apollo-client"; import { DocumentNode } from "graphql"; import { useApolloClient } from "@apollo/react-hooks"; -import { useAuth0, Auth0Provider, Auth0ProviderOptions } from "@auth0/auth0-react"; +import { useAuth0, Auth0Provider, Auth0ProviderOptions, LogoutOptions } from "@auth0/auth0-react"; import { plugins } from "@webiny/plugins"; import { ApolloLinkPlugin } from "@webiny/app/plugins/ApolloLinkPlugin"; import { useSecurity } from "@webiny/app-serverless-cms"; @@ -19,9 +19,12 @@ import { LoginContent, LoginLayout } from "~/components"; export type Auth0Options = Auth0ProviderOptions; +export type OnLogout = (logout: (options?: LogoutOptions) => Promise) => void; + export interface CreateAuthenticationConfig { getIdentityData?: GetIdentityDataCallable; loginMutation?: DocumentNode; + onLogout?: OnLogout; onError?(error: Error): void; auth0: Auth0Options; } @@ -47,7 +50,14 @@ const validatePermissions = (permissions: SecurityPermission[]) => { } }; -export const createAuthentication = ({ auth0, onError, ...config }: CreateAuthenticationConfig) => { +const defaultLogout: OnLogout = logout => logout(); + +export const createAuthentication = ({ + auth0, + onError, + onLogout = defaultLogout, + ...config +}: CreateAuthenticationConfig) => { const withGetIdentityData = ( Component: React.ComponentType ): React.ComponentType => { @@ -65,7 +75,7 @@ export const createAuthentication = ({ auth0, onError, ...config }: CreateAuthen useAuth0(); const apolloClient = useApolloClient(); - const { setIdentity, identity } = useSecurity(); + const { setIdentity, identity, setIdTokenProvider } = useSecurity(); const getIdToken = useCallback(async () => { const claims = await getIdTokenClaims(); @@ -77,6 +87,15 @@ export const createAuthentication = ({ auth0, onError, ...config }: CreateAuthen }, []); useEffect(() => { + /** + * We need to give the security layer a way to fetch the `idToken`, so other network clients can use + * it when sending requests to external services (APIs, websockets,...). + */ + setIdTokenProvider(async () => { + const claims = await getIdTokenClaims(); + return claims ? claims["__raw"] : undefined; + }); + plugins.register( new ApolloLinkPlugin(() => { return setContext(async (_, { headers }) => { @@ -116,7 +135,7 @@ export const createAuthentication = ({ auth0, onError, ...config }: CreateAuthen // Make sure current app client ID matches token's clientId, if not, log the user out. if (claims?.aud !== auth0.clientId) { setIdentity(null); - logout(); + onLogout(logout); return; } @@ -133,7 +152,7 @@ export const createAuthentication = ({ auth0, onError, ...config }: CreateAuthen ...other, logout() { setIdentity(null); - logout(); + onLogout(logout); } }); @@ -144,7 +163,7 @@ export const createAuthentication = ({ auth0, onError, ...config }: CreateAuthen onError(err); } else { console.error(err); - logout(); + onLogout(logout); } } }; @@ -180,10 +199,13 @@ export const createAuthentication = ({ auth0, onError, ...config }: CreateAuthen return function Authentication({ children }: PropsWithChildren) { return ( {children} diff --git a/packages/app-admin-cognito/src/index.tsx b/packages/app-admin-cognito/src/index.tsx index d92ad083b48..84f670a75f6 100644 --- a/packages/app-admin-cognito/src/index.tsx +++ b/packages/app-admin-cognito/src/index.tsx @@ -102,7 +102,7 @@ export const createAuthentication: AuthenticationFactory = ({ const Authentication = (props: AuthenticationProps) => { const { children } = props; const [loadingIdentity, setLoadingIdentity] = useState(false); - const { setIdentity } = useSecurity(); + const { setIdentity, setIdTokenProvider } = useSecurity(); const client = useApolloClient(); const onToken = useCallback(async (token: CognitoIdToken) => { @@ -143,6 +143,17 @@ export const createAuthentication: AuthenticationFactory = ({ }, []); useEffect(() => { + /** + * We need to give the security layer a way to fetch the `idToken`, so other network clients can use + * it when sending requests to external services (APIs, websockets,...). + */ + setIdTokenProvider(async () => { + const user = await Auth.currentSession(); + const idToken = user.getIdToken(); + + return idToken ? idToken.getJwtToken() : undefined; + }); + plugins.register(createApolloLinkPlugin()); }, []); diff --git a/packages/app-admin-okta/src/createAuthentication.tsx b/packages/app-admin-okta/src/createAuthentication.tsx index 5ef525dff78..ef0ac0e3658 100644 --- a/packages/app-admin-okta/src/createAuthentication.tsx +++ b/packages/app-admin-okta/src/createAuthentication.tsx @@ -81,10 +81,18 @@ export const createAuthentication = ({ const Authentication = ({ getIdentityData, children }: AuthenticationProps) => { const timerRef = useRef(undefined); const apolloClient = useApolloClient(); - const { identity, setIdentity } = useSecurity(); + const { identity, setIdentity, setIdTokenProvider } = useSecurity(); const [isAuthenticated, setIsAuthenticated] = useState(false); useEffect(() => { + /** + * We need to give the security layer a way to fetch the `idToken`, so other network clients can use + * it when sending requests to external services (APIs, websockets,...). + */ + setIdTokenProvider(() => { + return oktaAuth.getIdToken(); + }); + plugins.register( new ApolloLinkPlugin(() => { return setContext(async (_, payload) => { diff --git a/packages/app-security/src/contexts/Security.tsx b/packages/app-security/src/contexts/Security.tsx index 8350d7f03c7..7779ca9eba0 100644 --- a/packages/app-security/src/contexts/Security.tsx +++ b/packages/app-security/src/contexts/Security.tsx @@ -1,41 +1,33 @@ import minimatch from "minimatch"; import React, { Dispatch, SetStateAction, useCallback, useMemo, useState } from "react"; -import { SecurityIdentity, SecurityPermission } from "~/types"; +import { IdTokenProvider, SecurityIdentity, SecurityPermission } from "~/types"; export interface SecurityContext { identity: SecurityIdentity | null; getIdentityId: () => string | null; - setIdentity: Dispatch>; - getPermission( name: string, exact?: boolean ): T | null; - getPermissions(name: string): T[]; + setIdTokenProvider: (provider: IdTokenProvider) => void; + getIdToken: IdTokenProvider; } interface SecurityProviderProps { children: React.ReactNode; } -export const SecurityContext = React.createContext({ - identity: null, - getIdentityId: () => null, - setIdentity: () => { - return void 0; - }, - getPermission: () => { - return null; - }, - getPermissions: () => { - return []; - } -}); +const defaultIdTokenProvider: IdTokenProvider = () => undefined; + +export const SecurityContext = React.createContext(undefined); export const SecurityProvider = (props: SecurityProviderProps) => { const [identity, setIdentity] = useState(null); + const [idTokenProvider, setIdTokenProvider] = useState( + () => defaultIdTokenProvider + ); const getPermission = useCallback( ( @@ -88,7 +80,7 @@ export const SecurityProvider = (props: SecurityProviderProps) => { return identity.id || identity.login || null; }, [identity]); - const value = useMemo(() => { + const value: SecurityContext = useMemo(() => { return { identity: identity ? { @@ -100,9 +92,13 @@ export const SecurityProvider = (props: SecurityProviderProps) => { getIdentityId, setIdentity, getPermission, - getPermissions + getPermissions, + getIdToken: idTokenProvider, + setIdTokenProvider: provider => { + setIdTokenProvider(() => provider); + } }; - }, [identity]); + }, [idTokenProvider, identity]); return {props.children}; }; diff --git a/packages/app-security/src/hooks/useSecurity.ts b/packages/app-security/src/hooks/useSecurity.ts index 2d094499023..6cc750c168b 100644 --- a/packages/app-security/src/hooks/useSecurity.ts +++ b/packages/app-security/src/hooks/useSecurity.ts @@ -2,5 +2,11 @@ import { useContext } from "react"; import { SecurityContext } from "~/contexts/Security"; export function useSecurity() { - return useContext(SecurityContext); + const context = useContext(SecurityContext); + + if (!context) { + throw Error(`Missing in the component hierarchy!`); + } + + return context; } diff --git a/packages/app-security/src/types.ts b/packages/app-security/src/types.ts index 0179ec8cb78..ad72cce7e82 100644 --- a/packages/app-security/src/types.ts +++ b/packages/app-security/src/types.ts @@ -41,3 +41,7 @@ export interface SecurityIdentity { getPermission?(name: string): T | null; [key: string]: any; } + +export type IdToken = string; + +export type IdTokenProvider = () => Promise | IdToken | undefined; diff --git a/packages/app-serverless-cms/tsconfig.build.json b/packages/app-serverless-cms/tsconfig.build.json index 75c2b18e2c4..cc24fc322e4 100644 --- a/packages/app-serverless-cms/tsconfig.build.json +++ b/packages/app-serverless-cms/tsconfig.build.json @@ -15,9 +15,9 @@ { "path": "../app-headless-cms/tsconfig.build.json" }, { "path": "../app-i18n/tsconfig.build.json" }, { "path": "../app-i18n-content/tsconfig.build.json" }, - { "path": "../app-record-locking/tsconfig.build.json" }, { "path": "../app-mailer/tsconfig.build.json" }, { "path": "../app-page-builder/tsconfig.build.json" }, + { "path": "../app-record-locking/tsconfig.build.json" }, { "path": "../app-security/tsconfig.build.json" }, { "path": "../app-security-access-management/tsconfig.build.json" }, { "path": "../app-tenancy/tsconfig.build.json" }, diff --git a/packages/app-serverless-cms/tsconfig.json b/packages/app-serverless-cms/tsconfig.json index ae8e16405ec..2dd825a4813 100644 --- a/packages/app-serverless-cms/tsconfig.json +++ b/packages/app-serverless-cms/tsconfig.json @@ -15,9 +15,9 @@ { "path": "../app-headless-cms" }, { "path": "../app-i18n" }, { "path": "../app-i18n-content" }, - { "path": "../app-record-locking" }, { "path": "../app-mailer" }, { "path": "../app-page-builder" }, + { "path": "../app-record-locking" }, { "path": "../app-security" }, { "path": "../app-security-access-management" }, { "path": "../app-tenancy" }, @@ -60,12 +60,12 @@ "@webiny/app-i18n": ["../app-i18n/src"], "@webiny/app-i18n-content/*": ["../app-i18n-content/src/*"], "@webiny/app-i18n-content": ["../app-i18n-content/src"], - "@webiny/app-record-locking/*": ["../app-record-locking/src/*"], - "@webiny/app-record-locking": ["../app-record-locking/src"], "@webiny/app-mailer/*": ["../app-mailer/src/*"], "@webiny/app-mailer": ["../app-mailer/src"], "@webiny/app-page-builder/*": ["../app-page-builder/src/*"], "@webiny/app-page-builder": ["../app-page-builder/src"], + "@webiny/app-record-locking/*": ["../app-record-locking/src/*"], + "@webiny/app-record-locking": ["../app-record-locking/src"], "@webiny/app-security/*": ["../app-security/src/*"], "@webiny/app-security": ["../app-security/src"], "@webiny/app-security-access-management/*": ["../app-security-access-management/src/*"], diff --git a/packages/app-websockets/package.json b/packages/app-websockets/package.json index 5bc6d31c243..eff74c84a40 100644 --- a/packages/app-websockets/package.json +++ b/packages/app-websockets/package.json @@ -13,9 +13,9 @@ ], "license": "MIT", "dependencies": { - "@aws-amplify/auth": "^5.1.9", "@webiny/app": "0.0.0", "@webiny/app-i18n": "0.0.0", + "@webiny/app-security": "0.0.0", "@webiny/app-tenancy": "0.0.0", "@webiny/utils": "0.0.0", "react": "18.2.0", diff --git a/packages/app-websockets/src/WebsocketsContextProvider.tsx b/packages/app-websockets/src/WebsocketsContextProvider.tsx index a104b04c4c4..2fa8d86dae8 100644 --- a/packages/app-websockets/src/WebsocketsContextProvider.tsx +++ b/packages/app-websockets/src/WebsocketsContextProvider.tsx @@ -1,7 +1,7 @@ import React, { useCallback, useEffect, useMemo, useRef, useState } from "react"; import { useTenancy } from "@webiny/app-tenancy"; import { useI18N } from "@webiny/app-i18n"; -import { getToken } from "./utils/getToken"; +import { useSecurity } from "@webiny/app-security"; import { IncomingGenericData, IWebsocketsContext, @@ -35,12 +35,17 @@ interface ICurrentData { export const WebsocketsContextProvider = (props: IWebsocketsContextProviderProps) => { const { tenant } = useTenancy(); const { getCurrentLocale } = useI18N(); + const { getIdToken } = useSecurity(); const locale = getCurrentLocale("default"); const socketsRef = useRef(); const [current, setCurrent] = useState({}); + const getToken = useCallback(async () => { + return await getIdToken(); + }, [getIdToken]); + const subscriptionManager = useMemo(() => { const manager = createWebsocketsSubscriptionManager(); @@ -134,7 +139,7 @@ export const WebsocketsContextProvider = (props: IWebsocketsContextProviderProps locale }); })(); - }, [tenant, locale, subscriptionManager]); + }, [tenant, locale, subscriptionManager, getToken]); const websocketActions = useMemo(() => { return createWebsocketsActions({ @@ -143,7 +148,7 @@ export const WebsocketsContextProvider = (props: IWebsocketsContextProviderProps locale, getToken }); - }, [socketsRef.current, tenant, locale]); + }, [socketsRef.current, tenant, locale, getToken]); const send = useCallback( async (action, data, timeout) => { diff --git a/packages/app-websockets/src/domain/WebsocketsActions.ts b/packages/app-websockets/src/domain/WebsocketsActions.ts index 30232b6da93..9c6adf72a8b 100644 --- a/packages/app-websockets/src/domain/WebsocketsActions.ts +++ b/packages/app-websockets/src/domain/WebsocketsActions.ts @@ -10,13 +10,13 @@ export interface IWebsocketActionsParams { manager: IWebsocketsManager; tenant: string | null; locale: string | null; - getToken: () => Promise; + getToken: () => Promise; } export class WebsocketsActions implements IWebsocketsActions { public readonly manager: IWebsocketsManager; - private readonly getToken: () => Promise; + private readonly getToken: () => Promise; private readonly tenant: string | null; private readonly locale: string | null; diff --git a/packages/app-websockets/src/domain/WebsocketsConnection.ts b/packages/app-websockets/src/domain/WebsocketsConnection.ts index 131ae4131d3..45a421f0a0c 100644 --- a/packages/app-websockets/src/domain/WebsocketsConnection.ts +++ b/packages/app-websockets/src/domain/WebsocketsConnection.ts @@ -43,7 +43,7 @@ export interface IWebsocketsConnectionParams { url: string; tenant: string; locale: string; - getToken(): Promise; + getToken(): Promise; subscriptionManager: IWebsocketsSubscriptionManager; protocol?: IWebsocketsConnectProtocol; factory?: IWebsocketsConnectionFactory; @@ -51,7 +51,7 @@ export interface IWebsocketsConnectionParams { export class WebsocketsConnection implements IWebsocketsConnection { private readonly url: string; - private readonly getToken: () => Promise; + private readonly getToken: () => Promise; private tenant: string; private locale: string; private readonly protocol: IWebsocketsConnectProtocol; diff --git a/packages/app-websockets/src/utils/getToken.ts b/packages/app-websockets/src/utils/getToken.ts deleted file mode 100644 index 10ae88f16f7..00000000000 --- a/packages/app-websockets/src/utils/getToken.ts +++ /dev/null @@ -1,13 +0,0 @@ -import { Auth } from "@aws-amplify/auth"; - -export const getToken = async (): Promise => { - const user = await Auth.currentSession(); - if (!user) { - return null; - } - const token = user.getIdToken(); - if (!token) { - return null; - } - return token.getJwtToken(); -}; diff --git a/packages/app-websockets/tsconfig.build.json b/packages/app-websockets/tsconfig.build.json index dc0f42b8992..7efae3ffdbf 100644 --- a/packages/app-websockets/tsconfig.build.json +++ b/packages/app-websockets/tsconfig.build.json @@ -4,6 +4,7 @@ "references": [ { "path": "../app/tsconfig.build.json" }, { "path": "../app-i18n/tsconfig.build.json" }, + { "path": "../app-security/tsconfig.build.json" }, { "path": "../app-tenancy/tsconfig.build.json" }, { "path": "../utils/tsconfig.build.json" } ], diff --git a/packages/app-websockets/tsconfig.json b/packages/app-websockets/tsconfig.json index c2874200728..370896ebf54 100644 --- a/packages/app-websockets/tsconfig.json +++ b/packages/app-websockets/tsconfig.json @@ -4,6 +4,7 @@ "references": [ { "path": "../app" }, { "path": "../app-i18n" }, + { "path": "../app-security" }, { "path": "../app-tenancy" }, { "path": "../utils" } ], @@ -18,6 +19,8 @@ "@webiny/app": ["../app/src"], "@webiny/app-i18n/*": ["../app-i18n/src/*"], "@webiny/app-i18n": ["../app-i18n/src"], + "@webiny/app-security/*": ["../app-security/src/*"], + "@webiny/app-security": ["../app-security/src"], "@webiny/app-tenancy/*": ["../app-tenancy/src/*"], "@webiny/app-tenancy": ["../app-tenancy/src"], "@webiny/utils/*": ["../utils/src/*"], diff --git a/yarn.lock b/yarn.lock index cef241c66ec..e505581cf59 100644 --- a/yarn.lock +++ b/yarn.lock @@ -106,30 +106,22 @@ __metadata: languageName: node linkType: hard -"@auth0/auth0-react@npm:^1.10.1": - version: 1.12.1 - resolution: "@auth0/auth0-react@npm:1.12.1" +"@auth0/auth0-react@npm:^2.2.4": + version: 2.2.4 + resolution: "@auth0/auth0-react@npm:2.2.4" dependencies: - "@auth0/auth0-spa-js": ^1.22.6 + "@auth0/auth0-spa-js": ^2.1.3 peerDependencies: react: ^16.11.0 || ^17 || ^18 react-dom: ^16.11.0 || ^17 || ^18 - checksum: 2d5de5ac7447def4e4a1f5c9f5f0ac1fb8b04a36e31a3f4e07e1656a7a13fc0f322e067bc38facb2b2c328ba090b4eb9dc45b81fa1eac11ee097960367893aad + checksum: 10b8d4e9eaf6fe4f1e5bdeaae61bfade9a8439f0bfb5008aea34691ce45924bc92b046757884bcae99dd8902dcb62e77295e35692d51df2ed8eca6d0d7bbd335 languageName: node linkType: hard -"@auth0/auth0-spa-js@npm:^1.22.6": - version: 1.22.6 - resolution: "@auth0/auth0-spa-js@npm:1.22.6" - dependencies: - abortcontroller-polyfill: ^1.7.3 - browser-tabs-lock: ^1.2.15 - core-js: ^3.25.4 - es-cookie: ~1.3.2 - fast-text-encoding: ^1.0.6 - promise-polyfill: ^8.2.3 - unfetch: ^4.2.0 - checksum: 0155025b82898bd0533db373d7c8b85938fbae097c6fdf732a315ddee67d84a32c32a3b944465aec0600adb11d6d32e53eabbfc2ea04e6b5c90f92f72607bc30 +"@auth0/auth0-spa-js@npm:^2.1.3": + version: 2.1.3 + resolution: "@auth0/auth0-spa-js@npm:2.1.3" + checksum: aff5f3ab8a15ad63e9956e638ea13955dea22268b0d46d93561ae7accce0147198fc01203cbfe65637d709836c7ecf6a7efc5d38742dd0e251eb1387849efff5 languageName: node linkType: hard @@ -16112,7 +16104,7 @@ __metadata: resolution: "@webiny/app-admin-auth0@workspace:packages/app-admin-auth0" dependencies: "@apollo/react-hooks": ^3.1.5 - "@auth0/auth0-react": ^1.10.1 + "@auth0/auth0-react": ^2.2.4 "@babel/cli": ^7.23.9 "@babel/core": ^7.24.0 "@babel/plugin-proposal-class-properties": ^7.18.6 @@ -17521,7 +17513,6 @@ __metadata: version: 0.0.0-use.local resolution: "@webiny/app-websockets@workspace:packages/app-websockets" dependencies: - "@aws-amplify/auth": ^5.1.9 "@babel/cli": ^7.23.9 "@babel/core": ^7.24.0 "@babel/preset-env": ^7.24.0 @@ -17529,6 +17520,7 @@ __metadata: "@babel/preset-typescript": ^7.23.3 "@webiny/app": 0.0.0 "@webiny/app-i18n": 0.0.0 + "@webiny/app-security": 0.0.0 "@webiny/app-tenancy": 0.0.0 "@webiny/cli": 0.0.0 "@webiny/project-utils": 0.0.0 @@ -19217,13 +19209,6 @@ __metadata: languageName: node linkType: hard -"abortcontroller-polyfill@npm:^1.7.3": - version: 1.7.5 - resolution: "abortcontroller-polyfill@npm:1.7.5" - checksum: daf4169f4228ae0e4f4dbcfa782e501b923667f2666b7c55bd3b7664e5d6b100e333a93371173985fdf21f65d7dfba15bdb2e6031bdc9e57e4ce0297147da3aa - languageName: node - linkType: hard - "abstract-leveldown@npm:^6.2.1, abstract-leveldown@npm:^6.3.0": version: 6.3.0 resolution: "abstract-leveldown@npm:6.3.0" @@ -21164,15 +21149,6 @@ __metadata: languageName: node linkType: hard -"browser-tabs-lock@npm:^1.2.15": - version: 1.2.15 - resolution: "browser-tabs-lock@npm:1.2.15" - dependencies: - lodash: ">=4.17.21" - checksum: 79194361d5dbd0bf83f1e7e56fdbc209161d1c86f8211214512388df126d69b152512f073392e9415dec8d5e3b427045f9a9a3cb089ae7e7f6ed48dd6022ae74 - languageName: node - linkType: hard - "browserify-aes@npm:^1.0.0, browserify-aes@npm:^1.0.4, browserify-aes@npm:^1.2.0": version: 1.2.0 resolution: "browserify-aes@npm:1.2.0" @@ -22834,7 +22810,7 @@ __metadata: languageName: node linkType: hard -"core-js@npm:^3.0.1, core-js@npm:^3.15.1, core-js@npm:^3.25.4, core-js@npm:^3.6.1, core-js@npm:^3.6.5": +"core-js@npm:^3.0.1, core-js@npm:^3.15.1, core-js@npm:^3.6.1, core-js@npm:^3.6.5": version: 3.27.2 resolution: "core-js@npm:3.27.2" checksum: 718debd426f55a6b97cf9b757c936be258afd6d4f7052f89d0f96c982d7013e9000b0b006df42831a0cf32adad298e34d6a19052dce9ae1c7ab87162c0c665e0 @@ -25013,13 +24989,6 @@ __metadata: languageName: node linkType: hard -"es-cookie@npm:~1.3.2": - version: 1.3.2 - resolution: "es-cookie@npm:1.3.2" - checksum: 8509355a7d00bd2e3fcab4a76a90e0da35b40c1e76f114f8ffe805ab3fdfff51e8fc0e7cdccd9bf1536066150b6b9861e37d78ae14f80680513901902ac4f0df - languageName: node - linkType: hard - "es-get-iterator@npm:^1.1.2, es-get-iterator@npm:^1.1.3": version: 1.1.3 resolution: "es-get-iterator@npm:1.1.3" @@ -26050,13 +26019,6 @@ __metadata: languageName: node linkType: hard -"fast-text-encoding@npm:^1.0.6": - version: 1.0.6 - resolution: "fast-text-encoding@npm:1.0.6" - checksum: 9d58f694314b3283e785bf61954902536da228607ad246905e30256f9ab8331f780ac987e7222c9f5eafd04168d07e12b8054c85cedb76a2c05af0e82387a903 - languageName: node - linkType: hard - "fast-uri@npm:^2.0.0, fast-uri@npm:^2.1.0": version: 2.2.0 resolution: "fast-uri@npm:2.2.0" @@ -31198,7 +31160,7 @@ __metadata: languageName: node linkType: hard -"lodash@npm:4, lodash@npm:4.17.21, lodash@npm:>=4.17.21, lodash@npm:^4.0.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.3, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.3.0, lodash@npm:~4.17.21": +"lodash@npm:4, lodash@npm:4.17.21, lodash@npm:^4.0.1, lodash@npm:^4.17.11, lodash@npm:^4.17.14, lodash@npm:^4.17.15, lodash@npm:^4.17.19, lodash@npm:^4.17.20, lodash@npm:^4.17.21, lodash@npm:^4.17.3, lodash@npm:^4.17.4, lodash@npm:^4.17.5, lodash@npm:^4.3.0, lodash@npm:~4.17.21": version: 4.17.21 resolution: "lodash@npm:4.17.21" checksum: eb835a2e51d381e561e508ce932ea50a8e5a68f4ebdd771ea240d3048244a8d13658acbd502cd4829768c56f2e16bdd4340b9ea141297d472517b83868e677f7 @@ -35528,13 +35490,6 @@ __metadata: languageName: node linkType: hard -"promise-polyfill@npm:^8.2.3": - version: 8.3.0 - resolution: "promise-polyfill@npm:8.3.0" - checksum: 206373802076c77def0805758d0a8ece64120dfa6603f092404a1004211f8f2f67f33cadbc35953fc2a8ed0b0d38c774e88bdf01e20ce7a920723a60df84b7a5 - languageName: node - linkType: hard - "promise-retry@npm:^2.0.1": version: 2.0.1 resolution: "promise-retry@npm:2.0.1"