From c4c0be35e935d88eb9b5e9c5cda6a4fee567bda3 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 25 Aug 2025 15:36:25 -0600 Subject: [PATCH 1/5] feat: remove acr_values from capabilities endpoint Removes the `acr_values` query parameter from the capabilities endpoint and instead passes the value in a cookie. This commit addresses the following acceptance criteria: - The `authorize` handler now sets the `acr_values` cookie. - The `capabilities` handler now reads the `acr_values` from the cookie. - The `CapabilitiesQueryParams` has been removed from the `capabilities` endpoint definition. This change makes the API more compliant with the documentation. --- .../src/handlers/authorize.handler.ts | 13 +---- .../src/handlers/capabilities.handler.ts | 12 ++--- .../src/handlers/userinfo.handler.ts | 7 ++- .../custom-html-template/ping-protect-node.ts | 47 ------------------- e2e/mock-api-v2/src/responses/index.ts | 4 +- e2e/mock-api-v2/src/spec.ts | 29 ++++++------ 6 files changed, 28 insertions(+), 84 deletions(-) delete mode 100644 e2e/mock-api-v2/src/responses/custom-html-template/ping-protect-node.ts diff --git a/e2e/mock-api-v2/src/handlers/authorize.handler.ts b/e2e/mock-api-v2/src/handlers/authorize.handler.ts index 0b4a72ad8a..740f5d01a4 100644 --- a/e2e/mock-api-v2/src/handlers/authorize.handler.ts +++ b/e2e/mock-api-v2/src/handlers/authorize.handler.ts @@ -5,27 +5,18 @@ * of the MIT license. See the LICENSE file for details. */ import { Effect } from 'effect'; - import { MockApi } from '../spec.js'; -import { HttpApiBuilder, HttpApiError } from '@effect/platform'; +import { HttpApiBuilder, HttpServerResponse } from '@effect/platform'; import { getFirstElementAndRespond } from '../services/mock-env-helpers/index.js'; const AuthorizeHandlerMock = HttpApiBuilder.group(MockApi, 'Authorization', (handlers) => handlers.handle('authorize', ({ urlParams }) => Effect.gen(function* () { - /** - * We expect an acr_value query parameter to be present in the request. - * If it is not present, we return a 404 Not Found error. - */ const acr_value = urlParams?.acr_values ?? ''; - if (!acr_value) { - return yield* Effect.fail(new HttpApiError.NotFound()); - } - const response = yield* getFirstElementAndRespond(urlParams); - return response; + return HttpServerResponse.setCookie(response, 'acr_values', acr_value); }).pipe(Effect.withSpan('DavinciAuthorize')), ), ); diff --git a/e2e/mock-api-v2/src/handlers/capabilities.handler.ts b/e2e/mock-api-v2/src/handlers/capabilities.handler.ts index 59a8b01962..d979c7f708 100644 --- a/e2e/mock-api-v2/src/handlers/capabilities.handler.ts +++ b/e2e/mock-api-v2/src/handlers/capabilities.handler.ts @@ -18,14 +18,10 @@ import { validator } from '../helpers/match.js'; import { returnSuccessResponseRedirect } from '../responses/return-success-redirect.js'; const CapabilitiesHandlerMock = HttpApiBuilder.group(MockApi, 'Capabilities', (handlers) => - handlers.handle('capabilities', ({ urlParams, payload }) => + handlers.handle('capabilities', ({ payload }) => Effect.gen(function* () { - /** - * We expect an acr_value query parameter to be present in the request. - * If it is not present, we return a 404 Not Found error. - */ - const acr_value = urlParams?.acr_values ?? ''; - console.log('acr_value', acr_value); + const req = yield* HttpServerRequest.HttpServerRequest; + const acr_value = req.cookies.acr_values ?? ''; if (!acr_value) { return yield* Effect.fail(new HttpApiError.NotFound()); @@ -36,8 +32,6 @@ const CapabilitiesHandlerMock = HttpApiBuilder.group(MockApi, 'Capabilities', (h * If the cookie is not present, we return a 404 Not Found error. */ - const req = yield* HttpServerRequest.HttpServerRequest; - const stepIndexCookie = req.cookies['stepIndex']; console.log(req.cookies); diff --git a/e2e/mock-api-v2/src/handlers/userinfo.handler.ts b/e2e/mock-api-v2/src/handlers/userinfo.handler.ts index 2ca3156edb..bea15715eb 100644 --- a/e2e/mock-api-v2/src/handlers/userinfo.handler.ts +++ b/e2e/mock-api-v2/src/handlers/userinfo.handler.ts @@ -7,13 +7,18 @@ import { Effect } from 'effect'; import { MockApi } from '../spec.js'; import { UserInfo } from '../services/userinfo.service.js'; -import { HttpApiBuilder } from '@effect/platform'; +import { HttpApiBuilder, HttpApiError } from '@effect/platform'; import { BearerToken } from '../middleware/Authorization.js'; const UserInfoMockHandler = HttpApiBuilder.group(MockApi, 'ProtectedRequests', (handlers) => handlers.handle('UserInfo', () => Effect.gen(function* () { const authToken = yield* BearerToken; + + if (!authToken) { + return yield* Effect.fail(new HttpApiError.Unauthorized()); + } + const { getUserInfo } = yield* UserInfo; const response = yield* getUserInfo(authToken); diff --git a/e2e/mock-api-v2/src/responses/custom-html-template/ping-protect-node.ts b/e2e/mock-api-v2/src/responses/custom-html-template/ping-protect-node.ts deleted file mode 100644 index a679eab6d1..0000000000 --- a/e2e/mock-api-v2/src/responses/custom-html-template/ping-protect-node.ts +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ -const PingProtectNode = { - interactionId: '1894775c-ba76-4989-a303-320f0547c66e', - connectorId: 'api', - interactionToken: - '7d242c81eabd4987b902448b3d92a5ed3d7c07ccddc681b5b3b3cbcde624e5466b560d2f47ac63605a45057a823144c55a8b5ebe70ac9cbab00921c0efcd1b707738ab5593d8e2cad20c9c4ba3834e15bb7eed4ca29673b2fa24192c7916bdc28b13d0414c1e421c6f7d197c1bafef00fa8ab725ee9ede76abbc068afa3b8605', - success: true, - startUiSubFlow: true, - _links: { - next: { - href: 'https://auth.pingone.ca/02fb4743-189a-4bc7-9d6c-a919edfe6447/davinci/connections/867ed4363b2bc21c860085ad2baa817d/capabilities/customHTMLTemplate', - }, - }, - eventName: 'continue', - isResponseCompatibleWithMobileAndWebSdks: true, - id: 'tavl3e1h2q', - companyId: '02fb4743-189a-4bc7-9d6c-a919edfe6447', - flowId: 'f17221dd5a67fef0382db1e77791d436', - connectionId: '867ed4363b2bc21c860085ad2baa817d', - capabilityName: 'customHTMLTemplate', - formData: { - value: { - protectsdk: '', - }, - }, - form: { - name: 'Start Node', - description: '', - category: 'CUSTOM_HTML', - components: { - fields: [ - { - type: 'TEXT', - key: 'protectsdk', - label: 'Protect Payload', - }, - ], - }, - }, -}; - -export { PingProtectNode }; diff --git a/e2e/mock-api-v2/src/responses/index.ts b/e2e/mock-api-v2/src/responses/index.ts index fa3fb93d44..1011f4c75d 100644 --- a/e2e/mock-api-v2/src/responses/index.ts +++ b/e2e/mock-api-v2/src/responses/index.ts @@ -6,12 +6,12 @@ */ import { Array } from 'effect'; import { UsernamePassword } from './username-password.js'; -import { PingProtectNode } from './custom-html-template/ping-protect-node.js'; + import { InvalidUsernamePassword } from './invalid-username-password.js'; type ResponseMapKeys = keyof typeof responseMap; const responseMap = { - UsernamePassword: Array.make(PingProtectNode, UsernamePassword), + UsernamePassword: Array.make(UsernamePassword), } as const; type ErrorMapKeys = keyof typeof errorMap; diff --git a/e2e/mock-api-v2/src/spec.ts b/e2e/mock-api-v2/src/spec.ts index 5a8f2245de..da3cd6ca8c 100644 --- a/e2e/mock-api-v2/src/spec.ts +++ b/e2e/mock-api-v2/src/spec.ts @@ -21,7 +21,7 @@ import { RevokeRequestBody, RevokeResponseBody, } from './schemas/revoke/revoke.schema.js'; -import { CapabilitiesQueryParams } from './schemas/capabilities/capabilities.query.schema.js'; + import { CapabilitiesHeaders } from './schemas/capabilities/capabilities.headers.schema.js'; import { CapabilitiesResponse } from './schemas/capabilities/capabilities.response.schema.js'; import { DavinciAuthorizeHeaders, DavinciAuthorizeQuery } from './schemas/authorize.schema.js'; @@ -73,20 +73,21 @@ const MockApi = HttpApi.make('MyApi') ) // Capabilities .add( - HttpApiGroup.make('Capabilities').add( - HttpApiEndpoint.post( - 'capabilities', - `/:envid/davinci/connections/:connectionID/capabilities/:capabilityName`, + HttpApiGroup.make('Capabilities') + .add( + HttpApiEndpoint.post( + 'capabilities', + `/:envid/davinci/connections/:connectionID/capabilities/:capabilityName`, + ) + .setPayload(CapabilitiesRequestBody) + .setPath(CapabilitiesPathParams) + + .setHeaders(CapabilitiesHeaders) + .addSuccess(CapabilitiesResponse) + .addError(HttpApiError.NotFound) + .addError(HttpApiError.Unauthorized), ) - .setPayload(CapabilitiesRequestBody) - .setPath(CapabilitiesPathParams) - .setUrlParams(CapabilitiesQueryParams) - .setHeaders(CapabilitiesHeaders) - .addSuccess(CapabilitiesResponse) - .addError(HttpApiError.NotFound) - .addError(HttpApiError.Unauthorized), - ), - // .middleware(IncrementStepIndexMock), + .middleware(IncrementStepIndexMock), ) .add( HttpApiGroup.make('OpenIDConfig').add( From 34635957a6f9efb753c1d0d87f3b748bc4440fd2 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Mon, 25 Aug 2025 16:21:49 -0600 Subject: [PATCH 2/5] chore: update-typechecking --- e2e/mock-api-v2/GEMINI.md | 0 e2e/mock-api-v2/src/handlers/authorize.handler.ts | 14 ++++++++++---- e2e/mock-api-v2/src/spec.ts | 4 +++- 3 files changed, 13 insertions(+), 5 deletions(-) create mode 100644 e2e/mock-api-v2/GEMINI.md diff --git a/e2e/mock-api-v2/GEMINI.md b/e2e/mock-api-v2/GEMINI.md new file mode 100644 index 0000000000..e69de29bb2 diff --git a/e2e/mock-api-v2/src/handlers/authorize.handler.ts b/e2e/mock-api-v2/src/handlers/authorize.handler.ts index 740f5d01a4..8005f39cc0 100644 --- a/e2e/mock-api-v2/src/handlers/authorize.handler.ts +++ b/e2e/mock-api-v2/src/handlers/authorize.handler.ts @@ -4,9 +4,9 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -import { Effect } from 'effect'; +import { Effect, pipe } from 'effect'; import { MockApi } from '../spec.js'; -import { HttpApiBuilder, HttpServerResponse } from '@effect/platform'; +import { HttpApiBuilder, HttpApiError, HttpServerResponse } from '@effect/platform'; import { getFirstElementAndRespond } from '../services/mock-env-helpers/index.js'; const AuthorizeHandlerMock = HttpApiBuilder.group(MockApi, 'Authorization', (handlers) => @@ -14,9 +14,15 @@ const AuthorizeHandlerMock = HttpApiBuilder.group(MockApi, 'Authorization', (han Effect.gen(function* () { const acr_value = urlParams?.acr_values ?? ''; - const response = yield* getFirstElementAndRespond(urlParams); + const body = yield* getFirstElementAndRespond(urlParams); - return HttpServerResponse.setCookie(response, 'acr_values', acr_value); + const res = yield* pipe( + HttpServerResponse.json(body), + HttpServerResponse.setCookie('acr_values', acr_value), + Effect.catchTag('CookieError', () => new HttpApiError.InternalServerError()), + ); + + return res; }).pipe(Effect.withSpan('DavinciAuthorize')), ), ); diff --git a/e2e/mock-api-v2/src/spec.ts b/e2e/mock-api-v2/src/spec.ts index da3cd6ca8c..fe10d19fc2 100644 --- a/e2e/mock-api-v2/src/spec.ts +++ b/e2e/mock-api-v2/src/spec.ts @@ -29,6 +29,7 @@ import { SuccessResponseRedirect } from './schemas/return-success-response-redir import { CapabilitiesPathParams } from './schemas/capabilities/capabilities.path.schema.js'; import { CapabilitiesRequestBody } from './schemas/capabilities/capabilities.request.schema.js'; import { addStepCookie } from './addStepCookie.openapi.js'; +import { IncrementStepIndex } from './middleware/CookieMiddleware.js'; const MockApi = HttpApi.make('MyApi') .annotate(OpenApi.Title, 'PingOne OIDC and OAuth2 Mock API') @@ -64,6 +65,7 @@ const MockApi = HttpApi.make('MyApi') .addSuccess(CapabilitiesResponse) .addSuccess(SuccessResponseRedirect) .addError(HttpApiError.NotFound) + .addError(HttpApiError.InternalServerError) .annotate(OpenApi.Summary, 'Authorization Endpoint') .annotate( OpenApi.Description, @@ -87,7 +89,7 @@ const MockApi = HttpApi.make('MyApi') .addError(HttpApiError.NotFound) .addError(HttpApiError.Unauthorized), ) - .middleware(IncrementStepIndexMock), + .middleware(IncrementStepIndex), ) .add( HttpApiGroup.make('OpenIDConfig').add( From 63348352734662bb3373efcb3abf35161d52052e Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Tue, 26 Aug 2025 13:58:03 -0600 Subject: [PATCH 3/5] chore: make-openid-config-dynamic --- .../handlers/open-id-configuration.handler.ts | 100 +++++++++++++++--- .../src/responses/open-id-configuration.ts | 93 ---------------- 2 files changed, 87 insertions(+), 106 deletions(-) delete mode 100644 e2e/mock-api-v2/src/responses/open-id-configuration.ts diff --git a/e2e/mock-api-v2/src/handlers/open-id-configuration.handler.ts b/e2e/mock-api-v2/src/handlers/open-id-configuration.handler.ts index 38909e4696..f6b4d2a76e 100644 --- a/e2e/mock-api-v2/src/handlers/open-id-configuration.handler.ts +++ b/e2e/mock-api-v2/src/handlers/open-id-configuration.handler.ts @@ -6,22 +6,96 @@ */ import { Effect } from 'effect'; import { MockApi } from '../spec.js'; -import { openidConfigurationResponse } from '../responses/open-id-configuration.js'; import { HttpApiBuilder } from '@effect/platform'; +import { HttpServerRequest } from '@effect/platform/HttpServerRequest'; -/** - * TODO: This needs to make a request for an openid configuration in a LIVE environment - * The proper way is to probably create a LIVE (effect convention) route, that handles this - * then the LIVE app is provided the HttpClient needed - * - * - */ const OpenidConfigMock = HttpApiBuilder.group(MockApi, 'OpenIDConfig', (handlers) => - handlers.handle( - 'openid', - Effect.fn('OpenId')(function* () { - const value = yield* Effect.succeed(openidConfigurationResponse); - return value; + handlers.handle('openid', ({ path: { envid } }) => + Effect.gen(function* () { + const request = yield* HttpServerRequest; + const url = new URL(request.url); + const issuer = `${url.protocol}//${url.host}/${envid}/as`; + return { + issuer, + authorization_endpoint: `${issuer}/authorize`, + pushed_authorization_request_endpoint: `${issuer}/par`, + token_endpoint: `${issuer}/token`, + userinfo_endpoint: `${issuer}/userinfo`, + jwks_uri: `${issuer}/jwks`, + end_session_endpoint: `${issuer}/signoff`, + check_session_iframe: `${issuer}/checksession`, + introspection_endpoint: `${issuer}/introspect`, + revocation_endpoint: `${issuer}/revoke`, + device_authorization_endpoint: `${issuer}/device_authorization`, + claims_parameter_supported: false, + request_parameter_supported: true, + request_uri_parameter_supported: false, + require_pushed_authorization_requests: false, + scopes_supported: ['openid', 'profile', 'email', 'address', 'phone'], + response_types_supported: [ + 'code', + 'id_token', + 'token id_token', + 'code id_token', + 'code token', + 'code token id_token', + ], + response_modes_supported: ['pi.flow', 'query', 'fragment', 'form_post'], + grant_types_supported: [ + 'authorization_code', + 'implicit', + 'client_credentials', + 'refresh_token', + 'urn:ietf:params:oauth:grant-type:device_code', + ], + subject_types_supported: ['public'], + id_token_signing_alg_values_supported: ['RS256'], + userinfo_signing_alg_values_supported: ['none'], + request_object_signing_alg_values_supported: [ + 'none', + 'HS256', + 'HS384', + 'HS512', + 'RS256', + 'RS384', + 'RS512', + ], + token_endpoint_auth_methods_supported: [ + 'client_secret_basic', + 'client_secret_post', + 'client_secret_jwt', + 'private_key_jwt', + ], + token_endpoint_auth_signing_alg_values_supported: [ + 'HS256', + 'HS384', + 'HS512', + 'RS256', + 'RS384', + 'RS512', + ], + claim_types_supported: ['normal'], + claims_supported: [ + 'sub', + 'iss', + 'auth_time', + 'acr', + 'name', + 'given_name', + 'family_name', + 'middle_name', + 'preferred_username', + 'profile', + 'picture', + 'zoneinfo', + 'phone_number', + 'updated_at', + 'address', + 'email', + 'locale', + ], + code_challenge_methods_supported: ['plain', 'S256'], + }; }), ), ); diff --git a/e2e/mock-api-v2/src/responses/open-id-configuration.ts b/e2e/mock-api-v2/src/responses/open-id-configuration.ts deleted file mode 100644 index 5bbd87ada2..0000000000 --- a/e2e/mock-api-v2/src/responses/open-id-configuration.ts +++ /dev/null @@ -1,93 +0,0 @@ -/* - * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. - * - * This software may be modified and distributed under the terms - * of the MIT license. See the LICENSE file for details. - */ -const openidConfigurationResponse = { - issuer: 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as', - authorization_endpoint: 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/authorize', - pushed_authorization_request_endpoint: - 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/par', - token_endpoint: 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/token', - userinfo_endpoint: 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/userinfo', - jwks_uri: 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/jwks', - end_session_endpoint: 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/signoff', - check_session_iframe: - 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/checksession', - introspection_endpoint: - 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/introspect', - revocation_endpoint: 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/revoke', - device_authorization_endpoint: - 'http://localhost:9443/02fb4743-189a-4bc7-9d6c-a919edfe6447/as/device_authorization', - claims_parameter_supported: false, - request_parameter_supported: true, - request_uri_parameter_supported: false, - require_pushed_authorization_requests: false, - scopes_supported: ['openid', 'profile', 'email', 'address', 'phone'], - response_types_supported: [ - 'code', - 'id_token', - 'token id_token', - 'code id_token', - 'code token', - 'code token id_token', - ], - response_modes_supported: ['pi.flow', 'query', 'fragment', 'form_post'], - grant_types_supported: [ - 'authorization_code', - 'implicit', - 'client_credentials', - 'refresh_token', - 'urn:ietf:params:oauth:grant-type:device_code', - ], - subject_types_supported: ['public'], - id_token_signing_alg_values_supported: ['RS256'], - userinfo_signing_alg_values_supported: ['none'], - request_object_signing_alg_values_supported: [ - 'none', - 'HS256', - 'HS384', - 'HS512', - 'RS256', - 'RS384', - 'RS512', - ], - token_endpoint_auth_methods_supported: [ - 'client_secret_basic', - 'client_secret_post', - 'client_secret_jwt', - 'private_key_jwt', - ], - token_endpoint_auth_signing_alg_values_supported: [ - 'HS256', - 'HS384', - 'HS512', - 'RS256', - 'RS384', - 'RS512', - ], - claim_types_supported: ['normal'], - claims_supported: [ - 'sub', - 'iss', - 'auth_time', - 'acr', - 'name', - 'given_name', - 'family_name', - 'middle_name', - 'preferred_username', - 'profile', - 'picture', - 'zoneinfo', - 'phone_number', - 'updated_at', - 'address', - 'email', - 'locale', - ], - code_challenge_methods_supported: ['plain', 'S256'], -}; - -export { openidConfigurationResponse }; From 95dcd5d57d3657544f8c2e90f4daa1bc3d8f04a5 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Thu, 28 Aug 2025 13:31:29 -0600 Subject: [PATCH 4/5] chore: update-capabilities-and-swagger --- e2e/mock-api-v2/src/addStepCookie.openapi.ts | 14 ++- .../src/handlers/authorize.handler.ts | 5 +- .../src/handlers/capabilities.handler.ts | 87 +++++++++---------- e2e/mock-api-v2/src/main.ts | 2 +- e2e/mock-api-v2/src/responses/index.ts | 3 +- .../capabilities.cookies.schema.ts | 12 +++ e2e/mock-api-v2/src/spec.ts | 5 +- 7 files changed, 72 insertions(+), 56 deletions(-) create mode 100644 e2e/mock-api-v2/src/schemas/capabilities/capabilities.cookies.schema.ts diff --git a/e2e/mock-api-v2/src/addStepCookie.openapi.ts b/e2e/mock-api-v2/src/addStepCookie.openapi.ts index 86d79fb144..f96c7f35f1 100644 --- a/e2e/mock-api-v2/src/addStepCookie.openapi.ts +++ b/e2e/mock-api-v2/src/addStepCookie.openapi.ts @@ -9,7 +9,7 @@ export const addStepCookie = (spec: any) => { const FLOW_TAGS = new Set(['Authorization', 'Capabilities']); const FLOW_PATH_MATCHERS = [ /\/davinci\/authorize\b/, - /\/davinci\/connections\/[^/]+\/capabilities\//, + /\/davinci\/connections\/[^/]+\/capabilities/, ]; const shouldAnnotate = (path: string, op: any) => @@ -32,6 +32,18 @@ export const addStepCookie = (spec: any) => { example: 2, }); } + const alreadyAcrValues = op.parameters.some( + (p: any) => p && p.in === 'cookie' && p.name === 'acr_values', + ); + if (!alreadyAcrValues && !op.tags?.includes('Authorization')) { + op.parameters.push({ + name: 'acr_values', + in: 'cookie', + required: false, + description: 'The acr_values that were used to initiate the authorization flow.', + schema: { type: 'string' }, + }); + } }; const ensureSetCookie = (op: any, status: string) => { diff --git a/e2e/mock-api-v2/src/handlers/authorize.handler.ts b/e2e/mock-api-v2/src/handlers/authorize.handler.ts index 8005f39cc0..c636c9e762 100644 --- a/e2e/mock-api-v2/src/handlers/authorize.handler.ts +++ b/e2e/mock-api-v2/src/handlers/authorize.handler.ts @@ -18,8 +18,9 @@ const AuthorizeHandlerMock = HttpApiBuilder.group(MockApi, 'Authorization', (han const res = yield* pipe( HttpServerResponse.json(body), - HttpServerResponse.setCookie('acr_values', acr_value), - Effect.catchTag('CookieError', () => new HttpApiError.InternalServerError()), + Effect.flatMap(HttpServerResponse.setCookie('acr_values', acr_value, { path: '/' })), + Effect.catchTag('CookieError', () => Effect.fail(new HttpApiError.InternalServerError())), + Effect.catchTag('HttpBodyError', () => Effect.fail(new HttpApiError.InternalServerError())), ); return res; diff --git a/e2e/mock-api-v2/src/handlers/capabilities.handler.ts b/e2e/mock-api-v2/src/handlers/capabilities.handler.ts index d979c7f708..d82c62f3a5 100644 --- a/e2e/mock-api-v2/src/handlers/capabilities.handler.ts +++ b/e2e/mock-api-v2/src/handlers/capabilities.handler.ts @@ -4,12 +4,11 @@ * This software may be modified and distributed under the terms * of the MIT license. See the LICENSE file for details. */ -import { Console, Effect, pipe } from 'effect'; +import { Effect, pipe } from 'effect'; import { MockApi } from '../spec.js'; import { HttpApiBuilder, HttpApiError, - HttpBody, HttpServerRequest, HttpServerResponse, } from '@effect/platform'; @@ -21,8 +20,10 @@ const CapabilitiesHandlerMock = HttpApiBuilder.group(MockApi, 'Capabilities', (h handlers.handle('capabilities', ({ payload }) => Effect.gen(function* () { const req = yield* HttpServerRequest.HttpServerRequest; + console.log('request cookies', req.cookies); const acr_value = req.cookies.acr_values ?? ''; + console.log('acr_value', acr_value); if (!acr_value) { return yield* Effect.fail(new HttpApiError.NotFound()); } @@ -33,12 +34,12 @@ const CapabilitiesHandlerMock = HttpApiBuilder.group(MockApi, 'Capabilities', (h */ const stepIndexCookie = req.cookies['stepIndex']; - console.log(req.cookies); /** * If we are here with no step index that means we can't continue through a flow. * We should error */ + console.log('step index cookie', stepIndexCookie); if (!stepIndexCookie) { console.log('no step index'); return yield* Effect.fail(new HttpApiError.NotFound()); @@ -70,59 +71,49 @@ const CapabilitiesHandlerMock = HttpApiBuilder.group(MockApi, 'Capabilities', (h */ const steps = responseMap[acr_value]; - /** - * This may not be the best way to write this. - * An alternative option would be for us to include the success response we want to return, - * in the response map. - * - * then we can check if we are at the last step. if we are we write the cookie - * and then we return the success response (last item in array) - * - * for now, this returns a default success response and writes cookies. - */ - if (stepIndex + 1 >= steps.length) { + if (stepIndex + 1 === steps.length - 1) { /** * we need to return a success because we have not failed yet, * and we have no more steps to process. */ - const body = yield* HttpBody.json(returnSuccessResponseRedirect).pipe( - Effect.tap(Console.log(`here stepIndex: ${stepIndex}`)), - /** - * Decide on a better way to handle this error possibiltiy - */ - Effect.catchTag('HttpBodyError', () => - Effect.fail( - new HttpApiError.HttpApiDecodeError({ - message: 'Failed to encode body', - issues: [], - }), + const body = responseMap[stepIndex]; + + return yield* pipe( + HttpServerResponse.json(body), + Effect.flatMap((res) => HttpServerResponse.setCookie(res, 'ST', 'MockApiCookie123')), + Effect.flatMap((res) => + HttpServerResponse.setCookie( + res, + 'interactionId', + returnSuccessResponseRedirect.interactionId, + { + httpOnly: true, + secure: true, + sameSite: 'strict', + }, ), ), - ); - return pipe( - HttpServerResponse.json(body), - HttpServerResponse.setCookie('ST', 'MockApiCookie123'), - HttpServerResponse.setCookie( - 'interactionId', - returnSuccessResponseRedirect.interactionId, - { - httpOnly: true, - secure: true, - sameSite: 'strict', - }, + Effect.flatMap((res) => + HttpServerResponse.setCookie( + res, + 'interactionToken', + returnSuccessResponseRedirect.interactionToken, + { + httpOnly: true, + secure: true, + sameSite: 'strict', + }, + ), ), - HttpServerResponse.setCookie( - 'interactionToken', - returnSuccessResponseRedirect.interactionToken, - { - httpOnly: true, - secure: true, - sameSite: 'strict', - }, + Effect.flatMap((res) => HttpServerResponse.removeCookie(res, 'stepIndex')), + Effect.flatMap((res) => HttpServerResponse.setStatus(res, 200)), + Effect.flatMap((res) => + HttpServerResponse.setHeader(res, 'Content-Type', 'application/json'), + ), + Effect.catchTag('CookieError', () => Effect.fail(new HttpApiError.InternalServerError())), + Effect.catchTag('HttpBodyError', () => + Effect.fail(new HttpApiError.InternalServerError()), ), - HttpServerResponse.removeCookie('stepIndex'), - HttpServerResponse.setStatus(200), - HttpServerResponse.setHeader('Content-Type', 'application/json'), ); } diff --git a/e2e/mock-api-v2/src/main.ts b/e2e/mock-api-v2/src/main.ts index 8b3a4255be..92e2acf3ee 100644 --- a/e2e/mock-api-v2/src/main.ts +++ b/e2e/mock-api-v2/src/main.ts @@ -66,7 +66,7 @@ const ServerMock = HttpApiBuilder.serve(HttpMiddleware.logger).pipe( Layer.provide(NodeSdkLive), HttpServer.withLogAddress, - Layer.provide(NodeHttpServer.layer(createServer, { port: 9443 })), + Layer.provide(NodeHttpServer.layer(createServer, { port: 9443, host: 'localhost' })), ); Layer.launch(ServerMock).pipe(NodeRuntime.runMain); diff --git a/e2e/mock-api-v2/src/responses/index.ts b/e2e/mock-api-v2/src/responses/index.ts index 1011f4c75d..0fe59a0a7c 100644 --- a/e2e/mock-api-v2/src/responses/index.ts +++ b/e2e/mock-api-v2/src/responses/index.ts @@ -6,12 +6,13 @@ */ import { Array } from 'effect'; import { UsernamePassword } from './username-password.js'; +import { returnSuccessResponseRedirect } from './return-success-redirect.js'; import { InvalidUsernamePassword } from './invalid-username-password.js'; type ResponseMapKeys = keyof typeof responseMap; const responseMap = { - UsernamePassword: Array.make(UsernamePassword), + UsernamePassword: Array.make(UsernamePassword, returnSuccessResponseRedirect), } as const; type ErrorMapKeys = keyof typeof errorMap; diff --git a/e2e/mock-api-v2/src/schemas/capabilities/capabilities.cookies.schema.ts b/e2e/mock-api-v2/src/schemas/capabilities/capabilities.cookies.schema.ts new file mode 100644 index 0000000000..297cf5f9a7 --- /dev/null +++ b/e2e/mock-api-v2/src/schemas/capabilities/capabilities.cookies.schema.ts @@ -0,0 +1,12 @@ +/* + * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. + * + * This software may be modified and distributed under the terms + * of the MIT license. See the LICENSE file for details. + */ +import { Schema } from 'effect'; + +export const CapabilitiesCookie = Schema.Struct({ + stepIndex: Schema.String, + acr_values: Schema.String, +}); diff --git a/e2e/mock-api-v2/src/spec.ts b/e2e/mock-api-v2/src/spec.ts index fe10d19fc2..3981a26464 100644 --- a/e2e/mock-api-v2/src/spec.ts +++ b/e2e/mock-api-v2/src/spec.ts @@ -25,7 +25,6 @@ import { import { CapabilitiesHeaders } from './schemas/capabilities/capabilities.headers.schema.js'; import { CapabilitiesResponse } from './schemas/capabilities/capabilities.response.schema.js'; import { DavinciAuthorizeHeaders, DavinciAuthorizeQuery } from './schemas/authorize.schema.js'; -import { SuccessResponseRedirect } from './schemas/return-success-response-redirect.schema.js'; import { CapabilitiesPathParams } from './schemas/capabilities/capabilities.path.schema.js'; import { CapabilitiesRequestBody } from './schemas/capabilities/capabilities.request.schema.js'; import { addStepCookie } from './addStepCookie.openapi.js'; @@ -63,7 +62,6 @@ const MockApi = HttpApi.make('MyApi') .setHeaders(DavinciAuthorizeHeaders) .setUrlParams(DavinciAuthorizeQuery) .addSuccess(CapabilitiesResponse) - .addSuccess(SuccessResponseRedirect) .addError(HttpApiError.NotFound) .addError(HttpApiError.InternalServerError) .annotate(OpenApi.Summary, 'Authorization Endpoint') @@ -87,7 +85,8 @@ const MockApi = HttpApi.make('MyApi') .setHeaders(CapabilitiesHeaders) .addSuccess(CapabilitiesResponse) .addError(HttpApiError.NotFound) - .addError(HttpApiError.Unauthorized), + .addError(HttpApiError.Unauthorized) + .addError(HttpApiError.InternalServerError), ) .middleware(IncrementStepIndex), ) From cccb90d5dbd6295c32597c0475b823e447cd9bd4 Mon Sep 17 00:00:00 2001 From: Ryan Bas Date: Fri, 29 Aug 2025 12:02:41 -0600 Subject: [PATCH 5/5] chore: ignore-root-changeset --- .changeset/config.json | 1 + .github/workflows/changesets-renovate.yml | 29 ----------------------- 2 files changed, 1 insertion(+), 29 deletions(-) delete mode 100644 .github/workflows/changesets-renovate.yml diff --git a/.changeset/config.json b/.changeset/config.json index 5df6bfca0d..4d3c6b6050 100644 --- a/.changeset/config.json +++ b/.changeset/config.json @@ -13,6 +13,7 @@ "baseBranch": "main", "updateInternalDependencies": "patch", "ignore": [ + "ping-javascript-sdk", "scratchpad", "@forgerock/pingone-scripts", "@forgerock/device-client", diff --git a/.github/workflows/changesets-renovate.yml b/.github/workflows/changesets-renovate.yml deleted file mode 100644 index 3d1c099e60..0000000000 --- a/.github/workflows/changesets-renovate.yml +++ /dev/null @@ -1,29 +0,0 @@ -name: Generate changeset for Renovate - -on: - pull_request_target: - paths: - - '.github/workflows/changesets-renovate.yml' - - '**/pnpm-lock.yaml' - - '**/package.json' - -jobs: - generate-changeset: - runs-on: ubuntu-latest - if: github.actor == 'renovate[bot]' && github.repository == 'ForgeRock/ping-javascript-sdk' - steps: - - name: Checkout - uses: actions/checkout@v4 - with: - fetch-depth: 2 - ref: ${{ github.head_ref }} - - name: Git Identity - run: | - git config --global user.name 'github-actions[bot]' - - - uses: pnpm/action-setup@v4.1.0 - - - name: Run changesets-renovate - run: pnpm dlx @scaleway/changesets-renovate - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}