@atproto/oauth-types 0.2.0 → 0.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/atproto-loopback-client-metadata.d.ts +4 -1
  3. package/dist/atproto-loopback-client-metadata.d.ts.map +1 -1
  4. package/dist/atproto-loopback-client-metadata.js.map +1 -1
  5. package/dist/index.d.ts +2 -0
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +2 -0
  8. package/dist/index.js.map +1 -1
  9. package/dist/oauth-authorization-code-grant-token-request.d.ts +2 -2
  10. package/dist/oauth-authorization-code-grant-token-request.d.ts.map +1 -1
  11. package/dist/oauth-authorization-code-grant-token-request.js +2 -1
  12. package/dist/oauth-authorization-code-grant-token-request.js.map +1 -1
  13. package/dist/oauth-authorization-details.d.ts +42 -4
  14. package/dist/oauth-authorization-details.d.ts.map +1 -1
  15. package/dist/oauth-authorization-details.js +21 -1
  16. package/dist/oauth-authorization-details.js.map +1 -1
  17. package/dist/oauth-authorization-request-jar.d.ts +1 -1
  18. package/dist/oauth-authorization-request-par.d.ts +8 -8
  19. package/dist/oauth-authorization-request-parameters.d.ts +7 -7
  20. package/dist/oauth-authorization-request-parameters.d.ts.map +1 -1
  21. package/dist/oauth-authorization-request-parameters.js +2 -1
  22. package/dist/oauth-authorization-request-parameters.js.map +1 -1
  23. package/dist/oauth-authorization-request-query.d.ts +8 -8
  24. package/dist/oauth-authorization-server-metadata.d.ts +69 -66
  25. package/dist/oauth-authorization-server-metadata.d.ts.map +1 -1
  26. package/dist/oauth-authorization-server-metadata.js +14 -10
  27. package/dist/oauth-authorization-server-metadata.js.map +1 -1
  28. package/dist/oauth-client-id-discoverable.d.ts +3 -2
  29. package/dist/oauth-client-id-discoverable.d.ts.map +1 -1
  30. package/dist/oauth-client-id-discoverable.js +52 -28
  31. package/dist/oauth-client-id-discoverable.js.map +1 -1
  32. package/dist/oauth-client-id-loopback.d.ts +5 -5
  33. package/dist/oauth-client-id-loopback.d.ts.map +1 -1
  34. package/dist/oauth-client-id-loopback.js +29 -27
  35. package/dist/oauth-client-id-loopback.js.map +1 -1
  36. package/dist/oauth-client-metadata.d.ts +22 -12
  37. package/dist/oauth-client-metadata.d.ts.map +1 -1
  38. package/dist/oauth-client-metadata.js +18 -8
  39. package/dist/oauth-client-metadata.js.map +1 -1
  40. package/dist/oauth-issuer-identifier.d.ts +1 -1
  41. package/dist/oauth-issuer-identifier.d.ts.map +1 -1
  42. package/dist/oauth-issuer-identifier.js +3 -19
  43. package/dist/oauth-issuer-identifier.js.map +1 -1
  44. package/dist/oauth-protected-resource-metadata.d.ts +15 -12
  45. package/dist/oauth-protected-resource-metadata.d.ts.map +1 -1
  46. package/dist/oauth-protected-resource-metadata.js +15 -5
  47. package/dist/oauth-protected-resource-metadata.js.map +1 -1
  48. package/dist/oauth-redirect-uri.d.ts +10 -0
  49. package/dist/oauth-redirect-uri.d.ts.map +1 -0
  50. package/dist/oauth-redirect-uri.js +35 -0
  51. package/dist/oauth-redirect-uri.js.map +1 -0
  52. package/dist/oauth-token-request.d.ts +2 -2
  53. package/dist/oauth-token-response.d.ts +6 -6
  54. package/dist/uri.d.ts +20 -0
  55. package/dist/uri.d.ts.map +1 -0
  56. package/dist/uri.js +127 -0
  57. package/dist/uri.js.map +1 -0
  58. package/package.json +2 -2
  59. package/src/atproto-loopback-client-metadata.ts +8 -3
  60. package/src/index.ts +2 -0
  61. package/src/oauth-authorization-code-grant-token-request.ts +2 -1
  62. package/src/oauth-authorization-details.ts +21 -1
  63. package/src/oauth-authorization-request-parameters.ts +2 -1
  64. package/src/oauth-authorization-server-metadata.ts +14 -10
  65. package/src/oauth-client-id-discoverable.ts +69 -51
  66. package/src/oauth-client-id-loopback.ts +40 -40
  67. package/src/oauth-client-metadata.ts +18 -8
  68. package/src/oauth-issuer-identifier.ts +6 -21
  69. package/src/oauth-protected-resource-metadata.ts +15 -5
  70. package/src/oauth-redirect-uri.ts +56 -0
  71. package/src/uri.ts +171 -0
  72. package/tsconfig.build.tsbuildinfo +1 -1
@@ -1,9 +1,8 @@
1
1
  import { z } from 'zod'
2
- import { safeUrl } from './util.js'
2
+ import { webUriSchema } from './uri.js'
3
3
 
4
- export const oauthIssuerIdentifierSchema = z
5
- .string()
6
- .superRefine((value, ctx): value is `${'http' | 'https'}://${string}` => {
4
+ export const oauthIssuerIdentifierSchema = webUriSchema.superRefine(
5
+ (value, ctx) => {
7
6
  // Validate the issuer (MIX-UP attacks)
8
7
 
9
8
  if (value.endsWith('/')) {
@@ -14,22 +13,7 @@ export const oauthIssuerIdentifierSchema = z
14
13
  return false
15
14
  }
16
15
 
17
- const url = safeUrl(value)
18
- if (!url) {
19
- ctx.addIssue({
20
- code: z.ZodIssueCode.custom,
21
- message: 'Invalid url',
22
- })
23
- return false
24
- }
25
-
26
- if (url.protocol !== 'https:' && url.protocol !== 'http:') {
27
- ctx.addIssue({
28
- code: z.ZodIssueCode.custom,
29
- message: `Invalid issuer URL protocol "${url.protocol}"`,
30
- })
31
- return false
32
- }
16
+ const url = new URL(value)
33
17
 
34
18
  if (url.username || url.password) {
35
19
  ctx.addIssue({
@@ -57,6 +41,7 @@ export const oauthIssuerIdentifierSchema = z
57
41
  }
58
42
 
59
43
  return true
60
- })
44
+ },
45
+ )
61
46
 
62
47
  export type OAuthIssuerIdentifier = z.infer<typeof oauthIssuerIdentifierSchema>
@@ -1,6 +1,7 @@
1
1
  import { z } from 'zod'
2
2
 
3
3
  import { oauthIssuerIdentifierSchema } from './oauth-issuer-identifier.js'
4
+ import { webUriSchema } from './uri.js'
4
5
 
5
6
  /**
6
7
  * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-resource-metadata-05#name-protected-resource-metadata-r}
@@ -10,8 +11,17 @@ export const oauthProtectedResourceMetadataSchema = z.object({
10
11
  * REQUIRED. The protected resource's resource identifier, which is a URL that
11
12
  * uses the https scheme and has no query or fragment components. Using these
12
13
  * well-known resources is described in Section 3.
14
+ *
15
+ * @note This schema allows non https URLs for testing & development purposes.
16
+ * Make sure to validate the URL before using it in a production environment.
13
17
  */
14
- resource: z.string().url(),
18
+ resource: webUriSchema
19
+ .refine((url) => !url.includes('?'), {
20
+ message: 'Resource URL must not contain query parameters',
21
+ })
22
+ .refine((url) => !url.includes('#'), {
23
+ message: 'Resource URL must not contain a fragment',
24
+ }),
15
25
 
16
26
  /**
17
27
  * OPTIONAL. JSON array containing a list of OAuth authorization server issuer
@@ -31,7 +41,7 @@ export const oauthProtectedResourceMetadataSchema = z.object({
31
41
  * available, a use (public key use) parameter value is REQUIRED for all keys
32
42
  * in the referenced JWK Set to indicate each key's intended usage.
33
43
  */
34
- jwks_uri: z.string().url().optional(),
44
+ jwks_uri: webUriSchema.optional(),
35
45
 
36
46
  /**
37
47
  * RECOMMENDED. JSON array containing a list of the OAuth 2.0 [RFC6749] scope
@@ -64,20 +74,20 @@ export const oauthProtectedResourceMetadataSchema = z.object({
64
74
  * OPTIONAL. URL of a page containing human-readable information that
65
75
  * developers might want or need to know when using the protected resource
66
76
  */
67
- resource_documentation: z.string().url().optional(),
77
+ resource_documentation: webUriSchema.optional(),
68
78
 
69
79
  /**
70
80
  * OPTIONAL. URL that the protected resource provides to read about the
71
81
  * protected resource's requirements on how the client can use the data
72
82
  * provided by the protected resource
73
83
  */
74
- resource_policy_uri: z.string().url().optional(),
84
+ resource_policy_uri: webUriSchema.optional(),
75
85
 
76
86
  /**
77
87
  * OPTIONAL. URL that the protected resource provides to read about the
78
88
  * protected resource's terms of service
79
89
  */
80
- resource_tos_uri: z.string().url().optional(),
90
+ resource_tos_uri: webUriSchema.optional(),
81
91
  })
82
92
 
83
93
  export type OAuthProtectedResourceMetadata = z.infer<
@@ -0,0 +1,56 @@
1
+ import { TypeOf, z, ZodIssueCode } from 'zod'
2
+ import {
3
+ httpsUriSchema,
4
+ LoopbackUri,
5
+ loopbackUriSchema,
6
+ privateUseUriSchema,
7
+ } from './uri.js'
8
+
9
+ export const oauthLoopbackRedirectURISchema = loopbackUriSchema.superRefine(
10
+ (value, ctx): value is Exclude<LoopbackUri, `http://localhost${string}`> => {
11
+ if (value.startsWith('http://localhost')) {
12
+ // https://datatracker.ietf.org/doc/html/rfc8252#section-8.3
13
+ //
14
+ // > While redirect URIs using localhost (i.e.,
15
+ // > "http://localhost:{port}/{path}") function similarly to loopback IP
16
+ // > redirects described in Section 7.3, the use of localhost is NOT
17
+ // > RECOMMENDED. Specifying a redirect URI with the loopback IP literal
18
+ // > rather than localhost avoids inadvertently listening on network
19
+ // > interfaces other than the loopback interface. It is also less
20
+ // > susceptible to client-side firewalls and misconfigured host name
21
+ // > resolution on the user's device.
22
+ ctx.addIssue({
23
+ code: ZodIssueCode.custom,
24
+ message:
25
+ 'Use of "localhost" hostname is not allowed (RFC 8252), use a loopback IP such as "127.0.0.1" instead',
26
+ })
27
+ return false
28
+ }
29
+
30
+ return true
31
+ },
32
+ )
33
+ export type OAuthLoopbackRedirectURI = TypeOf<
34
+ typeof oauthLoopbackRedirectURISchema
35
+ >
36
+
37
+ export const oauthHttpsRedirectURISchema = httpsUriSchema
38
+ export type OAuthHttpsRedirectURI = TypeOf<typeof oauthHttpsRedirectURISchema>
39
+
40
+ export const oauthPrivateUseRedirectURISchema = privateUseUriSchema
41
+ export type OAuthPrivateUseRedirectURI = TypeOf<
42
+ typeof oauthPrivateUseRedirectURISchema
43
+ >
44
+
45
+ export const oauthRedirectUriSchema = z.union(
46
+ [
47
+ oauthLoopbackRedirectURISchema,
48
+ oauthHttpsRedirectURISchema,
49
+ oauthPrivateUseRedirectURISchema,
50
+ ],
51
+ {
52
+ message: `URL must use the "https:" or "http:" protocol, or a private-use URI scheme (RFC 8252)`,
53
+ },
54
+ )
55
+
56
+ export type OAuthRedirectUri = TypeOf<typeof oauthRedirectUriSchema>
package/src/uri.ts ADDED
@@ -0,0 +1,171 @@
1
+ import { TypeOf, z, ZodIssueCode } from 'zod'
2
+ import { isHostnameIP, isLoopbackHost } from './util.js'
3
+
4
+ /**
5
+ * Valid, but potentially dangerous URL (`data:`, `file:`, `javascript:`, etc.).
6
+ *
7
+ * Any value that matches this schema is safe to parse using `new URL()`.
8
+ */
9
+ export const dangerousUriSchema = z
10
+ .string()
11
+ .refine(
12
+ (data): data is `${string}:${string}` =>
13
+ data.includes(':') && URL.canParse(data),
14
+ {
15
+ message: 'Invalid URL',
16
+ },
17
+ )
18
+
19
+ /**
20
+ * Valid, but potentially dangerous URL (`data:`, `file:`, `javascript:`, etc.).
21
+ */
22
+ export type DangerousUrl = TypeOf<typeof dangerousUriSchema>
23
+
24
+ export const loopbackUriSchema = dangerousUriSchema.superRefine(
25
+ (
26
+ value,
27
+ ctx,
28
+ ): value is
29
+ | `http://[::1]${string}`
30
+ | `http://localhost${'' | `${':' | '/' | '?' | '#'}${string}`}`
31
+ | `http://127.0.0.1${'' | `${':' | '/' | '?' | '#'}${string}`}` => {
32
+ // Loopback url must use the "http:" protocol
33
+ if (!value.startsWith('http://')) {
34
+ ctx.addIssue({
35
+ code: ZodIssueCode.custom,
36
+ message: 'URL must use the "http:" protocol',
37
+ })
38
+ return false
39
+ }
40
+
41
+ const url = new URL(value)
42
+
43
+ if (!isLoopbackHost(url.hostname)) {
44
+ ctx.addIssue({
45
+ code: ZodIssueCode.custom,
46
+ message: 'URL must use "localhost", "127.0.0.1" or "[::1]" as hostname',
47
+ })
48
+ return false
49
+ }
50
+
51
+ return true
52
+ },
53
+ )
54
+
55
+ export type LoopbackUri = TypeOf<typeof loopbackUriSchema>
56
+
57
+ export const httpsUriSchema = dangerousUriSchema.superRefine(
58
+ (value, ctx): value is `https://${string}` => {
59
+ if (!value.startsWith('https://')) {
60
+ ctx.addIssue({
61
+ code: ZodIssueCode.custom,
62
+ message: 'URL must use the "https:" protocol',
63
+ })
64
+ return false
65
+ }
66
+
67
+ const url = new URL(value)
68
+
69
+ // Disallow loopback URLs with the `https:` protocol
70
+ if (isLoopbackHost(url.hostname)) {
71
+ ctx.addIssue({
72
+ code: ZodIssueCode.custom,
73
+ message: 'https: URL must not use a loopback host',
74
+ })
75
+ return false
76
+ }
77
+
78
+ if (isHostnameIP(url.hostname)) {
79
+ // Hostname is an IP address
80
+ } else {
81
+ // Hostname is a domain name
82
+ if (!url.hostname.includes('.')) {
83
+ // we don't depend on PSL here, so we only check for a dot
84
+ ctx.addIssue({
85
+ code: ZodIssueCode.custom,
86
+ message: 'Domain name must contain at least two segments',
87
+ })
88
+ return false
89
+ }
90
+
91
+ if (url.hostname.endsWith('.local')) {
92
+ ctx.addIssue({
93
+ code: ZodIssueCode.custom,
94
+ message: 'Domain name must not end with ".local"',
95
+ })
96
+ return false
97
+ }
98
+ }
99
+
100
+ return true
101
+ },
102
+ )
103
+
104
+ export type HttpsUri = TypeOf<typeof httpsUriSchema>
105
+
106
+ export const webUriSchema = z
107
+ .string()
108
+ .superRefine((value, ctx): value is LoopbackUri | HttpsUri => {
109
+ // discriminated union of `loopbackUriSchema` and `httpsUriSchema`
110
+ if (value.startsWith('http://')) {
111
+ const result = loopbackUriSchema.safeParse(value)
112
+ if (!result.success) result.error.issues.forEach(ctx.addIssue, ctx)
113
+ return result.success
114
+ }
115
+
116
+ if (value.startsWith('https://')) {
117
+ const result = httpsUriSchema.safeParse(value)
118
+ if (!result.success) result.error.issues.forEach(ctx.addIssue, ctx)
119
+ return result.success
120
+ }
121
+
122
+ ctx.addIssue({
123
+ code: ZodIssueCode.custom,
124
+ message: 'URL must use the "http:" or "https:" protocol',
125
+ })
126
+ return false
127
+ })
128
+
129
+ export type WebUri = TypeOf<typeof webUriSchema>
130
+
131
+ export const privateUseUriSchema = dangerousUriSchema.superRefine(
132
+ (value, ctx): value is `${string}.${string}:/${string}` => {
133
+ const dotIdx = value.indexOf('.')
134
+ const colonIdx = value.indexOf(':')
135
+
136
+ // Optimization: avoid parsing the URL if the protocol does not contain a "."
137
+ if (dotIdx === -1 || colonIdx === -1 || dotIdx > colonIdx) {
138
+ ctx.addIssue({
139
+ code: ZodIssueCode.custom,
140
+ message:
141
+ 'Private-use URI scheme requires a "." as part of the protocol',
142
+ })
143
+ return false
144
+ }
145
+
146
+ const url = new URL(value)
147
+
148
+ // Should be covered by the check before, but let's be extra sure
149
+ if (!url.protocol.includes('.')) {
150
+ ctx.addIssue({
151
+ code: ZodIssueCode.custom,
152
+ message: 'Invalid private-use URI scheme',
153
+ })
154
+ return false
155
+ }
156
+
157
+ if (url.hostname) {
158
+ // https://datatracker.ietf.org/doc/html/rfc8252#section-7.1
159
+ ctx.addIssue({
160
+ code: ZodIssueCode.custom,
161
+ message:
162
+ 'Private-use URI schemes must not include a hostname (only one "/" is allowed after the protocol, as per RFC 8252)',
163
+ })
164
+ return false
165
+ }
166
+
167
+ return true
168
+ },
169
+ )
170
+
171
+ export type PrivateUseUri = TypeOf<typeof privateUseUriSchema>
@@ -1 +1 @@
1
- {"root":["./src/atproto-loopback-client-metadata.ts","./src/constants.ts","./src/index.ts","./src/oauth-access-token.ts","./src/oauth-authorization-code-grant-token-request.ts","./src/oauth-authorization-details.ts","./src/oauth-authorization-request-jar.ts","./src/oauth-authorization-request-par.ts","./src/oauth-authorization-request-parameters.ts","./src/oauth-authorization-request-query.ts","./src/oauth-authorization-request-uri.ts","./src/oauth-authorization-server-metadata.ts","./src/oauth-client-credentials-grant-token-request.ts","./src/oauth-client-credentials.ts","./src/oauth-client-id-discoverable.ts","./src/oauth-client-id-loopback.ts","./src/oauth-client-id.ts","./src/oauth-client-metadata.ts","./src/oauth-code-challenge-method.ts","./src/oauth-endpoint-auth-method.ts","./src/oauth-endpoint-name.ts","./src/oauth-grant-type.ts","./src/oauth-introspection-response.ts","./src/oauth-issuer-identifier.ts","./src/oauth-par-response.ts","./src/oauth-password-grant-token-request.ts","./src/oauth-protected-resource-metadata.ts","./src/oauth-refresh-token-grant-token-request.ts","./src/oauth-refresh-token.ts","./src/oauth-request-uri.ts","./src/oauth-response-mode.ts","./src/oauth-response-type.ts","./src/oauth-scope.ts","./src/oauth-token-identification.ts","./src/oauth-token-request.ts","./src/oauth-token-response.ts","./src/oauth-token-type.ts","./src/oidc-claims-parameter.ts","./src/oidc-claims-properties.ts","./src/oidc-entity-type.ts","./src/util.ts"],"version":"5.6.3"}
1
+ {"root":["./src/atproto-loopback-client-metadata.ts","./src/constants.ts","./src/index.ts","./src/oauth-access-token.ts","./src/oauth-authorization-code-grant-token-request.ts","./src/oauth-authorization-details.ts","./src/oauth-authorization-request-jar.ts","./src/oauth-authorization-request-par.ts","./src/oauth-authorization-request-parameters.ts","./src/oauth-authorization-request-query.ts","./src/oauth-authorization-request-uri.ts","./src/oauth-authorization-server-metadata.ts","./src/oauth-client-credentials-grant-token-request.ts","./src/oauth-client-credentials.ts","./src/oauth-client-id-discoverable.ts","./src/oauth-client-id-loopback.ts","./src/oauth-client-id.ts","./src/oauth-client-metadata.ts","./src/oauth-code-challenge-method.ts","./src/oauth-endpoint-auth-method.ts","./src/oauth-endpoint-name.ts","./src/oauth-grant-type.ts","./src/oauth-introspection-response.ts","./src/oauth-issuer-identifier.ts","./src/oauth-par-response.ts","./src/oauth-password-grant-token-request.ts","./src/oauth-protected-resource-metadata.ts","./src/oauth-redirect-uri.ts","./src/oauth-refresh-token-grant-token-request.ts","./src/oauth-refresh-token.ts","./src/oauth-request-uri.ts","./src/oauth-response-mode.ts","./src/oauth-response-type.ts","./src/oauth-scope.ts","./src/oauth-token-identification.ts","./src/oauth-token-request.ts","./src/oauth-token-response.ts","./src/oauth-token-type.ts","./src/oidc-claims-parameter.ts","./src/oidc-claims-properties.ts","./src/oidc-entity-type.ts","./src/uri.ts","./src/util.ts"],"version":"5.6.3"}