|
1 | | -import { iFrameManager } from '@forgerock/iframe-manager'; |
2 | | -import { createAuthorizeUrl, GetAuthorizationUrlOptions } from '@forgerock/sdk-oidc'; |
| 1 | +/* |
| 2 | + * Copyright (c) 2025 Ping Identity Corporation. All rights reserved. |
| 3 | + * |
| 4 | + * This software may be modified and distributed under the terms |
| 5 | + * of the MIT license. See the LICENSE file for details. |
| 6 | + */ |
| 7 | +import { CustomLogger } from '@forgerock/sdk-logger'; |
| 8 | +import { GetAuthorizationUrlOptions } from '@forgerock/sdk-oidc'; |
3 | 9 | import { Micro } from 'effect'; |
4 | 10 |
|
5 | | -import { createAuthorizeOptions, handleError, handleResponse } from './authorize.request.utils.js'; |
| 11 | +import { |
| 12 | + authorizeFetchµ, |
| 13 | + createAuthorizeUrlµ, |
| 14 | + authorizeIframeµ, |
| 15 | + buildAuthorizeOptionsµ, |
| 16 | + createAuthorizeErrorµ, |
| 17 | +} from './authorize.request.utils.js'; |
6 | 18 |
|
7 | 19 | import type { WellKnownResponse } from '@forgerock/sdk-types'; |
8 | 20 |
|
9 | 21 | import type { OidcConfig } from './config.types.js'; |
| 22 | +import { AuthorizeErrorResponse, AuthorizeSuccessResponse } from './authorize.request.types.js'; |
10 | 23 |
|
11 | | -export async function authorize( |
| 24 | +export async function authorizeµ( |
12 | 25 | wellknown: WellKnownResponse, |
13 | 26 | config: OidcConfig, |
| 27 | + log: CustomLogger, |
14 | 28 | options?: GetAuthorizationUrlOptions, |
15 | 29 | ) { |
16 | | - const authorizePath = wellknown.authorization_endpoint; |
17 | | - const optionsWithDefaults = createAuthorizeOptions(config, options); |
18 | | - |
19 | | - let response: Record<string, unknown>; |
20 | | - |
21 | | - try { |
22 | | - /** |
23 | | - * If we support the pi.flow field, this means we are using a PingOne server. |
24 | | - * PingOne servers do not support redirection through iframes because they |
25 | | - * set iframe's to DENY. |
26 | | - */ |
27 | | - if (wellknown.response_modes_supported?.includes('pi.flow')) { |
28 | | - /** |
29 | | - * We need to make a post (or a get) request and both are supported by |
30 | | - * PingOne. |
31 | | - */ |
32 | | - const authorizeUrlMicro = Micro.promise(() => |
33 | | - createAuthorizeUrl(authorizePath, { |
34 | | - ...optionsWithDefaults, |
35 | | - prompt: 'none', |
36 | | - responseMode: 'pi.flow', |
37 | | - }), |
38 | | - ); |
39 | | - |
40 | | - const fetchMicro = (url: string) => |
41 | | - Micro.promise(() => |
42 | | - fetch(url, { |
43 | | - method: 'POST', |
44 | | - credentials: 'include', |
| 30 | + return buildAuthorizeOptionsµ(wellknown, config, options).pipe( |
| 31 | + Micro.flatMap(([url, config, options]) => createAuthorizeUrlµ(url, config, options)), |
| 32 | + Micro.tap((url) => log.debug('Authorize URL created', url)), |
| 33 | + Micro.tapError((url) => Micro.sync(() => log.error('Error creating authorize URL', url))), |
| 34 | + Micro.flatMap(([url, config, options]) => { |
| 35 | + if (options.responseMode === 'pi.flow') { |
| 36 | + /** |
| 37 | + * If we support the pi.flow field, this means we are using a PingOne server. |
| 38 | + * PingOne servers do not support redirection through iframes because they |
| 39 | + * set iframe's to DENY. |
| 40 | + */ |
| 41 | + return authorizeFetchµ(url).pipe( |
| 42 | + Micro.flatMap((response) => { |
| 43 | + if ('authorizeResponse' in response) { |
| 44 | + log.debug('Received authorize response', response.authorizeResponse); |
| 45 | + return Micro.succeed(response.authorizeResponse); |
| 46 | + } |
| 47 | + log.error('Error in authorize response', response); |
| 48 | + return Micro.fail(createAuthorizeErrorµ(response, wellknown, config, options)); |
45 | 49 | }), |
46 | 50 | ); |
47 | | - |
48 | | - const authorizeRequest = authorizeUrlMicro.pipe( |
49 | | - Micro.flatMap(fetchMicro), |
50 | | - Micro.flatMap((response) => Micro.promise(response.json)), |
51 | | - ); |
52 | | - response = await Micro.runPromise(authorizeRequest); |
53 | | - } else { |
54 | | - const authorizeUrlMicro = Micro.promise(() => |
55 | | - createAuthorizeUrl(authorizePath, { |
56 | | - ...optionsWithDefaults, |
57 | | - prompt: 'none', |
58 | | - }), |
59 | | - ); |
60 | | - |
61 | | - const iframeMicro = (url: string) => |
62 | | - Micro.promise(() => |
63 | | - iFrameManager().getParamsByRedirect({ |
64 | | - url, |
65 | | - /*** |
66 | | - * https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2 |
67 | | - * The client MUST ignore unrecognized response parameters. |
68 | | - */ |
69 | | - successParams: ['code', 'state'], |
70 | | - errorParams: ['error', 'error_description'], |
71 | | - timeout: config.serverConfig.timeout || 3000, |
| 51 | + } else { |
| 52 | + /** |
| 53 | + * If the response mode is not pi.flow, then we are likely using a traditional |
| 54 | + * redirect based server supporting iframes. An example would be PingAM. |
| 55 | + */ |
| 56 | + return authorizeIframeµ(url, config).pipe( |
| 57 | + Micro.flatMap((response) => { |
| 58 | + if ('code' in response && 'state' in response) { |
| 59 | + log.debug('Received authorization code', response); |
| 60 | + return Micro.succeed(response as unknown as AuthorizeSuccessResponse); |
| 61 | + } |
| 62 | + log.error('Error in authorize response', response); |
| 63 | + const errorResponse = response as unknown as AuthorizeErrorResponse; |
| 64 | + return Micro.fail(createAuthorizeErrorµ(errorResponse, wellknown, config, options)); |
72 | 65 | }), |
73 | 66 | ); |
74 | | - |
75 | | - const authorizeRequest = authorizeUrlMicro.pipe(Micro.flatMap(iframeMicro)); |
76 | | - response = await Micro.runPromise(authorizeRequest); |
77 | | - } |
78 | | - |
79 | | - // Normalize response, for both success and failure, to handle both |
80 | | - // fetch and iframe |
81 | | - return await handleResponse(response, authorizePath, optionsWithDefaults); |
82 | | - } catch (error) { |
83 | | - // If an error occurs, we return an error response with the authorize URL |
84 | | - // so the application can handle the redirect. |
85 | | - return handleError(error, authorizePath, optionsWithDefaults); |
86 | | - } |
| 67 | + } |
| 68 | + }), |
| 69 | + ); |
87 | 70 | } |
0 commit comments