feat(sso): allow to use OIDC and SAML (#7246)
## What it does ### Backend - [x] Add a mutation to create OIDC and SAML configuration - [x] Add a mutation to delete an SSO config - [x] Add a feature flag to toggle SSO - [x] Add a mutation to activate/deactivate an SSO config - [x] Add a mutation to delete an SSO config - [x] Add strategy to use OIDC or SAML - [ ] Improve error management ### Frontend - [x] Add section "security" in settings - [x] Add page to list SSO configurations - [x] Add page and forms to create OIDC or SAML configuration - [x] Add field to "connect with SSO" in the signin/signup process - [x] Trigger auth when a user switch to a workspace with SSO enable - [x] Add an option on the security page to activate/deactivate the global invitation link - [ ] Add new Icons for SSO Identity Providers (okta, Auth0, Azure, Microsoft) --------- Co-authored-by: Félix Malfait <felix@twenty.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
@ -71,6 +71,7 @@ export type AuthProviders = {
|
||||
magicLink: Scalars['Boolean']['output'];
|
||||
microsoft: Scalars['Boolean']['output'];
|
||||
password: Scalars['Boolean']['output'];
|
||||
sso: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
export type AuthToken = {
|
||||
@ -148,6 +149,7 @@ export enum CaptchaDriverType {
|
||||
|
||||
export type ClientConfig = {
|
||||
__typename?: 'ClientConfig';
|
||||
analyticsEnabled: Scalars['Boolean']['output'];
|
||||
api: ApiConfig;
|
||||
authProviders: AuthProviders;
|
||||
billing: Billing;
|
||||
@ -275,6 +277,15 @@ export type DeleteServerlessFunctionInput = {
|
||||
id: Scalars['ID']['input'];
|
||||
};
|
||||
|
||||
export type DeleteSsoInput = {
|
||||
identityProviderId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type DeleteSsoOutput = {
|
||||
__typename?: 'DeleteSsoOutput';
|
||||
identityProviderId: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
/** Schema update on a table */
|
||||
export enum DistantTableUpdate {
|
||||
ColumnsAdded = 'COLUMNS_ADDED',
|
||||
@ -283,6 +294,20 @@ export enum DistantTableUpdate {
|
||||
TableDeleted = 'TABLE_DELETED'
|
||||
}
|
||||
|
||||
export type EditSsoInput = {
|
||||
id: Scalars['String']['input'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
};
|
||||
|
||||
export type EditSsoOutput = {
|
||||
__typename?: 'EditSsoOutput';
|
||||
id: Scalars['String']['output'];
|
||||
issuer: Scalars['String']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
type: IdpType;
|
||||
};
|
||||
|
||||
export type EmailPasswordResetLink = {
|
||||
__typename?: 'EmailPasswordResetLink';
|
||||
/** Boolean that confirms query was dispatched */
|
||||
@ -372,6 +397,20 @@ export enum FileFolder {
|
||||
WorkspaceLogo = 'WorkspaceLogo'
|
||||
}
|
||||
|
||||
export type FindAvailableSsoidpInput = {
|
||||
email: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type FindAvailableSsoidpOutput = {
|
||||
__typename?: 'FindAvailableSSOIDPOutput';
|
||||
id: Scalars['String']['output'];
|
||||
issuer: Scalars['String']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
type: IdpType;
|
||||
workspace: WorkspaceNameAndId;
|
||||
};
|
||||
|
||||
export type FindManyRemoteTablesInput = {
|
||||
/** The id of the remote server. */
|
||||
id: Scalars['ID']['input'];
|
||||
@ -385,6 +424,33 @@ export type FullName = {
|
||||
lastName: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type GenerateJwt = GenerateJwtOutputWithAuthTokens | GenerateJwtOutputWithSsoauth;
|
||||
|
||||
export type GenerateJwtOutputWithAuthTokens = {
|
||||
__typename?: 'GenerateJWTOutputWithAuthTokens';
|
||||
authTokens: AuthTokens;
|
||||
reason: Scalars['String']['output'];
|
||||
success: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
export type GenerateJwtOutputWithSsoauth = {
|
||||
__typename?: 'GenerateJWTOutputWithSSOAUTH';
|
||||
availableSSOIDPs: Array<FindAvailableSsoidpOutput>;
|
||||
reason: Scalars['String']['output'];
|
||||
success: Scalars['Boolean']['output'];
|
||||
};
|
||||
|
||||
export type GetAuthorizationUrlInput = {
|
||||
identityProviderId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type GetAuthorizationUrlOutput = {
|
||||
__typename?: 'GetAuthorizationUrlOutput';
|
||||
authorizationURL: Scalars['String']['output'];
|
||||
id: Scalars['String']['output'];
|
||||
type: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type GetServerlessFunctionSourceCodeInput = {
|
||||
/** The id of the function. */
|
||||
id: Scalars['ID']['input'];
|
||||
@ -392,6 +458,11 @@ export type GetServerlessFunctionSourceCodeInput = {
|
||||
version?: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export enum IdpType {
|
||||
Oidc = 'OIDC',
|
||||
Saml = 'SAML'
|
||||
}
|
||||
|
||||
export type IndexConnection = {
|
||||
__typename?: 'IndexConnection';
|
||||
/** Array of edges. */
|
||||
@ -461,12 +532,14 @@ export type Mutation = {
|
||||
authorizeApp: AuthorizeApp;
|
||||
challenge: LoginToken;
|
||||
checkoutSession: SessionEntity;
|
||||
createOIDCIdentityProvider: SetupSsoOutput;
|
||||
createOneAppToken: AppToken;
|
||||
createOneField: Field;
|
||||
createOneObject: Object;
|
||||
createOneRelation: Relation;
|
||||
createOneRemoteServer: RemoteServer;
|
||||
createOneServerlessFunction: ServerlessFunction;
|
||||
createSAMLIdentityProvider: SetupSsoOutput;
|
||||
deactivateWorkflowVersion: Scalars['Boolean']['output'];
|
||||
deleteCurrentWorkspace: Workspace;
|
||||
deleteOneField: Field;
|
||||
@ -474,16 +547,20 @@ export type Mutation = {
|
||||
deleteOneRelation: Relation;
|
||||
deleteOneRemoteServer: RemoteServer;
|
||||
deleteOneServerlessFunction: ServerlessFunction;
|
||||
deleteSSOIdentityProvider: DeleteSsoOutput;
|
||||
deleteUser: User;
|
||||
deleteWorkspaceInvitation: Scalars['String']['output'];
|
||||
disablePostgresProxy: PostgresCredentials;
|
||||
editSSOIdentityProvider: EditSsoOutput;
|
||||
emailPasswordResetLink: EmailPasswordResetLink;
|
||||
enablePostgresProxy: PostgresCredentials;
|
||||
exchangeAuthorizationCode: ExchangeAuthCode;
|
||||
executeOneServerlessFunction: ServerlessFunctionExecutionResult;
|
||||
findAvailableSSOIdentityProviders: Array<FindAvailableSsoidpOutput>;
|
||||
generateApiKeyToken: ApiKeyToken;
|
||||
generateJWT: AuthTokens;
|
||||
generateJWT: GenerateJwt;
|
||||
generateTransientToken: TransientToken;
|
||||
getAuthorizationUrl: GetAuthorizationUrlOutput;
|
||||
impersonate: Verify;
|
||||
publishServerlessFunction: ServerlessFunction;
|
||||
renewToken: AuthTokens;
|
||||
@ -551,6 +628,11 @@ export type MutationCheckoutSessionArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateOidcIdentityProviderArgs = {
|
||||
input: SetupOidcSsoInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateOneAppTokenArgs = {
|
||||
input: CreateOneAppTokenInput;
|
||||
};
|
||||
@ -581,6 +663,11 @@ export type MutationCreateOneServerlessFunctionArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationCreateSamlIdentityProviderArgs = {
|
||||
input: SetupSamlSsoInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeactivateWorkflowVersionArgs = {
|
||||
workflowVersionId: Scalars['String']['input'];
|
||||
};
|
||||
@ -611,11 +698,21 @@ export type MutationDeleteOneServerlessFunctionArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteSsoIdentityProviderArgs = {
|
||||
input: DeleteSsoInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationDeleteWorkspaceInvitationArgs = {
|
||||
appTokenId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
|
||||
export type MutationEditSsoIdentityProviderArgs = {
|
||||
input: EditSsoInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationEmailPasswordResetLinkArgs = {
|
||||
email: Scalars['String']['input'];
|
||||
};
|
||||
@ -633,6 +730,11 @@ export type MutationExecuteOneServerlessFunctionArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationFindAvailableSsoIdentityProvidersArgs = {
|
||||
input: FindAvailableSsoidpInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationGenerateApiKeyTokenArgs = {
|
||||
apiKeyId: Scalars['String']['input'];
|
||||
expiresAt: Scalars['String']['input'];
|
||||
@ -644,6 +746,11 @@ export type MutationGenerateJwtArgs = {
|
||||
};
|
||||
|
||||
|
||||
export type MutationGetAuthorizationUrlArgs = {
|
||||
input: GetAuthorizationUrlInput;
|
||||
};
|
||||
|
||||
|
||||
export type MutationImpersonateArgs = {
|
||||
userId: Scalars['String']['input'];
|
||||
};
|
||||
@ -865,6 +972,7 @@ export type Query = {
|
||||
getTimelineThreadsFromPersonId: TimelineThreadsWithTotal;
|
||||
index: Index;
|
||||
indexMetadatas: IndexConnection;
|
||||
listSSOIdentityProvidersByWorkspaceId: Array<FindAvailableSsoidpOutput>;
|
||||
object: Object;
|
||||
objects: ObjectConnection;
|
||||
relation: Relation;
|
||||
@ -1091,6 +1199,12 @@ export type RunWorkflowVersionInput = {
|
||||
workflowVersionId: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export enum SsoIdentityProviderStatus {
|
||||
Active = 'Active',
|
||||
Error = 'Error',
|
||||
Inactive = 'Inactive'
|
||||
}
|
||||
|
||||
export type SendInvitationsOutput = {
|
||||
__typename?: 'SendInvitationsOutput';
|
||||
errors: Array<Scalars['String']['output']>;
|
||||
@ -1179,6 +1293,31 @@ export type SessionEntity = {
|
||||
url?: Maybe<Scalars['String']['output']>;
|
||||
};
|
||||
|
||||
export type SetupOidcSsoInput = {
|
||||
clientID: Scalars['String']['input'];
|
||||
clientSecret: Scalars['String']['input'];
|
||||
issuer: Scalars['String']['input'];
|
||||
name: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type SetupSamlSsoInput = {
|
||||
certificate: Scalars['String']['input'];
|
||||
fingerprint?: InputMaybe<Scalars['String']['input']>;
|
||||
id: Scalars['String']['input'];
|
||||
issuer: Scalars['String']['input'];
|
||||
name: Scalars['String']['input'];
|
||||
ssoURL: Scalars['String']['input'];
|
||||
};
|
||||
|
||||
export type SetupSsoOutput = {
|
||||
__typename?: 'SetupSsoOutput';
|
||||
id: Scalars['String']['output'];
|
||||
issuer: Scalars['String']['output'];
|
||||
name: Scalars['String']['output'];
|
||||
status: SsoIdentityProviderStatus;
|
||||
type: IdpType;
|
||||
};
|
||||
|
||||
/** Sort Directions */
|
||||
export enum SortDirection {
|
||||
Asc = 'ASC',
|
||||
@ -1368,11 +1507,13 @@ export type UpdateWorkspaceInput = {
|
||||
displayName?: InputMaybe<Scalars['String']['input']>;
|
||||
domainName?: InputMaybe<Scalars['String']['input']>;
|
||||
inviteHash?: InputMaybe<Scalars['String']['input']>;
|
||||
isPublicInviteLinkEnabled?: InputMaybe<Scalars['Boolean']['input']>;
|
||||
logo?: InputMaybe<Scalars['String']['input']>;
|
||||
};
|
||||
|
||||
export type User = {
|
||||
__typename?: 'User';
|
||||
analyticsTinybirdJwt?: Maybe<Scalars['String']['output']>;
|
||||
canImpersonate: Scalars['Boolean']['output'];
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
defaultAvatarUrl?: Maybe<Scalars['String']['output']>;
|
||||
@ -1467,6 +1608,7 @@ export type Workspace = {
|
||||
featureFlags?: Maybe<Array<FeatureFlag>>;
|
||||
id: Scalars['UUID']['output'];
|
||||
inviteHash?: Maybe<Scalars['String']['output']>;
|
||||
isPublicInviteLinkEnabled: Scalars['Boolean']['output'];
|
||||
logo?: Maybe<Scalars['String']['output']>;
|
||||
metadataVersion: Scalars['Float']['output'];
|
||||
updatedAt: Scalars['DateTime']['output'];
|
||||
@ -1539,6 +1681,12 @@ export enum WorkspaceMemberTimeFormatEnum {
|
||||
System = 'SYSTEM'
|
||||
}
|
||||
|
||||
export type WorkspaceNameAndId = {
|
||||
__typename?: 'WorkspaceNameAndId';
|
||||
displayName?: Maybe<Scalars['String']['output']>;
|
||||
id: Scalars['String']['output'];
|
||||
};
|
||||
|
||||
export type Field = {
|
||||
__typename?: 'field';
|
||||
createdAt: Scalars['DateTime']['output'];
|
||||
|
||||
Reference in New Issue
Block a user