diff --git a/README.md b/README.md index fc24f2d19..40ac9636b 100644 --- a/README.md +++ b/README.md @@ -1102,6 +1102,13 @@ await descopeClient.management.sso.newSettings(tenantID, ssoId, displayName); // You can delete existing SSO configuration // You can pass ssoId in case using multi SSO and you want to delete specific SSO configuration await descopeClient.management.sso.deleteSettings(tenantID); + +// You can disable an SSO configuration without deleting it, and enable it again later. +// Its settings, mappings and domains are kept, so re-enabling needs no payload. +// You can pass ssoId in case using multi SSO and you want to disable a specific SSO configuration +await descopeClient.management.sso.configureAuthType(tenantID, 'none', ssoId); +// Enable it again on the protocol it is configured for ('saml' or 'oidc') +await descopeClient.management.sso.configureAuthType(tenantID, 'saml', ssoId); ``` Note: Certificates should have a similar structure to: diff --git a/lib/management/paths.ts b/lib/management/paths.ts index 10ed1992b..e21fc2da9 100644 --- a/lib/management/paths.ts +++ b/lib/management/paths.ts @@ -144,6 +144,7 @@ export default { sso: { settings: '/v1/mgmt/sso/settings', settingsNew: '/v1/mgmt/sso/settings/new', + authType: '/v1/mgmt/sso/settings/authtype', metadata: '/v1/mgmt/sso/metadata', mapping: '/v1/mgmt/sso/mapping', settingsv2: '/v2/mgmt/sso/settings', diff --git a/lib/management/sso.test.ts b/lib/management/sso.test.ts index 75ae07b58..70c085527 100644 --- a/lib/management/sso.test.ts +++ b/lib/management/sso.test.ts @@ -484,6 +484,61 @@ describe('Management SSO', () => { }); }); + describe('configureAuthType', () => { + it('should disable a specific SSO configuration', async () => { + const httpResponse = { + ok: true, + json: () => {}, + clone: () => ({ + json: () => Promise.resolve({}), + }), + status: 200, + }; + mockHttpClient.post.mockResolvedValue(httpResponse); + + const resp = await management.sso.configureAuthType('t1', 'none', 'conf1'); + + expect(mockHttpClient.post).toHaveBeenCalledWith(apiPaths.sso.authType, { + tenantId: 't1', + authType: 'none', + ssoId: 'conf1', + }); + + expect(resp).toEqual({ + code: 200, + ok: true, + response: httpResponse, + data: {}, + }); + }); + + it('should target the default configuration when no ssoId is given', async () => { + const httpResponse = { + ok: true, + json: () => {}, + clone: () => ({ + json: () => Promise.resolve({}), + }), + status: 200, + }; + mockHttpClient.post.mockResolvedValue(httpResponse); + + const resp = await management.sso.configureAuthType('t1', 'saml'); + + expect(mockHttpClient.post).toHaveBeenCalledWith(apiPaths.sso.authType, { + tenantId: 't1', + authType: 'saml', + }); + + expect(resp).toEqual({ + code: 200, + ok: true, + response: httpResponse, + data: {}, + }); + }); + }); + describe('newSettings', () => { it('should send the correct request and receive correct response', async () => { const mockResponse = { diff --git a/lib/management/sso.ts b/lib/management/sso.ts index f01fc5649..1167bfb52 100644 --- a/lib/management/sso.ts +++ b/lib/management/sso.ts @@ -8,6 +8,7 @@ import { SSOSAMLSettings, SSOSAMLByMetadataSettings, SSOSettings, + SSOAuthType, XAASettings, XAASettingsResponse, } from './types'; @@ -156,6 +157,26 @@ const withSSOSettings = (httpClient: HttpClient) => ({ }), ); }, + /** + * Set the authentication type of a single SSO configuration, leaving its stored SAML/OIDC + * settings, mappings and domains untouched. `none` disables the configuration without deleting + * it, `saml`/`oidc` enable it on that protocol. + * @param tenantId the tenant the configuration belongs to + * @param authType `none` to disable, `saml` or `oidc` to enable on that protocol + * @param ssoId the SSO configuration to change; omit for the tenant's default configuration + */ + configureAuthType: ( + tenantId: string, + authType: SSOAuthType, + ssoId?: string, + ): Promise> => + transformResponse( + httpClient.post(apiPaths.sso.authType, { + tenantId, + authType, + ...(ssoId ? { ssoId } : {}), + }), + ), configureSAMLSettings: ( tenantId: string, settings: SSOSAMLSettings, diff --git a/lib/management/types.ts b/lib/management/types.ts index b3785a90e..5e5db1381 100644 --- a/lib/management/types.ts +++ b/lib/management/types.ts @@ -247,6 +247,12 @@ export type ClientAssertionResponse = { /** Represents a tenant in a project. It has an id, a name and an array of * self provisioning domains used to associate users with that tenant. */ +/** Authentication type of an SSO configuration. `none` means the configuration is disabled: it + * keeps its stored settings, mappings and domains, and serves no logins until it is set back to + * `saml` or `oidc`. + */ +export type SSOAuthType = 'none' | 'saml' | 'oidc'; + export type Tenant = { id: string; name: string; @@ -254,7 +260,7 @@ export type Tenant = { createdTime: number; customAttributes?: Record; domains?: string[]; - authType?: 'none' | 'saml' | 'oidc'; + authType?: SSOAuthType; enforceSSO?: boolean; disabled?: boolean; defaultRoles?: string[]; @@ -280,7 +286,7 @@ export type SSOSetupSuiteSettings = { export type TenantSettings = { selfProvisioningDomains: string[]; domains?: string[]; - authType?: 'none' | 'saml' | 'oidc'; + authType?: SSOAuthType; enabled?: boolean; refreshTokenExpiration?: number; refreshTokenExpirationUnit?: ExpirationUnit;