@atcute/oauth-types 0.1.0 → 1.0.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/README.md +6 -5
- package/dist/build-client-metadata.d.ts +18 -160
- package/dist/build-client-metadata.d.ts.map +1 -1
- package/dist/build-client-metadata.js +73 -3
- package/dist/build-client-metadata.js.map +1 -1
- package/dist/index.d.ts +31 -30
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/dist/index.js.map +1 -1
- package/dist/schemas/atcute-client-shared.d.ts +8 -0
- package/dist/schemas/atcute-client-shared.d.ts.map +1 -0
- package/dist/schemas/atcute-client-shared.js +15 -0
- package/dist/schemas/atcute-client-shared.js.map +1 -0
- package/dist/schemas/atcute-confidential-client-metadata.d.ts +228 -4
- package/dist/schemas/atcute-confidential-client-metadata.d.ts.map +1 -1
- package/dist/schemas/atcute-confidential-client-metadata.js +48 -88
- package/dist/schemas/atcute-confidential-client-metadata.js.map +1 -1
- package/dist/schemas/atcute-public-client-metadata.d.ts +95 -0
- package/dist/schemas/atcute-public-client-metadata.d.ts.map +1 -0
- package/dist/schemas/atcute-public-client-metadata.js +74 -0
- package/dist/schemas/atcute-public-client-metadata.js.map +1 -0
- package/dist/schemas/atproto-authorization-server-metadata.d.ts +786 -4
- package/dist/schemas/atproto-authorization-server-metadata.d.ts.map +1 -1
- package/dist/schemas/atproto-authorization-server-metadata.js +2 -18
- package/dist/schemas/atproto-authorization-server-metadata.js.map +1 -1
- package/dist/schemas/atproto-oauth-scope.d.ts +3 -3
- package/dist/schemas/atproto-oauth-scope.d.ts.map +1 -1
- package/dist/schemas/atproto-oauth-scope.js +2 -2
- package/dist/schemas/atproto-oauth-scope.js.map +1 -1
- package/dist/schemas/atproto-oauth-token-response.d.ts +17 -17
- package/dist/schemas/atproto-oauth-token-response.d.ts.map +1 -1
- package/dist/schemas/atproto-oauth-token-response.js +6 -6
- package/dist/schemas/atproto-oauth-token-response.js.map +1 -1
- package/dist/schemas/atproto-protected-resource-metadata.d.ts +100 -4
- package/dist/schemas/atproto-protected-resource-metadata.d.ts.map +1 -1
- package/dist/schemas/atproto-protected-resource-metadata.js +2 -11
- package/dist/schemas/atproto-protected-resource-metadata.js.map +1 -1
- package/dist/schemas/jwk.d.ts +4289 -42
- package/dist/schemas/jwk.d.ts.map +1 -1
- package/dist/schemas/jwk.js +58 -91
- package/dist/schemas/jwk.js.map +1 -1
- package/dist/schemas/jwks.d.ts +87 -42
- package/dist/schemas/jwks.d.ts.map +1 -1
- package/dist/schemas/jwks.js +13 -29
- package/dist/schemas/jwks.js.map +1 -1
- package/dist/schemas/oauth-authorization-details.d.ts +18 -18
- package/dist/schemas/oauth-authorization-details.d.ts.map +1 -1
- package/dist/schemas/oauth-authorization-details.js +7 -7
- package/dist/schemas/oauth-authorization-details.js.map +1 -1
- package/dist/schemas/oauth-authorization-server-metadata.d.ts +462 -48
- package/dist/schemas/oauth-authorization-server-metadata.d.ts.map +1 -1
- package/dist/schemas/oauth-authorization-server-metadata.js +46 -65
- package/dist/schemas/oauth-authorization-server-metadata.js.map +1 -1
- package/dist/schemas/oauth-client-id-discoverable.d.ts +2 -2
- package/dist/schemas/oauth-client-id-discoverable.d.ts.map +1 -1
- package/dist/schemas/oauth-client-id-discoverable.js +20 -22
- package/dist/schemas/oauth-client-id-discoverable.js.map +1 -1
- package/dist/schemas/oauth-client-id.d.ts +3 -3
- package/dist/schemas/oauth-client-id.d.ts.map +1 -1
- package/dist/schemas/oauth-client-id.js +2 -2
- package/dist/schemas/oauth-client-id.js.map +1 -1
- package/dist/schemas/oauth-client-metadata.d.ts +73 -51
- package/dist/schemas/oauth-client-metadata.d.ts.map +1 -1
- package/dist/schemas/oauth-client-metadata.js +33 -40
- package/dist/schemas/oauth-client-metadata.js.map +1 -1
- package/dist/schemas/oauth-code-challenge-method.d.ts +3 -3
- package/dist/schemas/oauth-code-challenge-method.d.ts.map +1 -1
- package/dist/schemas/oauth-code-challenge-method.js +2 -2
- package/dist/schemas/oauth-code-challenge-method.js.map +1 -1
- package/dist/schemas/oauth-endpoint-auth-method.d.ts +3 -3
- package/dist/schemas/oauth-endpoint-auth-method.d.ts.map +1 -1
- package/dist/schemas/oauth-endpoint-auth-method.js +10 -2
- package/dist/schemas/oauth-endpoint-auth-method.js.map +1 -1
- package/dist/schemas/oauth-grant-type.d.ts +3 -3
- package/dist/schemas/oauth-grant-type.d.ts.map +1 -1
- package/dist/schemas/oauth-grant-type.js +10 -3
- package/dist/schemas/oauth-grant-type.js.map +1 -1
- package/dist/schemas/oauth-issuer-identifier.d.ts +3 -3
- package/dist/schemas/oauth-issuer-identifier.d.ts.map +1 -1
- package/dist/schemas/oauth-issuer-identifier.js +16 -9
- package/dist/schemas/oauth-issuer-identifier.js.map +1 -1
- package/dist/schemas/oauth-par-response.d.ts +5 -5
- package/dist/schemas/oauth-par-response.d.ts.map +1 -1
- package/dist/schemas/oauth-par-response.js +3 -3
- package/dist/schemas/oauth-par-response.js.map +1 -1
- package/dist/schemas/oauth-prompt.d.ts +3 -3
- package/dist/schemas/oauth-prompt.d.ts.map +1 -1
- package/dist/schemas/oauth-prompt.js +2 -2
- package/dist/schemas/oauth-prompt.js.map +1 -1
- package/dist/schemas/oauth-protected-resource-metadata.d.ts +88 -16
- package/dist/schemas/oauth-protected-resource-metadata.d.ts.map +1 -1
- package/dist/schemas/oauth-protected-resource-metadata.js +14 -26
- package/dist/schemas/oauth-protected-resource-metadata.js.map +1 -1
- package/dist/schemas/oauth-redirect-uri.d.ts +5 -5
- package/dist/schemas/oauth-redirect-uri.d.ts.map +1 -1
- package/dist/schemas/oauth-redirect-uri.js +3 -16
- package/dist/schemas/oauth-redirect-uri.js.map +1 -1
- package/dist/schemas/oauth-response-mode.d.ts +3 -3
- package/dist/schemas/oauth-response-mode.d.ts.map +1 -1
- package/dist/schemas/oauth-response-mode.js +2 -2
- package/dist/schemas/oauth-response-mode.js.map +1 -1
- package/dist/schemas/oauth-response-type.d.ts +3 -3
- package/dist/schemas/oauth-response-type.d.ts.map +1 -1
- package/dist/schemas/oauth-response-type.js +13 -7
- package/dist/schemas/oauth-response-type.js.map +1 -1
- package/dist/schemas/oauth-scope.d.ts +3 -3
- package/dist/schemas/oauth-scope.d.ts.map +1 -1
- package/dist/schemas/oauth-scope.js +2 -2
- package/dist/schemas/oauth-scope.js.map +1 -1
- package/dist/schemas/oauth-token-response.d.ts +17 -17
- package/dist/schemas/oauth-token-response.d.ts.map +1 -1
- package/dist/schemas/oauth-token-response.js +7 -7
- package/dist/schemas/oauth-token-response.js.map +1 -1
- package/dist/schemas/oauth-token-type.d.ts +3 -3
- package/dist/schemas/oauth-token-type.d.ts.map +1 -1
- package/dist/schemas/oauth-token-type.js +8 -7
- package/dist/schemas/oauth-token-type.js.map +1 -1
- package/dist/schemas/uri.d.ts +7 -7
- package/dist/schemas/uri.d.ts.map +1 -1
- package/dist/schemas/uri.js +44 -44
- package/dist/schemas/uri.js.map +1 -1
- package/dist/schemas/utils.d.ts.map +1 -1
- package/dist/schemas/utils.js.map +1 -1
- package/dist/scope.d.ts.map +1 -1
- package/dist/scope.js.map +1 -1
- package/lib/build-client-metadata.ts +92 -6
- package/lib/index.ts +38 -30
- package/lib/schemas/atcute-client-shared.ts +25 -0
- package/lib/schemas/atcute-confidential-client-metadata.ts +81 -111
- package/lib/schemas/atcute-public-client-metadata.ts +101 -0
- package/lib/schemas/atproto-authorization-server-metadata.ts +22 -23
- package/lib/schemas/atproto-oauth-scope.ts +8 -5
- package/lib/schemas/atproto-oauth-token-response.ts +10 -9
- package/lib/schemas/atproto-protected-resource-metadata.ts +15 -15
- package/lib/schemas/jwk.ts +104 -120
- package/lib/schemas/jwks.ts +28 -40
- package/lib/schemas/oauth-authorization-details.ts +10 -10
- package/lib/schemas/oauth-authorization-server-metadata.ts +72 -74
- package/lib/schemas/oauth-client-id-discoverable.ts +43 -48
- package/lib/schemas/oauth-client-id.ts +3 -3
- package/lib/schemas/oauth-client-metadata.ts +45 -49
- package/lib/schemas/oauth-code-challenge-method.ts +3 -3
- package/lib/schemas/oauth-endpoint-auth-method.ts +11 -11
- package/lib/schemas/oauth-grant-type.ts +11 -11
- package/lib/schemas/oauth-issuer-identifier.ts +35 -27
- package/lib/schemas/oauth-par-response.ts +4 -4
- package/lib/schemas/oauth-prompt.ts +3 -9
- package/lib/schemas/oauth-protected-resource-metadata.ts +26 -35
- package/lib/schemas/oauth-redirect-uri.ts +15 -23
- package/lib/schemas/oauth-response-mode.ts +3 -7
- package/lib/schemas/oauth-response-type.ts +12 -12
- package/lib/schemas/oauth-scope.ts +3 -3
- package/lib/schemas/oauth-token-response.ts +10 -10
- package/lib/schemas/oauth-token-type.ts +16 -12
- package/lib/schemas/uri.ts +89 -76
- package/package.json +9 -8
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
|
|
3
|
+
import { scopeSchema } from './atcute-client-shared.ts';
|
|
4
|
+
import { oauthClientIdDiscoverableSchema } from './oauth-client-id-discoverable.ts';
|
|
5
|
+
import { loopbackRedirectUriSchema, oauthRedirectUriSchema } from './oauth-redirect-uri.ts';
|
|
6
|
+
import { nonLocalWebUriSchema, webUriSchema } from './uri.ts';
|
|
7
|
+
|
|
8
|
+
const redirectUrisSchema = v.pipe(
|
|
9
|
+
v.array(oauthRedirectUriSchema),
|
|
10
|
+
v.minLength(1, `must have at least one redirect URI`),
|
|
11
|
+
v.checkItems((uri) => {
|
|
12
|
+
// private-use URIs don't have URL-style credentials
|
|
13
|
+
if (!uri.includes('://')) {
|
|
14
|
+
return true;
|
|
15
|
+
}
|
|
16
|
+
const url = new URL(uri);
|
|
17
|
+
return !url.username && !url.password;
|
|
18
|
+
}, `redirect URI must not contain credentials`),
|
|
19
|
+
);
|
|
20
|
+
|
|
21
|
+
const loopbackRedirectUrisSchema = v.pipe(
|
|
22
|
+
redirectUrisSchema,
|
|
23
|
+
v.checkItems(
|
|
24
|
+
(uri) => v.is(loopbackRedirectUriSchema, uri),
|
|
25
|
+
`loopback clients require loopback redirect URIs (127.0.0.1 or [::1])`,
|
|
26
|
+
),
|
|
27
|
+
);
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* user-facing client metadata for configuring a loopback public OAuth client.
|
|
31
|
+
*
|
|
32
|
+
* loopback clients are for localhost development and CLI tools. they use
|
|
33
|
+
* `http://localhost` as the client_id origin, which is built automatically
|
|
34
|
+
* from the redirect_uris and scope.
|
|
35
|
+
*/
|
|
36
|
+
export const loopbackClientMetadataSchema = v.looseObject({
|
|
37
|
+
/** must not be provided for loopback clients */
|
|
38
|
+
client_id: v.optional(v.undefined()),
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* redirect URIs for authorization responses.
|
|
42
|
+
*
|
|
43
|
+
* must be loopback IP addresses (127.0.0.1 or [::1]).
|
|
44
|
+
* per RFC 8252, port numbers are ignored during redirect URI matching,
|
|
45
|
+
* allowing ephemeral ports.
|
|
46
|
+
*/
|
|
47
|
+
redirect_uris: loopbackRedirectUrisSchema,
|
|
48
|
+
|
|
49
|
+
/** OAuth scope (must include "atproto") */
|
|
50
|
+
scope: scopeSchema,
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
export type LoopbackClientMetadata = v.InferOutput<typeof loopbackClientMetadataSchema>;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* user-facing client metadata for configuring a discoverable public OAuth client.
|
|
57
|
+
*
|
|
58
|
+
* discoverable public clients have an HTTPS client_id URL where metadata is hosted,
|
|
59
|
+
* but don't use a keyset (token_endpoint_auth_method: 'none').
|
|
60
|
+
*/
|
|
61
|
+
export const discoverablePublicClientMetadataSchema = v.looseObject({
|
|
62
|
+
/** discoverable HTTPS client_id URL */
|
|
63
|
+
client_id: oauthClientIdDiscoverableSchema,
|
|
64
|
+
|
|
65
|
+
/** redirect URIs for authorization responses */
|
|
66
|
+
redirect_uris: redirectUrisSchema,
|
|
67
|
+
|
|
68
|
+
/** OAuth scope (must include "atproto") */
|
|
69
|
+
scope: scopeSchema,
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* application type - defaults to 'web'.
|
|
73
|
+
*/
|
|
74
|
+
application_type: v.optional(v.picklist(['web', 'native'])),
|
|
75
|
+
|
|
76
|
+
/** optional client homepage */
|
|
77
|
+
client_uri: v.optional(webUriSchema),
|
|
78
|
+
/** optional display name */
|
|
79
|
+
client_name: v.optional(v.string()),
|
|
80
|
+
/** optional policy url */
|
|
81
|
+
policy_uri: v.optional(nonLocalWebUriSchema),
|
|
82
|
+
/** optional terms of service url */
|
|
83
|
+
tos_uri: v.optional(nonLocalWebUriSchema),
|
|
84
|
+
/** optional logo url */
|
|
85
|
+
logo_uri: v.optional(nonLocalWebUriSchema),
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
export type DiscoverablePublicClientMetadata = v.InferOutput<typeof discoverablePublicClientMetadataSchema>;
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* user-facing client metadata for configuring a public OAuth client.
|
|
92
|
+
*
|
|
93
|
+
* - if `client_id` is omitted: loopback client (for localhost dev / CLI tools)
|
|
94
|
+
* - if `client_id` is provided: discoverable public client (HTTPS URL)
|
|
95
|
+
*/
|
|
96
|
+
export const publicClientMetadataSchema = v.union([
|
|
97
|
+
loopbackClientMetadataSchema,
|
|
98
|
+
discoverablePublicClientMetadataSchema,
|
|
99
|
+
]);
|
|
100
|
+
|
|
101
|
+
export type PublicClientMetadata = v.InferOutput<typeof publicClientMetadataSchema>;
|
|
@@ -1,32 +1,31 @@
|
|
|
1
|
-
import * as v from '
|
|
1
|
+
import * as v from 'valibot';
|
|
2
2
|
|
|
3
|
-
import { oauthAuthorizationServerMetadataValidator } from './oauth-authorization-server-metadata.
|
|
3
|
+
import { oauthAuthorizationServerMetadataValidator } from './oauth-authorization-server-metadata.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* AT Protocol authorization server metadata with required fields and assertions.
|
|
7
7
|
*
|
|
8
8
|
* @see {@link https://atproto.com/specs/oauth}
|
|
9
9
|
*/
|
|
10
|
-
export const atprotoAuthorizationServerMetadataValidator =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
return v.ok(data as typeof data & { pushed_authorization_request_endpoint: string });
|
|
29
|
-
},
|
|
10
|
+
export const atprotoAuthorizationServerMetadataValidator = v.pipe(
|
|
11
|
+
oauthAuthorizationServerMetadataValidator,
|
|
12
|
+
v.forward(
|
|
13
|
+
v.check(
|
|
14
|
+
(data) => data.client_id_metadata_document_supported === true,
|
|
15
|
+
`atproto requires client_id_metadata_document_supported to be true`,
|
|
16
|
+
),
|
|
17
|
+
['client_id_metadata_document_supported'],
|
|
18
|
+
),
|
|
19
|
+
v.forward(
|
|
20
|
+
v.check(
|
|
21
|
+
(data) => !!data.pushed_authorization_request_endpoint,
|
|
22
|
+
`atproto requires pushed_authorization_request_endpoint to be true`,
|
|
23
|
+
),
|
|
24
|
+
['pushed_authorization_request_endpoint'],
|
|
25
|
+
),
|
|
26
|
+
v.transform((data) => data as typeof data & { pushed_authorization_request_endpoint: string }),
|
|
30
27
|
);
|
|
31
28
|
|
|
32
|
-
export type AtprotoAuthorizationServerMetadata = v.
|
|
29
|
+
export type AtprotoAuthorizationServerMetadata = v.InferOutput<
|
|
30
|
+
typeof atprotoAuthorizationServerMetadataValidator
|
|
31
|
+
>;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import * as v from '
|
|
1
|
+
import * as v from 'valibot';
|
|
2
2
|
|
|
3
|
-
import { isOAuthScope } from './oauth-scope.
|
|
4
|
-
import { isSpaceSeparatedValue } from './utils.
|
|
3
|
+
import { isOAuthScope } from './oauth-scope.ts';
|
|
4
|
+
import { isSpaceSeparatedValue } from './utils.ts';
|
|
5
5
|
|
|
6
6
|
export const ATPROTO_SCOPE_VALUE = 'atproto';
|
|
7
7
|
|
|
@@ -10,9 +10,12 @@ const isAtprotoOAuthScope = (input: string): boolean => {
|
|
|
10
10
|
};
|
|
11
11
|
|
|
12
12
|
/** atproto OAuth scope (must include "atproto") */
|
|
13
|
-
export const atprotoOAuthScopeSchema = v.
|
|
13
|
+
export const atprotoOAuthScopeSchema = v.pipe(
|
|
14
|
+
v.string(),
|
|
15
|
+
v.check(isAtprotoOAuthScope, `invalid atproto OAuth scope`),
|
|
16
|
+
);
|
|
14
17
|
|
|
15
|
-
export type AtprotoOAuthScope = v.
|
|
18
|
+
export type AtprotoOAuthScope = v.InferOutput<typeof atprotoOAuthScopeSchema>;
|
|
16
19
|
|
|
17
20
|
/** default scope is for reading identity (did) only */
|
|
18
21
|
export const DEFAULT_ATPROTO_OAUTH_SCOPE: AtprotoOAuthScope = ATPROTO_SCOPE_VALUE;
|
|
@@ -1,20 +1,21 @@
|
|
|
1
1
|
import { isAtprotoDid } from '@atcute/identity';
|
|
2
|
+
import type { Did } from '@atcute/lexicons/syntax';
|
|
2
3
|
|
|
3
|
-
import * as v from '
|
|
4
|
+
import * as v from 'valibot';
|
|
4
5
|
|
|
5
|
-
import { atprotoOAuthScopeSchema } from './atproto-oauth-scope.
|
|
6
|
-
import { oauthAuthorizationDetailsSchema } from './oauth-authorization-details.
|
|
6
|
+
import { atprotoOAuthScopeSchema } from './atproto-oauth-scope.ts';
|
|
7
|
+
import { oauthAuthorizationDetailsSchema } from './oauth-authorization-details.ts';
|
|
7
8
|
|
|
8
|
-
export const atprotoOAuthTokenResponseSchema = v.
|
|
9
|
+
export const atprotoOAuthTokenResponseSchema = v.looseObject({
|
|
9
10
|
access_token: v.string(),
|
|
10
11
|
token_type: v.literal('DPoP'),
|
|
11
|
-
sub: v.
|
|
12
|
+
sub: v.custom<Did>(isAtprotoDid, `must be a did:plc or did:web`),
|
|
12
13
|
scope: atprotoOAuthScopeSchema,
|
|
13
|
-
refresh_token: v.
|
|
14
|
-
expires_in: v.
|
|
14
|
+
refresh_token: v.optional(v.string()),
|
|
15
|
+
expires_in: v.optional(v.number()),
|
|
15
16
|
// https://datatracker.ietf.org/doc/html/rfc9396#name-enriched-authorization-deta
|
|
16
|
-
authorization_details:
|
|
17
|
+
authorization_details: v.optional(oauthAuthorizationDetailsSchema),
|
|
17
18
|
// OpenID is not compatible with atproto identities
|
|
18
19
|
});
|
|
19
20
|
|
|
20
|
-
export type AtprotoOAuthTokenResponse = v.
|
|
21
|
+
export type AtprotoOAuthTokenResponse = v.InferOutput<typeof atprotoOAuthTokenResponseSchema>;
|
|
@@ -1,24 +1,24 @@
|
|
|
1
|
-
import * as v from '
|
|
1
|
+
import * as v from 'valibot';
|
|
2
2
|
|
|
3
|
-
import { oauthProtectedResourceMetadataValidator } from './oauth-protected-resource-metadata.
|
|
3
|
+
import { oauthProtectedResourceMetadataValidator } from './oauth-protected-resource-metadata.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* AT Protocol protected resource metadata with required fields.
|
|
7
7
|
*
|
|
8
8
|
* @see {@link https://atproto.com/specs/oauth}
|
|
9
9
|
*/
|
|
10
|
-
export const atprotoProtectedResourceMetadataValidator =
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
return v.ok(data as typeof data & { authorization_servers: [string] });
|
|
21
|
-
},
|
|
10
|
+
export const atprotoProtectedResourceMetadataValidator = v.pipe(
|
|
11
|
+
oauthProtectedResourceMetadataValidator,
|
|
12
|
+
v.forward(
|
|
13
|
+
v.check(
|
|
14
|
+
(data) => data.authorization_servers?.length === 1,
|
|
15
|
+
`atproto requires exactly one authorization server`,
|
|
16
|
+
),
|
|
17
|
+
['authorization_servers'],
|
|
18
|
+
),
|
|
19
|
+
v.transform((data) => data as typeof data & { authorization_servers: [string] }),
|
|
22
20
|
);
|
|
23
21
|
|
|
24
|
-
export type AtprotoProtectedResourceMetadata = v.
|
|
22
|
+
export type AtprotoProtectedResourceMetadata = v.InferOutput<
|
|
23
|
+
typeof atprotoProtectedResourceMetadataValidator
|
|
24
|
+
>;
|
package/lib/schemas/jwk.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import * as v from '
|
|
1
|
+
import * as v from 'valibot';
|
|
2
2
|
|
|
3
|
-
import { isLastOccurrence } from './utils.
|
|
3
|
+
import { isLastOccurrence } from './utils.ts';
|
|
4
4
|
|
|
5
5
|
// key usage constants
|
|
6
6
|
const PUBLIC_KEY_USAGE = ['verify', 'encrypt', 'wrapKey'] as const;
|
|
@@ -20,107 +20,94 @@ const isPrivateKeyUsage = (usage: unknown): usage is (typeof PRIVATE_KEY_USAGE)[
|
|
|
20
20
|
const isSigKeyUsage = (v: InternalKeyUsage): boolean => v === 'verify';
|
|
21
21
|
const isEncKeyUsage = (v: InternalKeyUsage): boolean => v === 'encrypt' || v === 'wrapKey';
|
|
22
22
|
|
|
23
|
-
export const keyUsageSchema = v.
|
|
24
|
-
v.literal('verify'),
|
|
25
|
-
v.literal('encrypt'),
|
|
26
|
-
v.literal('wrapKey'),
|
|
27
|
-
v.literal('sign'),
|
|
28
|
-
v.literal('decrypt'),
|
|
29
|
-
v.literal('unwrapKey'),
|
|
30
|
-
v.literal('deriveKey'),
|
|
31
|
-
v.literal('deriveBits'),
|
|
32
|
-
);
|
|
23
|
+
export const keyUsageSchema = v.picklist(KEY_USAGE);
|
|
33
24
|
|
|
34
|
-
export const publicKeyUsageSchema = v.
|
|
25
|
+
export const publicKeyUsageSchema = v.picklist(PUBLIC_KEY_USAGE);
|
|
35
26
|
|
|
36
|
-
const
|
|
27
|
+
const jwkBaseEntries = {
|
|
37
28
|
kty: v.string(),
|
|
38
|
-
alg: v.
|
|
39
|
-
kid: v.
|
|
40
|
-
use: v.
|
|
41
|
-
key_ops: v.array(keyUsageSchema)
|
|
29
|
+
alg: v.optional(v.string()),
|
|
30
|
+
kid: v.optional(v.string()),
|
|
31
|
+
use: v.optional(v.picklist(['sig', 'enc'])),
|
|
32
|
+
key_ops: v.optional(v.array(keyUsageSchema)),
|
|
42
33
|
|
|
43
34
|
// X.509
|
|
44
|
-
x5c: v.array(v.string())
|
|
45
|
-
x5t: v.
|
|
46
|
-
'x5t#S256': v.
|
|
47
|
-
x5u: v.
|
|
35
|
+
x5c: v.optional(v.array(v.string())),
|
|
36
|
+
x5t: v.optional(v.string()),
|
|
37
|
+
'x5t#S256': v.optional(v.string()),
|
|
38
|
+
x5u: v.optional(v.string()),
|
|
48
39
|
|
|
49
40
|
// WebCrypto
|
|
50
|
-
ext: v.
|
|
41
|
+
ext: v.optional(v.boolean()),
|
|
51
42
|
|
|
52
43
|
// Federation Historical Keys Response
|
|
53
|
-
iat: v.
|
|
54
|
-
exp: v.
|
|
55
|
-
nbf: v.
|
|
56
|
-
revoked: v
|
|
57
|
-
.
|
|
44
|
+
iat: v.optional(v.number()),
|
|
45
|
+
exp: v.optional(v.number()),
|
|
46
|
+
nbf: v.optional(v.number()),
|
|
47
|
+
revoked: v.optional(
|
|
48
|
+
v.looseObject({
|
|
58
49
|
revoked_at: v.number(),
|
|
59
|
-
reason: v.
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
}
|
|
50
|
+
reason: v.optional(v.string()),
|
|
51
|
+
}),
|
|
52
|
+
),
|
|
53
|
+
};
|
|
63
54
|
|
|
64
|
-
const jwkRsaKeySchema =
|
|
55
|
+
const jwkRsaKeySchema = v.looseObject({
|
|
56
|
+
...jwkBaseEntries,
|
|
65
57
|
kty: v.literal('RSA'),
|
|
66
|
-
alg: v
|
|
67
|
-
.union(
|
|
68
|
-
v.literal('RS256'),
|
|
69
|
-
v.literal('RS384'),
|
|
70
|
-
v.literal('RS512'),
|
|
71
|
-
v.literal('PS256'),
|
|
72
|
-
v.literal('PS384'),
|
|
73
|
-
v.literal('PS512'),
|
|
74
|
-
)
|
|
75
|
-
.optional(),
|
|
58
|
+
alg: v.optional(v.picklist(['RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512'])),
|
|
76
59
|
n: v.string(),
|
|
77
60
|
e: v.string(),
|
|
78
|
-
d: v.
|
|
79
|
-
p: v.
|
|
80
|
-
q: v.
|
|
81
|
-
dp: v.
|
|
82
|
-
dq: v.
|
|
83
|
-
qi: v.
|
|
84
|
-
oth: v
|
|
85
|
-
.array(
|
|
86
|
-
v.
|
|
87
|
-
r: v.
|
|
88
|
-
d: v.
|
|
89
|
-
t: v.
|
|
61
|
+
d: v.optional(v.string()),
|
|
62
|
+
p: v.optional(v.string()),
|
|
63
|
+
q: v.optional(v.string()),
|
|
64
|
+
dp: v.optional(v.string()),
|
|
65
|
+
dq: v.optional(v.string()),
|
|
66
|
+
qi: v.optional(v.string()),
|
|
67
|
+
oth: v.optional(
|
|
68
|
+
v.array(
|
|
69
|
+
v.looseObject({
|
|
70
|
+
r: v.optional(v.string()),
|
|
71
|
+
d: v.optional(v.string()),
|
|
72
|
+
t: v.optional(v.string()),
|
|
90
73
|
}),
|
|
91
|
-
)
|
|
92
|
-
|
|
74
|
+
),
|
|
75
|
+
),
|
|
93
76
|
});
|
|
94
77
|
|
|
95
|
-
const jwkEcKeySchema =
|
|
78
|
+
const jwkEcKeySchema = v.looseObject({
|
|
79
|
+
...jwkBaseEntries,
|
|
96
80
|
kty: v.literal('EC'),
|
|
97
|
-
alg: v.
|
|
98
|
-
crv: v.
|
|
81
|
+
alg: v.optional(v.picklist(['ES256', 'ES384', 'ES512'])),
|
|
82
|
+
crv: v.picklist(['P-256', 'P-384', 'P-521']),
|
|
99
83
|
x: v.string(),
|
|
100
84
|
y: v.string(),
|
|
101
|
-
d: v.
|
|
85
|
+
d: v.optional(v.string()),
|
|
102
86
|
});
|
|
103
87
|
|
|
104
|
-
const jwkEcSecp256k1KeySchema =
|
|
88
|
+
const jwkEcSecp256k1KeySchema = v.looseObject({
|
|
89
|
+
...jwkBaseEntries,
|
|
105
90
|
kty: v.literal('EC'),
|
|
106
|
-
alg: v.literal('ES256K')
|
|
91
|
+
alg: v.optional(v.literal('ES256K')),
|
|
107
92
|
crv: v.literal('secp256k1'),
|
|
108
93
|
x: v.string(),
|
|
109
94
|
y: v.string(),
|
|
110
|
-
d: v.
|
|
95
|
+
d: v.optional(v.string()),
|
|
111
96
|
});
|
|
112
97
|
|
|
113
|
-
const jwkOkpKeySchema =
|
|
98
|
+
const jwkOkpKeySchema = v.looseObject({
|
|
99
|
+
...jwkBaseEntries,
|
|
114
100
|
kty: v.literal('OKP'),
|
|
115
|
-
alg: v.literal('EdDSA')
|
|
116
|
-
crv: v.
|
|
101
|
+
alg: v.optional(v.literal('EdDSA')),
|
|
102
|
+
crv: v.picklist(['Ed25519', 'Ed448']),
|
|
117
103
|
x: v.string(),
|
|
118
|
-
d: v.
|
|
104
|
+
d: v.optional(v.string()),
|
|
119
105
|
});
|
|
120
106
|
|
|
121
|
-
const jwkSymKeySchema =
|
|
107
|
+
const jwkSymKeySchema = v.looseObject({
|
|
108
|
+
...jwkBaseEntries,
|
|
122
109
|
kty: v.literal('oct'),
|
|
123
|
-
alg: v.
|
|
110
|
+
alg: v.optional(v.picklist(['HS256', 'HS384', 'HS512'])),
|
|
124
111
|
k: v.string(),
|
|
125
112
|
});
|
|
126
113
|
|
|
@@ -133,57 +120,54 @@ const isPublicJwk = <J extends object>(jwk: J): boolean => {
|
|
|
133
120
|
};
|
|
134
121
|
|
|
135
122
|
/** JWK schema for known key types */
|
|
136
|
-
export const jwkSchema = v
|
|
137
|
-
.union(jwkRsaKeySchema, jwkEcKeySchema, jwkEcSecp256k1KeySchema, jwkOkpKeySchema, jwkSymKeySchema)
|
|
138
|
-
.
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
(k.use === 'sig' && k.key_ops.every(isSigKeyUsage)) ||
|
|
158
|
-
(k.use === 'enc' && k.key_ops.every(isEncKeyUsage));
|
|
159
|
-
if (!consistent) {
|
|
160
|
-
return v.err({ message: `"key_ops" must be consistent with "use"`, path: ['key_ops'] });
|
|
123
|
+
export const jwkSchema = v.pipe(
|
|
124
|
+
v.union([jwkRsaKeySchema, jwkEcKeySchema, jwkEcSecp256k1KeySchema, jwkOkpKeySchema, jwkSymKeySchema]),
|
|
125
|
+
v.forward(
|
|
126
|
+
v.check((k) => k.use == null || isPublicJwk(k), `"use" can only be used with public keys`),
|
|
127
|
+
['use'],
|
|
128
|
+
),
|
|
129
|
+
v.forward(
|
|
130
|
+
v.check(
|
|
131
|
+
(k) => !(k.key_ops?.some(isPrivateKeyUsage) && isPublicJwk(k)),
|
|
132
|
+
`private key usage not allowed for public keys`,
|
|
133
|
+
),
|
|
134
|
+
['key_ops'],
|
|
135
|
+
),
|
|
136
|
+
v.forward(
|
|
137
|
+
v.check((k) => !k.key_ops || k.key_ops.every(isLastOccurrence), `key_ops must not contain duplicates`),
|
|
138
|
+
['key_ops'],
|
|
139
|
+
),
|
|
140
|
+
v.forward(
|
|
141
|
+
v.check((k) => {
|
|
142
|
+
if (k.use == null || k.key_ops == null) {
|
|
143
|
+
return true;
|
|
161
144
|
}
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
145
|
+
return (
|
|
146
|
+
(k.use === 'sig' && k.key_ops.every(isSigKeyUsage)) ||
|
|
147
|
+
(k.use === 'enc' && k.key_ops.every(isEncKeyUsage))
|
|
148
|
+
);
|
|
149
|
+
}, `"key_ops" must be consistent with "use"`),
|
|
150
|
+
['key_ops'],
|
|
151
|
+
),
|
|
152
|
+
);
|
|
166
153
|
|
|
167
154
|
/** public JWK schema (kid required, no private keys) */
|
|
168
|
-
export const jwkPubSchema =
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
return v.ok(k);
|
|
185
|
-
});
|
|
155
|
+
export const jwkPubSchema = v.pipe(
|
|
156
|
+
jwkSchema,
|
|
157
|
+
v.forward(
|
|
158
|
+
v.check((k) => k.kid != null, `"kid" is required`),
|
|
159
|
+
['kid'],
|
|
160
|
+
),
|
|
161
|
+
v.check((k) => isPublicJwk(k), `private key not allowed`),
|
|
162
|
+
v.forward(
|
|
163
|
+
v.check(
|
|
164
|
+
(k) => !k.key_ops || k.key_ops.every(isPublicKeyUsage),
|
|
165
|
+
`"key_ops" must not contain private key usage for public keys`,
|
|
166
|
+
),
|
|
167
|
+
['key_ops'],
|
|
168
|
+
),
|
|
169
|
+
);
|
|
186
170
|
|
|
187
|
-
export type KeyUsage = v.
|
|
188
|
-
export type Jwk = v.
|
|
189
|
-
export type JwkPub = v.
|
|
171
|
+
export type KeyUsage = v.InferOutput<typeof keyUsageSchema>;
|
|
172
|
+
export type Jwk = v.InferOutput<typeof jwkSchema>;
|
|
173
|
+
export type JwkPub = v.InferOutput<typeof jwkPubSchema>;
|
package/lib/schemas/jwks.ts
CHANGED
|
@@ -1,45 +1,33 @@
|
|
|
1
|
-
import * as v from '
|
|
2
|
-
|
|
3
|
-
import { jwkPubSchema, jwkSchema, type Jwk, type JwkPub } from './jwk.
|
|
4
|
-
|
|
5
|
-
/** JWKS (JSON Web Key Set)
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
keys.push(result.value);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
return v.ok(keys);
|
|
23
|
-
}),
|
|
1
|
+
import * as v from 'valibot';
|
|
2
|
+
|
|
3
|
+
import { jwkPubSchema, jwkSchema, type Jwk, type JwkPub } from './jwk.ts';
|
|
4
|
+
|
|
5
|
+
/** JWKS (JSON Web Key Set). implementations SHOULD ignore JWKs within a JWK Set that use unknown
|
|
6
|
+
* `kty` values, are missing required members, or have values out of the supported ranges. */
|
|
7
|
+
export const jwksSchema = v.looseObject({
|
|
8
|
+
keys: v.pipe(
|
|
9
|
+
v.array(v.unknown()),
|
|
10
|
+
v.transform((input): Jwk[] =>
|
|
11
|
+
input.flatMap((entry) => {
|
|
12
|
+
const result = v.safeParse(jwkSchema, entry);
|
|
13
|
+
return result.success ? [result.output] : [];
|
|
14
|
+
}),
|
|
15
|
+
),
|
|
16
|
+
),
|
|
24
17
|
});
|
|
25
18
|
|
|
26
19
|
/** public JWKS (JSON Web Key Set with only public keys) */
|
|
27
|
-
export const jwksPubSchema = v.
|
|
28
|
-
keys: v.
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
keys.push(result.value);
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
return v.ok(keys);
|
|
41
|
-
}),
|
|
20
|
+
export const jwksPubSchema = v.looseObject({
|
|
21
|
+
keys: v.pipe(
|
|
22
|
+
v.array(v.unknown()),
|
|
23
|
+
v.transform((input): JwkPub[] =>
|
|
24
|
+
input.flatMap((entry) => {
|
|
25
|
+
const result = v.safeParse(jwkPubSchema, entry);
|
|
26
|
+
return result.success ? [result.output] : [];
|
|
27
|
+
}),
|
|
28
|
+
),
|
|
29
|
+
),
|
|
42
30
|
});
|
|
43
31
|
|
|
44
|
-
export type Jwks = v.
|
|
45
|
-
export type JwksPub = v.
|
|
32
|
+
export type Jwks = v.InferOutput<typeof jwksSchema>;
|
|
33
|
+
export type JwksPub = v.InferOutput<typeof jwksPubSchema>;
|
|
@@ -1,43 +1,43 @@
|
|
|
1
|
-
import * as v from '
|
|
1
|
+
import * as v from 'valibot';
|
|
2
2
|
|
|
3
|
-
import { urlSchema } from './uri.
|
|
3
|
+
import { urlSchema } from './uri.ts';
|
|
4
4
|
|
|
5
5
|
/**
|
|
6
6
|
* @see {@link https://datatracker.ietf.org/doc/html/rfc9396#section-2 | RFC 9396, Section 2}
|
|
7
7
|
*/
|
|
8
|
-
export const oauthAuthorizationDetailSchema = v.
|
|
8
|
+
export const oauthAuthorizationDetailSchema = v.looseObject({
|
|
9
9
|
type: v.string(),
|
|
10
10
|
/**
|
|
11
11
|
* an array of strings representing the location of the resource or RS. these
|
|
12
12
|
* strings are typically URIs identifying the location of the RS.
|
|
13
13
|
*/
|
|
14
|
-
locations: v.array(urlSchema)
|
|
14
|
+
locations: v.optional(v.array(urlSchema)),
|
|
15
15
|
/**
|
|
16
16
|
* an array of strings representing the kinds of actions to be taken at the
|
|
17
17
|
* resource.
|
|
18
18
|
*/
|
|
19
|
-
actions: v.array(v.string())
|
|
19
|
+
actions: v.optional(v.array(v.string())),
|
|
20
20
|
/**
|
|
21
21
|
* an array of strings representing the kinds of data being requested from the
|
|
22
22
|
* resource.
|
|
23
23
|
*/
|
|
24
|
-
datatypes: v.array(v.string())
|
|
24
|
+
datatypes: v.optional(v.array(v.string())),
|
|
25
25
|
/**
|
|
26
26
|
* a string identifier indicating a specific resource available at the API.
|
|
27
27
|
*/
|
|
28
|
-
identifier: v.
|
|
28
|
+
identifier: v.optional(v.string()),
|
|
29
29
|
/**
|
|
30
30
|
* an array of strings representing the types or levels of privilege being
|
|
31
31
|
* requested at the resource.
|
|
32
32
|
*/
|
|
33
|
-
privileges: v.array(v.string())
|
|
33
|
+
privileges: v.optional(v.array(v.string())),
|
|
34
34
|
});
|
|
35
35
|
|
|
36
|
-
export type OAuthAuthorizationDetail = v.
|
|
36
|
+
export type OAuthAuthorizationDetail = v.InferOutput<typeof oauthAuthorizationDetailSchema>;
|
|
37
37
|
|
|
38
38
|
/**
|
|
39
39
|
* @see {@link https://datatracker.ietf.org/doc/html/rfc9396#section-2 | RFC 9396, Section 2}
|
|
40
40
|
*/
|
|
41
41
|
export const oauthAuthorizationDetailsSchema = v.array(oauthAuthorizationDetailSchema);
|
|
42
42
|
|
|
43
|
-
export type OAuthAuthorizationDetails = v.
|
|
43
|
+
export type OAuthAuthorizationDetails = v.InferOutput<typeof oauthAuthorizationDetailsSchema>;
|