@azure/core-rest-pipeline 1.17.1-alpha.20241031.2 → 1.17.1-alpha.20241111.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +26 -7
- package/dist/browser/policies/bearerTokenAuthenticationPolicy.d.ts +18 -0
- package/dist/browser/policies/bearerTokenAuthenticationPolicy.d.ts.map +1 -1
- package/dist/browser/policies/bearerTokenAuthenticationPolicy.js +156 -26
- package/dist/browser/policies/bearerTokenAuthenticationPolicy.js.map +1 -1
- package/dist/commonjs/policies/bearerTokenAuthenticationPolicy.d.ts +18 -0
- package/dist/commonjs/policies/bearerTokenAuthenticationPolicy.d.ts.map +1 -1
- package/dist/commonjs/policies/bearerTokenAuthenticationPolicy.js +157 -26
- package/dist/commonjs/policies/bearerTokenAuthenticationPolicy.js.map +1 -1
- package/dist/esm/policies/bearerTokenAuthenticationPolicy.d.ts +18 -0
- package/dist/esm/policies/bearerTokenAuthenticationPolicy.d.ts.map +1 -1
- package/dist/esm/policies/bearerTokenAuthenticationPolicy.js +156 -26
- package/dist/esm/policies/bearerTokenAuthenticationPolicy.js.map +1 -1
- package/dist/react-native/policies/bearerTokenAuthenticationPolicy.d.ts +18 -0
- package/dist/react-native/policies/bearerTokenAuthenticationPolicy.d.ts.map +1 -1
- package/dist/react-native/policies/bearerTokenAuthenticationPolicy.js +156 -26
- package/dist/react-native/policies/bearerTokenAuthenticationPolicy.js.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -32,7 +32,9 @@ A `PipelineResponse` describes the HTTP response (body, headers, and status code
|
|
|
32
32
|
A `SendRequest` method is a method that given a `PipelineRequest` can asynchronously return a `PipelineResponse`.
|
|
33
33
|
|
|
34
34
|
```ts snippet:send_request
|
|
35
|
-
|
|
35
|
+
import { PipelineRequest, PipelineResponse } from "@azure/core-rest-pipeline";
|
|
36
|
+
|
|
37
|
+
type SendRequest = (request: PipelineRequest) => Promise<PipelineResponse>;
|
|
36
38
|
```
|
|
37
39
|
|
|
38
40
|
### HttpClient
|
|
@@ -40,7 +42,9 @@ export type SendRequest = (request: PipelineRequest) => Promise<PipelineResponse
|
|
|
40
42
|
An `HttpClient` is any object that satisfies the following interface to implement a `SendRequest` method:
|
|
41
43
|
|
|
42
44
|
```ts snippet:http_request
|
|
43
|
-
|
|
45
|
+
import { SendRequest } from "@azure/core-rest-pipeline";
|
|
46
|
+
|
|
47
|
+
interface HttpClient {
|
|
44
48
|
/**
|
|
45
49
|
* The method that makes the request and returns a response.
|
|
46
50
|
*/
|
|
@@ -55,7 +59,9 @@ export interface HttpClient {
|
|
|
55
59
|
A `PipelinePolicy` is a simple object that implements the following interface:
|
|
56
60
|
|
|
57
61
|
```ts snippet:pipeline_policy
|
|
58
|
-
|
|
62
|
+
import { PipelineRequest, SendRequest, PipelineResponse } from "@azure/core-rest-pipeline";
|
|
63
|
+
|
|
64
|
+
interface PipelinePolicy {
|
|
59
65
|
/**
|
|
60
66
|
* The policy name. Must be a unique string in the pipeline.
|
|
61
67
|
*/
|
|
@@ -76,13 +82,15 @@ One can view the role of policies as that of `middleware`, a concept that is fam
|
|
|
76
82
|
The `sendRequest` implementation can both transform the outgoing request as well as the incoming response:
|
|
77
83
|
|
|
78
84
|
```ts snippet:custom_policy
|
|
85
|
+
import { PipelineRequest, SendRequest, PipelineResponse } from "@azure/core-rest-pipeline";
|
|
86
|
+
|
|
79
87
|
const customPolicy = {
|
|
80
88
|
name: "My wonderful policy",
|
|
81
89
|
async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {
|
|
82
90
|
// Change the outgoing request by adding a new header
|
|
83
91
|
request.headers.set("X-Cool-Header", 42);
|
|
84
92
|
const result = await next(request);
|
|
85
|
-
if (
|
|
93
|
+
if (result.status === 403) {
|
|
86
94
|
// Do something special if this policy sees Forbidden
|
|
87
95
|
}
|
|
88
96
|
return result;
|
|
@@ -101,8 +109,17 @@ You can think of policies being applied like a stack (first-in/last-out.) The fi
|
|
|
101
109
|
A `Pipeline` satisfies the following interface:
|
|
102
110
|
|
|
103
111
|
```ts snippet:pipeline
|
|
104
|
-
|
|
105
|
-
|
|
112
|
+
import {
|
|
113
|
+
PipelinePolicy,
|
|
114
|
+
AddPipelineOptions,
|
|
115
|
+
PipelinePhase,
|
|
116
|
+
HttpClient,
|
|
117
|
+
PipelineRequest,
|
|
118
|
+
PipelineResponse,
|
|
119
|
+
} from "@azure/core-rest-pipeline";
|
|
120
|
+
|
|
121
|
+
interface Pipeline {
|
|
122
|
+
addPolicy(policy: PipelinePolicy, options?: AddPipelineOptions): void;
|
|
106
123
|
removePolicy(options: { name?: string; phase?: PipelinePhase }): PipelinePolicy[];
|
|
107
124
|
sendRequest(httpClient: HttpClient, request: PipelineRequest): Promise<PipelineResponse>;
|
|
108
125
|
getOrderedPolicies(): PipelinePolicy[];
|
|
@@ -124,7 +141,9 @@ Phases occur in the above order, with serialization policies being applied first
|
|
|
124
141
|
When adding a policy to the pipeline you can specify not only what phase a policy is in, but also if it has any dependencies:
|
|
125
142
|
|
|
126
143
|
```ts snippet:add_policy_options
|
|
127
|
-
|
|
144
|
+
import { PipelinePhase } from "@azure/core-rest-pipeline";
|
|
145
|
+
|
|
146
|
+
interface AddPipelineOptions {
|
|
128
147
|
beforePolicies?: string[];
|
|
129
148
|
afterPolicies?: string[];
|
|
130
149
|
afterPhase?: PipelinePhase;
|
|
@@ -96,4 +96,22 @@ export interface BearerTokenAuthenticationPolicyOptions {
|
|
|
96
96
|
* then apply it to the Authorization header of a request as a Bearer token.
|
|
97
97
|
*/
|
|
98
98
|
export declare function bearerTokenAuthenticationPolicy(options: BearerTokenAuthenticationPolicyOptions): PipelinePolicy;
|
|
99
|
+
/**
|
|
100
|
+
*
|
|
101
|
+
* Interface to represent a parsed challenge.
|
|
102
|
+
*
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
interface AuthChallenge {
|
|
106
|
+
scheme: string;
|
|
107
|
+
params: Record<string, string>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Converts: `Bearer a="b", c="d", Pop e="f", g="h"`.
|
|
111
|
+
* Into: `[ { scheme: 'Bearer', params: { a: 'b', c: 'd' } }, { scheme: 'Pop', params: { e: 'f', g: 'h' } } ]`.
|
|
112
|
+
*
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
export declare function parseChallenges(challenges: string): AuthChallenge[];
|
|
116
|
+
export {};
|
|
99
117
|
//# sourceMappingURL=bearerTokenAuthenticationPolicy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bearerTokenAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/bearerTokenAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAe,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"bearerTokenAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/bearerTokenAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAe,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKrD;;GAEG;AACH,eAAO,MAAM,mCAAmC,oCAAoC,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC5F;;OAEG;IACH,OAAO,EAAE,eAAe,CAAC;IACzB;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kCAAkC;IACjD;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC5F;;OAEG;IACH,OAAO,EAAE,eAAe,CAAC;IACzB;;OAEG;IACH,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,gBAAgB,CAAC,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE;;;;;OAKG;IACH,2BAA2B,CAAC,CAAC,OAAO,EAAE,kCAAkC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7F;AAED;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACrD;;OAEG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;OAEG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AA6ED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C,cAAc,CAqIhB;AAED;;;;;GAKG;AACH,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE,CA0BnE"}
|
|
@@ -2,18 +2,43 @@
|
|
|
2
2
|
// Licensed under the MIT License.
|
|
3
3
|
import { createTokenCycler } from "../util/tokenCycler.js";
|
|
4
4
|
import { logger as coreLogger } from "../log.js";
|
|
5
|
+
import { isRestError } from "../restError.js";
|
|
5
6
|
/**
|
|
6
7
|
* The programmatic identifier of the bearerTokenAuthenticationPolicy.
|
|
7
8
|
*/
|
|
8
9
|
export const bearerTokenAuthenticationPolicyName = "bearerTokenAuthenticationPolicy";
|
|
10
|
+
/**
|
|
11
|
+
* Try to send the given request.
|
|
12
|
+
*
|
|
13
|
+
* When a response is received, returns a tuple of the response received and, if the response was received
|
|
14
|
+
* inside a thrown RestError, the RestError that was thrown.
|
|
15
|
+
*
|
|
16
|
+
* Otherwise, if an error was thrown while sending the request that did not provide an underlying response, it
|
|
17
|
+
* will be rethrown.
|
|
18
|
+
*/
|
|
19
|
+
async function trySendRequest(request, next) {
|
|
20
|
+
try {
|
|
21
|
+
return [await next(request), undefined];
|
|
22
|
+
}
|
|
23
|
+
catch (e) {
|
|
24
|
+
if (isRestError(e) && e.response) {
|
|
25
|
+
return [e.response, e];
|
|
26
|
+
}
|
|
27
|
+
else {
|
|
28
|
+
throw e;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
9
32
|
/**
|
|
10
33
|
* Default authorize request handler
|
|
11
34
|
*/
|
|
12
35
|
async function defaultAuthorizeRequest(options) {
|
|
13
36
|
const { scopes, getAccessToken, request } = options;
|
|
37
|
+
// Enable CAE true by default
|
|
14
38
|
const getTokenOptions = {
|
|
15
39
|
abortSignal: request.abortSignal,
|
|
16
40
|
tracingOptions: request.tracingOptions,
|
|
41
|
+
enableCae: true,
|
|
17
42
|
};
|
|
18
43
|
const accessToken = await getAccessToken(scopes, getTokenOptions);
|
|
19
44
|
if (accessToken) {
|
|
@@ -24,12 +49,26 @@ async function defaultAuthorizeRequest(options) {
|
|
|
24
49
|
* We will retrieve the challenge only if the response status code was 401,
|
|
25
50
|
* and if the response contained the header "WWW-Authenticate" with a non-empty value.
|
|
26
51
|
*/
|
|
27
|
-
function
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
52
|
+
function isChallengeResponse(response) {
|
|
53
|
+
return response.status === 401 && response.headers.has("WWW-Authenticate");
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Re-authorize the request for CAE challenge.
|
|
57
|
+
* The response containing the challenge is `options.response`.
|
|
58
|
+
* If this method returns true, the underlying request will be sent once again.
|
|
59
|
+
*/
|
|
60
|
+
async function authorizeRequestOnCaeChallenge(onChallengeOptions, caeClaims) {
|
|
61
|
+
var _a;
|
|
62
|
+
const { scopes } = onChallengeOptions;
|
|
63
|
+
const accessToken = await onChallengeOptions.getAccessToken(scopes, {
|
|
64
|
+
enableCae: true,
|
|
65
|
+
claims: caeClaims,
|
|
66
|
+
});
|
|
67
|
+
if (!accessToken) {
|
|
68
|
+
return false;
|
|
31
69
|
}
|
|
32
|
-
|
|
70
|
+
onChallengeOptions.request.headers.set("Authorization", `${(_a = accessToken.tokenType) !== null && _a !== void 0 ? _a : "Bearer"} ${accessToken.token}`);
|
|
71
|
+
return true;
|
|
33
72
|
}
|
|
34
73
|
/**
|
|
35
74
|
* A policy that can request a token from a TokenCredential implementation and
|
|
@@ -39,7 +78,10 @@ export function bearerTokenAuthenticationPolicy(options) {
|
|
|
39
78
|
var _a;
|
|
40
79
|
const { credential, scopes, challengeCallbacks } = options;
|
|
41
80
|
const logger = options.logger || coreLogger;
|
|
42
|
-
const callbacks =
|
|
81
|
+
const callbacks = {
|
|
82
|
+
authorizeRequest: (_a = challengeCallbacks === null || challengeCallbacks === void 0 ? void 0 : challengeCallbacks.authorizeRequest) !== null && _a !== void 0 ? _a : defaultAuthorizeRequest,
|
|
83
|
+
authorizeRequestOnChallenge: challengeCallbacks === null || challengeCallbacks === void 0 ? void 0 : challengeCallbacks.authorizeRequestOnChallenge,
|
|
84
|
+
};
|
|
43
85
|
// This function encapsulates the entire process of reliably retrieving the token
|
|
44
86
|
// The options are left out of the public API until there's demand to configure this.
|
|
45
87
|
// Remember to extend `BearerTokenAuthenticationPolicyOptions` with `TokenCyclerOptions`
|
|
@@ -74,26 +116,71 @@ export function bearerTokenAuthenticationPolicy(options) {
|
|
|
74
116
|
});
|
|
75
117
|
let response;
|
|
76
118
|
let error;
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
119
|
+
let shouldSendRequest;
|
|
120
|
+
[response, error] = await trySendRequest(request, next);
|
|
121
|
+
if (isChallengeResponse(response)) {
|
|
122
|
+
let claims = getCaeChallengeClaims(response.headers.get("WWW-Authenticate"));
|
|
123
|
+
// Handle CAE by default when receive CAE claim
|
|
124
|
+
if (claims) {
|
|
125
|
+
let parsedClaim;
|
|
126
|
+
// Return the response immediately if claims is not a valid base64 encoded string
|
|
127
|
+
try {
|
|
128
|
+
parsedClaim = atob(claims);
|
|
129
|
+
}
|
|
130
|
+
catch (e) {
|
|
131
|
+
logger.warning(`The WWW-Authenticate header contains "claims" that cannot be parsed. Unable to perform the Continuous Access Evaluation authentication flow. Unparsable claims: ${claims}`);
|
|
132
|
+
return response;
|
|
133
|
+
}
|
|
134
|
+
shouldSendRequest = await authorizeRequestOnCaeChallenge({
|
|
135
|
+
scopes: Array.isArray(scopes) ? scopes : [scopes],
|
|
136
|
+
response,
|
|
137
|
+
request,
|
|
138
|
+
getAccessToken,
|
|
139
|
+
logger,
|
|
140
|
+
}, parsedClaim);
|
|
141
|
+
// Send updated request and handle response for RestError
|
|
142
|
+
if (shouldSendRequest) {
|
|
143
|
+
[response, error] = await trySendRequest(request, next);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if (callbacks.authorizeRequestOnChallenge) {
|
|
147
|
+
// Handle custom challenges when client provides custom callback
|
|
148
|
+
shouldSendRequest = await callbacks.authorizeRequestOnChallenge({
|
|
149
|
+
scopes: Array.isArray(scopes) ? scopes : [scopes],
|
|
150
|
+
request,
|
|
151
|
+
response,
|
|
152
|
+
getAccessToken,
|
|
153
|
+
logger,
|
|
154
|
+
});
|
|
155
|
+
// Send updated request and handle response for RestError
|
|
156
|
+
if (shouldSendRequest) {
|
|
157
|
+
[response, error] = await trySendRequest(request, next);
|
|
158
|
+
}
|
|
159
|
+
// If we get another CAE Claim, we will handle it by default and return whatever value we receive for this
|
|
160
|
+
if (isChallengeResponse(response)) {
|
|
161
|
+
claims = getCaeChallengeClaims(response.headers.get("WWW-Authenticate"));
|
|
162
|
+
if (claims) {
|
|
163
|
+
let parsedClaim;
|
|
164
|
+
try {
|
|
165
|
+
parsedClaim = atob(claims);
|
|
166
|
+
}
|
|
167
|
+
catch (e) {
|
|
168
|
+
logger.warning(`The WWW-Authenticate header contains "claims" that cannot be parsed. Unable to perform the Continuous Access Evaluation authentication flow. Unparsable claims: ${claims}`);
|
|
169
|
+
return response;
|
|
170
|
+
}
|
|
171
|
+
shouldSendRequest = await authorizeRequestOnCaeChallenge({
|
|
172
|
+
scopes: Array.isArray(scopes) ? scopes : [scopes],
|
|
173
|
+
response,
|
|
174
|
+
request,
|
|
175
|
+
getAccessToken,
|
|
176
|
+
logger,
|
|
177
|
+
}, parsedClaim);
|
|
178
|
+
// Send updated request and handle response for RestError
|
|
179
|
+
if (shouldSendRequest) {
|
|
180
|
+
[response, error] = await trySendRequest(request, next);
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
}
|
|
97
184
|
}
|
|
98
185
|
}
|
|
99
186
|
if (error) {
|
|
@@ -105,4 +192,47 @@ export function bearerTokenAuthenticationPolicy(options) {
|
|
|
105
192
|
},
|
|
106
193
|
};
|
|
107
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Converts: `Bearer a="b", c="d", Pop e="f", g="h"`.
|
|
197
|
+
* Into: `[ { scheme: 'Bearer', params: { a: 'b', c: 'd' } }, { scheme: 'Pop', params: { e: 'f', g: 'h' } } ]`.
|
|
198
|
+
*
|
|
199
|
+
* @internal
|
|
200
|
+
*/
|
|
201
|
+
export function parseChallenges(challenges) {
|
|
202
|
+
// Challenge regex seperates the string to individual challenges with different schemes in the format `Scheme a="b", c=d`
|
|
203
|
+
// The challenge regex captures parameteres with either quotes values or unquoted values
|
|
204
|
+
const challengeRegex = /(\w+)\s+((?:\w+=(?:"[^"]*"|[^,]*),?\s*)+)/g;
|
|
205
|
+
// Parameter regex captures the claims group removed from the scheme in the format `a="b"` and `c="d"`
|
|
206
|
+
// CAE challenge always have quoted parameters. For more reference, https://learn.microsoft.com/entra/identity-platform/claims-challenge
|
|
207
|
+
const paramRegex = /(\w+)="([^"]*)"/g;
|
|
208
|
+
const parsedChallenges = [];
|
|
209
|
+
let match;
|
|
210
|
+
// Iterate over each challenge match
|
|
211
|
+
while ((match = challengeRegex.exec(challenges)) !== null) {
|
|
212
|
+
const scheme = match[1];
|
|
213
|
+
const paramsString = match[2];
|
|
214
|
+
const params = {};
|
|
215
|
+
let paramMatch;
|
|
216
|
+
// Iterate over each parameter match
|
|
217
|
+
while ((paramMatch = paramRegex.exec(paramsString)) !== null) {
|
|
218
|
+
params[paramMatch[1]] = paramMatch[2];
|
|
219
|
+
}
|
|
220
|
+
parsedChallenges.push({ scheme, params });
|
|
221
|
+
}
|
|
222
|
+
return parsedChallenges;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Parse a pipeline response and look for a CAE challenge with "Bearer" scheme
|
|
226
|
+
* Return the value in the header without parsing the challenge
|
|
227
|
+
* @internal
|
|
228
|
+
*/
|
|
229
|
+
function getCaeChallengeClaims(challenges) {
|
|
230
|
+
var _a;
|
|
231
|
+
if (!challenges) {
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
// Find all challenges present in the header
|
|
235
|
+
const parsedChallenges = parseChallenges(challenges);
|
|
236
|
+
return (_a = parsedChallenges.find((x) => x.scheme === "Bearer" && x.params.claims && x.params.error === "insufficient_claims")) === null || _a === void 0 ? void 0 : _a.params.claims;
|
|
237
|
+
}
|
|
108
238
|
//# sourceMappingURL=bearerTokenAuthenticationPolicy.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bearerTokenAuthenticationPolicy.js","sourceRoot":"","sources":["../../../src/policies/bearerTokenAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AccessToken, GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport type { AzureLogger } from \"@azure/logger\";\nimport type { PipelineRequest, PipelineResponse, SendRequest } from \"../interfaces.js\";\nimport type { PipelinePolicy } from \"../pipeline.js\";\nimport { createTokenCycler } from \"../util/tokenCycler.js\";\nimport { logger as coreLogger } from \"../log.js\";\n\n/**\n * The programmatic identifier of the bearerTokenAuthenticationPolicy.\n */\nexport const bearerTokenAuthenticationPolicyName = \"bearerTokenAuthenticationPolicy\";\n\n/**\n * Options sent to the authorizeRequest callback\n */\nexport interface AuthorizeRequestOptions {\n /**\n * The scopes for which the bearer token applies.\n */\n scopes: string[];\n /**\n * Function that retrieves either a cached access token or a new access token.\n */\n getAccessToken: (scopes: string[], options: GetTokenOptions) => Promise<AccessToken | null>;\n /**\n * Request that the policy is trying to fulfill.\n */\n request: PipelineRequest;\n /**\n * A logger, if one was sent through the HTTP pipeline.\n */\n logger?: AzureLogger;\n}\n\n/**\n * Options sent to the authorizeRequestOnChallenge callback\n */\nexport interface AuthorizeRequestOnChallengeOptions {\n /**\n * The scopes for which the bearer token applies.\n */\n scopes: string[];\n /**\n * Function that retrieves either a cached access token or a new access token.\n */\n getAccessToken: (scopes: string[], options: GetTokenOptions) => Promise<AccessToken | null>;\n /**\n * Request that the policy is trying to fulfill.\n */\n request: PipelineRequest;\n /**\n * Response containing the challenge.\n */\n response: PipelineResponse;\n /**\n * A logger, if one was sent through the HTTP pipeline.\n */\n logger?: AzureLogger;\n}\n\n/**\n * Options to override the processing of [Continuous Access Evaluation](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation) challenges.\n */\nexport interface ChallengeCallbacks {\n /**\n * Allows for the authorization of the main request of this policy before it's sent.\n */\n authorizeRequest?(options: AuthorizeRequestOptions): Promise<void>;\n /**\n * Allows to handle authentication challenges and to re-authorize the request.\n * The response containing the challenge is `options.response`.\n * If this method returns true, the underlying request will be sent once again.\n * The request may be modified before being sent.\n */\n authorizeRequestOnChallenge?(options: AuthorizeRequestOnChallengeOptions): Promise<boolean>;\n}\n\n/**\n * Options to configure the bearerTokenAuthenticationPolicy\n */\nexport interface BearerTokenAuthenticationPolicyOptions {\n /**\n * The TokenCredential implementation that can supply the bearer token.\n */\n credential?: TokenCredential;\n /**\n * The scopes for which the bearer token applies.\n */\n scopes: string | string[];\n /**\n * Allows for the processing of [Continuous Access Evaluation](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation) challenges.\n * If provided, it must contain at least the `authorizeRequestOnChallenge` method.\n * If provided, after a request is sent, if it has a challenge, it can be processed to re-send the original request with the relevant challenge information.\n */\n challengeCallbacks?: ChallengeCallbacks;\n /**\n * A logger can be sent for debugging purposes.\n */\n logger?: AzureLogger;\n}\n\n/**\n * Default authorize request handler\n */\nasync function defaultAuthorizeRequest(options: AuthorizeRequestOptions): Promise<void> {\n const { scopes, getAccessToken, request } = options;\n const getTokenOptions: GetTokenOptions = {\n abortSignal: request.abortSignal,\n tracingOptions: request.tracingOptions,\n };\n const accessToken = await getAccessToken(scopes, getTokenOptions);\n\n if (accessToken) {\n options.request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n }\n}\n\n/**\n * We will retrieve the challenge only if the response status code was 401,\n * and if the response contained the header \"WWW-Authenticate\" with a non-empty value.\n */\nfunction getChallenge(response: PipelineResponse): string | undefined {\n const challenge = response.headers.get(\"WWW-Authenticate\");\n if (response.status === 401 && challenge) {\n return challenge;\n }\n return;\n}\n\n/**\n * A policy that can request a token from a TokenCredential implementation and\n * then apply it to the Authorization header of a request as a Bearer token.\n */\nexport function bearerTokenAuthenticationPolicy(\n options: BearerTokenAuthenticationPolicyOptions,\n): PipelinePolicy {\n const { credential, scopes, challengeCallbacks } = options;\n const logger = options.logger || coreLogger;\n const callbacks = {\n authorizeRequest: challengeCallbacks?.authorizeRequest ?? defaultAuthorizeRequest,\n authorizeRequestOnChallenge: challengeCallbacks?.authorizeRequestOnChallenge,\n // keep all other properties\n ...challengeCallbacks,\n };\n\n // This function encapsulates the entire process of reliably retrieving the token\n // The options are left out of the public API until there's demand to configure this.\n // Remember to extend `BearerTokenAuthenticationPolicyOptions` with `TokenCyclerOptions`\n // in order to pass through the `options` object.\n const getAccessToken = credential\n ? createTokenCycler(credential /* , options */)\n : () => Promise.resolve(null);\n\n return {\n name: bearerTokenAuthenticationPolicyName,\n /**\n * If there's no challenge parameter:\n * - It will try to retrieve the token using the cache, or the credential's getToken.\n * - Then it will try the next policy with or without the retrieved token.\n *\n * It uses the challenge parameters to:\n * - Skip a first attempt to get the token from the credential if there's no cached token,\n * since it expects the token to be retrievable only after the challenge.\n * - Prepare the outgoing request if the `prepareRequest` method has been provided.\n * - Send an initial request to receive the challenge if it fails.\n * - Process a challenge if the response contains it.\n * - Retrieve a token with the challenge information, then re-send the request.\n */\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n if (!request.url.toLowerCase().startsWith(\"https://\")) {\n throw new Error(\n \"Bearer token authentication is not permitted for non-TLS protected (non-https) URLs.\",\n );\n }\n\n await callbacks.authorizeRequest({\n scopes: Array.isArray(scopes) ? scopes : [scopes],\n request,\n getAccessToken,\n logger,\n });\n\n let response: PipelineResponse;\n let error: Error | undefined;\n try {\n response = await next(request);\n } catch (err: any) {\n error = err;\n response = err.response;\n }\n\n if (\n callbacks.authorizeRequestOnChallenge &&\n response?.status === 401 &&\n getChallenge(response)\n ) {\n // processes challenge\n const shouldSendRequest = await callbacks.authorizeRequestOnChallenge({\n scopes: Array.isArray(scopes) ? scopes : [scopes],\n request,\n response,\n getAccessToken,\n logger,\n });\n\n if (shouldSendRequest) {\n return next(request);\n }\n }\n\n if (error) {\n throw error;\n } else {\n return response;\n }\n },\n };\n}\n"]}
|
|
1
|
+
{"version":3,"file":"bearerTokenAuthenticationPolicy.js","sourceRoot":"","sources":["../../../src/policies/bearerTokenAuthenticationPolicy.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright (c) Microsoft Corporation.\n// Licensed under the MIT License.\n\nimport type { AccessToken, GetTokenOptions, TokenCredential } from \"@azure/core-auth\";\nimport type { AzureLogger } from \"@azure/logger\";\nimport type { PipelineRequest, PipelineResponse, SendRequest } from \"../interfaces.js\";\nimport type { PipelinePolicy } from \"../pipeline.js\";\nimport { createTokenCycler } from \"../util/tokenCycler.js\";\nimport { logger as coreLogger } from \"../log.js\";\nimport { isRestError, RestError } from \"../restError.js\";\n\n/**\n * The programmatic identifier of the bearerTokenAuthenticationPolicy.\n */\nexport const bearerTokenAuthenticationPolicyName = \"bearerTokenAuthenticationPolicy\";\n\n/**\n * Options sent to the authorizeRequest callback\n */\nexport interface AuthorizeRequestOptions {\n /**\n * The scopes for which the bearer token applies.\n */\n scopes: string[];\n /**\n * Function that retrieves either a cached access token or a new access token.\n */\n getAccessToken: (scopes: string[], options: GetTokenOptions) => Promise<AccessToken | null>;\n /**\n * Request that the policy is trying to fulfill.\n */\n request: PipelineRequest;\n /**\n * A logger, if one was sent through the HTTP pipeline.\n */\n logger?: AzureLogger;\n}\n\n/**\n * Options sent to the authorizeRequestOnChallenge callback\n */\nexport interface AuthorizeRequestOnChallengeOptions {\n /**\n * The scopes for which the bearer token applies.\n */\n scopes: string[];\n /**\n * Function that retrieves either a cached access token or a new access token.\n */\n getAccessToken: (scopes: string[], options: GetTokenOptions) => Promise<AccessToken | null>;\n /**\n * Request that the policy is trying to fulfill.\n */\n request: PipelineRequest;\n /**\n * Response containing the challenge.\n */\n response: PipelineResponse;\n /**\n * A logger, if one was sent through the HTTP pipeline.\n */\n logger?: AzureLogger;\n}\n\n/**\n * Options to override the processing of [Continuous Access Evaluation](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation) challenges.\n */\nexport interface ChallengeCallbacks {\n /**\n * Allows for the authorization of the main request of this policy before it's sent.\n */\n authorizeRequest?(options: AuthorizeRequestOptions): Promise<void>;\n /**\n * Allows to handle authentication challenges and to re-authorize the request.\n * The response containing the challenge is `options.response`.\n * If this method returns true, the underlying request will be sent once again.\n * The request may be modified before being sent.\n */\n authorizeRequestOnChallenge?(options: AuthorizeRequestOnChallengeOptions): Promise<boolean>;\n}\n\n/**\n * Options to configure the bearerTokenAuthenticationPolicy\n */\nexport interface BearerTokenAuthenticationPolicyOptions {\n /**\n * The TokenCredential implementation that can supply the bearer token.\n */\n credential?: TokenCredential;\n /**\n * The scopes for which the bearer token applies.\n */\n scopes: string | string[];\n /**\n * Allows for the processing of [Continuous Access Evaluation](https://docs.microsoft.com/azure/active-directory/conditional-access/concept-continuous-access-evaluation) challenges.\n * If provided, it must contain at least the `authorizeRequestOnChallenge` method.\n * If provided, after a request is sent, if it has a challenge, it can be processed to re-send the original request with the relevant challenge information.\n */\n challengeCallbacks?: ChallengeCallbacks;\n /**\n * A logger can be sent for debugging purposes.\n */\n logger?: AzureLogger;\n}\n/**\n * Try to send the given request.\n *\n * When a response is received, returns a tuple of the response received and, if the response was received\n * inside a thrown RestError, the RestError that was thrown.\n *\n * Otherwise, if an error was thrown while sending the request that did not provide an underlying response, it\n * will be rethrown.\n */\nasync function trySendRequest(\n request: PipelineRequest,\n next: SendRequest,\n): Promise<[PipelineResponse, RestError | undefined]> {\n try {\n return [await next(request), undefined];\n } catch (e: any) {\n if (isRestError(e) && e.response) {\n return [e.response, e];\n } else {\n throw e;\n }\n }\n}\n/**\n * Default authorize request handler\n */\nasync function defaultAuthorizeRequest(options: AuthorizeRequestOptions): Promise<void> {\n const { scopes, getAccessToken, request } = options;\n // Enable CAE true by default\n const getTokenOptions: GetTokenOptions = {\n abortSignal: request.abortSignal,\n tracingOptions: request.tracingOptions,\n enableCae: true,\n };\n\n const accessToken = await getAccessToken(scopes, getTokenOptions);\n\n if (accessToken) {\n options.request.headers.set(\"Authorization\", `Bearer ${accessToken.token}`);\n }\n}\n\n/**\n * We will retrieve the challenge only if the response status code was 401,\n * and if the response contained the header \"WWW-Authenticate\" with a non-empty value.\n */\nfunction isChallengeResponse(response: PipelineResponse): boolean {\n return response.status === 401 && response.headers.has(\"WWW-Authenticate\");\n}\n\n/**\n * Re-authorize the request for CAE challenge.\n * The response containing the challenge is `options.response`.\n * If this method returns true, the underlying request will be sent once again.\n */\nasync function authorizeRequestOnCaeChallenge(\n onChallengeOptions: AuthorizeRequestOnChallengeOptions,\n caeClaims: string,\n): Promise<boolean> {\n const { scopes } = onChallengeOptions;\n\n const accessToken = await onChallengeOptions.getAccessToken(scopes, {\n enableCae: true,\n claims: caeClaims,\n });\n if (!accessToken) {\n return false;\n }\n\n onChallengeOptions.request.headers.set(\n \"Authorization\",\n `${accessToken.tokenType ?? \"Bearer\"} ${accessToken.token}`,\n );\n return true;\n}\n\n/**\n * A policy that can request a token from a TokenCredential implementation and\n * then apply it to the Authorization header of a request as a Bearer token.\n */\nexport function bearerTokenAuthenticationPolicy(\n options: BearerTokenAuthenticationPolicyOptions,\n): PipelinePolicy {\n const { credential, scopes, challengeCallbacks } = options;\n const logger = options.logger || coreLogger;\n const callbacks = {\n authorizeRequest: challengeCallbacks?.authorizeRequest ?? defaultAuthorizeRequest,\n authorizeRequestOnChallenge: challengeCallbacks?.authorizeRequestOnChallenge,\n };\n\n // This function encapsulates the entire process of reliably retrieving the token\n // The options are left out of the public API until there's demand to configure this.\n // Remember to extend `BearerTokenAuthenticationPolicyOptions` with `TokenCyclerOptions`\n // in order to pass through the `options` object.\n const getAccessToken = credential\n ? createTokenCycler(credential /* , options */)\n : () => Promise.resolve(null);\n\n return {\n name: bearerTokenAuthenticationPolicyName,\n /**\n * If there's no challenge parameter:\n * - It will try to retrieve the token using the cache, or the credential's getToken.\n * - Then it will try the next policy with or without the retrieved token.\n *\n * It uses the challenge parameters to:\n * - Skip a first attempt to get the token from the credential if there's no cached token,\n * since it expects the token to be retrievable only after the challenge.\n * - Prepare the outgoing request if the `prepareRequest` method has been provided.\n * - Send an initial request to receive the challenge if it fails.\n * - Process a challenge if the response contains it.\n * - Retrieve a token with the challenge information, then re-send the request.\n */\n async sendRequest(request: PipelineRequest, next: SendRequest): Promise<PipelineResponse> {\n if (!request.url.toLowerCase().startsWith(\"https://\")) {\n throw new Error(\n \"Bearer token authentication is not permitted for non-TLS protected (non-https) URLs.\",\n );\n }\n\n await callbacks.authorizeRequest({\n scopes: Array.isArray(scopes) ? scopes : [scopes],\n request,\n getAccessToken,\n logger,\n });\n\n let response: PipelineResponse;\n let error: Error | undefined;\n let shouldSendRequest: boolean;\n [response, error] = await trySendRequest(request, next);\n\n if (isChallengeResponse(response)) {\n let claims = getCaeChallengeClaims(response.headers.get(\"WWW-Authenticate\"));\n // Handle CAE by default when receive CAE claim\n if (claims) {\n let parsedClaim: string;\n // Return the response immediately if claims is not a valid base64 encoded string\n try {\n parsedClaim = atob(claims);\n } catch (e) {\n logger.warning(\n `The WWW-Authenticate header contains \"claims\" that cannot be parsed. Unable to perform the Continuous Access Evaluation authentication flow. Unparsable claims: ${claims}`,\n );\n return response;\n }\n shouldSendRequest = await authorizeRequestOnCaeChallenge(\n {\n scopes: Array.isArray(scopes) ? scopes : [scopes],\n response,\n request,\n getAccessToken,\n logger,\n },\n parsedClaim,\n );\n // Send updated request and handle response for RestError\n if (shouldSendRequest) {\n [response, error] = await trySendRequest(request, next);\n }\n } else if (callbacks.authorizeRequestOnChallenge) {\n // Handle custom challenges when client provides custom callback\n shouldSendRequest = await callbacks.authorizeRequestOnChallenge({\n scopes: Array.isArray(scopes) ? scopes : [scopes],\n request,\n response,\n getAccessToken,\n logger,\n });\n\n // Send updated request and handle response for RestError\n if (shouldSendRequest) {\n [response, error] = await trySendRequest(request, next);\n }\n\n // If we get another CAE Claim, we will handle it by default and return whatever value we receive for this\n if (isChallengeResponse(response)) {\n claims = getCaeChallengeClaims(response.headers.get(\"WWW-Authenticate\") as string);\n if (claims) {\n let parsedClaim: string;\n try {\n parsedClaim = atob(claims);\n } catch (e) {\n logger.warning(\n `The WWW-Authenticate header contains \"claims\" that cannot be parsed. Unable to perform the Continuous Access Evaluation authentication flow. Unparsable claims: ${claims}`,\n );\n return response;\n }\n\n shouldSendRequest = await authorizeRequestOnCaeChallenge(\n {\n scopes: Array.isArray(scopes) ? scopes : [scopes],\n response,\n request,\n getAccessToken,\n logger,\n },\n parsedClaim,\n );\n // Send updated request and handle response for RestError\n if (shouldSendRequest) {\n [response, error] = await trySendRequest(request, next);\n }\n }\n }\n }\n }\n\n if (error) {\n throw error;\n } else {\n return response;\n }\n },\n };\n}\n\n/**\n *\n * Interface to represent a parsed challenge.\n *\n * @internal\n */\ninterface AuthChallenge {\n scheme: string;\n params: Record<string, string>;\n}\n\n/**\n * Converts: `Bearer a=\"b\", c=\"d\", Pop e=\"f\", g=\"h\"`.\n * Into: `[ { scheme: 'Bearer', params: { a: 'b', c: 'd' } }, { scheme: 'Pop', params: { e: 'f', g: 'h' } } ]`.\n *\n * @internal\n */\nexport function parseChallenges(challenges: string): AuthChallenge[] {\n // Challenge regex seperates the string to individual challenges with different schemes in the format `Scheme a=\"b\", c=d`\n // The challenge regex captures parameteres with either quotes values or unquoted values\n const challengeRegex = /(\\w+)\\s+((?:\\w+=(?:\"[^\"]*\"|[^,]*),?\\s*)+)/g;\n // Parameter regex captures the claims group removed from the scheme in the format `a=\"b\"` and `c=\"d\"`\n // CAE challenge always have quoted parameters. For more reference, https://learn.microsoft.com/entra/identity-platform/claims-challenge\n const paramRegex = /(\\w+)=\"([^\"]*)\"/g;\n\n const parsedChallenges: AuthChallenge[] = [];\n let match;\n\n // Iterate over each challenge match\n while ((match = challengeRegex.exec(challenges)) !== null) {\n const scheme = match[1];\n const paramsString = match[2];\n const params: Record<string, string> = {};\n let paramMatch;\n\n // Iterate over each parameter match\n while ((paramMatch = paramRegex.exec(paramsString)) !== null) {\n params[paramMatch[1]] = paramMatch[2];\n }\n\n parsedChallenges.push({ scheme, params });\n }\n return parsedChallenges;\n}\n\n/**\n * Parse a pipeline response and look for a CAE challenge with \"Bearer\" scheme\n * Return the value in the header without parsing the challenge\n * @internal\n */\nfunction getCaeChallengeClaims(challenges: string | undefined): string | undefined {\n if (!challenges) {\n return;\n }\n // Find all challenges present in the header\n const parsedChallenges = parseChallenges(challenges);\n return parsedChallenges.find(\n (x) => x.scheme === \"Bearer\" && x.params.claims && x.params.error === \"insufficient_claims\",\n )?.params.claims;\n}\n"]}
|
|
@@ -96,4 +96,22 @@ export interface BearerTokenAuthenticationPolicyOptions {
|
|
|
96
96
|
* then apply it to the Authorization header of a request as a Bearer token.
|
|
97
97
|
*/
|
|
98
98
|
export declare function bearerTokenAuthenticationPolicy(options: BearerTokenAuthenticationPolicyOptions): PipelinePolicy;
|
|
99
|
+
/**
|
|
100
|
+
*
|
|
101
|
+
* Interface to represent a parsed challenge.
|
|
102
|
+
*
|
|
103
|
+
* @internal
|
|
104
|
+
*/
|
|
105
|
+
interface AuthChallenge {
|
|
106
|
+
scheme: string;
|
|
107
|
+
params: Record<string, string>;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Converts: `Bearer a="b", c="d", Pop e="f", g="h"`.
|
|
111
|
+
* Into: `[ { scheme: 'Bearer', params: { a: 'b', c: 'd' } }, { scheme: 'Pop', params: { e: 'f', g: 'h' } } ]`.
|
|
112
|
+
*
|
|
113
|
+
* @internal
|
|
114
|
+
*/
|
|
115
|
+
export declare function parseChallenges(challenges: string): AuthChallenge[];
|
|
116
|
+
export {};
|
|
99
117
|
//# sourceMappingURL=bearerTokenAuthenticationPolicy.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bearerTokenAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/bearerTokenAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAe,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"bearerTokenAuthenticationPolicy.d.ts","sourceRoot":"","sources":["../../../src/policies/bearerTokenAuthenticationPolicy.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,eAAe,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AACtF,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,eAAe,CAAC;AACjD,OAAO,KAAK,EAAE,eAAe,EAAE,gBAAgB,EAAe,MAAM,kBAAkB,CAAC;AACvF,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAKrD;;GAEG;AACH,eAAO,MAAM,mCAAmC,oCAAoC,CAAC;AAErF;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC5F;;OAEG;IACH,OAAO,EAAE,eAAe,CAAC;IACzB;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kCAAkC;IACjD;;OAEG;IACH,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB;;OAEG;IACH,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,OAAO,EAAE,eAAe,KAAK,OAAO,CAAC,WAAW,GAAG,IAAI,CAAC,CAAC;IAC5F;;OAEG;IACH,OAAO,EAAE,eAAe,CAAC;IACzB;;OAEG;IACH,QAAQ,EAAE,gBAAgB,CAAC;IAC3B;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED;;GAEG;AACH,MAAM,WAAW,kBAAkB;IACjC;;OAEG;IACH,gBAAgB,CAAC,CAAC,OAAO,EAAE,uBAAuB,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACnE;;;;;OAKG;IACH,2BAA2B,CAAC,CAAC,OAAO,EAAE,kCAAkC,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;CAC7F;AAED;;GAEG;AACH,MAAM,WAAW,sCAAsC;IACrD;;OAEG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;OAEG;IACH,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1B;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,kBAAkB,CAAC;IACxC;;OAEG;IACH,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AA6ED;;;GAGG;AACH,wBAAgB,+BAA+B,CAC7C,OAAO,EAAE,sCAAsC,GAC9C,cAAc,CAqIhB;AAED;;;;;GAKG;AACH,UAAU,aAAa;IACrB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED;;;;;GAKG;AACH,wBAAgB,eAAe,CAAC,UAAU,EAAE,MAAM,GAAG,aAAa,EAAE,CA0BnE"}
|