Introduce SSO bypass permission. (#15417)
Closes [Core Issue #1772](https://github.com/twentyhq/core-team-issues/issues/1772). <!-- CURSOR_SUMMARY --> --- > [!NOTE] > Introduces SSO bypass with a new permission flag and workspace-level provider toggles, enabling permitted users to log in via Google/Microsoft/Password when SSO-only, with backend enforcement and frontend UI/hooks/queries. > > - **Backend**: > - **Permission & Enforcement**: Add `PermissionFlagType.SSO_BYPASS`; update `AuthService` to allow login via non-SSO providers when workspace bypass is enabled and user has `SSO_BYPASS`. > - **Workspace Model**: Add `isGoogleAuthBypassEnabled`, `isMicrosoftAuthBypassEnabled`, `isPasswordAuthBypassEnabled` (migration, entity, update input, service validation). > - **Public API**: Extend `PublicWorkspaceDataOutput` with `authBypassProviders`; resolver computes it; permissions defaults include `SSO_BYPASS`. > - **Frontend**: > - **GraphQL/State**: Generate new types/fields; add `authBypassProviders` to `GetPublicWorkspaceDataByDomain`; new states `workspaceAuthBypassProvidersState`, `workspaceBypassModeState`. > - **Auth UI/Logic**: Add `useWorkspaceBypass`; update sign-in form and footer to offer "Bypass SSO" and use merged providers when enabled; remove auto-redirect when single SSO. > - **Settings**: Add Security section to toggle bypass methods per provider; conditionally show Change Password via `useCanChangePassword`. > - **Tests/Mocks**: Update mocks and tests to include bypass flags/providers. > > <sup>Written by [Cursor Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit 8c393b2bad387fb6e8b8f40027f8637dd6e85723. This will update automatically on new commits. Configure [here](https://cursor.com/dashboard?tab=bugbot).</sup> <!-- /CURSOR_SUMMARY --> --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
This commit is contained in:
@@ -4,12 +4,14 @@ import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceSta
|
||||
import { currentWorkspaceMemberState } from '@/auth/states/currentWorkspaceMemberState';
|
||||
import { currentWorkspaceMembersState } from '@/auth/states/currentWorkspaceMembersState';
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { authProvidersState } from '@/client-config/states/authProvidersState';
|
||||
import { useIsCurrentLocationOnAWorkspace } from '@/domain-manager/hooks/useIsCurrentLocationOnAWorkspace';
|
||||
import { useLastAuthenticatedWorkspaceDomain } from '@/domain-manager/hooks/useLastAuthenticatedWorkspaceDomain';
|
||||
import { useInitializeFormatPreferences } from '@/localization/hooks/useInitializeFormatPreferences';
|
||||
import { coreViewsState } from '@/views/states/coreViewState';
|
||||
import { workspaceAuthBypassProvidersState } from '@/workspace/states/workspaceAuthBypassProvidersState';
|
||||
import { useCallback } from 'react';
|
||||
import { useSetRecoilState } from 'recoil';
|
||||
import { useRecoilValue, useSetRecoilState } from 'recoil';
|
||||
import { SOURCE_LOCALE, type APP_LOCALES } from 'twenty-shared/translations';
|
||||
import { type ObjectPermissions } from 'twenty-shared/types';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
@@ -36,6 +38,10 @@ export const useLoadCurrentUser = () => {
|
||||
const setCurrentWorkspace = useSetRecoilState(currentWorkspaceState);
|
||||
const { initializeFormatPreferences } = useInitializeFormatPreferences();
|
||||
const setCoreViews = useSetRecoilState(coreViewsState);
|
||||
const setWorkspaceAuthBypassProviders = useSetRecoilState(
|
||||
workspaceAuthBypassProvidersState,
|
||||
);
|
||||
const authProviders = useRecoilValue(authProvidersState);
|
||||
|
||||
const { isOnAWorkspace } = useIsCurrentLocationOnAWorkspace();
|
||||
|
||||
@@ -103,6 +109,16 @@ export const useLoadCurrentUser = () => {
|
||||
|
||||
setCurrentWorkspace(workspace);
|
||||
|
||||
if (isDefined(workspace)) {
|
||||
setWorkspaceAuthBypassProviders({
|
||||
google: authProviders.google && workspace.isGoogleAuthBypassEnabled,
|
||||
microsoft:
|
||||
authProviders.microsoft && workspace.isMicrosoftAuthBypassEnabled,
|
||||
password:
|
||||
authProviders.password && workspace.isPasswordAuthBypassEnabled,
|
||||
});
|
||||
}
|
||||
|
||||
if (isDefined(workspace) && isOnAWorkspace) {
|
||||
setLastAuthenticateWorkspaceDomain({
|
||||
workspaceId: workspace.id,
|
||||
@@ -132,6 +148,8 @@ export const useLoadCurrentUser = () => {
|
||||
initializeFormatPreferences,
|
||||
setLastAuthenticateWorkspaceDomain,
|
||||
setCoreViews,
|
||||
authProviders,
|
||||
setWorkspaceAuthBypassProviders,
|
||||
]);
|
||||
|
||||
return {
|
||||
|
||||
Reference in New Issue
Block a user