mirror of
https://github.com/logto-io/logto.git
synced 2024-12-30 20:33:54 -05:00
feat(core): add patch and delete SAML IdP initiated config API (#6662)
* feat(core): add patch and delete saml idp initiated config api add patch and delete sso connector idp initiated auth config api * fix(test): fix integration test fix integration test * fix(core): fix the rebase issue fix the rebase issue
This commit is contained in:
parent
14a07dcead
commit
17c2a79caf
8 changed files with 335 additions and 93 deletions
|
@ -19,6 +19,7 @@ import { mockLogtoConfigsLibrary } from '#src/test-utils/mock-libraries.js';
|
||||||
|
|
||||||
import { createCloudConnectionLibrary } from '../cloud-connection.js';
|
import { createCloudConnectionLibrary } from '../cloud-connection.js';
|
||||||
import { createConnectorLibrary } from '../connector.js';
|
import { createConnectorLibrary } from '../connector.js';
|
||||||
|
import { type SsoConnectorLibrary } from '../sso-connector.js';
|
||||||
|
|
||||||
const { jest } = import.meta;
|
const { jest } = import.meta;
|
||||||
|
|
||||||
|
@ -40,11 +41,12 @@ const signInExperiences = {
|
||||||
};
|
};
|
||||||
const { findDefaultSignInExperience, updateDefaultSignInExperience } = signInExperiences;
|
const { findDefaultSignInExperience, updateDefaultSignInExperience } = signInExperiences;
|
||||||
|
|
||||||
const ssoConnectorLibrary = {
|
const ssoConnectorLibrary: jest.Mocked<SsoConnectorLibrary> = {
|
||||||
getSsoConnectors: jest.fn(),
|
getSsoConnectors: jest.fn(),
|
||||||
getSsoConnectorById: jest.fn(),
|
getSsoConnectorById: jest.fn(),
|
||||||
getAvailableSsoConnectors: jest.fn(),
|
getAvailableSsoConnectors: jest.fn(),
|
||||||
createSsoConnectorIdpInitiatedAuthConfig: jest.fn(),
|
createSsoConnectorIdpInitiatedAuthConfig: jest.fn(),
|
||||||
|
updateSsoConnectorIdpInitiatedAuthConfig: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
const { MockQueries } = await import('#src/test-utils/tenant.js');
|
const { MockQueries } = await import('#src/test-utils/tenant.js');
|
||||||
|
|
|
@ -81,10 +81,37 @@ export const createSsoConnectorLibrary = (queries: Queries) => {
|
||||||
return ssoConnectors.insertIdpInitiatedAuthConfig(data);
|
return ssoConnectors.insertIdpInitiatedAuthConfig(data);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const updateSsoConnectorIdpInitiatedAuthConfig = async (
|
||||||
|
connectorId: string,
|
||||||
|
set: Pick<
|
||||||
|
Partial<CreateSsoConnectorIdpInitiatedAuthConfig>,
|
||||||
|
'defaultApplicationId' | 'redirectUri' | 'authParameters'
|
||||||
|
>
|
||||||
|
) => {
|
||||||
|
const { defaultApplicationId } = set;
|
||||||
|
|
||||||
|
if (defaultApplicationId) {
|
||||||
|
// Throws an 404 error if the application is not found
|
||||||
|
const application = await applications.findApplicationById(defaultApplicationId);
|
||||||
|
|
||||||
|
assertThat(
|
||||||
|
application.type === ApplicationType.Traditional && !application.isThirdParty,
|
||||||
|
new RequestError('connector.saml_idp_initiated_auth_invalid_application_type')
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ssoConnectors.updateIdpInitiatedAuthConfig({
|
||||||
|
set,
|
||||||
|
where: { connectorId },
|
||||||
|
jsonbMode: 'replace',
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
getSsoConnectors,
|
getSsoConnectors,
|
||||||
getAvailableSsoConnectors,
|
getAvailableSsoConnectors,
|
||||||
getSsoConnectorById,
|
getSsoConnectorById,
|
||||||
createSsoConnectorIdpInitiatedAuthConfig,
|
createSsoConnectorIdpInitiatedAuthConfig,
|
||||||
|
updateSsoConnectorIdpInitiatedAuthConfig,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
|
@ -13,6 +13,7 @@ import { convertToIdentifiers } from '#src/utils/sql.js';
|
||||||
|
|
||||||
import { buildInsertIntoWithPool } from '../database/insert-into.js';
|
import { buildInsertIntoWithPool } from '../database/insert-into.js';
|
||||||
import { buildUpdateWhereWithPool } from '../database/update-where.js';
|
import { buildUpdateWhereWithPool } from '../database/update-where.js';
|
||||||
|
import { DeletionError } from '../errors/SlonikError/index.js';
|
||||||
|
|
||||||
const {
|
const {
|
||||||
table: ssoConnectorIdpInitiatedAuthConfigsTable,
|
table: ssoConnectorIdpInitiatedAuthConfigsTable,
|
||||||
|
@ -66,4 +67,15 @@ export default class SsoConnectorQueries extends SchemaQueries<
|
||||||
WHERE ${ssoConnectorIdpInitiatedAuthConfigsFields.connectorId}=${connectorId}
|
WHERE ${ssoConnectorIdpInitiatedAuthConfigsFields.connectorId}=${connectorId}
|
||||||
`);
|
`);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async deleteIdpInitiatedAuthConfigByConnectorId(connectorId: string) {
|
||||||
|
const { rowCount } = await this.pool.query(sql`
|
||||||
|
DELETE FROM ${ssoConnectorIdpInitiatedAuthConfigsTable}
|
||||||
|
WHERE ${ssoConnectorIdpInitiatedAuthConfigsFields.connectorId}=${connectorId}
|
||||||
|
`);
|
||||||
|
|
||||||
|
if (rowCount < 1) {
|
||||||
|
throw new DeletionError(SsoConnectorIdpInitiatedAuthConfigs.table);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,11 +9,12 @@ const { jest } = import.meta;
|
||||||
|
|
||||||
const getAvailableSsoConnectorsMock = jest.fn();
|
const getAvailableSsoConnectorsMock = jest.fn();
|
||||||
|
|
||||||
const mockSsoConnectorLibrary: SsoConnectorLibrary = {
|
const mockSsoConnectorLibrary: jest.Mocked<SsoConnectorLibrary> = {
|
||||||
getAvailableSsoConnectors: getAvailableSsoConnectorsMock,
|
getAvailableSsoConnectors: getAvailableSsoConnectorsMock,
|
||||||
getSsoConnectors: jest.fn(),
|
getSsoConnectors: jest.fn(),
|
||||||
getSsoConnectorById: jest.fn(),
|
getSsoConnectorById: jest.fn(),
|
||||||
createSsoConnectorIdpInitiatedAuthConfig: jest.fn(),
|
createSsoConnectorIdpInitiatedAuthConfig: jest.fn(),
|
||||||
|
updateSsoConnectorIdpInitiatedAuthConfig: jest.fn(),
|
||||||
};
|
};
|
||||||
|
|
||||||
describe('verifyEmailIdentifier tests', () => {
|
describe('verifyEmailIdentifier tests', () => {
|
||||||
|
|
|
@ -51,6 +51,52 @@
|
||||||
"description": "The request body is invalid. The SSO connector is not a SAML connector or the application is not a Traditional web application."
|
"description": "The request body is invalid. The SSO connector is not a SAML connector or the application is not a Traditional web application."
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"patch": {
|
||||||
|
"summary": "Update IdP initiated auth config",
|
||||||
|
"description": "Partially update the IdP initiated authentication config of the given SAML SSO connector.",
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"properties": {
|
||||||
|
"defaultApplicationId": {
|
||||||
|
"description": "The unique identifier for the application that users will sign in to using IdP initiated authentication. The application type must be `Traditional` and cannot be a third-party application."
|
||||||
|
},
|
||||||
|
"redirectUri": {
|
||||||
|
"description": "The sign-in redirect URI for the application. This URI must be registered in the application's OIDC client metadata. If not provided, Logto will use the first registered redirect URI of the application."
|
||||||
|
},
|
||||||
|
"authParameters": {
|
||||||
|
"description": "The additional parameters to be sent to the application's OIDC authorization endpoint, e.g. `resources` and `scopes`."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "The updated IdP initiated auth config."
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "IdP initiated auth config not found for the given SSO connector or the provided application ID is not found."
|
||||||
|
},
|
||||||
|
"400": {
|
||||||
|
"description": "The request body is invalid. The application is not a first-party Traditional web application."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"delete": {
|
||||||
|
"summary": "Delete IdP initiated auth config",
|
||||||
|
"description": "Delete the IdP initiated authentication config of the given SAML SSO connector.",
|
||||||
|
"responses": {
|
||||||
|
"204": {
|
||||||
|
"description": "The IdP initiated auth config has been deleted."
|
||||||
|
},
|
||||||
|
"404": {
|
||||||
|
"description": "IdP initiated auth config not found for the given SSO connector."
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,7 +21,11 @@ export default function ssoConnectorIdpInitiatedAuthConfigRoutes<T extends Manag
|
||||||
{
|
{
|
||||||
queries,
|
queries,
|
||||||
libraries: {
|
libraries: {
|
||||||
ssoConnectors: { getSsoConnectorById, createSsoConnectorIdpInitiatedAuthConfig },
|
ssoConnectors: {
|
||||||
|
getSsoConnectorById,
|
||||||
|
createSsoConnectorIdpInitiatedAuthConfig,
|
||||||
|
updateSsoConnectorIdpInitiatedAuthConfig,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
] = args;
|
] = args;
|
||||||
|
@ -95,4 +99,50 @@ export default function ssoConnectorIdpInitiatedAuthConfigRoutes<T extends Manag
|
||||||
return next();
|
return next();
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
router.patch(
|
||||||
|
pathPrefix,
|
||||||
|
koaGuard({
|
||||||
|
body: SsoConnectorIdpInitiatedAuthConfigs.updateGuard.pick({
|
||||||
|
defaultApplicationId: true,
|
||||||
|
redirectUri: true,
|
||||||
|
authParameters: true,
|
||||||
|
}),
|
||||||
|
params: z.object({ id: z.string().min(1) }),
|
||||||
|
response: SsoConnectorIdpInitiatedAuthConfigs.guard,
|
||||||
|
status: [200, 400, 404],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const {
|
||||||
|
body,
|
||||||
|
params: { id },
|
||||||
|
} = ctx.guard;
|
||||||
|
|
||||||
|
const config = await updateSsoConnectorIdpInitiatedAuthConfig(id, body);
|
||||||
|
|
||||||
|
ctx.body = config;
|
||||||
|
ctx.status = 200;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
router.delete(
|
||||||
|
pathPrefix,
|
||||||
|
koaGuard({
|
||||||
|
params: z.object({ id: z.string().min(1) }),
|
||||||
|
status: [204, 404],
|
||||||
|
}),
|
||||||
|
async (ctx, next) => {
|
||||||
|
const {
|
||||||
|
params: { id },
|
||||||
|
} = ctx.guard;
|
||||||
|
|
||||||
|
await queries.ssoConnectors.deleteIdpInitiatedAuthConfigByConnectorId(id);
|
||||||
|
|
||||||
|
ctx.status = 204;
|
||||||
|
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -115,6 +115,26 @@ export class SsoConnectorApi {
|
||||||
.json<SsoConnectorIdpInitiatedAuthConfig>();
|
.json<SsoConnectorIdpInitiatedAuthConfig>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async updateSsoConnectorIdpInitiatedAuthConfig(
|
||||||
|
connectorId: string,
|
||||||
|
data: Pick<
|
||||||
|
Partial<CreateSsoConnectorIdpInitiatedAuthConfig>,
|
||||||
|
'defaultApplicationId' | 'redirectUri' | 'authParameters'
|
||||||
|
>
|
||||||
|
) {
|
||||||
|
return authedAdminApi
|
||||||
|
.patch(`sso-connectors/${connectorId}/idp-initiated-auth-config`, {
|
||||||
|
json: data,
|
||||||
|
})
|
||||||
|
.json<SsoConnectorIdpInitiatedAuthConfig>();
|
||||||
|
}
|
||||||
|
|
||||||
|
async deleteSsoConnectorIdpInitiatedAuthConfig(connectorId: string) {
|
||||||
|
return authedAdminApi
|
||||||
|
.delete(`sso-connectors/${connectorId}/idp-initiated-auth-config`)
|
||||||
|
.json<void>();
|
||||||
|
}
|
||||||
|
|
||||||
get firstConnectorId() {
|
get firstConnectorId() {
|
||||||
return Array.from(this.connectorInstances.keys())[0];
|
return Array.from(this.connectorInstances.keys())[0];
|
||||||
}
|
}
|
||||||
|
|
|
@ -39,117 +39,201 @@ devFeatureTest.describe('SAML IdP initiated authentication config', () => {
|
||||||
await ssoConnectorsApi.cleanUp();
|
await ssoConnectorsApi.cleanUp();
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw 404 if the connector is not found', async () => {
|
describe('Set IdP-initiated authentication configuration', () => {
|
||||||
const defaultApplicationId = applications.get('traditional')!.id;
|
it('should throw 404 if the connector is not found', async () => {
|
||||||
|
const defaultApplicationId = applications.get('traditional')!.id;
|
||||||
|
|
||||||
await expectRejects(
|
|
||||||
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
|
||||||
connectorId: 'not-found',
|
|
||||||
defaultApplicationId,
|
|
||||||
redirectUri: 'https://example.com',
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
code: 'entity.not_exists_with_id',
|
|
||||||
status: 404,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw 400 if the connector is not SAML', async () => {
|
|
||||||
const defaultApplicationId = applications.get('traditional')!.id;
|
|
||||||
|
|
||||||
await expectRejects(
|
|
||||||
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
|
||||||
connectorId: ssoConnectors.get('oidc')!.id,
|
|
||||||
defaultApplicationId,
|
|
||||||
redirectUri: 'https://example.com',
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
code: 'connector.saml_only_idp_initiated_auth',
|
|
||||||
status: 400,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should throw 404 if the application is not found', async () => {
|
|
||||||
await expectRejects(
|
|
||||||
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
|
||||||
connectorId: ssoConnectors.get('saml')!.id,
|
|
||||||
defaultApplicationId: 'not-found',
|
|
||||||
redirectUri: 'https://example.com',
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
code: 'entity.not_exists_with_id',
|
|
||||||
status: 404,
|
|
||||||
}
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
it.each(['spa', 'thirdParty'])(
|
|
||||||
'should throw 400 if the application is not a first-party traditional web application',
|
|
||||||
async (applicationKey) => {
|
|
||||||
const defaultApplicationId = applications.get(applicationKey)!.id;
|
|
||||||
await expectRejects(
|
await expectRejects(
|
||||||
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
||||||
connectorId: ssoConnectors.get('saml')!.id,
|
connectorId: 'not-found',
|
||||||
defaultApplicationId,
|
defaultApplicationId,
|
||||||
redirectUri: 'https://example.com',
|
redirectUri: 'https://example.com',
|
||||||
}),
|
}),
|
||||||
{
|
{
|
||||||
code: 'connector.saml_idp_initiated_auth_invalid_application_type',
|
code: 'entity.not_exists_with_id',
|
||||||
|
status: 404,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw 400 if the connector is not SAML', async () => {
|
||||||
|
const defaultApplicationId = applications.get('traditional')!.id;
|
||||||
|
|
||||||
|
await expectRejects(
|
||||||
|
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
||||||
|
connectorId: ssoConnectors.get('oidc')!.id,
|
||||||
|
defaultApplicationId,
|
||||||
|
redirectUri: 'https://example.com',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'connector.saml_only_idp_initiated_auth',
|
||||||
status: 400,
|
status: 400,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
it('should create a new IdP-initiated authentication configuration for a SAML SSO connector', async () => {
|
|
||||||
const defaultApplicationId = applications.get('traditional')!.id;
|
|
||||||
const redirectUri = 'https://example.com';
|
|
||||||
const authParameters = {
|
|
||||||
resources: ['resource1', 'resource2'],
|
|
||||||
scopes: ['profile', 'email'],
|
|
||||||
};
|
|
||||||
const connectorId = ssoConnectors.get('saml')!.id;
|
|
||||||
|
|
||||||
const config = await ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
|
||||||
connectorId,
|
|
||||||
defaultApplicationId,
|
|
||||||
redirectUri,
|
|
||||||
authParameters,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(config).toMatchObject({
|
it('should throw 404 if the application is not found', async () => {
|
||||||
defaultApplicationId,
|
await expectRejects(
|
||||||
redirectUri,
|
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
||||||
authParameters,
|
connectorId: ssoConnectors.get('saml')!.id,
|
||||||
|
defaultApplicationId: 'not-found',
|
||||||
|
redirectUri: 'https://example.com',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'entity.not_exists_with_id',
|
||||||
|
status: 404,
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
const fetchedConfig = await ssoConnectorsApi.getSsoConnectorIdpInitiatedAuthConfig(connectorId);
|
it.each(['spa', 'thirdParty'])(
|
||||||
|
'should throw 400 if the application is not a first-party traditional web application',
|
||||||
|
async (applicationKey) => {
|
||||||
|
const defaultApplicationId = applications.get(applicationKey)!.id;
|
||||||
|
await expectRejects(
|
||||||
|
ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
||||||
|
connectorId: ssoConnectors.get('saml')!.id,
|
||||||
|
defaultApplicationId,
|
||||||
|
redirectUri: 'https://example.com',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'connector.saml_idp_initiated_auth_invalid_application_type',
|
||||||
|
status: 400,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
expect(fetchedConfig).toMatchObject(config);
|
it('should create a new IdP-initiated authentication configuration for a SAML SSO connector', async () => {
|
||||||
|
const defaultApplicationId = applications.get('traditional')!.id;
|
||||||
|
const redirectUri = 'https://example.com';
|
||||||
|
const authParameters = {
|
||||||
|
resources: ['resource1', 'resource2'],
|
||||||
|
scopes: ['profile', 'email'],
|
||||||
|
};
|
||||||
|
const connectorId = ssoConnectors.get('saml')!.id;
|
||||||
|
|
||||||
|
const config = await ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
||||||
|
connectorId,
|
||||||
|
defaultApplicationId,
|
||||||
|
redirectUri,
|
||||||
|
authParameters,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(config).toMatchObject({
|
||||||
|
defaultApplicationId,
|
||||||
|
redirectUri,
|
||||||
|
authParameters,
|
||||||
|
});
|
||||||
|
|
||||||
|
const fetchedConfig = await ssoConnectorsApi.getSsoConnectorIdpInitiatedAuthConfig(
|
||||||
|
connectorId
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(fetchedConfig).toMatchObject(config);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should cascade delete the IdP-initiated authentication configuration when the application is deleted', async () => {
|
||||||
|
const application = await createApplication(
|
||||||
|
`web-app-${randomString()}`,
|
||||||
|
ApplicationType.Traditional
|
||||||
|
);
|
||||||
|
const connectorId = ssoConnectors.get('saml')!.id;
|
||||||
|
|
||||||
|
const config = await ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
||||||
|
connectorId,
|
||||||
|
defaultApplicationId: application.id,
|
||||||
|
redirectUri: 'https://example.com',
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(config).not.toBeNull();
|
||||||
|
|
||||||
|
await deleteApplication(application.id);
|
||||||
|
|
||||||
|
await expectRejects(ssoConnectorsApi.getSsoConnectorIdpInitiatedAuthConfig(connectorId), {
|
||||||
|
code: 'entity.not_found',
|
||||||
|
status: 404,
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should cascade delete the IdP-initiated authentication configuration when the application is deleted', async () => {
|
describe('Update IdP-initiated authentication configuration', () => {
|
||||||
const application = await createApplication(
|
it('should throw 404 if the application is not found', async () => {
|
||||||
`web-app-${randomString()}`,
|
await expectRejects(
|
||||||
ApplicationType.Traditional
|
ssoConnectorsApi.updateSsoConnectorIdpInitiatedAuthConfig(ssoConnectors.get('saml')!.id, {
|
||||||
);
|
defaultApplicationId: 'not-found',
|
||||||
const connectorId = ssoConnectors.get('saml')!.id;
|
redirectUri: 'https://example.com',
|
||||||
|
}),
|
||||||
const config = await ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
{
|
||||||
connectorId,
|
code: 'entity.not_exists_with_id',
|
||||||
defaultApplicationId: application.id,
|
status: 404,
|
||||||
redirectUri: 'https://example.com',
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
expect(config).not.toBeNull();
|
it.each(['spa', 'thirdParty'])(
|
||||||
|
'should throw 400 if the application is not a first-party traditional web application',
|
||||||
|
async (applicationKey) => {
|
||||||
|
await expectRejects(
|
||||||
|
ssoConnectorsApi.updateSsoConnectorIdpInitiatedAuthConfig(ssoConnectors.get('saml')!.id, {
|
||||||
|
defaultApplicationId: applications.get(applicationKey)!.id,
|
||||||
|
redirectUri: 'https://example.com',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'connector.saml_idp_initiated_auth_invalid_application_type',
|
||||||
|
status: 400,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
await deleteApplication(application.id);
|
it('should update the IdP-initiated authentication configuration for a SAML SSO connector', async () => {
|
||||||
|
const connectorId = ssoConnectors.get('saml')!.id;
|
||||||
|
const defaultApplicationId = applications.get('traditional')!.id;
|
||||||
|
|
||||||
await expectRejects(ssoConnectorsApi.getSsoConnectorIdpInitiatedAuthConfig(connectorId), {
|
const config = await ssoConnectorsApi.setSsoConnectorIdpInitiatedAuthConfig({
|
||||||
code: 'entity.not_found',
|
connectorId,
|
||||||
status: 404,
|
defaultApplicationId,
|
||||||
|
redirectUri: 'https://example.com',
|
||||||
|
});
|
||||||
|
|
||||||
|
const updatedConfig = await ssoConnectorsApi.updateSsoConnectorIdpInitiatedAuthConfig(
|
||||||
|
connectorId,
|
||||||
|
{
|
||||||
|
redirectUri: 'https://updated.com',
|
||||||
|
authParameters: {
|
||||||
|
resources: ['resource1', 'resource2'],
|
||||||
|
scopes: ['profile', 'email'],
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
expect(updatedConfig).toMatchObject({
|
||||||
|
connectorId,
|
||||||
|
defaultApplicationId,
|
||||||
|
redirectUri: 'https://updated.com',
|
||||||
|
authParameters: {
|
||||||
|
resources: ['resource1', 'resource2'],
|
||||||
|
scopes: ['profile', 'email'],
|
||||||
|
},
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should throw 404 if the IdP-initiated authentication configuration is not found', async () => {
|
||||||
|
const connectorId = ssoConnectors.get('saml')!.id;
|
||||||
|
|
||||||
|
await ssoConnectorsApi.deleteSsoConnectorIdpInitiatedAuthConfig(connectorId);
|
||||||
|
|
||||||
|
await expectRejects(
|
||||||
|
ssoConnectorsApi.updateSsoConnectorIdpInitiatedAuthConfig(connectorId, {
|
||||||
|
defaultApplicationId: applications.get('traditional')!.id,
|
||||||
|
redirectUri: 'https://example.com',
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
code: 'entity.not_exists',
|
||||||
|
status: 404,
|
||||||
|
}
|
||||||
|
);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Loading…
Reference in a new issue