From 4b6bad7d9ae20acaa98d090ff5e3184ae01252aa Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Wed, 22 Jan 2025 11:33:32 +0800 Subject: [PATCH 1/9] chore: add SAML app API docs --- .../saml-applications.openapi.json | 514 ++++++++++++++++++ .../src/routes/swagger/utils/documents.ts | 8 +- .../core/src/routes/swagger/utils/general.ts | 5 +- .../src/routes/swagger/utils/operation.ts | 7 - 4 files changed, 525 insertions(+), 9 deletions(-) create mode 100644 packages/core/src/routes/saml-application/saml-applications.openapi.json diff --git a/packages/core/src/routes/saml-application/saml-applications.openapi.json b/packages/core/src/routes/saml-application/saml-applications.openapi.json new file mode 100644 index 00000000000..b95cf328ccf --- /dev/null +++ b/packages/core/src/routes/saml-application/saml-applications.openapi.json @@ -0,0 +1,514 @@ +{ + "tags": [ + { + "name": "SAML Applications", + "description": "SAML (Security Assertion Markup Language) applications represent applications that use SAML protocol for single sign-on (SSO). These endpoints allow you to manage SAML applications, including their configurations and signing certificates." + }, + { + "name": "Dev feature" + }, + { + "name": "Cloud only" + } + ], + "paths": { + "/saml-applications/{id}/metadata": { + "get": { + "summary": "Get SAML application metadata", + "description": "Get the SAML metadata XML for the application.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "responses": { + "200": { + "description": "The SAML metadata XML.", + "content": { + "text/xml": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid request." + }, + "404": { + "description": "The SAML application was not found." + } + } + } + }, + "/saml-applications/{id}/callback": { + "get": { + "summary": "SAML application callback", + "description": "Handle the OIDC callback for SAML application and generate SAML response.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + }, + { + "name": "code", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "The authorization code from OIDC callback." + }, + { + "name": "state", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The state parameter from OIDC callback." + }, + { + "name": "redirectUri", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The redirect URI for the callback." + } + ], + "responses": { + "200": { + "description": "Returns an HTML form that automatically submits the SAML response." + }, + "400": { + "description": "Invalid request or OIDC error." + } + } + } + }, + "/saml/{id}/authn": { + "get": { + "summary": "Handle SAML authentication request (Redirect binding)", + "description": "Process SAML authentication request using HTTP Redirect binding.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + }, + { + "name": "SAMLRequest", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "The SAML request message." + }, + { + "name": "Signature", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The signature of the request." + }, + { + "name": "SigAlg", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The signature algorithm." + }, + { + "name": "RelayState", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The relay state parameter." + } + ], + "responses": { + "302": { + "description": "Redirects to the sign-in page." + }, + "400": { + "description": "Invalid SAML request." + }, + "404": { + "description": "The SAML application was not found." + } + } + }, + "post": { + "summary": "Handle SAML authentication request (POST binding)", + "description": "Process SAML authentication request using HTTP POST binding.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "requestBody": { + "required": true, + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "SAMLRequest": { + "type": "string", + "description": "The SAML request message." + }, + "RelayState": { + "type": "string", + "description": "The relay state parameter." + } + }, + "required": ["SAMLRequest"] + } + } + } + }, + "responses": { + "302": { + "description": "Redirects to the sign-in page." + }, + "400": { + "description": "Invalid SAML request." + }, + "404": { + "description": "The SAML application was not found." + } + } + } + }, + "/api/saml-applications": { + "post": { + "summary": "Create SAML application", + "description": "Create a new SAML application with the given configuration. A default signing certificate with 3 years lifetime will be automatically created.", + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the SAML application." + }, + "description": { + "type": "string", + "description": "Optional description of the SAML application." + }, + "customData": { + "type": "object", + "description": "Optional custom data for the application." + }, + "acsUrl": { + "type": "string", + "description": "The Assertion Consumer Service (ACS) URL where the SAML response will be sent." + } + }, + "required": ["name"] + } + } + } + }, + "responses": { + "201": { + "description": "The SAML application was created successfully." + }, + "400": { + "description": "Invalid request body." + }, + "422": { + "description": "Validation error. The ACS URL is invalid or other validation errors." + } + } + } + }, + "/api/saml-applications/{id}": { + "get": { + "summary": "Get SAML application", + "description": "Get SAML application details by ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "responses": { + "200": { + "description": "The SAML application details." + }, + "404": { + "description": "The SAML application was not found." + } + } + }, + "patch": { + "summary": "Update SAML application", + "description": "Update SAML application details by ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "The name of the SAML application." + }, + "description": { + "type": "string", + "description": "Description of the SAML application." + }, + "customData": { + "type": "object", + "description": "Custom data for the application." + }, + "acsUrl": { + "type": "string", + "description": "The Assertion Consumer Service (ACS) URL." + } + } + } + } + } + }, + "responses": { + "200": { + "description": "The SAML application was updated successfully." + }, + "404": { + "description": "The SAML application was not found." + }, + "422": { + "description": "Validation error." + } + } + }, + "delete": { + "summary": "Delete SAML application", + "description": "Delete a SAML application by ID.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "responses": { + "204": { + "description": "The SAML application was deleted successfully." + }, + "404": { + "description": "The SAML application was not found." + }, + "422": { + "description": "The specified application is not a SAML application." + } + } + } + }, + "/api/saml-applications/{id}/secrets": { + "post": { + "summary": "Create SAML application secret", + "description": "Create a new signing certificate for the SAML application.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "lifeSpanInYears": { + "type": "integer", + "minimum": 1, + "description": "The lifetime of the certificate in years (minimum 1 year)." + } + }, + "required": ["lifeSpanInYears"] + } + } + } + }, + "responses": { + "201": { + "description": "The signing certificate was created successfully." + }, + "400": { + "description": "Invalid request body." + }, + "404": { + "description": "The SAML application was not found." + } + } + }, + "get": { + "summary": "List SAML application secrets", + "description": "Get all signing certificates of the SAML application.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "responses": { + "200": { + "description": "A list of signing certificates." + }, + "404": { + "description": "The SAML application was not found." + } + } + } + }, + "/api/saml-applications/{id}/secrets/{secretId}": { + "delete": { + "summary": "Delete SAML application secret", + "description": "Delete a signing certificate of the SAML application. Active certificates cannot be deleted.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + }, + { + "name": "secretId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the signing certificate." + } + ], + "responses": { + "204": { + "description": "The signing certificate was deleted successfully." + }, + "400": { + "description": "Cannot delete an active certificate." + }, + "404": { + "description": "The SAML application or certificate was not found." + } + } + }, + "patch": { + "summary": "Update SAML application secret", + "description": "Update the status of a signing certificate.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + }, + { + "name": "secretId", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the signing certificate." + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "type": "object", + "properties": { + "active": { + "type": "boolean", + "description": "Whether the certificate is active." + } + }, + "required": ["active"] + } + } + } + }, + "responses": { + "200": { + "description": "The signing certificate was updated successfully." + }, + "400": { + "description": "Invalid request body." + }, + "404": { + "description": "The SAML application or certificate was not found." + } + } + } + } + } +} diff --git a/packages/core/src/routes/swagger/utils/documents.ts b/packages/core/src/routes/swagger/utils/documents.ts index 2fe2ba2a200..41c1f7c8e7f 100644 --- a/packages/core/src/routes/swagger/utils/documents.ts +++ b/packages/core/src/routes/swagger/utils/documents.ts @@ -47,11 +47,17 @@ const managementApiIdentifiableEntityNames = Object.freeze([ 'organization-role', 'organization-scope', 'organization-invitation', + 'saml-application', ]); /** Additional tags that cannot be inferred from the path. */ const additionalTags = Object.freeze( - condArray('Organization applications', 'Custom UI assets', 'Organization users') + condArray( + 'Organization applications', + 'Custom UI assets', + 'Organization users', + EnvSet.values.isDevFeaturesEnabled && 'SAML applications' + ) ); export const buildManagementApiBaseDocument = ( diff --git a/packages/core/src/routes/swagger/utils/general.ts b/packages/core/src/routes/swagger/utils/general.ts index 12f3be59647..cca3be9e268 100644 --- a/packages/core/src/routes/swagger/utils/general.ts +++ b/packages/core/src/routes/swagger/utils/general.ts @@ -35,6 +35,7 @@ const tagMap = new Map([ ['sso-connectors', 'SSO connectors'], ['sso-connector-providers', 'SSO connector providers'], ['.well-known', 'Well-known'], + ['saml-applications', 'SAML applications'], ]); /** @@ -138,7 +139,9 @@ const validateSupplementPaths = ( if ( isKeyInObject(operation, 'tags') && Array.isArray(operation.tags) && - !operation.tags.every((tag) => typeof tag === 'string' && reservedTags.has(tag)) + !operation.tags.every( + (tag) => typeof tag === 'string' && [cloudOnlyTag, devFeatureTag].includes(tag) + ) ) { throw new TypeError( `Cannot use \`tags\` in supplement document on path \`${path}\` and operation \`${method}\` except for tag \`${cloudOnlyTag}\` and \`${devFeatureTag}\`. Define tags in the document root instead.` diff --git a/packages/core/src/routes/swagger/utils/operation.ts b/packages/core/src/routes/swagger/utils/operation.ts index 0e921a3e2c4..4a3b726b19b 100644 --- a/packages/core/src/routes/swagger/utils/operation.ts +++ b/packages/core/src/routes/swagger/utils/operation.ts @@ -133,13 +133,6 @@ export const buildRouterObjects = (routers: T[], option router.stack // Filter out universal routes (mostly like a proxy route to withtyped) .filter(({ path }) => !path.includes('.*')) - // TODO: Remove this and bring back `/saml-applications` routes before release. - // Exclude `/saml-applications` routes and `/saml/:id/authn` for now. - .filter( - ({ path }) => - !path.startsWith('/saml-applications') && - !(path.startsWith('/saml') && path.endsWith('/authn')) - ) .flatMap(({ path: routerPath, stack, methods }) => methods .map((method) => method.toLowerCase()) From 1d74f58ba3ccbe46f22b95e80b1233780b0f7083 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Wed, 22 Jan 2025 15:26:22 +0800 Subject: [PATCH 2/9] chore: fix code --- .../saml-applications.openapi.json | 14 +++++----- .../src/routes/swagger/utils/documents.ts | 28 ++++++++++++++----- .../core/src/routes/swagger/utils/general.ts | 6 ++-- 3 files changed, 30 insertions(+), 18 deletions(-) diff --git a/packages/core/src/routes/saml-application/saml-applications.openapi.json b/packages/core/src/routes/saml-application/saml-applications.openapi.json index b95cf328ccf..0938feeb7a6 100644 --- a/packages/core/src/routes/saml-application/saml-applications.openapi.json +++ b/packages/core/src/routes/saml-application/saml-applications.openapi.json @@ -1,18 +1,15 @@ { "tags": [ { - "name": "SAML Applications", + "name": "SAML applications", "description": "SAML (Security Assertion Markup Language) applications represent applications that use SAML protocol for single sign-on (SSO). These endpoints allow you to manage SAML applications, including their configurations and signing certificates." }, - { - "name": "Dev feature" - }, { "name": "Cloud only" } ], "paths": { - "/saml-applications/{id}/metadata": { + "/api/saml-applications/{id}/metadata": { "get": { "summary": "Get SAML application metadata", "description": "Get the SAML metadata XML for the application.", @@ -47,7 +44,7 @@ } } }, - "/saml-applications/{id}/callback": { + "/api/saml-applications/{id}/callback": { "get": { "summary": "SAML application callback", "description": "Handle the OIDC callback for SAML application and generate SAML response.", @@ -93,11 +90,14 @@ }, "400": { "description": "Invalid request or OIDC error." + }, + "404": { + "description": "The SAML application was not found." } } } }, - "/saml/{id}/authn": { + "/api/saml/{id}/authn": { "get": { "summary": "Handle SAML authentication request (Redirect binding)", "description": "Process SAML authentication request using HTTP Redirect binding.", diff --git a/packages/core/src/routes/swagger/utils/documents.ts b/packages/core/src/routes/swagger/utils/documents.ts index 41c1f7c8e7f..2cd02008315 100644 --- a/packages/core/src/routes/swagger/utils/documents.ts +++ b/packages/core/src/routes/swagger/utils/documents.ts @@ -18,6 +18,7 @@ import { managementApiAuthDescription, userApiAuthDescription } from '../consts. import { type FindSupplementFilesOptions, devFeatureTag, + cloudOnlyTag, findSupplementFiles, pruneSwaggerDocument, removeUnnecessaryOperations, @@ -56,7 +57,7 @@ const additionalTags = Object.freeze( 'Organization applications', 'Custom UI assets', 'Organization users', - EnvSet.values.isDevFeaturesEnabled && 'SAML applications' + EnvSet.values.isCloud && 'SAML applications' ) ); @@ -234,12 +235,25 @@ export const getSupplementDocuments = async ( ) ); - // Filter out supplement documents that are for dev features when dev features are disabled. - const supplementDocuments = allSupplementDocuments.filter( - (supplement) => - EnvSet.values.isDevFeaturesEnabled || - !supplement.tags?.find((tag) => tag?.name === devFeatureTag) - ); + const supplementDocuments = allSupplementDocuments.filter((supplement) => { + // Include documents without special tags + if ( + !supplement.tags?.some((tag) => tag?.name === devFeatureTag || tag?.name === cloudOnlyTag) + ) { + return true; + } + + // Include dev feature documents when dev features are enabled and cloud features when in cloud mode + if ( + EnvSet.values.isDevFeaturesEnabled && + supplement.tags.some((tag) => tag?.name === devFeatureTag) && + (!supplement.tags.some((tag) => tag?.name === cloudOnlyTag) || EnvSet.values.isCloud) + ) { + return true; + } + + return false; + }); return supplementDocuments; }; diff --git a/packages/core/src/routes/swagger/utils/general.ts b/packages/core/src/routes/swagger/utils/general.ts index cca3be9e268..5ce9d42cda1 100644 --- a/packages/core/src/routes/swagger/utils/general.ts +++ b/packages/core/src/routes/swagger/utils/general.ts @@ -16,7 +16,7 @@ import { isKoaAuthMiddleware } from '../../../middleware/koa-auth/index.js'; const capitalize = (value: string) => value.charAt(0).toUpperCase() + value.slice(1); /** The tag name used in the supplement document to indicate that the operation is cloud only. */ -const cloudOnlyTag = 'Cloud only'; +export const cloudOnlyTag = 'Cloud only'; /** The tag name is used in the supplement document to indicate that the corresponding API operation is a dev feature. */ export const devFeatureTag = 'Dev feature'; @@ -139,9 +139,7 @@ const validateSupplementPaths = ( if ( isKeyInObject(operation, 'tags') && Array.isArray(operation.tags) && - !operation.tags.every( - (tag) => typeof tag === 'string' && [cloudOnlyTag, devFeatureTag].includes(tag) - ) + !operation.tags.every((tag) => typeof tag === 'string' && reservedTags.has(tag)) ) { throw new TypeError( `Cannot use \`tags\` in supplement document on path \`${path}\` and operation \`${method}\` except for tag \`${cloudOnlyTag}\` and \`${devFeatureTag}\`. Define tags in the document root instead.` From 7117118f3913d4fd7d203e80c6df13660054bcf3 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Mon, 27 Jan 2025 00:19:13 +0800 Subject: [PATCH 3/9] refactor: refactor API query guard to fit swagger flow --- .../src/routes/saml-application/anonymous.ts | 71 +++++++++++++++---- 1 file changed, 56 insertions(+), 15 deletions(-) diff --git a/packages/core/src/routes/saml-application/anonymous.ts b/packages/core/src/routes/saml-application/anonymous.ts index 201b7acac19..c25d0b70034 100644 --- a/packages/core/src/routes/saml-application/anonymous.ts +++ b/packages/core/src/routes/saml-application/anonymous.ts @@ -16,17 +16,15 @@ import { generateAutoSubmitForm } from '#src/saml-application/SamlApplication/ut import assertThat from '#src/utils/assert-that.js'; import { getConsoleLogFromContext } from '#src/utils/console.js'; -const samlApplicationSignInCallbackQueryParametersGuard = z.union([ - z.object({ +const samlApplicationSignInCallbackQueryParametersGuard = z + .object({ code: z.string(), - state: z.string().optional(), - redirectUri: z.string().optional(), - }), - z.object({ + state: z.string(), + redirectUri: z.string(), error: z.string(), - error_description: z.string().optional(), - }), -]); + error_description: z.string(), + }) + .partial(); export default function samlApplicationAnonymousRoutes( ...[router, { id: tenantId, libraries, queries, envSet }]: RouterInitArgs @@ -70,6 +68,7 @@ export default function samlApplicationAnonymousRoutes { const consoleLog = getConsoleLogFromContext(ctx); const { @@ -77,21 +76,63 @@ export default function samlApplicationAnonymousRoutes Date: Mon, 27 Jan 2025 12:53:56 +0800 Subject: [PATCH 4/9] chore: fix SAML app openapi json files --- ...ations.openapi.json => index.openapi.json} | 196 ---------------- .../saml-app-auth-flow.openapi.json | 209 ++++++++++++++++++ .../src/routes/swagger/utils/documents.ts | 3 +- .../core/src/routes/swagger/utils/general.ts | 5 +- 4 files changed, 215 insertions(+), 198 deletions(-) rename packages/core/src/routes/saml-application/{saml-applications.openapi.json => index.openapi.json} (63%) create mode 100644 packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json diff --git a/packages/core/src/routes/saml-application/saml-applications.openapi.json b/packages/core/src/routes/saml-application/index.openapi.json similarity index 63% rename from packages/core/src/routes/saml-application/saml-applications.openapi.json rename to packages/core/src/routes/saml-application/index.openapi.json index 0938feeb7a6..29d0004aef8 100644 --- a/packages/core/src/routes/saml-application/saml-applications.openapi.json +++ b/packages/core/src/routes/saml-application/index.openapi.json @@ -9,202 +9,6 @@ } ], "paths": { - "/api/saml-applications/{id}/metadata": { - "get": { - "summary": "Get SAML application metadata", - "description": "Get the SAML metadata XML for the application.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], - "responses": { - "200": { - "description": "The SAML metadata XML.", - "content": { - "text/xml": { - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "Invalid request." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, - "/api/saml-applications/{id}/callback": { - "get": { - "summary": "SAML application callback", - "description": "Handle the OIDC callback for SAML application and generate SAML response.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - }, - { - "name": "code", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "description": "The authorization code from OIDC callback." - }, - { - "name": "state", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The state parameter from OIDC callback." - }, - { - "name": "redirectUri", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The redirect URI for the callback." - } - ], - "responses": { - "200": { - "description": "Returns an HTML form that automatically submits the SAML response." - }, - "400": { - "description": "Invalid request or OIDC error." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, - "/api/saml/{id}/authn": { - "get": { - "summary": "Handle SAML authentication request (Redirect binding)", - "description": "Process SAML authentication request using HTTP Redirect binding.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - }, - { - "name": "SAMLRequest", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "description": "The SAML request message." - }, - { - "name": "Signature", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The signature of the request." - }, - { - "name": "SigAlg", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The signature algorithm." - }, - { - "name": "RelayState", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The relay state parameter." - } - ], - "responses": { - "302": { - "description": "Redirects to the sign-in page." - }, - "400": { - "description": "Invalid SAML request." - }, - "404": { - "description": "The SAML application was not found." - } - } - }, - "post": { - "summary": "Handle SAML authentication request (POST binding)", - "description": "Process SAML authentication request using HTTP POST binding.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], - "requestBody": { - "required": true, - "content": { - "application/x-www-form-urlencoded": { - "schema": { - "type": "object", - "properties": { - "SAMLRequest": { - "type": "string", - "description": "The SAML request message." - }, - "RelayState": { - "type": "string", - "description": "The relay state parameter." - } - }, - "required": ["SAMLRequest"] - } - } - } - }, - "responses": { - "302": { - "description": "Redirects to the sign-in page." - }, - "400": { - "description": "Invalid SAML request." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, "/api/saml-applications": { "post": { "summary": "Create SAML application", diff --git a/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json new file mode 100644 index 00000000000..fb7c5c23a44 --- /dev/null +++ b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json @@ -0,0 +1,209 @@ +{ + "tags": [ + { + "name": "SAML applications auth flow", + "description": "Endpoints for SAML (Security Assertion Markup Language) applications auth flow." + }, + { + "name": "Cloud only" + } + ], + "paths": { + "/api/saml-applications/{id}/metadata": { + "get": { + "summary": "Get SAML application metadata", + "description": "Get the SAML metadata XML for the application.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "responses": { + "200": { + "description": "The SAML metadata XML.", + "content": { + "text/xml": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid request." + }, + "404": { + "description": "The SAML application was not found." + } + } + } + }, + "/api/saml-applications/{id}/callback": { + "get": { + "summary": "SAML application callback", + "description": "Handle the OIDC callback for SAML application and generate SAML response.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + }, + { + "name": "code", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "The authorization code from OIDC callback." + }, + { + "name": "state", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The state parameter from OIDC callback." + }, + { + "name": "redirectUri", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The redirect URI for the callback." + } + ], + "responses": { + "200": { + "description": "Returns an HTML form that automatically submits the SAML response." + }, + "400": { + "description": "Invalid request or OIDC error." + }, + "404": { + "description": "The SAML application was not found." + } + } + } + }, + "/api/saml/{id}/authn": { + "get": { + "summary": "Handle SAML authentication request (Redirect binding)", + "description": "Process SAML authentication request using HTTP Redirect binding.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + }, + { + "name": "SAMLRequest", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "The SAML request message." + }, + { + "name": "Signature", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The signature of the request." + }, + { + "name": "SigAlg", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The signature algorithm." + }, + { + "name": "RelayState", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The relay state parameter." + } + ], + "responses": { + "302": { + "description": "Redirects to the sign-in page." + }, + "400": { + "description": "Invalid SAML request." + }, + "404": { + "description": "The SAML application was not found." + } + } + }, + "post": { + "summary": "Handle SAML authentication request (POST binding)", + "description": "Process SAML authentication request using HTTP POST binding.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "requestBody": { + "required": true, + "content": { + "application/x-www-form-urlencoded": { + "schema": { + "type": "object", + "properties": { + "SAMLRequest": { + "type": "string", + "description": "The SAML request message." + }, + "RelayState": { + "type": "string", + "description": "The relay state parameter." + } + }, + "required": ["SAMLRequest"] + } + } + } + }, + "responses": { + "302": { + "description": "Redirects to the sign-in page." + }, + "400": { + "description": "Invalid SAML request." + }, + "404": { + "description": "The SAML application was not found." + } + } + } + } + } +} diff --git a/packages/core/src/routes/swagger/utils/documents.ts b/packages/core/src/routes/swagger/utils/documents.ts index 2cd02008315..33da4fb75ed 100644 --- a/packages/core/src/routes/swagger/utils/documents.ts +++ b/packages/core/src/routes/swagger/utils/documents.ts @@ -57,7 +57,8 @@ const additionalTags = Object.freeze( 'Organization applications', 'Custom UI assets', 'Organization users', - EnvSet.values.isCloud && 'SAML applications' + (EnvSet.values.isCloud || EnvSet.values.isIntegrationTest) && 'SAML applications', + (EnvSet.values.isCloud || EnvSet.values.isIntegrationTest) && 'SAML applications auth flow' ) ); diff --git a/packages/core/src/routes/swagger/utils/general.ts b/packages/core/src/routes/swagger/utils/general.ts index 5ce9d42cda1..35c89df50e6 100644 --- a/packages/core/src/routes/swagger/utils/general.ts +++ b/packages/core/src/routes/swagger/utils/general.ts @@ -35,8 +35,11 @@ const tagMap = new Map([ ['sso-connectors', 'SSO connectors'], ['sso-connector-providers', 'SSO connector providers'], ['.well-known', 'Well-known'], - ['saml-applications', 'SAML applications'], ]); +if (EnvSet.values.isCloud || EnvSet.values.isIntegrationTest) { + tagMap.set('saml-applications', 'SAML applications'); + tagMap.set('saml', 'SAML applications auth flow'); +} /** * Build a tag name from the given absolute path. The function will get the root component name From 742564e7d32be77066cf96cbf27848e7c39b004f Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Mon, 27 Jan 2025 13:12:56 +0800 Subject: [PATCH 5/9] chore: refactor code --- .../src/routes/swagger/utils/documents.ts | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/packages/core/src/routes/swagger/utils/documents.ts b/packages/core/src/routes/swagger/utils/documents.ts index 33da4fb75ed..40bda9f5164 100644 --- a/packages/core/src/routes/swagger/utils/documents.ts +++ b/packages/core/src/routes/swagger/utils/documents.ts @@ -237,23 +237,30 @@ export const getSupplementDocuments = async ( ); const supplementDocuments = allSupplementDocuments.filter((supplement) => { - // Include documents without special tags - if ( - !supplement.tags?.some((tag) => tag?.name === devFeatureTag || tag?.name === cloudOnlyTag) - ) { + if (EnvSet.values.isIntegrationTest) { return true; } - // Include dev feature documents when dev features are enabled and cloud features when in cloud mode - if ( - EnvSet.values.isDevFeaturesEnabled && - supplement.tags.some((tag) => tag?.name === devFeatureTag) && - (!supplement.tags.some((tag) => tag?.name === cloudOnlyTag) || EnvSet.values.isCloud) - ) { + const hasDevFeatureTag = supplement.tags?.some((tag) => tag?.name === devFeatureTag) ?? false; + const hasCloudOnlyTag = supplement.tags?.some((tag) => tag?.name === cloudOnlyTag) ?? false; + + // 1. Return true if there is no special tag + if (!hasDevFeatureTag && !hasCloudOnlyTag) { return true; } - return false; + // 2. devFeatureTag only + if (hasDevFeatureTag && !hasCloudOnlyTag) { + return EnvSet.values.isDevFeaturesEnabled; + } + + // 3. cloudOnlyTag only + if (!hasDevFeatureTag && hasCloudOnlyTag) { + return EnvSet.values.isCloud; + } + + // 4. devFeatureTag and cloudOnlyTag + return EnvSet.values.isDevFeaturesEnabled && EnvSet.values.isCloud; }); return supplementDocuments; From d5fd4065cdc528883edc0a9537e28bbf177eea31 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Wed, 5 Feb 2025 18:16:30 +0800 Subject: [PATCH 6/9] chore: remove cloud only filter for SAML APIs --- .../core/src/routes/saml-application/index.openapi.json | 3 --- .../routes/saml-application/saml-app-auth-flow.openapi.json | 3 --- packages/core/src/routes/swagger/utils/documents.ts | 4 ++-- packages/core/src/routes/swagger/utils/general.ts | 6 ++---- 4 files changed, 4 insertions(+), 12 deletions(-) diff --git a/packages/core/src/routes/saml-application/index.openapi.json b/packages/core/src/routes/saml-application/index.openapi.json index 29d0004aef8..b04d6368c95 100644 --- a/packages/core/src/routes/saml-application/index.openapi.json +++ b/packages/core/src/routes/saml-application/index.openapi.json @@ -3,9 +3,6 @@ { "name": "SAML applications", "description": "SAML (Security Assertion Markup Language) applications represent applications that use SAML protocol for single sign-on (SSO). These endpoints allow you to manage SAML applications, including their configurations and signing certificates." - }, - { - "name": "Cloud only" } ], "paths": { diff --git a/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json index fb7c5c23a44..ff901a7efa0 100644 --- a/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json +++ b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json @@ -3,9 +3,6 @@ { "name": "SAML applications auth flow", "description": "Endpoints for SAML (Security Assertion Markup Language) applications auth flow." - }, - { - "name": "Cloud only" } ], "paths": { diff --git a/packages/core/src/routes/swagger/utils/documents.ts b/packages/core/src/routes/swagger/utils/documents.ts index 40bda9f5164..20d7201867f 100644 --- a/packages/core/src/routes/swagger/utils/documents.ts +++ b/packages/core/src/routes/swagger/utils/documents.ts @@ -57,8 +57,8 @@ const additionalTags = Object.freeze( 'Organization applications', 'Custom UI assets', 'Organization users', - (EnvSet.values.isCloud || EnvSet.values.isIntegrationTest) && 'SAML applications', - (EnvSet.values.isCloud || EnvSet.values.isIntegrationTest) && 'SAML applications auth flow' + 'SAML applications', + 'SAML applications auth flow' ) ); diff --git a/packages/core/src/routes/swagger/utils/general.ts b/packages/core/src/routes/swagger/utils/general.ts index 35c89df50e6..4bf50b473c7 100644 --- a/packages/core/src/routes/swagger/utils/general.ts +++ b/packages/core/src/routes/swagger/utils/general.ts @@ -35,11 +35,9 @@ const tagMap = new Map([ ['sso-connectors', 'SSO connectors'], ['sso-connector-providers', 'SSO connector providers'], ['.well-known', 'Well-known'], + ['saml-applications', 'SAML applications'], + ['saml', 'SAML applications auth flow'], ]); -if (EnvSet.values.isCloud || EnvSet.values.isIntegrationTest) { - tagMap.set('saml-applications', 'SAML applications'); - tagMap.set('saml', 'SAML applications auth flow'); -} /** * Build a tag name from the given absolute path. The function will get the root component name From 4fca56846c02effcd578b7975919f3aca4f76fc6 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Wed, 5 Feb 2025 23:44:06 +0800 Subject: [PATCH 7/9] fix: reorg openapi.json files --- .../saml-application/index.openapi.json | 88 +++++++++++++++++++ .../saml-app-auth-flow.openapi.json | 88 ------------------- 2 files changed, 88 insertions(+), 88 deletions(-) diff --git a/packages/core/src/routes/saml-application/index.openapi.json b/packages/core/src/routes/saml-application/index.openapi.json index b04d6368c95..a32b23fcee2 100644 --- a/packages/core/src/routes/saml-application/index.openapi.json +++ b/packages/core/src/routes/saml-application/index.openapi.json @@ -310,6 +310,94 @@ } } } + }, + "/api/saml-applications/{id}/metadata": { + "get": { + "summary": "Get SAML application metadata", + "description": "Get the SAML metadata XML for the application.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + } + ], + "responses": { + "200": { + "description": "The SAML metadata XML.", + "content": { + "text/xml": { + "schema": { + "type": "string" + } + } + } + }, + "400": { + "description": "Invalid request." + }, + "404": { + "description": "The SAML application was not found." + } + } + } + }, + "/api/saml-applications/{id}/callback": { + "get": { + "summary": "SAML application callback", + "description": "Handle the OIDC callback for SAML application and generate SAML response.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "schema": { + "type": "string" + }, + "description": "The ID of the SAML application." + }, + { + "name": "code", + "in": "query", + "required": true, + "schema": { + "type": "string" + }, + "description": "The authorization code from OIDC callback." + }, + { + "name": "state", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The state parameter from OIDC callback." + }, + { + "name": "redirectUri", + "in": "query", + "schema": { + "type": "string" + }, + "description": "The redirect URI for the callback." + } + ], + "responses": { + "200": { + "description": "Returns an HTML form that automatically submits the SAML response." + }, + "400": { + "description": "Invalid request or OIDC error." + }, + "404": { + "description": "The SAML application was not found." + } + } + } } } } diff --git a/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json index ff901a7efa0..4fddefc53d1 100644 --- a/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json +++ b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json @@ -6,94 +6,6 @@ } ], "paths": { - "/api/saml-applications/{id}/metadata": { - "get": { - "summary": "Get SAML application metadata", - "description": "Get the SAML metadata XML for the application.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], - "responses": { - "200": { - "description": "The SAML metadata XML.", - "content": { - "text/xml": { - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "Invalid request." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, - "/api/saml-applications/{id}/callback": { - "get": { - "summary": "SAML application callback", - "description": "Handle the OIDC callback for SAML application and generate SAML response.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - }, - { - "name": "code", - "in": "query", - "required": true, - "schema": { - "type": "string" - }, - "description": "The authorization code from OIDC callback." - }, - { - "name": "state", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The state parameter from OIDC callback." - }, - { - "name": "redirectUri", - "in": "query", - "schema": { - "type": "string" - }, - "description": "The redirect URI for the callback." - } - ], - "responses": { - "200": { - "description": "Returns an HTML form that automatically submits the SAML response." - }, - "400": { - "description": "Invalid request or OIDC error." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, "/api/saml/{id}/authn": { "get": { "summary": "Handle SAML authentication request (Redirect binding)", From 5ea853fac272428e40a9390ab4706000189d2f51 Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Thu, 6 Feb 2025 16:58:52 +0800 Subject: [PATCH 8/9] chore: undo changes of documents.ts --- .../src/routes/swagger/utils/documents.ts | 33 ++++--------------- .../core/src/routes/swagger/utils/general.ts | 2 +- 2 files changed, 7 insertions(+), 28 deletions(-) diff --git a/packages/core/src/routes/swagger/utils/documents.ts b/packages/core/src/routes/swagger/utils/documents.ts index 20d7201867f..8da901f517f 100644 --- a/packages/core/src/routes/swagger/utils/documents.ts +++ b/packages/core/src/routes/swagger/utils/documents.ts @@ -18,7 +18,6 @@ import { managementApiAuthDescription, userApiAuthDescription } from '../consts. import { type FindSupplementFilesOptions, devFeatureTag, - cloudOnlyTag, findSupplementFiles, pruneSwaggerDocument, removeUnnecessaryOperations, @@ -236,32 +235,12 @@ export const getSupplementDocuments = async ( ) ); - const supplementDocuments = allSupplementDocuments.filter((supplement) => { - if (EnvSet.values.isIntegrationTest) { - return true; - } - - const hasDevFeatureTag = supplement.tags?.some((tag) => tag?.name === devFeatureTag) ?? false; - const hasCloudOnlyTag = supplement.tags?.some((tag) => tag?.name === cloudOnlyTag) ?? false; - - // 1. Return true if there is no special tag - if (!hasDevFeatureTag && !hasCloudOnlyTag) { - return true; - } - - // 2. devFeatureTag only - if (hasDevFeatureTag && !hasCloudOnlyTag) { - return EnvSet.values.isDevFeaturesEnabled; - } - - // 3. cloudOnlyTag only - if (!hasDevFeatureTag && hasCloudOnlyTag) { - return EnvSet.values.isCloud; - } - - // 4. devFeatureTag and cloudOnlyTag - return EnvSet.values.isDevFeaturesEnabled && EnvSet.values.isCloud; - }); + // Filter out supplement documents that are for dev features when dev features are disabled. + const supplementDocuments = allSupplementDocuments.filter( + (supplement) => + EnvSet.values.isDevFeaturesEnabled || + !supplement.tags?.find((tag) => tag?.name === devFeatureTag) + ); return supplementDocuments; }; diff --git a/packages/core/src/routes/swagger/utils/general.ts b/packages/core/src/routes/swagger/utils/general.ts index 4bf50b473c7..eed9341e568 100644 --- a/packages/core/src/routes/swagger/utils/general.ts +++ b/packages/core/src/routes/swagger/utils/general.ts @@ -16,7 +16,7 @@ import { isKoaAuthMiddleware } from '../../../middleware/koa-auth/index.js'; const capitalize = (value: string) => value.charAt(0).toUpperCase() + value.slice(1); /** The tag name used in the supplement document to indicate that the operation is cloud only. */ -export const cloudOnlyTag = 'Cloud only'; +const cloudOnlyTag = 'Cloud only'; /** The tag name is used in the supplement document to indicate that the corresponding API operation is a dev feature. */ export const devFeatureTag = 'Dev feature'; From fce7da416d8a7c52f6e6ffe6e8d9e7664eccdb9d Mon Sep 17 00:00:00 2001 From: Darcy Ye Date: Thu, 6 Feb 2025 20:24:15 +0800 Subject: [PATCH 9/9] fix: fix openapi.json --- .../saml-application/index.openapi.json | 241 ++++++++++-------- .../saml-app-auth-flow.openapi.json | 77 +++--- .../src/routes/swagger/utils/documents.ts | 1 + 3 files changed, 173 insertions(+), 146 deletions(-) diff --git a/packages/core/src/routes/saml-application/index.openapi.json b/packages/core/src/routes/saml-application/index.openapi.json index a32b23fcee2..e560aa4148a 100644 --- a/packages/core/src/routes/saml-application/index.openapi.json +++ b/packages/core/src/routes/saml-application/index.openapi.json @@ -5,7 +5,88 @@ "description": "SAML (Security Assertion Markup Language) applications represent applications that use SAML protocol for single sign-on (SSO). These endpoints allow you to manage SAML applications, including their configurations and signing certificates." } ], + "components": { + "parameters": { + "saml-applicationId-root": { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the SAML application.", + "schema": { + "type": "string" + } + } + }, + "schemas": { + "SamlApplicationResponse": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the SAML application." + }, + "name": { + "type": "string", + "description": "The name of the SAML application." + }, + "description": { + "type": "string", + "description": "Description of the SAML application." + }, + "customData": { + "type": "object", + "description": "Custom data for the application." + }, + "acsUrl": { + "type": "string", + "description": "The Assertion Consumer Service (ACS) URL." + }, + "entityId": { + "type": "string", + "description": "The SAML entity ID." + }, + "createdAt": { + "type": "string", + "format": "date-time", + "description": "The creation time of the SAML application." + } + } + } + } + }, "paths": { + "/api/saml-applications/{id}/metadata": { + "get": { + "summary": "Get SAML application metadata", + "description": "Get the SAML metadata XML for the application.", + "parameters": [ + { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the SAML application.", + "schema": { + "type": "string" + } + } + ], + "responses": { + "200": { + "description": "The SAML metadata XML.", + "content": { + "text/xml": { + "schema": { + "type": "string" + } + } + } + }, + "404": { + "description": "The SAML application was not found." + } + } + } + }, "/api/saml-applications": { "post": { "summary": "Create SAML application", @@ -32,8 +113,7 @@ "type": "string", "description": "The Assertion Consumer Service (ACS) URL where the SAML response will be sent." } - }, - "required": ["name"] + } } } } @@ -60,10 +140,10 @@ "name": "id", "in": "path", "required": true, + "description": "The ID of the SAML application.", "schema": { "type": "string" - }, - "description": "The ID of the SAML application." + } } ], "responses": { @@ -78,17 +158,6 @@ "patch": { "summary": "Update SAML application", "description": "Update SAML application details by ID.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], "requestBody": { "content": { "application/json": { @@ -131,17 +200,7 @@ "delete": { "summary": "Delete SAML application", "description": "Delete a SAML application by ID.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], + "responses": { "204": { "description": "The SAML application was deleted successfully." @@ -164,10 +223,10 @@ "name": "id", "in": "path", "required": true, + "description": "The ID of the SAML application.", "schema": { "type": "string" - }, - "description": "The ID of the SAML application." + } } ], "requestBody": { @@ -181,8 +240,7 @@ "minimum": 1, "description": "The lifetime of the certificate in years (minimum 1 year)." } - }, - "required": ["lifeSpanInYears"] + } } } } @@ -202,20 +260,42 @@ "get": { "summary": "List SAML application secrets", "description": "Get all signing certificates of the SAML application.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], "responses": { "200": { - "description": "A list of signing certificates." + "description": "A list of signing certificates.", + "content": { + "application/json": { + "schema": { + "type": "array", + "items": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "The ID of the signing certificate." + }, + "certificate": { + "type": "string", + "description": "The X.509 certificate in PEM format." + }, + "fingerprint": { + "type": "string", + "description": "The SHA-256 fingerprint of the certificate." + }, + "isActive": { + "type": "boolean", + "description": "Whether this certificate is currently active." + }, + "expiresAt": { + "type": "string", + "format": "date-time", + "description": "The expiration time of the certificate." + } + } + } + } + } + } }, "404": { "description": "The SAML application was not found." @@ -232,19 +312,10 @@ "name": "id", "in": "path", "required": true, + "description": "The ID of the SAML application.", "schema": { "type": "string" - }, - "description": "The ID of the SAML application." - }, - { - "name": "secretId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the signing certificate." + } } ], "responses": { @@ -262,26 +333,7 @@ "patch": { "summary": "Update SAML application secret", "description": "Update the status of a signing certificate.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - }, - { - "name": "secretId", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the signing certificate." - } - ], + "parameters": [], "requestBody": { "content": { "application/json": { @@ -292,8 +344,7 @@ "type": "boolean", "description": "Whether the certificate is active." } - }, - "required": ["active"] + } } } } @@ -311,41 +362,6 @@ } } }, - "/api/saml-applications/{id}/metadata": { - "get": { - "summary": "Get SAML application metadata", - "description": "Get the SAML metadata XML for the application.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], - "responses": { - "200": { - "description": "The SAML metadata XML.", - "content": { - "text/xml": { - "schema": { - "type": "string" - } - } - } - }, - "400": { - "description": "Invalid request." - }, - "404": { - "description": "The SAML application was not found." - } - } - } - }, "/api/saml-applications/{id}/callback": { "get": { "summary": "SAML application callback", @@ -355,15 +371,14 @@ "name": "id", "in": "path", "required": true, + "description": "The ID of the SAML application.", "schema": { "type": "string" - }, - "description": "The ID of the SAML application." + } }, { "name": "code", "in": "query", - "required": true, "schema": { "type": "string" }, diff --git a/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json index 4fddefc53d1..50c11a556ef 100644 --- a/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json +++ b/packages/core/src/routes/saml-application/saml-app-auth-flow.openapi.json @@ -5,53 +5,74 @@ "description": "Endpoints for SAML (Security Assertion Markup Language) applications auth flow." } ], + "components": { + "parameters": { + "samlId-root": { + "name": "id", + "in": "path", + "required": true, + "description": "The ID of the SAML application.", + "schema": { + "type": "string" + } + } + }, + "schemas": { + "SamlRequest": { + "type": "string", + "description": "Base64-encoded SAML request message." + }, + "SamlSignature": { + "type": "string", + "description": "Base64-encoded signature of the request." + }, + "SamlSignatureAlgorithm": { + "type": "string", + "description": "The signature algorithm used to sign the request." + }, + "RelayState": { + "type": "string", + "description": "Optional state parameter to be returned in the response." + } + } + }, "paths": { "/api/saml/{id}/authn": { "get": { "summary": "Handle SAML authentication request (Redirect binding)", "description": "Process SAML authentication request using HTTP Redirect binding.", "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - }, { "name": "SAMLRequest", "in": "query", - "required": true, + "description": "The SAML request message.", "schema": { "type": "string" - }, - "description": "The SAML request message." + } }, { "name": "Signature", "in": "query", + "description": "The signature of the request.", "schema": { "type": "string" - }, - "description": "The signature of the request." + } }, { "name": "SigAlg", "in": "query", + "description": "The signature algorithm.", "schema": { "type": "string" - }, - "description": "The signature algorithm." + } }, { "name": "RelayState", "in": "query", + "description": "The relay state parameter.", "schema": { "type": "string" - }, - "description": "The relay state parameter." + } } ], "responses": { @@ -69,34 +90,24 @@ "post": { "summary": "Handle SAML authentication request (POST binding)", "description": "Process SAML authentication request using HTTP POST binding.", - "parameters": [ - { - "name": "id", - "in": "path", - "required": true, - "schema": { - "type": "string" - }, - "description": "The ID of the SAML application." - } - ], + "parameters": [], "requestBody": { "required": true, "content": { "application/x-www-form-urlencoded": { "schema": { "type": "object", + "required": ["SAMLRequest"], "properties": { "SAMLRequest": { "type": "string", - "description": "The SAML request message." + "description": "Base64-encoded SAML request message." }, "RelayState": { "type": "string", - "description": "The relay state parameter." + "description": "Optional state parameter to be returned in the response." } - }, - "required": ["SAMLRequest"] + } } } } diff --git a/packages/core/src/routes/swagger/utils/documents.ts b/packages/core/src/routes/swagger/utils/documents.ts index 8da901f517f..288ff6a5432 100644 --- a/packages/core/src/routes/swagger/utils/documents.ts +++ b/packages/core/src/routes/swagger/utils/documents.ts @@ -48,6 +48,7 @@ const managementApiIdentifiableEntityNames = Object.freeze([ 'organization-scope', 'organization-invitation', 'saml-application', + 'secret', ]); /** Additional tags that cannot be inferred from the path. */