@atproto/oauth-provider 0.1.2 → 0.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 +46 -0
- package/dist/account/account.d.ts +6 -2
- package/dist/account/account.d.ts.map +1 -1
- package/dist/assets/app/bundle-manifest.json +3 -3
- package/dist/assets/app/main.css +1 -1
- package/dist/assets/app/main.js +3 -3
- package/dist/assets/app/main.js.map +1 -1
- package/dist/assets/assets-middleware.d.ts +2 -1
- package/dist/assets/assets-middleware.d.ts.map +1 -1
- package/dist/assets/assets-middleware.js +7 -0
- package/dist/assets/assets-middleware.js.map +1 -1
- package/dist/client/client-manager.d.ts +4 -3
- package/dist/client/client-manager.d.ts.map +1 -1
- package/dist/client/client-manager.js +91 -77
- package/dist/client/client-manager.js.map +1 -1
- package/dist/client/client.d.ts +2 -3
- package/dist/client/client.d.ts.map +1 -1
- package/dist/client/client.js +6 -12
- package/dist/client/client.js.map +1 -1
- package/dist/constants.d.ts +2 -0
- package/dist/constants.d.ts.map +1 -1
- package/dist/constants.js +3 -1
- package/dist/constants.js.map +1 -1
- package/dist/device/device-manager.d.ts +1 -1
- package/dist/device/device-manager.d.ts.map +1 -1
- package/dist/device/device-manager.js +2 -2
- package/dist/device/device-manager.js.map +1 -1
- package/dist/dpop/dpop-manager.d.ts +0 -1
- package/dist/dpop/dpop-manager.d.ts.map +1 -1
- package/dist/dpop/dpop-manager.js +1 -4
- package/dist/dpop/dpop-manager.js.map +1 -1
- package/dist/errors/invalid-authorization-details-error.d.ts +4 -3
- package/dist/errors/invalid-authorization-details-error.d.ts.map +1 -1
- package/dist/errors/invalid-authorization-details-error.js +4 -4
- package/dist/errors/invalid-authorization-details-error.js.map +1 -1
- package/dist/lib/http/parser.d.ts +13 -7
- package/dist/lib/http/parser.d.ts.map +1 -1
- package/dist/lib/http/parser.js +29 -9
- package/dist/lib/http/parser.js.map +1 -1
- package/dist/lib/http/request.d.ts +8 -5
- package/dist/lib/http/request.d.ts.map +1 -1
- package/dist/lib/http/request.js +24 -12
- package/dist/lib/http/request.js.map +1 -1
- package/dist/lib/http/stream.d.ts.map +1 -1
- package/dist/lib/http/stream.js +3 -2
- package/dist/lib/http/stream.js.map +1 -1
- package/dist/metadata/build-metadata.d.ts +0 -1
- package/dist/metadata/build-metadata.d.ts.map +1 -1
- package/dist/metadata/build-metadata.js +9 -49
- package/dist/metadata/build-metadata.js.map +1 -1
- package/dist/oauth-hooks.d.ts +3 -10
- package/dist/oauth-hooks.d.ts.map +1 -1
- package/dist/oauth-provider.d.ts +10 -15
- package/dist/oauth-provider.d.ts.map +1 -1
- package/dist/oauth-provider.js +176 -114
- package/dist/oauth-provider.js.map +1 -1
- package/dist/oauth-verifier.d.ts +1 -2
- package/dist/oauth-verifier.d.ts.map +1 -1
- package/dist/oauth-verifier.js.map +1 -1
- package/dist/output/build-authorize-data.d.ts +6 -0
- package/dist/output/build-authorize-data.d.ts.map +1 -1
- package/dist/output/build-authorize-data.js +1 -0
- package/dist/output/build-authorize-data.js.map +1 -1
- package/dist/replay/replay-manager.d.ts +1 -0
- package/dist/replay/replay-manager.d.ts.map +1 -1
- package/dist/replay/replay-manager.js +3 -0
- package/dist/replay/replay-manager.js.map +1 -1
- package/dist/replay/replay-store.d.ts +1 -1
- package/dist/request/request-info.d.ts +2 -0
- package/dist/request/request-info.d.ts.map +1 -1
- package/dist/request/request-manager.d.ts +3 -9
- package/dist/request/request-manager.d.ts.map +1 -1
- package/dist/request/request-manager.js +52 -77
- package/dist/request/request-manager.js.map +1 -1
- package/dist/request/types.d.ts +10 -10
- package/dist/signer/signed-token-payload.d.ts +88 -88
- package/dist/signer/signer.d.ts +24 -31
- package/dist/signer/signer.d.ts.map +1 -1
- package/dist/signer/signer.js +0 -40
- package/dist/signer/signer.js.map +1 -1
- package/dist/token/token-claims.d.ts +84 -84
- package/dist/token/token-manager.d.ts +1 -2
- package/dist/token/token-manager.d.ts.map +1 -1
- package/dist/token/token-manager.js +10 -37
- package/dist/token/token-manager.js.map +1 -1
- package/dist/token/types.d.ts +10 -10
- package/package.json +3 -3
- package/src/account/account.ts +11 -7
- package/src/assets/app/backend-data.ts +9 -2
- package/src/assets/app/components/accept-form.tsx +65 -51
- package/src/assets/app/components/client-name.tsx +24 -16
- package/src/assets/app/components/url-viewer.tsx +3 -3
- package/src/assets/app/views/accept-view.tsx +7 -4
- package/src/assets/app/views/authorize-view.tsx +2 -1
- package/src/assets/assets-middleware.ts +14 -2
- package/src/client/client-manager.ts +124 -120
- package/src/client/client.ts +5 -17
- package/src/constants.ts +3 -0
- package/src/device/device-manager.ts +7 -1
- package/src/dpop/dpop-manager.ts +1 -6
- package/src/errors/invalid-authorization-details-error.ts +9 -4
- package/src/lib/http/parser.ts +37 -13
- package/src/lib/http/request.ts +61 -15
- package/src/lib/http/stream.ts +5 -2
- package/src/metadata/build-metadata.ts +9 -56
- package/src/oauth-hooks.ts +3 -13
- package/src/oauth-provider.ts +187 -177
- package/src/oauth-verifier.ts +1 -2
- package/src/output/build-authorize-data.ts +8 -0
- package/src/replay/replay-manager.ts +9 -0
- package/src/replay/replay-store.ts +1 -1
- package/src/request/request-info.ts +2 -0
- package/src/request/request-manager.ts +81 -107
- package/src/signer/signer.ts +0 -63
- package/src/token/token-manager.ts +8 -41
- package/dist/oidc/claims.d.ts +0 -16
- package/dist/oidc/claims.d.ts.map +0 -1
- package/dist/oidc/claims.js +0 -29
- package/dist/oidc/claims.js.map +0 -1
- package/dist/oidc/userinfo.d.ts +0 -7
- package/dist/oidc/userinfo.d.ts.map +0 -1
- package/dist/oidc/userinfo.js +0 -3
- package/dist/oidc/userinfo.js.map +0 -1
- package/dist/parameters/claims-requested.d.ts +0 -3
- package/dist/parameters/claims-requested.d.ts.map +0 -1
- package/dist/parameters/claims-requested.js +0 -77
- package/dist/parameters/claims-requested.js.map +0 -1
- package/dist/parameters/oidc-payload.d.ts +0 -31
- package/dist/parameters/oidc-payload.d.ts.map +0 -1
- package/dist/parameters/oidc-payload.js +0 -25
- package/dist/parameters/oidc-payload.js.map +0 -1
- package/src/assets/app/components/client-identifier.tsx +0 -31
- package/src/oidc/claims.ts +0 -35
- package/src/oidc/userinfo.ts +0 -11
- package/src/parameters/claims-requested.ts +0 -106
- package/src/parameters/oidc-payload.ts +0 -28
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-hooks.d.ts","sourceRoot":"","sources":["../src/oauth-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EACL,oCAAoC,EACpC,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAG9C,YAAY,EACV,OAAO,EACP,MAAM,EACN,UAAU,EACV,QAAQ,EACR,UAAU,EACV,IAAI,EACJ,oCAAoC,EACpC,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,GACnB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CACb,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE;QAAE,QAAQ,EAAE,mBAAmB,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAA;KAAE,KACjD,SAAS,CAAC,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAA;IAEtD;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC9B,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,oCAAoC,CAAA;QAChD,OAAO,EAAE,OAAO,CAAA;KACjB,KAAK,SAAS,CAAC,SAAS,GAAG,yBAAyB,CAAC,CAAA;
|
|
1
|
+
{"version":3,"file":"oauth-hooks.d.ts","sourceRoot":"","sources":["../src/oauth-hooks.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,cAAc,CAAA;AACnC,OAAO,EACL,oCAAoC,EACpC,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,EACnB,MAAM,sBAAsB,CAAA;AAE7B,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACpD,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAA;AAC3C,OAAO,EAAE,gCAAgC,EAAE,MAAM,iDAAiD,CAAA;AAClG,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAG9C,YAAY,EACV,OAAO,EACP,MAAM,EACN,UAAU,EACV,QAAQ,EACR,UAAU,EACV,gCAAgC,EAChC,IAAI,EACJ,oCAAoC,EACpC,yBAAyB,EACzB,mBAAmB,EACnB,kBAAkB,GACnB,CAAA;AAED,MAAM,MAAM,UAAU,GAAG;IACvB;;;;;;OAMG;IACH,YAAY,CAAC,EAAE,CACb,QAAQ,EAAE,QAAQ,EAClB,IAAI,EAAE;QAAE,QAAQ,EAAE,mBAAmB,CAAC;QAAC,IAAI,CAAC,EAAE,IAAI,CAAA;KAAE,KACjD,SAAS,CAAC,IAAI,GAAG,SAAS,GAAG,OAAO,CAAC,UAAU,CAAC,CAAC,CAAA;IAEtD;;;;;OAKG;IACH,sBAAsB,CAAC,EAAE,CAAC,IAAI,EAAE;QAC9B,MAAM,EAAE,MAAM,CAAA;QACd,UAAU,EAAE,oCAAoC,CAAA;QAChD,OAAO,EAAE,OAAO,CAAA;KACjB,KAAK,SAAS,CAAC,SAAS,GAAG,yBAAyB,CAAC,CAAA;CACvD,CAAA"}
|
package/dist/oauth-provider.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
/// <reference types="node" />
|
|
2
2
|
import { SimpleStore } from '@atproto-labs/simple-store';
|
|
3
|
-
import { Jwks, Keyset
|
|
4
|
-
import { AccessToken, OAuthAuthenticationRequestParameters, OAuthAuthorizationServerMetadata, OAuthClientIdentification, OAuthClientMetadata,
|
|
3
|
+
import { Jwks, Keyset } from '@atproto/jwk';
|
|
4
|
+
import { AccessToken, OAuthAuthenticationRequestParameters, OAuthAuthorizationServerMetadata, OAuthClientIdentification, OAuthClientMetadata, OAuthTokenResponse, OAuthTokenType } from '@atproto/oauth-types';
|
|
5
5
|
import { Redis, type RedisOptions } from 'ioredis';
|
|
6
6
|
import { AccountManager } from './account/account-manager.js';
|
|
7
|
-
import {
|
|
7
|
+
import { AccountStore, DeviceAccountInfo, SignInCredentials } from './account/account-store.js';
|
|
8
8
|
import { Account } from './account/account.js';
|
|
9
9
|
import { ClientAuth } from './client/client-auth.js';
|
|
10
10
|
import { ClientId } from './client/client-id.js';
|
|
@@ -18,7 +18,6 @@ import { Override } from './lib/util/type.js';
|
|
|
18
18
|
import { CustomMetadata } from './metadata/build-metadata.js';
|
|
19
19
|
import { OAuthHooks } from './oauth-hooks.js';
|
|
20
20
|
import { OAuthVerifier, OAuthVerifierOptions } from './oauth-verifier.js';
|
|
21
|
-
import { Userinfo } from './oidc/userinfo.js';
|
|
22
21
|
import { AuthorizationResultAuthorize } from './output/build-authorize-data.js';
|
|
23
22
|
import { Customization } from './output/customization.js';
|
|
24
23
|
import { AuthorizationResultRedirect } from './output/send-authorize-redirect.js';
|
|
@@ -28,7 +27,7 @@ import { RequestStore } from './request/request-store.js';
|
|
|
28
27
|
import { RequestUri } from './request/request-uri.js';
|
|
29
28
|
import { AuthorizationRequestJar, AuthorizationRequestQuery, PushedAuthorizationRequest } from './request/types.js';
|
|
30
29
|
import { TokenManager } from './token/token-manager.js';
|
|
31
|
-
import {
|
|
30
|
+
import { TokenStore } from './token/token-store.js';
|
|
32
31
|
import { CodeGrantRequest, Introspect, IntrospectionResponse, RefreshGrantRequest, Revoke, TokenRequest } from './token/types.js';
|
|
33
32
|
import { VerifyTokenClaimsOptions } from './token/verify-token-claims.js';
|
|
34
33
|
export type OAuthProviderStore = Partial<ClientStore & AccountStore & DeviceStore & TokenStore & RequestStore & ReplayStore>;
|
|
@@ -39,9 +38,7 @@ export type RouterOptions<Req extends IncomingMessage = IncomingMessage, Res ext
|
|
|
39
38
|
export type OAuthProviderOptions = Override<OAuthVerifierOptions & OAuthHooks, {
|
|
40
39
|
/**
|
|
41
40
|
* Maximum age a device/account session can be before requiring
|
|
42
|
-
* re-authentication.
|
|
43
|
-
* using the `max_age` parameter and on a client basis using the
|
|
44
|
-
* `default_max_age` client metadata.
|
|
41
|
+
* re-authentication.
|
|
45
42
|
*/
|
|
46
43
|
authenticationMaxAge?: number;
|
|
47
44
|
/**
|
|
@@ -217,7 +214,7 @@ export declare class OAuthProvider extends OAuthVerifier {
|
|
|
217
214
|
})[];
|
|
218
215
|
};
|
|
219
216
|
protected loginRequired(client: Client, parameters: OAuthAuthenticationRequestParameters, info: DeviceAccountInfo): boolean;
|
|
220
|
-
protected authenticateClient(client: Client,
|
|
217
|
+
protected authenticateClient(client: Client, credentials: OAuthClientIdentification): Promise<ClientAuth>;
|
|
221
218
|
protected decodeJAR(client: Client, input: AuthorizationRequestJar): Promise<{
|
|
222
219
|
payload: OAuthAuthenticationRequestParameters;
|
|
223
220
|
} | {
|
|
@@ -246,7 +243,10 @@ export declare class OAuthProvider extends OAuthVerifier {
|
|
|
246
243
|
consentRequired: boolean;
|
|
247
244
|
matchesHint: boolean;
|
|
248
245
|
}[]>;
|
|
249
|
-
protected signIn(deviceId: DeviceId, credentials: SignInCredentials): Promise<
|
|
246
|
+
protected signIn(deviceId: DeviceId, uri: RequestUri, clientId: ClientId, credentials: SignInCredentials): Promise<{
|
|
247
|
+
account: Account;
|
|
248
|
+
consentRequired: boolean;
|
|
249
|
+
}>;
|
|
250
250
|
protected acceptRequest(deviceId: DeviceId, uri: RequestUri, clientId: ClientId, sub: string): Promise<AuthorizationResultRedirect>;
|
|
251
251
|
protected rejectRequest(deviceId: DeviceId, uri: RequestUri, clientId: ClientId): Promise<AuthorizationResultRedirect>;
|
|
252
252
|
protected token(input: TokenRequest, dpopJkt: null | string): Promise<OAuthTokenResponse>;
|
|
@@ -260,11 +260,6 @@ export declare class OAuthProvider extends OAuthVerifier {
|
|
|
260
260
|
* @see {@link https://datatracker.ietf.org/doc/html/rfc7662#section-2.1 rfc7662}
|
|
261
261
|
*/
|
|
262
262
|
protected introspect(input: Introspect): Promise<IntrospectionResponse>;
|
|
263
|
-
/**
|
|
264
|
-
* @see {@link https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.3.2 Successful UserInfo Response}
|
|
265
|
-
*/
|
|
266
|
-
protected userinfo({ data, account }: TokenInfo): Promise<Userinfo>;
|
|
267
|
-
protected signUserinfo(userinfo: Userinfo): Promise<SignedJwt>;
|
|
268
263
|
protected authenticateToken(tokenType: OAuthTokenType, token: AccessToken, dpopJkt: string | null, verifyOptions?: VerifyTokenClaimsOptions): Promise<import("./token/verify-token-claims.js").VerifyTokenClaimsResult>;
|
|
269
264
|
/**
|
|
270
265
|
* @returns An http request handler that can be used with node's http server
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"oauth-provider.d.ts","sourceRoot":"","sources":["../src/oauth-provider.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,WAAW,EAAE,MAAM,4BAA4B,CAAA;AAExD,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"oauth-provider.d.ts","sourceRoot":"","sources":["../src/oauth-provider.ts"],"names":[],"mappings":"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"}
|
package/dist/oauth-provider.js
CHANGED
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
2
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
26
|
exports.OAuthProvider = exports.Keyset = void 0;
|
|
4
27
|
const fetch_node_1 = require("@atproto-labs/fetch-node");
|
|
@@ -6,7 +29,7 @@ const simple_store_memory_1 = require("@atproto-labs/simple-store-memory");
|
|
|
6
29
|
const jwk_1 = require("@atproto/jwk");
|
|
7
30
|
Object.defineProperty(exports, "Keyset", { enumerable: true, get: function () { return jwk_1.Keyset; } });
|
|
8
31
|
const oauth_types_1 = require("@atproto/oauth-types");
|
|
9
|
-
const zod_1 = require("zod");
|
|
32
|
+
const zod_1 = __importStar(require("zod"));
|
|
10
33
|
const access_token_type_js_1 = require("./access-token/access-token-type.js");
|
|
11
34
|
const account_manager_js_1 = require("./account/account-manager.js");
|
|
12
35
|
const account_store_js_1 = require("./account/account-store.js");
|
|
@@ -36,7 +59,6 @@ const oauth_verifier_js_1 = require("./oauth-verifier.js");
|
|
|
36
59
|
const build_error_payload_js_1 = require("./output/build-error-payload.js");
|
|
37
60
|
const output_manager_js_1 = require("./output/output-manager.js");
|
|
38
61
|
const send_authorize_redirect_js_1 = require("./output/send-authorize-redirect.js");
|
|
39
|
-
const oidc_payload_js_1 = require("./parameters/oidc-payload.js");
|
|
40
62
|
const replay_store_js_1 = require("./replay/replay-store.js");
|
|
41
63
|
const request_manager_js_1 = require("./request/request-manager.js");
|
|
42
64
|
const request_store_memory_js_1 = require("./request/request-store-memory.js");
|
|
@@ -79,7 +101,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
79
101
|
this.customization = customization;
|
|
80
102
|
this.deviceStore = deviceStore;
|
|
81
103
|
this.accountManager = new account_manager_js_1.AccountManager(accountStore);
|
|
82
|
-
this.clientManager = new client_manager_js_1.ClientManager(this.keyset, rest, clientStore || null, loopbackMetadata || null, safeFetch, clientJwksCache, clientMetadataCache);
|
|
104
|
+
this.clientManager = new client_manager_js_1.ClientManager(this.metadata, this.keyset, rest, clientStore || null, loopbackMetadata || null, safeFetch, clientJwksCache, clientMetadataCache);
|
|
83
105
|
this.requestManager = new request_manager_js_1.RequestManager(requestStore, this.signer, this.metadata, rest);
|
|
84
106
|
this.tokenManager = new token_manager_js_1.TokenManager(tokenStore, this.signer, rest, this.accessTokenType, tokenMaxAge);
|
|
85
107
|
}
|
|
@@ -93,17 +115,12 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
93
115
|
if (!Number.isFinite(authAge) || authAge < 0) {
|
|
94
116
|
return true;
|
|
95
117
|
}
|
|
96
|
-
|
|
97
|
-
const maxAge = parameters.max_age ?? client.metadata.default_max_age;
|
|
98
|
-
if (maxAge != null && maxAge < this.authenticationMaxAge) {
|
|
99
|
-
return authAge >= maxAge;
|
|
100
|
-
}
|
|
101
|
-
else {
|
|
102
|
-
return authAge >= this.authenticationMaxAge;
|
|
103
|
-
}
|
|
118
|
+
return authAge >= this.authenticationMaxAge;
|
|
104
119
|
}
|
|
105
|
-
async authenticateClient(client,
|
|
106
|
-
const { clientAuth, nonce } = await client.verifyCredentials(credentials,
|
|
120
|
+
async authenticateClient(client, credentials) {
|
|
121
|
+
const { clientAuth, nonce } = await client.verifyCredentials(credentials, {
|
|
122
|
+
audience: this.issuer,
|
|
123
|
+
});
|
|
107
124
|
if (nonce != null) {
|
|
108
125
|
const unique = await this.replayManager.uniqueAuth(nonce, client.id);
|
|
109
126
|
if (!unique) {
|
|
@@ -145,7 +162,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
145
162
|
async pushedAuthorizationRequest(input, dpopJkt) {
|
|
146
163
|
try {
|
|
147
164
|
const client = await this.clientManager.getClient(input.client_id);
|
|
148
|
-
const clientAuth = await this.authenticateClient(client,
|
|
165
|
+
const clientAuth = await this.authenticateClient(client, input);
|
|
149
166
|
const { payload: parameters } = 'request' in input // Handle JAR
|
|
150
167
|
? await this.decodeJAR(client, input)
|
|
151
168
|
: { payload: input };
|
|
@@ -220,8 +237,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
220
237
|
if (ssoSession.consentRequired) {
|
|
221
238
|
throw new consent_required_error_js_1.ConsentRequiredError(parameters);
|
|
222
239
|
}
|
|
223
|
-
const
|
|
224
|
-
return { issuer, client, parameters, redirect };
|
|
240
|
+
const code = await this.requestManager.setAuthorized(client, uri, deviceId, ssoSession.account);
|
|
241
|
+
return { issuer, client, parameters, redirect: { code } };
|
|
225
242
|
}
|
|
226
243
|
// Automatic SSO when a did was provided
|
|
227
244
|
if (parameters.prompt == null && parameters.login_hint != null) {
|
|
@@ -229,8 +246,8 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
229
246
|
if (ssoSessions.length === 1) {
|
|
230
247
|
const ssoSession = ssoSessions[0];
|
|
231
248
|
if (!ssoSession.loginRequired && !ssoSession.consentRequired) {
|
|
232
|
-
const
|
|
233
|
-
return { issuer, client, parameters, redirect };
|
|
249
|
+
const code = await this.requestManager.setAuthorized(client, uri, deviceId, ssoSession.account);
|
|
250
|
+
return { issuer, client, parameters, redirect: { code } };
|
|
234
251
|
}
|
|
235
252
|
}
|
|
236
253
|
}
|
|
@@ -238,7 +255,20 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
238
255
|
issuer,
|
|
239
256
|
client,
|
|
240
257
|
parameters,
|
|
241
|
-
authorize: {
|
|
258
|
+
authorize: {
|
|
259
|
+
uri,
|
|
260
|
+
sessions,
|
|
261
|
+
scopeDetails: parameters.scope
|
|
262
|
+
?.split(/\s+/)
|
|
263
|
+
.filter(Boolean)
|
|
264
|
+
.sort((a, b) => a.localeCompare(b))
|
|
265
|
+
.map((scope) => ({
|
|
266
|
+
scope,
|
|
267
|
+
// @TODO Allow to customize the scope descriptions (e.g.
|
|
268
|
+
// using a hook)
|
|
269
|
+
description: undefined,
|
|
270
|
+
})),
|
|
271
|
+
},
|
|
242
272
|
};
|
|
243
273
|
}
|
|
244
274
|
catch (err) {
|
|
@@ -278,12 +308,28 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
278
308
|
loginRequired: parameters.prompt === 'login' ||
|
|
279
309
|
this.loginRequired(client, parameters, info),
|
|
280
310
|
consentRequired: parameters.prompt === 'consent' ||
|
|
311
|
+
// @TODO the "authorizedClients" should also include the scopes that
|
|
312
|
+
// were already authorized for the client. Otherwise a client could
|
|
313
|
+
// use silent authentication to get additional scopes without consent.
|
|
281
314
|
!info.authorizedClients.includes(client.id),
|
|
282
315
|
matchesHint: hint == null || matchesHint(account),
|
|
283
316
|
}));
|
|
284
317
|
}
|
|
285
|
-
async signIn(deviceId, credentials) {
|
|
286
|
-
|
|
318
|
+
async signIn(deviceId, uri, clientId, credentials) {
|
|
319
|
+
const client = await this.clientManager.getClient(clientId);
|
|
320
|
+
// Ensure the request is still valid (and update the request expiration)
|
|
321
|
+
// @TODO use the returned scopes to determine if consent is required
|
|
322
|
+
await this.requestManager.get(uri, clientId, deviceId);
|
|
323
|
+
const { account, info } = await this.accountManager.signIn(credentials, deviceId);
|
|
324
|
+
return {
|
|
325
|
+
account,
|
|
326
|
+
consentRequired: client.info.isFirstParty
|
|
327
|
+
? false
|
|
328
|
+
: // @TODO: the "authorizedClients" should also include the scopes that
|
|
329
|
+
// were already authorized for the client. Otherwise a client could
|
|
330
|
+
// use silent authentication to get additional scopes without consent.
|
|
331
|
+
!info.authorizedClients.includes(client.id),
|
|
332
|
+
};
|
|
287
333
|
}
|
|
288
334
|
async acceptRequest(deviceId, uri, clientId, sub) {
|
|
289
335
|
const { issuer } = this;
|
|
@@ -296,9 +342,9 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
296
342
|
if (this.loginRequired(client, parameters, info)) {
|
|
297
343
|
throw new login_required_error_js_1.LoginRequiredError(parameters, 'Account authentication required.');
|
|
298
344
|
}
|
|
299
|
-
const
|
|
345
|
+
const code = await this.requestManager.setAuthorized(client, uri, deviceId, account);
|
|
300
346
|
await this.accountManager.addAuthorizedClient(deviceId, account, client, clientAuth);
|
|
301
|
-
return { issuer, client, parameters, redirect };
|
|
347
|
+
return { issuer, client, parameters, redirect: { code } };
|
|
302
348
|
}
|
|
303
349
|
catch (err) {
|
|
304
350
|
await this.deleteRequest(uri, parameters);
|
|
@@ -335,7 +381,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
335
381
|
}
|
|
336
382
|
async token(input, dpopJkt) {
|
|
337
383
|
const client = await this.clientManager.getClient(input.client_id);
|
|
338
|
-
const clientAuth = await this.authenticateClient(client,
|
|
384
|
+
const clientAuth = await this.authenticateClient(client, input);
|
|
339
385
|
if (!client.metadata.grant_types.includes(input.grant_type)) {
|
|
340
386
|
throw new invalid_grant_error_js_1.InvalidGrantError(`"${input.grant_type}" grant type is not allowed for this client`);
|
|
341
387
|
}
|
|
@@ -352,6 +398,26 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
352
398
|
async codeGrant(client, clientAuth, input, dpopJkt) {
|
|
353
399
|
try {
|
|
354
400
|
const { sub, deviceId, parameters } = await this.requestManager.findCode(client, clientAuth, input.code);
|
|
401
|
+
// the following check prevents re-use of PKCE challenges, enforcing the
|
|
402
|
+
// clients to generate a new challenge for each authorization request. The
|
|
403
|
+
// replay manager typically prevents replay over a certain time frame,
|
|
404
|
+
// which might not cover the entire lifetime of the token (depending on
|
|
405
|
+
// the implementation of the replay store). For this reason, we should
|
|
406
|
+
// ideally ensure that the code_challenge was not already used by any
|
|
407
|
+
// existing token or any other pending request.
|
|
408
|
+
//
|
|
409
|
+
// The current implementation will cause client devs not issuing a new
|
|
410
|
+
// code challenge for each authorization request to fail, which should be
|
|
411
|
+
// a good enough incentive to follow the best practices, until we have a
|
|
412
|
+
// better implementation.
|
|
413
|
+
//
|
|
414
|
+
// @TODO: Use tokenManager to ensure uniqueness of code_challenge
|
|
415
|
+
if (parameters.code_challenge) {
|
|
416
|
+
const unique = await this.replayManager.uniqueCodeChallenge(parameters.code_challenge);
|
|
417
|
+
if (!unique) {
|
|
418
|
+
throw new invalid_grant_error_js_1.InvalidGrantError('code_challenge', 'Code challenge already used');
|
|
419
|
+
}
|
|
420
|
+
}
|
|
355
421
|
const { account, info } = await this.accountManager.get(deviceId, sub);
|
|
356
422
|
return await this.tokenManager.create(client, clientAuth, account, { id: deviceId, info }, parameters, input, dpopJkt);
|
|
357
423
|
}
|
|
@@ -380,7 +446,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
380
446
|
*/
|
|
381
447
|
async introspect(input) {
|
|
382
448
|
const client = await this.clientManager.getClient(input.client_id);
|
|
383
|
-
const clientAuth = await this.authenticateClient(client,
|
|
449
|
+
const clientAuth = await this.authenticateClient(client, input);
|
|
384
450
|
// RFC7662 states the following:
|
|
385
451
|
//
|
|
386
452
|
// > To prevent token scanning attacks, the endpoint MUST also require some
|
|
@@ -418,24 +484,6 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
418
484
|
};
|
|
419
485
|
}
|
|
420
486
|
}
|
|
421
|
-
/**
|
|
422
|
-
* @see {@link https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.3.2 Successful UserInfo Response}
|
|
423
|
-
*/
|
|
424
|
-
async userinfo({ data, account }) {
|
|
425
|
-
return {
|
|
426
|
-
...(0, oidc_payload_js_1.oidcPayload)(data.parameters, account),
|
|
427
|
-
sub: account.sub,
|
|
428
|
-
client_id: data.clientId,
|
|
429
|
-
username: account.preferred_username,
|
|
430
|
-
};
|
|
431
|
-
}
|
|
432
|
-
async signUserinfo(userinfo) {
|
|
433
|
-
const client = await this.clientManager.getClient(userinfo.client_id);
|
|
434
|
-
return this.signer.sign({
|
|
435
|
-
alg: client.metadata.userinfo_signed_response_alg,
|
|
436
|
-
typ: 'JWT',
|
|
437
|
-
}, userinfo);
|
|
438
|
-
}
|
|
439
487
|
async authenticateToken(tokenType, token, dpopJkt, verifyOptions) {
|
|
440
488
|
if ((0, token_id_js_1.isTokenId)(token)) {
|
|
441
489
|
this.assertTokenTypeAllowed(tokenType, access_token_type_js_1.AccessTokenType.id);
|
|
@@ -469,6 +517,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
469
517
|
const staticJson = (json) => (0, index_js_1.combineMiddlewares)([
|
|
470
518
|
function (req, res, next) {
|
|
471
519
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
520
|
+
res.setHeader('Access-Control-Allow-Headers', '*');
|
|
472
521
|
res.setHeader('Cache-Control', 'max-age=300');
|
|
473
522
|
next();
|
|
474
523
|
},
|
|
@@ -480,6 +529,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
480
529
|
*/
|
|
481
530
|
const jsonHandler = (buildJson, status) => async function (req, res) {
|
|
482
531
|
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
532
|
+
res.setHeader('Access-Control-Allow-Headers', '*');
|
|
483
533
|
// https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
|
|
484
534
|
res.setHeader('Cache-Control', 'no-store');
|
|
485
535
|
res.setHeader('Pragma', 'no-cache');
|
|
@@ -517,10 +567,13 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
517
567
|
}
|
|
518
568
|
};
|
|
519
569
|
const navigationHandler = (handler) => async function (req, res) {
|
|
570
|
+
res.setHeader('Access-Control-Allow-Origin', '*');
|
|
571
|
+
res.setHeader('Access-Control-Allow-Headers', '*');
|
|
520
572
|
res.setHeader('Cache-Control', 'no-store');
|
|
521
573
|
res.setHeader('Pragma', 'no-cache');
|
|
522
574
|
try {
|
|
523
575
|
(0, index_js_1.validateFetchMode)(req, res, ['navigate']);
|
|
576
|
+
(0, index_js_1.validateFetchDest)(req, res, ['document']);
|
|
524
577
|
(0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
|
|
525
578
|
await handler.call(this, req, res);
|
|
526
579
|
// Should never happen (fool proofing)
|
|
@@ -536,48 +589,56 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
536
589
|
}
|
|
537
590
|
};
|
|
538
591
|
//- Public OAuth endpoints
|
|
539
|
-
/*
|
|
540
|
-
* Although OpenID compatibility is not required to implement the Atproto
|
|
541
|
-
* OAuth2 specification, we do support OIDC discovery in this
|
|
542
|
-
* implementation as we believe this may:
|
|
543
|
-
* 1) Make the implementation of Atproto clients easier (since lots of
|
|
544
|
-
* libraries support OIDC discovery)
|
|
545
|
-
* 2) Allow self hosted PDS' to not implement authentication themselves
|
|
546
|
-
* but rely on a trusted Atproto actor to act as their OIDC providers.
|
|
547
|
-
* By supporting OIDC in the current implementation, Bluesky's
|
|
548
|
-
* Authorization Server server can be used as an OIDC provider for
|
|
549
|
-
* these users.
|
|
550
|
-
*/
|
|
551
|
-
router.get('/.well-known/openid-configuration', staticJson(server.metadata));
|
|
552
592
|
router.get('/.well-known/oauth-authorization-server', staticJson(server.metadata));
|
|
553
593
|
// CORS preflight
|
|
554
|
-
|
|
594
|
+
const corsPreflight = function (req, res, _next) {
|
|
555
595
|
res
|
|
556
596
|
.writeHead(204, {
|
|
557
|
-
|
|
558
|
-
|
|
597
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Origin
|
|
598
|
+
//
|
|
599
|
+
// > For requests without credentials, the literal value "*" can be
|
|
600
|
+
// > specified as a wildcard; the value tells browsers to allow
|
|
601
|
+
// > requesting code from any origin to access the resource.
|
|
602
|
+
// > Attempting to use the wildcard with credentials results in an
|
|
603
|
+
// > error.
|
|
604
|
+
//
|
|
605
|
+
// A "*" is safer to use than reflecting the request origin.
|
|
606
|
+
'Access-Control-Allow-Origin': '*',
|
|
607
|
+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Access-Control-Allow-Methods
|
|
608
|
+
// > The value "*" only counts as a special wildcard value for
|
|
609
|
+
// > requests without credentials (requests without HTTP cookies or
|
|
610
|
+
// > HTTP authentication information). In requests with credentials,
|
|
611
|
+
// > it is treated as the literal method name "*" without special
|
|
612
|
+
// > semantics.
|
|
613
|
+
'Access-Control-Allow-Methods': '*',
|
|
559
614
|
'Access-Control-Allow-Headers': 'Content-Type,Authorization,DPoP',
|
|
560
615
|
'Access-Control-Max-Age': '86400', // 1 day
|
|
561
616
|
})
|
|
562
617
|
.end();
|
|
563
|
-
}
|
|
618
|
+
};
|
|
564
619
|
router.get('/oauth/jwks', staticJson(server.jwks));
|
|
620
|
+
router.options('/oauth/par', corsPreflight);
|
|
565
621
|
router.post('/oauth/par', jsonHandler(async function (req, _res) {
|
|
566
|
-
const input = await (
|
|
622
|
+
const input = await validateRequest(req, types_js_1.pushedAuthorizationRequestSchema);
|
|
567
623
|
const dpopJkt = await server.checkDpopProof(req.headers['dpop'], req.method, this.url);
|
|
568
624
|
return server.pushedAuthorizationRequest(input, dpopJkt);
|
|
569
625
|
}, 201));
|
|
570
626
|
// https://datatracker.ietf.org/doc/html/rfc9126#section-2.3
|
|
571
|
-
|
|
627
|
+
// > If the request did not use the POST method, the authorization server
|
|
628
|
+
// > responds with an HTTP 405 (Method Not Allowed) status code.
|
|
629
|
+
router.options('/oauth/par', corsPreflight);
|
|
630
|
+
router.all('/oauth/par', (req, res) => {
|
|
572
631
|
res.writeHead(405).end();
|
|
573
632
|
});
|
|
633
|
+
router.options('/oauth/token', corsPreflight);
|
|
574
634
|
router.post('/oauth/token', jsonHandler(async function (req, _res) {
|
|
575
|
-
const input = await (
|
|
635
|
+
const input = await validateRequest(req, types_js_2.tokenRequestSchema);
|
|
576
636
|
const dpopJkt = await server.checkDpopProof(req.headers['dpop'], req.method, this.url);
|
|
577
637
|
return server.token(input, dpopJkt);
|
|
578
638
|
}));
|
|
639
|
+
router.options('/oauth/revoke', corsPreflight);
|
|
579
640
|
router.post('/oauth/revoke', jsonHandler(async function (req, res) {
|
|
580
|
-
const input = await (
|
|
641
|
+
const input = await validateRequest(req, types_js_2.revokeSchema);
|
|
581
642
|
try {
|
|
582
643
|
await server.revoke(input);
|
|
583
644
|
}
|
|
@@ -585,6 +646,7 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
585
646
|
onError?.(req, res, err, 'Failed to revoke token');
|
|
586
647
|
}
|
|
587
648
|
}));
|
|
649
|
+
router.options('/oauth/revoke', corsPreflight);
|
|
588
650
|
router.get('/oauth/revoke', navigationHandler(async function (req, res) {
|
|
589
651
|
const query = Object.fromEntries(this.url.searchParams);
|
|
590
652
|
const input = types_js_2.revokeSchema.parse(query, { path: ['query'] });
|
|
@@ -599,45 +661,13 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
599
661
|
throw new Error('You are successfully logged out. Redirect not implemented');
|
|
600
662
|
}));
|
|
601
663
|
router.post('/oauth/introspect', jsonHandler(async function (req, _res) {
|
|
602
|
-
const input = await (
|
|
664
|
+
const input = await validateRequest(req, types_js_2.introspectSchema);
|
|
603
665
|
return server.introspect(input);
|
|
604
666
|
}));
|
|
605
|
-
const userinfoBodySchema = zod_1.z.object({
|
|
606
|
-
access_token: jwk_1.signedJwtSchema.optional(),
|
|
607
|
-
});
|
|
608
|
-
router.addRoute(['GET', 'POST'], '/oauth/userinfo', (0, index_js_1.acceptMiddleware)(async function (req, _res) {
|
|
609
|
-
const body = req.method === 'POST'
|
|
610
|
-
? await (0, index_js_1.validateRequestPayload)(req, userinfoBodySchema)
|
|
611
|
-
: null;
|
|
612
|
-
if (body?.access_token && req.headers['authorization']) {
|
|
613
|
-
throw new invalid_request_error_js_1.InvalidRequestError('access token must be provided in either the authorization header or the request body');
|
|
614
|
-
}
|
|
615
|
-
const auth = await server.authenticateRequest(req.method, this.url, body?.access_token // Allow credentials to be parsed from body.
|
|
616
|
-
? {
|
|
617
|
-
authorization: `Bearer ${body.access_token}`,
|
|
618
|
-
dpop: undefined, // DPoP can only be used with headers
|
|
619
|
-
}
|
|
620
|
-
: req.headers, {
|
|
621
|
-
scope: ['profile'],
|
|
622
|
-
});
|
|
623
|
-
const tokenInfo = 'tokenInfo' in auth
|
|
624
|
-
? auth.tokenInfo
|
|
625
|
-
: await server.tokenManager.getTokenInfo(auth.tokenType, auth.tokenId);
|
|
626
|
-
return server.userinfo(tokenInfo);
|
|
627
|
-
}, {
|
|
628
|
-
'': 'application/json',
|
|
629
|
-
'application/json': jsonHandler(async function (_req, _res) {
|
|
630
|
-
return this.data;
|
|
631
|
-
}),
|
|
632
|
-
'application/jwt': jsonHandler(async function (_req, res) {
|
|
633
|
-
const jwt = await server.signUserinfo(this.data);
|
|
634
|
-
res.writeHead(200, { 'Content-Type': 'application/jwt' }).end(jwt);
|
|
635
|
-
return undefined;
|
|
636
|
-
}),
|
|
637
|
-
}));
|
|
638
667
|
//- Private authorization endpoints
|
|
639
668
|
router.use((0, assets_middleware_js_1.authorizeAssetsMiddleware)());
|
|
640
669
|
router.get('/oauth/authorize', navigationHandler(async function (req, res) {
|
|
670
|
+
(0, index_js_1.validateFetchSite)(req, res, ['cross-site', 'none']);
|
|
641
671
|
const query = Object.fromEntries(this.url.searchParams);
|
|
642
672
|
const input = await types_js_1.authorizationRequestQuerySchema.parseAsync(query, {
|
|
643
673
|
path: ['query'],
|
|
@@ -658,37 +688,43 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
658
688
|
}
|
|
659
689
|
}
|
|
660
690
|
}));
|
|
661
|
-
const signInPayloadSchema = zod_1.
|
|
662
|
-
csrf_token: zod_1.
|
|
691
|
+
const signInPayloadSchema = zod_1.default.object({
|
|
692
|
+
csrf_token: zod_1.default.string(),
|
|
663
693
|
request_uri: request_uri_js_1.requestUriSchema,
|
|
664
694
|
client_id: client_id_js_1.clientIdSchema,
|
|
665
695
|
credentials: account_store_js_1.signInCredentialsSchema,
|
|
666
696
|
});
|
|
697
|
+
router.options('/oauth/authorize/sign-in', corsPreflight);
|
|
667
698
|
router.post('/oauth/authorize/sign-in', jsonHandler(async function (req, res) {
|
|
668
699
|
(0, index_js_1.validateFetchMode)(req, res, ['same-origin']);
|
|
700
|
+
(0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
|
|
669
701
|
(0, index_js_1.validateSameOrigin)(req, res, issuerOrigin);
|
|
670
|
-
const input = await (
|
|
702
|
+
const input = await validateRequest(req, signInPayloadSchema);
|
|
671
703
|
(0, index_js_1.validateReferer)(req, res, {
|
|
672
704
|
origin: issuerOrigin,
|
|
673
705
|
pathname: '/oauth/authorize',
|
|
674
706
|
});
|
|
675
707
|
(0, index_js_1.validateCsrfToken)(req, res, input.csrf_token, csrfCookie(input.request_uri));
|
|
676
|
-
const { deviceId } = await deviceManager.load(req, res);
|
|
677
|
-
|
|
678
|
-
// Prevent fixation attacks
|
|
679
|
-
await deviceManager.rotate(req, res, deviceId);
|
|
680
|
-
return {
|
|
681
|
-
account,
|
|
682
|
-
consentRequired: !info.authorizedClients.includes(input.client_id),
|
|
683
|
-
};
|
|
708
|
+
const { deviceId } = await deviceManager.load(req, res, true);
|
|
709
|
+
return server.signIn(deviceId, input.request_uri, input.client_id, input.credentials);
|
|
684
710
|
}));
|
|
685
|
-
const acceptQuerySchema = zod_1.
|
|
686
|
-
csrf_token: zod_1.
|
|
711
|
+
const acceptQuerySchema = zod_1.default.object({
|
|
712
|
+
csrf_token: zod_1.default.string(),
|
|
687
713
|
request_uri: request_uri_js_1.requestUriSchema,
|
|
688
714
|
client_id: client_id_js_1.clientIdSchema,
|
|
689
|
-
account_sub: zod_1.
|
|
715
|
+
account_sub: zod_1.default.string(),
|
|
690
716
|
});
|
|
717
|
+
// Though this is a "no-cors" request, meaning that the browser will allow
|
|
718
|
+
// any cross-origin request, with credentials, to be sent, the handler will
|
|
719
|
+
// 1) validate the request origin,
|
|
720
|
+
// 2) validate the CSRF token,
|
|
721
|
+
// 3) validate the referer,
|
|
722
|
+
// 4) validate the sec-fetch-site header,
|
|
723
|
+
// 4) validate the sec-fetch-mode header,
|
|
724
|
+
// 5) validate the sec-fetch-dest header (see navigationHandler).
|
|
725
|
+
// And will error if any of these checks fail.
|
|
691
726
|
router.get('/oauth/authorize/accept', navigationHandler(async function (req, res) {
|
|
727
|
+
(0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
|
|
692
728
|
const query = Object.fromEntries(this.url.searchParams);
|
|
693
729
|
const input = await acceptQuerySchema.parseAsync(query, {
|
|
694
730
|
path: ['query'],
|
|
@@ -706,12 +742,22 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
706
742
|
const data = await server.acceptRequest(deviceId, input.request_uri, input.client_id, input.account_sub);
|
|
707
743
|
return await (0, send_authorize_redirect_js_1.sendAuthorizeRedirect)(res, data);
|
|
708
744
|
}));
|
|
709
|
-
const rejectQuerySchema = zod_1.
|
|
710
|
-
csrf_token: zod_1.
|
|
745
|
+
const rejectQuerySchema = zod_1.default.object({
|
|
746
|
+
csrf_token: zod_1.default.string(),
|
|
711
747
|
request_uri: request_uri_js_1.requestUriSchema,
|
|
712
748
|
client_id: client_id_js_1.clientIdSchema,
|
|
713
749
|
});
|
|
750
|
+
// Though this is a "no-cors" request, meaning that the browser will allow
|
|
751
|
+
// any cross-origin request, with credentials, to be sent, the handler will
|
|
752
|
+
// 1) validate the request origin,
|
|
753
|
+
// 2) validate the CSRF token,
|
|
754
|
+
// 3) validate the referer,
|
|
755
|
+
// 4) validate the sec-fetch-site header,
|
|
756
|
+
// 4) validate the sec-fetch-mode header,
|
|
757
|
+
// 5) validate the sec-fetch-dest header (see navigationHandler).
|
|
758
|
+
// And will error if any of these checks fail.
|
|
714
759
|
router.get('/oauth/authorize/reject', navigationHandler(async function (req, res) {
|
|
760
|
+
(0, index_js_1.validateFetchSite)(req, res, ['same-origin']);
|
|
715
761
|
const query = Object.fromEntries(this.url.searchParams);
|
|
716
762
|
const input = await rejectQuerySchema.parseAsync(query, {
|
|
717
763
|
path: ['query'],
|
|
@@ -733,4 +779,20 @@ class OAuthProvider extends oauth_verifier_js_1.OAuthVerifier {
|
|
|
733
779
|
}
|
|
734
780
|
}
|
|
735
781
|
exports.OAuthProvider = OAuthProvider;
|
|
782
|
+
async function validateRequest(req, schema) {
|
|
783
|
+
try {
|
|
784
|
+
return await (0, index_js_1.validateRequestPayload)(req, schema);
|
|
785
|
+
}
|
|
786
|
+
catch (err) {
|
|
787
|
+
if (err instanceof zod_1.ZodError) {
|
|
788
|
+
const issue = err.issues[0];
|
|
789
|
+
if (issue?.path.length) {
|
|
790
|
+
// "part" will typically be
|
|
791
|
+
const [part, ...path] = issue.path;
|
|
792
|
+
throw new invalid_request_error_js_1.InvalidRequestError(`Validation of ${part}'s "${path.join('.')}" with error: ${issue.message}`, err);
|
|
793
|
+
}
|
|
794
|
+
}
|
|
795
|
+
throw new invalid_request_error_js_1.InvalidRequestError('Input validation error', err);
|
|
796
|
+
}
|
|
797
|
+
}
|
|
736
798
|
//# sourceMappingURL=oauth-provider.js.map
|