@hawcx/oauth-client 5.2.0 → 6.2.0
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/CHANGELOG.md +355 -0
- package/README.md +247 -0
- package/dist/client-assertion.d.ts.map +1 -1
- package/dist/client-assertion.js +6 -2
- package/dist/client-assertion.js.map +1 -1
- package/dist/delegation/exceptions.d.ts +34 -0
- package/dist/delegation/exceptions.d.ts.map +1 -1
- package/dist/delegation/exceptions.js +39 -1
- package/dist/delegation/exceptions.js.map +1 -1
- package/dist/delegation/hawcx.d.ts +6 -2
- package/dist/delegation/hawcx.d.ts.map +1 -1
- package/dist/delegation/hawcx.js.map +1 -1
- package/dist/delegation/index.d.ts +2 -1
- package/dist/delegation/index.d.ts.map +1 -1
- package/dist/delegation/index.js +3 -1
- package/dist/delegation/index.js.map +1 -1
- package/dist/delegation/transport.d.ts +6 -2
- package/dist/delegation/transport.d.ts.map +1 -1
- package/dist/delegation/transport.js +9 -2
- package/dist/delegation/transport.js.map +1 -1
- package/dist/errors.d.ts +11 -1
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +1 -0
- package/dist/errors.js.map +1 -1
- package/dist/index.d.ts +6 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -2
- package/dist/index.js.map +1 -1
- package/dist/internal/url.d.ts +73 -0
- package/dist/internal/url.d.ts.map +1 -0
- package/dist/internal/url.js +210 -0
- package/dist/internal/url.js.map +1 -0
- package/dist/management/jwt_transport.d.ts +44 -3
- package/dist/management/jwt_transport.d.ts.map +1 -1
- package/dist/management/jwt_transport.js +60 -4
- package/dist/management/jwt_transport.js.map +1 -1
- package/dist/management/response_signature.d.ts +65 -0
- package/dist/management/response_signature.d.ts.map +1 -0
- package/dist/management/response_signature.js +170 -0
- package/dist/management/response_signature.js.map +1 -0
- package/dist/oauth.d.ts +89 -1
- package/dist/oauth.d.ts.map +1 -1
- package/dist/oauth.js +265 -34
- package/dist/oauth.js.map +1 -1
- package/dist/profile/client.d.ts +99 -0
- package/dist/profile/client.d.ts.map +1 -0
- package/dist/profile/client.js +209 -0
- package/dist/profile/client.js.map +1 -0
- package/dist/profile/errors.d.ts +62 -0
- package/dist/profile/errors.d.ts.map +1 -0
- package/dist/profile/errors.js +90 -0
- package/dist/profile/errors.js.map +1 -0
- package/dist/profile/http.d.ts +57 -0
- package/dist/profile/http.d.ts.map +1 -0
- package/dist/profile/http.js +295 -0
- package/dist/profile/http.js.map +1 -0
- package/dist/profile/tokens.d.ts +50 -0
- package/dist/profile/tokens.d.ts.map +1 -0
- package/dist/profile/tokens.js +137 -0
- package/dist/profile/tokens.js.map +1 -0
- package/dist/profile/types.d.ts +99 -0
- package/dist/profile/types.d.ts.map +1 -0
- package/dist/profile/types.js +7 -0
- package/dist/profile/types.js.map +1 -0
- package/dist/stepup/client.d.ts +11 -0
- package/dist/stepup/client.d.ts.map +1 -1
- package/dist/stepup/client.js +6 -1
- package/dist/stepup/client.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Verification of the management API's `X-Response-Signature` (TOB-HSDK-11).
|
|
4
|
+
*
|
|
5
|
+
* Every successful jwt-mode (Path A) management response from hx_auth carries
|
|
6
|
+
* `X-Response-Signature`: a compact JWS signed with the shared OAuth signing
|
|
7
|
+
* key that the OP publishes at `${issuer}/.well-known/jwks.json` — the same
|
|
8
|
+
* JWKS that verifies ID tokens. Its payload is exactly
|
|
9
|
+
*
|
|
10
|
+
* { "body_sha256": base64url(sha256(<exact response body bytes>)), "iat": <unix s> }
|
|
11
|
+
*
|
|
12
|
+
* Verifying it restores the response authentication the legacy ECIES
|
|
13
|
+
* transport always had (signature + timestamp skew on every response).
|
|
14
|
+
*
|
|
15
|
+
* Checks, all fail-closed:
|
|
16
|
+
* - the header is present and is a compact JWS whose protected header names
|
|
17
|
+
* a `kid`;
|
|
18
|
+
* - `alg` is one of ES256 / ES384 / ES512 (the server's KMS key is an EC key
|
|
19
|
+
* and the alg follows its curve). `none`, HS*, RS*, EdDSA are rejected —
|
|
20
|
+
* this forecloses alg-substitution / key-confusion;
|
|
21
|
+
* - the signature verifies against the issuer JWKS (jose `RemoteJWKSet`:
|
|
22
|
+
* cached, and refetched once when the `kid` is unknown — key rotation);
|
|
23
|
+
* - `iat` is present and within ±clockSkewSeconds of local time;
|
|
24
|
+
* - the payload is a response signature, not some other token minted with
|
|
25
|
+
* the same key (TOB-HSDK-9 type confusion): `body_sha256` is required and
|
|
26
|
+
* `aud`, `sub`, `nonce` must be absent — an ID token always carries
|
|
27
|
+
* `aud`/`sub`;
|
|
28
|
+
* - `body_sha256` equals base64url(sha256(raw body bytes)), compared in
|
|
29
|
+
* constant time. The hash is taken over the raw bytes as received, before
|
|
30
|
+
* any decoding or JSON parsing.
|
|
31
|
+
*
|
|
32
|
+
* Failures are split so callers can react correctly:
|
|
33
|
+
* - {@link ResponseSignatureInvalidError} (`code: 'response_signature_invalid'`)
|
|
34
|
+
* — missing/empty header or a signature that does not verify;
|
|
35
|
+
* - {@link ResponseSignatureUnavailableError}
|
|
36
|
+
* (`code: 'response_signature_unavailable'`) — the signing keys could not
|
|
37
|
+
* be obtained (JWKS fetch failed / timed out / non-200 / malformed, or the
|
|
38
|
+
* kid is unknown while jose's refetch cooldown suppresses a refetch). The
|
|
39
|
+
* request may already have been applied server-side.
|
|
40
|
+
* Both subclass `DelegationResponseError` and carry fixed, non-leaky messages.
|
|
41
|
+
*
|
|
42
|
+
* Not part of the public API (not re-exported from the package index).
|
|
43
|
+
*/
|
|
44
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
45
|
+
exports.ResponseSignatureVerifier = exports.RESPONSE_SIGNATURE_HEADER = void 0;
|
|
46
|
+
const node_crypto_1 = require("node:crypto");
|
|
47
|
+
const jose_1 = require("jose");
|
|
48
|
+
const exceptions_1 = require("../delegation/exceptions");
|
|
49
|
+
/** Response-signature header name (HTTP headers are case-insensitive). */
|
|
50
|
+
exports.RESPONSE_SIGNATURE_HEADER = 'X-Response-Signature';
|
|
51
|
+
/** EC algorithms the server's KMS signing key can produce — nothing else. */
|
|
52
|
+
const ALLOWED_ALGS = Object.freeze(['ES256', 'ES384', 'ES512']);
|
|
53
|
+
/**
|
|
54
|
+
* Claims that mark a token as something other than a response signature.
|
|
55
|
+
* Deliberately `aud` is forbidden rather than repurposed: request binding
|
|
56
|
+
* (anti-replay within the iat window) is designed server-side in
|
|
57
|
+
* hawcx/hx_auth#278 as dedicated custom claims (`req_jti` / `htm` / `htu` /
|
|
58
|
+
* `cid`), explicitly NOT `aud` — keep this list as is.
|
|
59
|
+
*/
|
|
60
|
+
const FORBIDDEN_CLAIMS = Object.freeze(['aud', 'sub', 'nonce']);
|
|
61
|
+
/** Generic, non-leaky messages; detail (if any) is attached as `cause`. */
|
|
62
|
+
const MSG_MISSING = 'Management API response is missing its signature';
|
|
63
|
+
const MSG_INVALID = 'Management API response signature verification failed';
|
|
64
|
+
const MSG_UNAVAILABLE = 'Management API response signature could not be verified: signing keys unavailable';
|
|
65
|
+
class ResponseSignatureVerifier {
|
|
66
|
+
constructor(options) {
|
|
67
|
+
// jose handles caching (10 min max age), concurrent-fetch coalescing, and a
|
|
68
|
+
// single refetch on an unknown `kid` (rate-limited by its 30s cooldown) —
|
|
69
|
+
// the same machinery HawcxOAuth uses for ID-token verification.
|
|
70
|
+
this.jwks = (0, jose_1.createRemoteJWKSet)(new URL(options.jwksUri), {
|
|
71
|
+
timeoutDuration: options.timeoutMs,
|
|
72
|
+
});
|
|
73
|
+
this.clockSkewSeconds = options.clockSkewSeconds;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Verify `signature` (the header value, or null when absent) over the EXACT
|
|
77
|
+
* response body bytes. Resolves on success; throws
|
|
78
|
+
* {@link ResponseSignatureInvalidError} or
|
|
79
|
+
* {@link ResponseSignatureUnavailableError} otherwise.
|
|
80
|
+
*/
|
|
81
|
+
async verify(signature, body) {
|
|
82
|
+
if (!signature) {
|
|
83
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_MISSING);
|
|
84
|
+
}
|
|
85
|
+
// Set by the key resolver when the keys could not be obtained, as opposed
|
|
86
|
+
// to a signature that was checked and found bad.
|
|
87
|
+
let keysUnavailable = false;
|
|
88
|
+
let payloadBytes;
|
|
89
|
+
try {
|
|
90
|
+
const result = await (0, jose_1.compactVerify)(signature, async (protectedHeader, token) => {
|
|
91
|
+
if (typeof protectedHeader.kid !== 'string' || protectedHeader.kid.length === 0) {
|
|
92
|
+
throw new Error('response signature has no kid');
|
|
93
|
+
}
|
|
94
|
+
// If jose may refetch on a miss (not cooling down), a miss means the
|
|
95
|
+
// kid is absent from a freshly fetched JWKS → invalid. If the
|
|
96
|
+
// cooldown suppresses the refetch, the kid may simply be newer than
|
|
97
|
+
// our cache (rotation) → unavailable.
|
|
98
|
+
const canRefetch = !this.jwks.coolingDown;
|
|
99
|
+
try {
|
|
100
|
+
return await this.jwks(protectedHeader, token);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
if (err instanceof jose_1.errors.JWKSNoMatchingKey) {
|
|
104
|
+
keysUnavailable = !canRefetch;
|
|
105
|
+
}
|
|
106
|
+
else if (!(err instanceof jose_1.errors.JWKSMultipleMatchingKeys)) {
|
|
107
|
+
// Fetch failure, timeout, non-200, malformed JWKS, network error.
|
|
108
|
+
keysUnavailable = true;
|
|
109
|
+
}
|
|
110
|
+
throw err;
|
|
111
|
+
}
|
|
112
|
+
}, { algorithms: [...ALLOWED_ALGS] });
|
|
113
|
+
payloadBytes = result.payload;
|
|
114
|
+
}
|
|
115
|
+
catch (err) {
|
|
116
|
+
if (keysUnavailable) {
|
|
117
|
+
throw new exceptions_1.ResponseSignatureUnavailableError(MSG_UNAVAILABLE, { cause: err });
|
|
118
|
+
}
|
|
119
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_INVALID, { cause: err });
|
|
120
|
+
}
|
|
121
|
+
let claims;
|
|
122
|
+
try {
|
|
123
|
+
const parsed = JSON.parse(Buffer.from(payloadBytes).toString('utf-8'));
|
|
124
|
+
if (typeof parsed !== 'object' || parsed === null || Array.isArray(parsed)) {
|
|
125
|
+
throw new Error('payload is not a JSON object');
|
|
126
|
+
}
|
|
127
|
+
claims = parsed;
|
|
128
|
+
}
|
|
129
|
+
catch (err) {
|
|
130
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_INVALID, { cause: err });
|
|
131
|
+
}
|
|
132
|
+
// Type-confusion guard (TOB-HSDK-9): only a response signature qualifies.
|
|
133
|
+
for (const claim of FORBIDDEN_CLAIMS) {
|
|
134
|
+
if (Object.prototype.hasOwnProperty.call(claims, claim)) {
|
|
135
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_INVALID, {
|
|
136
|
+
cause: new Error(`unexpected '${claim}' claim in response signature`),
|
|
137
|
+
});
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
const claimedHash = claims['body_sha256'];
|
|
141
|
+
if (typeof claimedHash !== 'string' || claimedHash.length === 0) {
|
|
142
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_INVALID, {
|
|
143
|
+
cause: new Error("response signature has no 'body_sha256' claim"),
|
|
144
|
+
});
|
|
145
|
+
}
|
|
146
|
+
// Freshness: `iat` required, within the configured skew either way.
|
|
147
|
+
const iat = claims['iat'];
|
|
148
|
+
if (typeof iat !== 'number' || !Number.isFinite(iat)) {
|
|
149
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_INVALID, {
|
|
150
|
+
cause: new Error("response signature has no numeric 'iat' claim"),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
const nowSec = Date.now() / 1000;
|
|
154
|
+
if (iat > nowSec + this.clockSkewSeconds || iat < nowSec - this.clockSkewSeconds) {
|
|
155
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_INVALID, {
|
|
156
|
+
cause: new Error(`response signature 'iat' outside allowed skew (${this.clockSkewSeconds}s)`),
|
|
157
|
+
});
|
|
158
|
+
}
|
|
159
|
+
// Body binding: base64url(sha256(raw bytes)), no padding, constant-time.
|
|
160
|
+
const actual = Buffer.from((0, node_crypto_1.createHash)('sha256').update(body).digest('base64url').replace(/=+$/, ''), 'utf-8');
|
|
161
|
+
const claimed = Buffer.from(claimedHash, 'utf-8');
|
|
162
|
+
if (actual.length !== claimed.length || !(0, node_crypto_1.timingSafeEqual)(actual, claimed)) {
|
|
163
|
+
throw new exceptions_1.ResponseSignatureInvalidError(MSG_INVALID, {
|
|
164
|
+
cause: new Error('response body does not match the signed body_sha256'),
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
exports.ResponseSignatureVerifier = ResponseSignatureVerifier;
|
|
170
|
+
//# sourceMappingURL=response_signature.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"response_signature.js","sourceRoot":"","sources":["../../src/management/response_signature.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth.d.ts
CHANGED
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import { type JWTPayload } from 'jose';
|
|
19
19
|
import { ClientAssertionSigner } from './client-assertion';
|
|
20
|
+
import { ProfileClient, type ProfileClientOptions } from './profile/client';
|
|
20
21
|
export interface HawcxOAuthConfig {
|
|
21
22
|
/** Your Hawcx config-id (sent as the `X-Config-Id` header on token exchange) */
|
|
22
23
|
configId: string;
|
|
@@ -66,6 +67,46 @@ export interface HawcxOAuthIssuerConfig {
|
|
|
66
67
|
* Spec.md §3.3.
|
|
67
68
|
*/
|
|
68
69
|
clockToleranceSeconds?: number;
|
|
70
|
+
/**
|
|
71
|
+
* Optional custom domain (a DNS CNAME to Hawcx), e.g.
|
|
72
|
+
* `https://auth.example.com`. `scheme://host[:port]` only: https (http only
|
|
73
|
+
* for loopback), ASCII/punycode host or IP literal, no userinfo, path,
|
|
74
|
+
* query or fragment; one trailing `/` allowed. Canonicalised (lower-cased,
|
|
75
|
+
* default port dropped). `null`/`undefined` = unset. Invalid →
|
|
76
|
+
* `HawcxOAuthError` `invalid_custom_domain`, before any discovery fetch.
|
|
77
|
+
*
|
|
78
|
+
* Affects only the browser-facing URLs from {@link HawcxOAuth.buildAuthorizeUrl}
|
|
79
|
+
* and {@link HawcxOAuth.buildEndSessionUrl} (scheme + host + port swapped,
|
|
80
|
+
* path kept). Discovery, token exchange, JWKS and the `iss` check stay on
|
|
81
|
+
* the issuer.
|
|
82
|
+
*/
|
|
83
|
+
customDomain?: string | null;
|
|
84
|
+
}
|
|
85
|
+
/** Options for {@link HawcxOAuth.buildAuthorizeUrl}. */
|
|
86
|
+
export interface AuthorizeUrlOptions {
|
|
87
|
+
/** `redirect_uri` registered for the client. Required. */
|
|
88
|
+
redirectUri: string;
|
|
89
|
+
/** Opaque CSRF `state`. Required. */
|
|
90
|
+
state: string;
|
|
91
|
+
/** PKCE S256 `code_challenge`. Required. */
|
|
92
|
+
codeChallenge: string;
|
|
93
|
+
/** Optional OIDC `nonce`; pass the same value to `exchangeCode` as `expectedNonce`. */
|
|
94
|
+
nonce?: string;
|
|
95
|
+
/** Space-separated scopes (default `openid`). */
|
|
96
|
+
scope?: string;
|
|
97
|
+
/** Optional OIDC `prompt` (e.g. `login`). */
|
|
98
|
+
prompt?: string;
|
|
99
|
+
/**
|
|
100
|
+
* Additional query parameters (non-empty keys, string values). May not
|
|
101
|
+
* override any parameter the SDK sets, nor `request` / `request_uri`.
|
|
102
|
+
*/
|
|
103
|
+
extraParams?: Record<string, string>;
|
|
104
|
+
}
|
|
105
|
+
/** Options for {@link HawcxOAuth.buildEndSessionUrl}. */
|
|
106
|
+
export interface EndSessionUrlOptions {
|
|
107
|
+
postLogoutRedirectUri?: string;
|
|
108
|
+
idTokenHint?: string;
|
|
109
|
+
state?: string;
|
|
69
110
|
}
|
|
70
111
|
export interface JwtClaims extends JWTPayload {
|
|
71
112
|
/** Subject (user ID) */
|
|
@@ -168,6 +209,8 @@ export declare class HawcxOAuth {
|
|
|
168
209
|
* which returns a *new* configured client (this field is `readonly`).
|
|
169
210
|
*/
|
|
170
211
|
private readonly clientAssertionSigner;
|
|
212
|
+
/** Validated custom-domain origin for browser URLs; `null` if unset or legacy mode. */
|
|
213
|
+
private readonly customDomain;
|
|
171
214
|
/**
|
|
172
215
|
* Construct a legacy-mode client. For discovery mode, prefer
|
|
173
216
|
* {@link HawcxOAuth.fromIssuer}.
|
|
@@ -212,6 +255,14 @@ export declare class HawcxOAuth {
|
|
|
212
255
|
* (the assertion would be silently ignored).
|
|
213
256
|
*/
|
|
214
257
|
withClientAssertion(signer: ClientAssertionSigner): HawcxOAuth;
|
|
258
|
+
/**
|
|
259
|
+
* Client for the Hawcx profile service, built from this discovery-mode
|
|
260
|
+
* client and its {@link withClientAssertion} signer. Same as
|
|
261
|
+
* `new ProfileClient(oauth, options)`.
|
|
262
|
+
*
|
|
263
|
+
* @throws {HawcxError} in legacy mode or without a client-assertion signer.
|
|
264
|
+
*/
|
|
265
|
+
profile(options?: ProfileClientOptions): ProfileClient;
|
|
215
266
|
/**
|
|
216
267
|
* Exchange authorization code for id_token and verified claims.
|
|
217
268
|
*
|
|
@@ -257,9 +308,46 @@ export declare class HawcxOAuth {
|
|
|
257
308
|
refreshJwks(): void;
|
|
258
309
|
/**
|
|
259
310
|
* @deprecated since 5.1.0 — renamed to {@link refreshJwks} for cross-SDK
|
|
260
|
-
* consistency. Will be removed in
|
|
311
|
+
* consistency. Will be removed in a future major version. Behaviour is identical.
|
|
261
312
|
*/
|
|
262
313
|
clearCache(): void;
|
|
263
314
|
private clearCacheWarned;
|
|
315
|
+
/**
|
|
316
|
+
* Build the browser `/authorize` URL (discovery mode only; no I/O).
|
|
317
|
+
*
|
|
318
|
+
* Base is the discovered `authorization_endpoint`; with `customDomain` set,
|
|
319
|
+
* its scheme + host + port are replaced by the custom domain's (path and any
|
|
320
|
+
* existing query kept). Sets `response_type=code`, `client_id`,
|
|
321
|
+
* `redirect_uri`, `scope` (default `openid`), `state`, `code_challenge`,
|
|
322
|
+
* `code_challenge_method=S256`, plus `nonce` / `prompt` / `extraParams`
|
|
323
|
+
* when given.
|
|
324
|
+
*
|
|
325
|
+
* @throws {HawcxOAuthError} `issuer_mode_required` in legacy mode;
|
|
326
|
+
* `invalid_request` for a missing required option or a reserved
|
|
327
|
+
* `extraParams` key.
|
|
328
|
+
* @throws {DiscoveryError} `invalid_discovery_document` if the discovered
|
|
329
|
+
* `authorization_endpoint` is missing or insecure.
|
|
330
|
+
*/
|
|
331
|
+
buildAuthorizeUrl(options: AuthorizeUrlOptions): string;
|
|
332
|
+
/**
|
|
333
|
+
* Build the browser RP-initiated logout URL (discovery mode only; no I/O).
|
|
334
|
+
* Base is the discovered `end_session_endpoint`, with the same
|
|
335
|
+
* `customDomain` origin swap as {@link buildAuthorizeUrl}. Always sets
|
|
336
|
+
* `client_id`; `post_logout_redirect_uri`, `id_token_hint` and `state` when given.
|
|
337
|
+
*
|
|
338
|
+
* @throws {HawcxOAuthError} `issuer_mode_required` in legacy mode.
|
|
339
|
+
* @throws {DiscoveryError} `invalid_discovery_document` if the discovered
|
|
340
|
+
* `end_session_endpoint` is missing or insecure.
|
|
341
|
+
*/
|
|
342
|
+
buildEndSessionUrl(options?: EndSessionUrlOptions | null): string;
|
|
343
|
+
/**
|
|
344
|
+
* Build a browser URL from a discovered endpoint, validated lazily (so
|
|
345
|
+
* fromIssuer keeps accepting documents without it): origin (custom domain
|
|
346
|
+
* if set, else the endpoint's) + RAW path + RAW query + `&` + SDK params.
|
|
347
|
+
* The endpoint query is copied verbatim, never re-serialised, and may not
|
|
348
|
+
* carry any of `sdkNames`; an `extraKeys` name it already carries is an
|
|
349
|
+
* `invalid_request` (RFC 6749 §3.1: no repeated parameters).
|
|
350
|
+
*/
|
|
351
|
+
private browserUrl;
|
|
264
352
|
}
|
|
265
353
|
//# sourceMappingURL=oauth.d.ts.map
|
package/dist/oauth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;GAgBG;AAEH,OAAO,EAAiC,KAAK,UAAU,EAAE,MAAM,MAAM,CAAC;AACtE,OAAO,EAAE,qBAAqB,EAAyB,MAAM,oBAAoB,CAAC;
|
|
1
|
+
{"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../src/oauth.ts"],"names":[],"mappings":"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"}
|