-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathauthorize.request.ts
More file actions
179 lines (162 loc) · 7.47 KB
/
Copy pathauthorize.request.ts
File metadata and controls
179 lines (162 loc) · 7.47 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
/*
* 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 { CustomLogger } from '@forgerock/sdk-logger';
import { Micro } from 'effect';
import {
createAuthorizeUrlµ,
buildAuthorizeOptionsµ,
createAuthorizeErrorµ,
} from './authorize.request.utils.js';
import type { GetAuthorizationUrlOptions, WellKnownResponse } from '@forgerock/sdk-types';
import type { AuthorizationError, AuthorizationSuccess } from './authorize.request.types.js';
import type { createClientStore } from './client.store.utils.js';
import type { OidcConfig } from './config.types.js';
import { oidcApi } from './oidc.api.js';
/**
* @function authorizeµ
* @description Creates an authorization URL for the OIDC client.
* @param {WellKnownResponse} wellknown - The well-known configuration for the OIDC server.
* @param {OidcConfig} config - The OIDC client configuration.
* @param {CustomLogger} log - The logger instance for logging debug information.
* @param {GetAuthorizationUrlOptions} options - Optional parameters for the authorization request.
* @returns {Micro.Micro<AuthorizationSuccess, AuthorizationError, never>} - A micro effect that resolves to the authorization response.
*/
export function authorizeµ(
wellknown: WellKnownResponse,
config: OidcConfig,
log: CustomLogger,
store: ReturnType<typeof createClientStore>,
options?: GetAuthorizationUrlOptions,
) {
return buildAuthorizeOptionsµ(wellknown, config, options).pipe(
Micro.flatMap(([url, options]) => createAuthorizeUrlµ(url, options)),
Micro.tap((url) => log.debug('Authorize URL created', url)),
Micro.tapError((url) => Micro.sync(() => log.error('Error creating authorize URL', url))),
Micro.flatMap(
([url, options]): Micro.Micro<AuthorizationSuccess, AuthorizationError, never> => {
if (options.responseMode === 'pi.flow') {
/**
* If we support the pi.flow field, this means we are using a PingOne server.
* PingOne servers do not support redirection through iframes because they
* set iframe's to DENY.
*
* We do not use RTK Query for this because we don't want caching, or store
* updates, and want the request to be made similar to the iframe method below.
*
* This returns a Micro that resolves to the parsed response JSON.
*/
return Micro.promise(() =>
store.dispatch(oidcApi.endpoints.authorizeFetch.initiate({ url })),
).pipe(
Micro.flatMap(
({ error, data }): Micro.Micro<AuthorizationSuccess, AuthorizationError, never> => {
if (error) {
// Check for serialized error
if (!('status' in error)) {
// This is a network or fetch error, so return it as-is
return Micro.fail({
error: error.code || 'Unknown_Error',
error_description:
error.message || 'An unknown error occurred during authorization',
type: 'unknown_error',
});
}
// If there is no data, this is an unknown error
if (!('data' in error)) {
return Micro.fail({
error: 'Unknown_Error',
error_description: 'An unknown error occurred during authorization',
type: 'unknown_error',
});
}
const errorDetails = error.data as AuthorizationError;
// If the error is a configuration issue, return it as-is
if ('statusText' in error && error.statusText === 'CONFIGURATION_ERROR') {
return Micro.fail(errorDetails);
}
// If the error is not a configuration issue, we build a new Authorize URL
// For redirection, we need to remove `pi.flow` from the options
const redirectOptions = options;
delete redirectOptions.responseMode;
// Create an error with a new Authorize URL
return createAuthorizeErrorµ(errorDetails, wellknown, options);
}
log.debug('Received success response', data);
if (data.authorizeResponse) {
// Authorization was successful
return Micro.succeed(data.authorizeResponse);
} else {
// This should never be reached, but just in case
return Micro.fail({
error: 'Unknown_Error',
error_description: 'Response schema was not recognized',
type: 'unknown_error',
});
}
},
),
);
} else {
/**
* If the response mode is not pi.flow, then we are likely using a traditional
* redirect based server supporting iframes. An example would be PingAM.
*
* This returns a Micro that's either the success URL parameters or error URL
* parameters.
*/
return Micro.promise(() =>
store.dispatch(oidcApi.endpoints.authorizeIframe.initiate({ url })),
).pipe(
Micro.flatMap(
({ error, data }): Micro.Micro<AuthorizationSuccess, AuthorizationError, never> => {
if (error) {
// Check for serialized error
if (!('status' in error)) {
// This is a network or fetch error, so return it as-is
return Micro.fail({
error: error.code || 'Unknown_Error',
error_description:
error.message || 'An unknown error occurred during authorization',
type: 'unknown_error',
});
}
// If there is no data, this is an unknown error
if (!('data' in error)) {
return Micro.fail({
error: 'Unknown_Error',
error_description: 'An unknown error occurred during authorization',
type: 'unknown_error',
});
}
const errorDetails = error.data as AuthorizationError;
// If the error is a configuration issue, return it as-is
if ('statusText' in error && error.statusText === 'CONFIGURATION_ERROR') {
return Micro.fail(errorDetails);
}
// This is an expected error, so combine error with a new Authorize URL
return createAuthorizeErrorµ(errorDetails, wellknown, options);
}
log.debug('Received success response', data);
if (data) {
// Authorization was successful
return Micro.succeed(data);
} else {
// This should never be reached, but just in case
return Micro.fail({
error: 'Unknown_Error',
error_description: 'Redirect parameters was not recognized',
type: 'unknown_error',
});
}
},
),
);
}
},
),
);
}