Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(*): allow to select auth providers + add multiworkspace with subdomain management #8656

Merged
merged 16 commits into from
Dec 3, 2024

Conversation

AMoreaux
Copy link
Contributor

Summary

Add support for multi-workspace feature and adjust configurations and states accordingly.

  • Introduced new state isMultiWorkspaceEnabledState.
  • Updated ClientConfigProviderEffect component to handle multi-workspace.
  • Modified GraphQL schema and queries to include multi-workspace related configurations.
  • Adjusted server environment variables and their respective documentation to support multi-workspace toggle.
  • Updated server-side logic to handle new multi-workspace configurations and conditions.

Replaced the 'IS_SIGN_UP_DISABLED' flag with 'IS_MULTIWORKSPACE_ENABLED' to control workspace creation. This change allows for conditional workspace setup based on the new environment variable. Also updated related service, resolver, and entity files.
Updated the IS_MULTIWORKSPACE_ENABLED setting description to indicate support for multiple workspaces. This update clarifies that a web server capable of managing subdomain wildcards is required.
Updated the .env.example file to replace the IS_SIGN_UP_DISABLED variable with IS_MULTIWORKSPACE_ENABLED. Removed the IS_SIGN_UP_DISABLED property from environment-variables.ts as it is no longer needed.
…nabled

Removed isSignUpDisabledState and associated references, and replaced them with isMultiworkspaceEnabledState. Updated GraphQL queries and related components to reflect this change, and ensured multi-workspace functionality is enabled where applicable.
Corrected the casing of the `isMultiWorkspaceEnabled` variable in the NavigationDrawerHeader component. This ensures consistent naming conventions and avoids potential bugs related to variable misnaming.
Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

This PR introduces multi-workspace functionality by replacing the sign-up disabled flag with a multi-workspace enabled flag across the application.

  • Inconsistent casing in state key naming (isMultiworkspaceEnabled vs isMultiWorkspaceEnabled) in /packages/twenty-front/src/modules/client-config/states/isMultiWorkspaceEnabledState.ts
  • Default value mismatch between frontend (true) and server (false) for isMultiWorkspaceEnabled could cause inconsistent behavior
  • Removal of signUpDisabled state without clear alternative mechanism needs review for impact on user registration flows
  • Added note in documentation about wildcard subdomain requirement for multi-workspace feature
  • Server-side logic in sign-in-up.service.ts now blocks creation of additional workspaces when multi-workspace is disabled

12 file(s) reviewed, 4 comment(s)
Edit PR Review Bot Settings | Greptile

Copy link
Contributor

@martmull martmull left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, one comment about default value

Corrected the typo from isMultiworkspaceEnabled to isMultiWorkspaceEnabled. This change affects multiple files to ensure consistency across the codebase, including GraphQL queries, state management, and server configurations.
@AMoreaux AMoreaux marked this pull request as draft November 21, 2024 16:58
@AMoreaux
Copy link
Contributor Author

AMoreaux commented Nov 21, 2024

@martmull I converted this PR to a draft to avoid the merge since we must wait for the review on child PR to have the full feature.

Especially this one #8654
And another one about subdomains, I will open soon.

@AMoreaux AMoreaux self-assigned this Nov 22, 2024
AMoreaux and others added 4 commits November 25, 2024 10:01
Removed unused `appTokenRepository` injection and renamed variable `numberOfWorkspaces` to `workspacesCount` for clarity. This cleanup helps improve code readability and maintainability.
…kspace-flag

# Conflicts:
#	packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx
Updated various utility file paths and refactored enums to a separate file. Renamed utility files for consistency and moved 'SignInUpMode' enum to its own type file. Also, removed deprecated workspace invitation and URL-related utility files.
@AMoreaux AMoreaux changed the title feat(): add IS_MULTIWORKSPACE_ENABLE flag feat(*): allow to select auth providers + add multiworkspace with subdomain management Nov 27, 2024
…kspace-flag

# Conflicts:
#	packages/twenty-front/src/generated/graphql.tsx
#	packages/twenty-front/src/modules/ui/navigation/navigation-drawer/hooks/useWorkspaceSwitching.ts
#	packages/twenty-website/src/content/developers/self-hosting/self-hosting-var.mdx

const { signInUpStep, continueWithEmail } = useSignInUp(form);

const checkAuthProviders = useCallback(() => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move to SignInUpWorkspaceScopeFormEffect


useEffect(() => {
try {
if (isDefined(workspacePublicData?.logo)) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see if can leverage react-helmet

@@ -0,0 +1,4 @@
export enum SignInUpMode {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

filenaming

import { IconKey } from 'twenty-ui';
import { useListSsoIdentityProvidersByWorkspaceIdQuery } from '~/generated/graphql';
import { SnackBarVariant } from '@/ui/feedback/snack-bar-manager/components/SnackBar';
import { SSOIdentitiesProvidersState } from '@/settings/security/states/SSOIdentitiesProviders.state';
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no .state


const [updateWorkspace] = useUpdateWorkspaceMutation();

const isValidAuthProvider = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

extract this to util so we can test it easily and we the typing complexity is "hidden"

@@ -0,0 +1,4 @@
export type AuthProvidersKeys =
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GOOGLE
MICROSOFT

isGoogleAuthEnabled
isMicrosoftAuthEnabled

map(authEnablekyey: AuthProvidersKeys): GOOGLE | MICROSOFT (

)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

not mandatory but I would have "avoided the complexity like this"

return url.toString();
};

export const redirectToHome = () => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

file naming + split into multi files

picture?: string | null;
fromSSO: boolean;
isAuthEnabled?: ReturnType<(typeof workspaceValidator)['isAuthEnabled']>;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

isAuthProviderEnabled

@@ -53,6 +58,13 @@ export class GoogleAuthController {
workspaceInviteHash,
workspacePersonalInviteToken,
fromSSO: true,
isAuthEnabled: workspaceValidator.isAuthEnabled(
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

authProvider: 'google' | 'microsoft'

);

if (isAuthEnabled)
workspaceValidator.validateAuth(isAuthEnabled, workspace);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

workspaceValidator.validateAuth(() => { isValid(authProvider), ${authProvider} not enabled)

import { User } from 'src/engine/core-modules/user/user.entity';
import { CustomException } from 'src/utils/custom-exception';

const assertIsExist = (
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertIsDefinedOrThrow

};

export const userValidator: {
assertIsExist: typeof assertIsExist;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we would avoid typeof usage usually to lower complexity

},
});

return appTokens.map(castAppTokenToWorkspaceInvitationUtil);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move cast to an util

@@ -144,4 +152,26 @@ export class WorkspaceResolver {
hasValidEntrepriseKey(): boolean {
return isDefined(this.environmentService.get('ENTERPRISE_KEY'));
}

@Query(() => PublicWorkspaceDataOutput)
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we could extract that to another resolver

exceptionToThrowCustom?: CustomException,
) => boolean;

const isAuthEnabled: IsAuthEnabled = (provider, exceptionToThrow) => {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

see comments above

…kspace-flag

# Conflicts:
#	packages/twenty-front/src/modules/auth/sign-in-up/hooks/useSignInUpForm.ts
#	packages/twenty-front/src/modules/client-config/components/ClientConfigProviderEffect.tsx
#	packages/twenty-server/src/engine/core-modules/workspace-invitation/services/workspace-invitation.service.ts
…kspace-flag

# Conflicts:
#	packages/twenty-front/src/generated-metadata/graphql.ts
#	packages/twenty-server/src/engine/core-modules/auth/services/sign-in-up.service.ts
Refactored GraphQL types to include new structures such as ActivateWorkspaceOutput and AvailableWorkspaceOutput, and updated IdentityProviderType enum. Added GraphQL mutations for workspace activation and switching, enhancing workspace management functionality. Revised authentication-related structures for improved SSO integration and user management.
@AMoreaux AMoreaux marked this pull request as ready for review December 3, 2024 18:02
@charlesBochet charlesBochet merged commit 7943141 into main Dec 3, 2024
11 of 18 checks passed
@charlesBochet charlesBochet deleted the feat/add-is-multi-workspace-flag branch December 3, 2024 18:06
Copy link

github-actions bot commented Dec 3, 2024

Thanks @AMoreaux for your contribution!
This marks your 16th PR on the repo. You're top 2% of all our contributors 🎉
See contributor page - Share on LinkedIn - Share on Twitter

Contributions

Copy link
Contributor

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PR Summary

(updates since last review)

This PR significantly expands the multi-workspace functionality with subdomain management and authentication provider configuration.

  • Added workspace subdomain support in /packages/twenty-server/src/engine/core-modules/workspace/workspace.entity.ts with unique constraint and auth provider flags
  • Introduced new DomainManagerService in /packages/twenty-server/src/engine/core-modules/domain-manager/service/domain-manager.service.ts for handling workspace URLs and subdomain management
  • Refactored authentication flow in /packages/twenty-front/src/modules/auth/hooks/useAuth.ts to support workspace-specific auth providers and subdomain-based routing
  • Added workspace validation utilities in /packages/twenty-server/src/engine/core-modules/workspace/workspace.validate.ts for auth provider and activation checks
  • Replaced environment variable FRONT_BASE_URL with granular configuration (FRONT_DOMAIN, FRONT_PROTOCOL, FRONT_PORT) for better multi-workspace support

The changes appear comprehensive but should be carefully tested for subdomain handling and authentication flows across workspaces.

166 file(s) reviewed, 136 comment(s)
Edit PR Review Bot Settings | Greptile

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: Removing the JWT generation mutation without a clear replacement could break authentication flows. Ensure there is a new mechanism in place for JWT generation that supports multi-workspace functionality.

Comment on lines +4 to +5
query GetPublicWorkspaceDataBySubdomain {
getPublicWorkspaceDataBySubdomain {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: query lacks parameters to specify which subdomain to query

params.workspacePersonalInviteToken,
);
}
const subdomain = getWorkspaceSubdomain;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: getWorkspaceSubdomain is being used as a value but appears to be a function - should be getWorkspaceSubdomain()

Comment on lines +215 to +223
if (isDefined(workspace) && isTwentyWorkspaceSubdomain) {
setLastAuthenticateWorkspaceState({
id: workspace.id,
subdomain: workspace.subdomain,
cookieAttributes: {
domain: `.${urlManager.frontDomain}`,
},
});
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider adding error handling in case workspace.subdomain is undefined when setting cookie domain

Comment on lines +48 to +51
onChange={(value: string) => {
onChange(value);
if (isDefined(onChangeFromProps)) onChangeFromProps(value);
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: onChange handler could potentially trigger multiple re-renders since it's recreated on each render. Consider memoizing this function with useCallback.

Comment on lines +38 to +41
if (provider === 'google' && workspace.isGoogleAuthEnabled) return true;
if (provider === 'microsoft' && workspace.isMicrosoftAuthEnabled)
return true;
if (provider === 'password' && workspace.isPasswordAuthEnabled) return true;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: consider using a switch statement or object lookup for better maintainability with multiple providers

(data: unknown, ctx: ExecutionContext) => {
const request = getRequest(ctx);

return request.headers['origin'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: headers['origin'] could be undefined - consider adding type safety checks and default value

(data: unknown, ctx: ExecutionContext) => {
const request = getRequest(ctx);

return request.headers['origin'];
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: origin header should be validated against expected format to prevent header injection attacks

Comment on lines 19 to +21
async validateToken(req: Request) {
try {
return await this.accessTokenService.validateToken(req);
return await this.accessTokenService.validateTokenByRequest(req);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

logic: validateToken method name doesn't match the actual call to validateTokenByRequest

Comment on lines +8 to +10
it('should return false for a personal email', () => {
expect(isWorkEmail('[email protected]')).toBe(false);
});
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

style: should test multiple personal email providers (yahoo, hotmail, outlook etc) since implementation uses a set of providers

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants