feat(auth): enhance SSO handling and workspace auth logic (#9858)

- Return only SSO providers with an `activate` status
- If only 1 SSO provider is enabled for auth, redirect the user to the
provider login page.
- if only SSO auth is available set the step to SSO selection.

---------

Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
Antoine Moreaux
2025-01-29 19:28:21 +01:00
committed by GitHub
parent 85df6ada52
commit 4edeb7f991
12 changed files with 215 additions and 149 deletions

View File

@ -1,5 +1,10 @@
import { getAuthProvidersByWorkspace } from 'src/engine/core-modules/workspace/utils/get-auth-providers-by-workspace.util';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import {
IdentityProviderType,
SSOIdentityProviderStatus,
WorkspaceSSOIdentityProvider,
} from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
describe('getAuthProvidersByWorkspace', () => {
const mockWorkspace = {
@ -10,8 +15,9 @@ describe('getAuthProvidersByWorkspace', () => {
{
id: 'sso1',
name: 'SSO Provider 1',
type: 'SAML',
status: 'active',
type: IdentityProviderType.SAML,
status: SSOIdentityProviderStatus.Active,
issuer: 'sso1.example.com',
},
],
@ -38,8 +44,9 @@ describe('getAuthProvidersByWorkspace', () => {
{
id: 'sso1',
name: 'SSO Provider 1',
type: 'SAML',
status: 'active',
type: IdentityProviderType.SAML,
status: SSOIdentityProviderStatus.Active,
issuer: 'sso1.example.com',
},
],
@ -66,6 +73,37 @@ describe('getAuthProvidersByWorkspace', () => {
sso: [],
});
});
it('should handle workspace with SSO providers inactive', () => {
const result = getAuthProvidersByWorkspace({
workspace: {
...mockWorkspace,
workspaceSSOIdentityProviders: [
{
id: 'sso1',
name: 'SSO Provider 1',
type: IdentityProviderType.SAML,
status: SSOIdentityProviderStatus.Inactive,
issuer: 'sso1.example.com',
} as WorkspaceSSOIdentityProvider,
],
},
systemEnabledProviders: {
google: true,
magicLink: false,
password: true,
microsoft: true,
sso: [],
},
});
expect(result).toEqual({
google: true,
magicLink: false,
password: true,
microsoft: false,
sso: [],
});
});
it('should disable Microsoft auth if isMicrosoftAuthEnabled is false', () => {
const result = getAuthProvidersByWorkspace({
@ -88,8 +126,9 @@ describe('getAuthProvidersByWorkspace', () => {
{
id: 'sso1',
name: 'SSO Provider 1',
type: 'SAML',
status: 'active',
type: IdentityProviderType.SAML,
status: SSOIdentityProviderStatus.Active,
issuer: 'sso1.example.com',
},
],

View File

@ -1,5 +1,7 @@
import { SSOIdentityProviderStatus } from 'src/engine/core-modules/sso/workspace-sso-identity-provider.entity';
import { AuthProviders } from 'src/engine/core-modules/workspace/dtos/public-workspace-data-output';
import { Workspace } from 'src/engine/core-modules/workspace/workspace.entity';
import { isDefined } from 'src/utils/is-defined';
export const getAuthProvidersByWorkspace = ({
workspace,
@ -21,12 +23,18 @@ export const getAuthProvidersByWorkspace = ({
workspace.isPasswordAuthEnabled && systemEnabledProviders.password,
microsoft:
workspace.isMicrosoftAuthEnabled && systemEnabledProviders.microsoft,
sso: workspace.workspaceSSOIdentityProviders.map((identityProvider) => ({
id: identityProvider.id,
name: identityProvider.name,
type: identityProvider.type,
status: identityProvider.status,
issuer: identityProvider.issuer,
})),
sso: workspace.workspaceSSOIdentityProviders
.map((identityProvider) =>
identityProvider.status === SSOIdentityProviderStatus.Active
? {
id: identityProvider.id,
name: identityProvider.name,
type: identityProvider.type,
status: identityProvider.status,
issuer: identityProvider.issuer,
}
: undefined,
)
.filter(isDefined),
};
};

View File

@ -148,11 +148,9 @@ export class WorkspaceResolver {
workspace.id,
);
const filteredFeatureFlags = featureFlags.filter((flag) =>
return featureFlags.filter((flag) =>
Object.values(FeatureFlagKey).includes(flag.key),
);
return filteredFeatureFlags;
}
@Mutation(() => Workspace)