@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.
Files changed (156) hide show
  1. package/README.md +6 -5
  2. package/dist/build-client-metadata.d.ts +18 -160
  3. package/dist/build-client-metadata.d.ts.map +1 -1
  4. package/dist/build-client-metadata.js +73 -3
  5. package/dist/build-client-metadata.js.map +1 -1
  6. package/dist/index.d.ts +31 -30
  7. package/dist/index.d.ts.map +1 -1
  8. package/dist/index.js +2 -1
  9. package/dist/index.js.map +1 -1
  10. package/dist/schemas/atcute-client-shared.d.ts +8 -0
  11. package/dist/schemas/atcute-client-shared.d.ts.map +1 -0
  12. package/dist/schemas/atcute-client-shared.js +15 -0
  13. package/dist/schemas/atcute-client-shared.js.map +1 -0
  14. package/dist/schemas/atcute-confidential-client-metadata.d.ts +228 -4
  15. package/dist/schemas/atcute-confidential-client-metadata.d.ts.map +1 -1
  16. package/dist/schemas/atcute-confidential-client-metadata.js +48 -88
  17. package/dist/schemas/atcute-confidential-client-metadata.js.map +1 -1
  18. package/dist/schemas/atcute-public-client-metadata.d.ts +95 -0
  19. package/dist/schemas/atcute-public-client-metadata.d.ts.map +1 -0
  20. package/dist/schemas/atcute-public-client-metadata.js +74 -0
  21. package/dist/schemas/atcute-public-client-metadata.js.map +1 -0
  22. package/dist/schemas/atproto-authorization-server-metadata.d.ts +786 -4
  23. package/dist/schemas/atproto-authorization-server-metadata.d.ts.map +1 -1
  24. package/dist/schemas/atproto-authorization-server-metadata.js +2 -18
  25. package/dist/schemas/atproto-authorization-server-metadata.js.map +1 -1
  26. package/dist/schemas/atproto-oauth-scope.d.ts +3 -3
  27. package/dist/schemas/atproto-oauth-scope.d.ts.map +1 -1
  28. package/dist/schemas/atproto-oauth-scope.js +2 -2
  29. package/dist/schemas/atproto-oauth-scope.js.map +1 -1
  30. package/dist/schemas/atproto-oauth-token-response.d.ts +17 -17
  31. package/dist/schemas/atproto-oauth-token-response.d.ts.map +1 -1
  32. package/dist/schemas/atproto-oauth-token-response.js +6 -6
  33. package/dist/schemas/atproto-oauth-token-response.js.map +1 -1
  34. package/dist/schemas/atproto-protected-resource-metadata.d.ts +100 -4
  35. package/dist/schemas/atproto-protected-resource-metadata.d.ts.map +1 -1
  36. package/dist/schemas/atproto-protected-resource-metadata.js +2 -11
  37. package/dist/schemas/atproto-protected-resource-metadata.js.map +1 -1
  38. package/dist/schemas/jwk.d.ts +4289 -42
  39. package/dist/schemas/jwk.d.ts.map +1 -1
  40. package/dist/schemas/jwk.js +58 -91
  41. package/dist/schemas/jwk.js.map +1 -1
  42. package/dist/schemas/jwks.d.ts +87 -42
  43. package/dist/schemas/jwks.d.ts.map +1 -1
  44. package/dist/schemas/jwks.js +13 -29
  45. package/dist/schemas/jwks.js.map +1 -1
  46. package/dist/schemas/oauth-authorization-details.d.ts +18 -18
  47. package/dist/schemas/oauth-authorization-details.d.ts.map +1 -1
  48. package/dist/schemas/oauth-authorization-details.js +7 -7
  49. package/dist/schemas/oauth-authorization-details.js.map +1 -1
  50. package/dist/schemas/oauth-authorization-server-metadata.d.ts +462 -48
  51. package/dist/schemas/oauth-authorization-server-metadata.d.ts.map +1 -1
  52. package/dist/schemas/oauth-authorization-server-metadata.js +46 -65
  53. package/dist/schemas/oauth-authorization-server-metadata.js.map +1 -1
  54. package/dist/schemas/oauth-client-id-discoverable.d.ts +2 -2
  55. package/dist/schemas/oauth-client-id-discoverable.d.ts.map +1 -1
  56. package/dist/schemas/oauth-client-id-discoverable.js +20 -22
  57. package/dist/schemas/oauth-client-id-discoverable.js.map +1 -1
  58. package/dist/schemas/oauth-client-id.d.ts +3 -3
  59. package/dist/schemas/oauth-client-id.d.ts.map +1 -1
  60. package/dist/schemas/oauth-client-id.js +2 -2
  61. package/dist/schemas/oauth-client-id.js.map +1 -1
  62. package/dist/schemas/oauth-client-metadata.d.ts +73 -51
  63. package/dist/schemas/oauth-client-metadata.d.ts.map +1 -1
  64. package/dist/schemas/oauth-client-metadata.js +33 -40
  65. package/dist/schemas/oauth-client-metadata.js.map +1 -1
  66. package/dist/schemas/oauth-code-challenge-method.d.ts +3 -3
  67. package/dist/schemas/oauth-code-challenge-method.d.ts.map +1 -1
  68. package/dist/schemas/oauth-code-challenge-method.js +2 -2
  69. package/dist/schemas/oauth-code-challenge-method.js.map +1 -1
  70. package/dist/schemas/oauth-endpoint-auth-method.d.ts +3 -3
  71. package/dist/schemas/oauth-endpoint-auth-method.d.ts.map +1 -1
  72. package/dist/schemas/oauth-endpoint-auth-method.js +10 -2
  73. package/dist/schemas/oauth-endpoint-auth-method.js.map +1 -1
  74. package/dist/schemas/oauth-grant-type.d.ts +3 -3
  75. package/dist/schemas/oauth-grant-type.d.ts.map +1 -1
  76. package/dist/schemas/oauth-grant-type.js +10 -3
  77. package/dist/schemas/oauth-grant-type.js.map +1 -1
  78. package/dist/schemas/oauth-issuer-identifier.d.ts +3 -3
  79. package/dist/schemas/oauth-issuer-identifier.d.ts.map +1 -1
  80. package/dist/schemas/oauth-issuer-identifier.js +16 -9
  81. package/dist/schemas/oauth-issuer-identifier.js.map +1 -1
  82. package/dist/schemas/oauth-par-response.d.ts +5 -5
  83. package/dist/schemas/oauth-par-response.d.ts.map +1 -1
  84. package/dist/schemas/oauth-par-response.js +3 -3
  85. package/dist/schemas/oauth-par-response.js.map +1 -1
  86. package/dist/schemas/oauth-prompt.d.ts +3 -3
  87. package/dist/schemas/oauth-prompt.d.ts.map +1 -1
  88. package/dist/schemas/oauth-prompt.js +2 -2
  89. package/dist/schemas/oauth-prompt.js.map +1 -1
  90. package/dist/schemas/oauth-protected-resource-metadata.d.ts +88 -16
  91. package/dist/schemas/oauth-protected-resource-metadata.d.ts.map +1 -1
  92. package/dist/schemas/oauth-protected-resource-metadata.js +14 -26
  93. package/dist/schemas/oauth-protected-resource-metadata.js.map +1 -1
  94. package/dist/schemas/oauth-redirect-uri.d.ts +5 -5
  95. package/dist/schemas/oauth-redirect-uri.d.ts.map +1 -1
  96. package/dist/schemas/oauth-redirect-uri.js +3 -16
  97. package/dist/schemas/oauth-redirect-uri.js.map +1 -1
  98. package/dist/schemas/oauth-response-mode.d.ts +3 -3
  99. package/dist/schemas/oauth-response-mode.d.ts.map +1 -1
  100. package/dist/schemas/oauth-response-mode.js +2 -2
  101. package/dist/schemas/oauth-response-mode.js.map +1 -1
  102. package/dist/schemas/oauth-response-type.d.ts +3 -3
  103. package/dist/schemas/oauth-response-type.d.ts.map +1 -1
  104. package/dist/schemas/oauth-response-type.js +13 -7
  105. package/dist/schemas/oauth-response-type.js.map +1 -1
  106. package/dist/schemas/oauth-scope.d.ts +3 -3
  107. package/dist/schemas/oauth-scope.d.ts.map +1 -1
  108. package/dist/schemas/oauth-scope.js +2 -2
  109. package/dist/schemas/oauth-scope.js.map +1 -1
  110. package/dist/schemas/oauth-token-response.d.ts +17 -17
  111. package/dist/schemas/oauth-token-response.d.ts.map +1 -1
  112. package/dist/schemas/oauth-token-response.js +7 -7
  113. package/dist/schemas/oauth-token-response.js.map +1 -1
  114. package/dist/schemas/oauth-token-type.d.ts +3 -3
  115. package/dist/schemas/oauth-token-type.d.ts.map +1 -1
  116. package/dist/schemas/oauth-token-type.js +8 -7
  117. package/dist/schemas/oauth-token-type.js.map +1 -1
  118. package/dist/schemas/uri.d.ts +7 -7
  119. package/dist/schemas/uri.d.ts.map +1 -1
  120. package/dist/schemas/uri.js +44 -44
  121. package/dist/schemas/uri.js.map +1 -1
  122. package/dist/schemas/utils.d.ts.map +1 -1
  123. package/dist/schemas/utils.js.map +1 -1
  124. package/dist/scope.d.ts.map +1 -1
  125. package/dist/scope.js.map +1 -1
  126. package/lib/build-client-metadata.ts +92 -6
  127. package/lib/index.ts +38 -30
  128. package/lib/schemas/atcute-client-shared.ts +25 -0
  129. package/lib/schemas/atcute-confidential-client-metadata.ts +81 -111
  130. package/lib/schemas/atcute-public-client-metadata.ts +101 -0
  131. package/lib/schemas/atproto-authorization-server-metadata.ts +22 -23
  132. package/lib/schemas/atproto-oauth-scope.ts +8 -5
  133. package/lib/schemas/atproto-oauth-token-response.ts +10 -9
  134. package/lib/schemas/atproto-protected-resource-metadata.ts +15 -15
  135. package/lib/schemas/jwk.ts +104 -120
  136. package/lib/schemas/jwks.ts +28 -40
  137. package/lib/schemas/oauth-authorization-details.ts +10 -10
  138. package/lib/schemas/oauth-authorization-server-metadata.ts +72 -74
  139. package/lib/schemas/oauth-client-id-discoverable.ts +43 -48
  140. package/lib/schemas/oauth-client-id.ts +3 -3
  141. package/lib/schemas/oauth-client-metadata.ts +45 -49
  142. package/lib/schemas/oauth-code-challenge-method.ts +3 -3
  143. package/lib/schemas/oauth-endpoint-auth-method.ts +11 -11
  144. package/lib/schemas/oauth-grant-type.ts +11 -11
  145. package/lib/schemas/oauth-issuer-identifier.ts +35 -27
  146. package/lib/schemas/oauth-par-response.ts +4 -4
  147. package/lib/schemas/oauth-prompt.ts +3 -9
  148. package/lib/schemas/oauth-protected-resource-metadata.ts +26 -35
  149. package/lib/schemas/oauth-redirect-uri.ts +15 -23
  150. package/lib/schemas/oauth-response-mode.ts +3 -7
  151. package/lib/schemas/oauth-response-type.ts +12 -12
  152. package/lib/schemas/oauth-scope.ts +3 -3
  153. package/lib/schemas/oauth-token-response.ts +10 -10
  154. package/lib/schemas/oauth-token-type.ts +16 -12
  155. package/lib/schemas/uri.ts +89 -76
  156. 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 '@badrap/valita';
1
+ import * as v from 'valibot';
2
2
 
3
- import { oauthAuthorizationServerMetadataValidator } from './oauth-authorization-server-metadata.js';
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 = oauthAuthorizationServerMetadataValidator.chain(
11
- (data) => {
12
- // atproto requires client_id_metadata_document support
13
- if (data.client_id_metadata_document_supported !== true) {
14
- return v.err({
15
- message: `atproto requires client_id_metadata_document_supported to be true`,
16
- path: ['client_id_metadata_document_supported'],
17
- });
18
- }
19
-
20
- // atproto requires PAR
21
- if (!data.pushed_authorization_request_endpoint) {
22
- return v.err({
23
- message: `atproto requires pushed_authorization_request_endpoint to be true`,
24
- path: ['pushed_authorization_request_endpoint'],
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.Infer<typeof atprotoAuthorizationServerMetadataValidator>;
29
+ export type AtprotoAuthorizationServerMetadata = v.InferOutput<
30
+ typeof atprotoAuthorizationServerMetadataValidator
31
+ >;
@@ -1,7 +1,7 @@
1
- import * as v from '@badrap/valita';
1
+ import * as v from 'valibot';
2
2
 
3
- import { isOAuthScope } from './oauth-scope.js';
4
- import { isSpaceSeparatedValue } from './utils.js';
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.string().assert(isAtprotoOAuthScope, `invalid atproto OAuth scope`);
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.Infer<typeof atprotoOAuthScopeSchema>;
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 '@badrap/valita';
4
+ import * as v from 'valibot';
4
5
 
5
- import { atprotoOAuthScopeSchema } from './atproto-oauth-scope.js';
6
- import { oauthAuthorizationDetailsSchema } from './oauth-authorization-details.js';
6
+ import { atprotoOAuthScopeSchema } from './atproto-oauth-scope.ts';
7
+ import { oauthAuthorizationDetailsSchema } from './oauth-authorization-details.ts';
7
8
 
8
- export const atprotoOAuthTokenResponseSchema = v.object({
9
+ export const atprotoOAuthTokenResponseSchema = v.looseObject({
9
10
  access_token: v.string(),
10
11
  token_type: v.literal('DPoP'),
11
- sub: v.string().assert(isAtprotoDid, `must be a did:plc or did:web`),
12
+ sub: v.custom<Did>(isAtprotoDid, `must be a did:plc or did:web`),
12
13
  scope: atprotoOAuthScopeSchema,
13
- refresh_token: v.string().optional(),
14
- expires_in: v.number().optional(),
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: oauthAuthorizationDetailsSchema.optional(),
17
+ authorization_details: v.optional(oauthAuthorizationDetailsSchema),
17
18
  // OpenID is not compatible with atproto identities
18
19
  });
19
20
 
20
- export type AtprotoOAuthTokenResponse = v.Infer<typeof atprotoOAuthTokenResponseSchema>;
21
+ export type AtprotoOAuthTokenResponse = v.InferOutput<typeof atprotoOAuthTokenResponseSchema>;
@@ -1,24 +1,24 @@
1
- import * as v from '@badrap/valita';
1
+ import * as v from 'valibot';
2
2
 
3
- import { oauthProtectedResourceMetadataValidator } from './oauth-protected-resource-metadata.js';
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 = oauthProtectedResourceMetadataValidator.chain(
11
- (data) => {
12
- // atproto requires exactly one authorization server
13
- if (data.authorization_servers?.length !== 1) {
14
- return v.err({
15
- message: `atproto requires exactly one authorization server`,
16
- path: ['authorization_servers'],
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.Infer<typeof atprotoProtectedResourceMetadataValidator>;
22
+ export type AtprotoProtectedResourceMetadata = v.InferOutput<
23
+ typeof atprotoProtectedResourceMetadataValidator
24
+ >;
@@ -1,6 +1,6 @@
1
- import * as v from '@badrap/valita';
1
+ import * as v from 'valibot';
2
2
 
3
- import { isLastOccurrence } from './utils.js';
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.union(
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.union(v.literal('verify'), v.literal('encrypt'), v.literal('wrapKey'));
25
+ export const publicKeyUsageSchema = v.picklist(PUBLIC_KEY_USAGE);
35
26
 
36
- const jwkBaseSchema = v.object({
27
+ const jwkBaseEntries = {
37
28
  kty: v.string(),
38
- alg: v.string().optional(),
39
- kid: v.string().optional(),
40
- use: v.union(v.literal('sig'), v.literal('enc')).optional(),
41
- key_ops: v.array(keyUsageSchema).optional(),
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()).optional(),
45
- x5t: v.string().optional(),
46
- 'x5t#S256': v.string().optional(),
47
- x5u: v.string().optional(),
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.boolean().optional(),
41
+ ext: v.optional(v.boolean()),
51
42
 
52
43
  // Federation Historical Keys Response
53
- iat: v.number().optional(),
54
- exp: v.number().optional(),
55
- nbf: v.number().optional(),
56
- revoked: v
57
- .object({
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.string().optional(),
60
- })
61
- .optional(),
62
- });
50
+ reason: v.optional(v.string()),
51
+ }),
52
+ ),
53
+ };
63
54
 
64
- const jwkRsaKeySchema = jwkBaseSchema.extend({
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.string().optional(),
79
- p: v.string().optional(),
80
- q: v.string().optional(),
81
- dp: v.string().optional(),
82
- dq: v.string().optional(),
83
- qi: v.string().optional(),
84
- oth: v
85
- .array(
86
- v.object({
87
- r: v.string().optional(),
88
- d: v.string().optional(),
89
- t: v.string().optional(),
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
- .optional(),
74
+ ),
75
+ ),
93
76
  });
94
77
 
95
- const jwkEcKeySchema = jwkBaseSchema.extend({
78
+ const jwkEcKeySchema = v.looseObject({
79
+ ...jwkBaseEntries,
96
80
  kty: v.literal('EC'),
97
- alg: v.union(v.literal('ES256'), v.literal('ES384'), v.literal('ES512')).optional(),
98
- crv: v.union(v.literal('P-256'), v.literal('P-384'), v.literal('P-521')),
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.string().optional(),
85
+ d: v.optional(v.string()),
102
86
  });
103
87
 
104
- const jwkEcSecp256k1KeySchema = jwkBaseSchema.extend({
88
+ const jwkEcSecp256k1KeySchema = v.looseObject({
89
+ ...jwkBaseEntries,
105
90
  kty: v.literal('EC'),
106
- alg: v.literal('ES256K').optional(),
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.string().optional(),
95
+ d: v.optional(v.string()),
111
96
  });
112
97
 
113
- const jwkOkpKeySchema = jwkBaseSchema.extend({
98
+ const jwkOkpKeySchema = v.looseObject({
99
+ ...jwkBaseEntries,
114
100
  kty: v.literal('OKP'),
115
- alg: v.literal('EdDSA').optional(),
116
- crv: v.union(v.literal('Ed25519'), v.literal('Ed448')),
101
+ alg: v.optional(v.literal('EdDSA')),
102
+ crv: v.picklist(['Ed25519', 'Ed448']),
117
103
  x: v.string(),
118
- d: v.string().optional(),
104
+ d: v.optional(v.string()),
119
105
  });
120
106
 
121
- const jwkSymKeySchema = jwkBaseSchema.extend({
107
+ const jwkSymKeySchema = v.looseObject({
108
+ ...jwkBaseEntries,
122
109
  kty: v.literal('oct'),
123
- alg: v.union(v.literal('HS256'), v.literal('HS384'), v.literal('HS512')).optional(),
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
- .chain((k) => {
139
- // "use" can only be used with public keys
140
- if (k.use != null && !isPublicJwk(k)) {
141
- return v.err({ message: `"use" can only be used with public keys`, path: ['use'] });
142
- }
143
-
144
- // private key usage not allowed for public keys
145
- if (k.key_ops?.some(isPrivateKeyUsage) && isPublicJwk(k)) {
146
- return v.err({ message: `private key usage not allowed for public keys`, path: ['key_ops'] });
147
- }
148
-
149
- // key_ops must not contain duplicates
150
- if (k.key_ops && !k.key_ops.every(isLastOccurrence)) {
151
- return v.err({ message: `key_ops must not contain duplicates`, path: ['key_ops'] });
152
- }
153
-
154
- // "use" and "key_ops" must be consistent
155
- if (k.use != null && k.key_ops != null) {
156
- const consistent =
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
- return v.ok(k);
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 = jwkSchema.chain((k) => {
169
- if (k.kid == null) {
170
- return v.err({ message: `"kid" is required`, path: ['kid'] });
171
- }
172
-
173
- if (!isPublicJwk(k)) {
174
- return v.err({ message: `private key not allowed` });
175
- }
176
-
177
- if (k.key_ops && !k.key_ops.every(isPublicKeyUsage)) {
178
- return v.err({
179
- message: `"key_ops" must not contain private key usage for public keys`,
180
- path: ['key_ops'],
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.Infer<typeof keyUsageSchema>;
188
- export type Jwk = v.Infer<typeof jwkSchema>;
189
- export type JwkPub = v.Infer<typeof jwkPubSchema>;
171
+ export type KeyUsage = v.InferOutput<typeof keyUsageSchema>;
172
+ export type Jwk = v.InferOutput<typeof jwkSchema>;
173
+ export type JwkPub = v.InferOutput<typeof jwkPubSchema>;
@@ -1,45 +1,33 @@
1
- import * as v from '@badrap/valita';
2
-
3
- import { jwkPubSchema, jwkSchema, type Jwk, type JwkPub } from './jwk.js';
4
-
5
- /** JWKS (JSON Web Key Set) */
6
- export const jwksSchema = v.object({
7
- keys: v.array(v.unknown()).chain((input, options) => {
8
- // implementations SHOULD ignore JWKs within a JWK Set that use "kty"
9
- // values that are not understood, are missing required members, or
10
- // have values out of the supported ranges.
11
- const keys: Jwk[] = [];
12
-
13
- for (const item of input) {
14
- const result = jwkSchema.try(item, options);
15
- if (!result.ok) {
16
- continue;
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.object({
28
- keys: v.array(v.unknown()).chain((input, options) => {
29
- const keys: JwkPub[] = [];
30
-
31
- for (const item of input) {
32
- const result = jwkPubSchema.try(item, options);
33
- if (!result.ok) {
34
- continue;
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.Infer<typeof jwksSchema>;
45
- export type JwksPub = v.Infer<typeof jwksPubSchema>;
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 '@badrap/valita';
1
+ import * as v from 'valibot';
2
2
 
3
- import { urlSchema } from './uri.js';
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.object({
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).optional(),
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()).optional(),
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()).optional(),
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.string().optional(),
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()).optional(),
33
+ privileges: v.optional(v.array(v.string())),
34
34
  });
35
35
 
36
- export type OAuthAuthorizationDetail = v.Infer<typeof oauthAuthorizationDetailSchema>;
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.Infer<typeof oauthAuthorizationDetailsSchema>;
43
+ export type OAuthAuthorizationDetails = v.InferOutput<typeof oauthAuthorizationDetailsSchema>;