@atproto/oauth-types 0.1.4 → 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.
Files changed (165) hide show
  1. package/CHANGELOG.md +44 -0
  2. package/dist/atproto-loopback-client-metadata.d.ts.map +1 -1
  3. package/dist/atproto-loopback-client-metadata.js +4 -16
  4. package/dist/atproto-loopback-client-metadata.js.map +1 -1
  5. package/dist/constants.d.ts +0 -6
  6. package/dist/constants.d.ts.map +1 -1
  7. package/dist/constants.js +1 -17
  8. package/dist/constants.js.map +1 -1
  9. package/dist/index.d.ts +18 -5
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +18 -5
  12. package/dist/index.js.map +1 -1
  13. package/dist/oauth-access-token.d.ts +4 -0
  14. package/dist/oauth-access-token.d.ts.map +1 -0
  15. package/dist/oauth-access-token.js +6 -0
  16. package/dist/oauth-access-token.js.map +1 -0
  17. package/dist/oauth-authorization-code-grant-token-request.d.ts +20 -0
  18. package/dist/oauth-authorization-code-grant-token-request.d.ts.map +1 -0
  19. package/dist/oauth-authorization-code-grant-token-request.js +17 -0
  20. package/dist/oauth-authorization-code-grant-token-request.js.map +1 -0
  21. package/dist/oauth-authorization-request-jar.d.ts +16 -0
  22. package/dist/oauth-authorization-request-jar.d.ts.map +1 -0
  23. package/dist/oauth-authorization-request-jar.js +15 -0
  24. package/dist/oauth-authorization-request-jar.js.map +1 -0
  25. package/dist/oauth-authorization-request-par.d.ts +122 -0
  26. package/dist/oauth-authorization-request-par.d.ts.map +1 -0
  27. package/dist/oauth-authorization-request-par.js +11 -0
  28. package/dist/oauth-authorization-request-par.js.map +1 -0
  29. package/dist/{oauth-authentication-request-parameters.d.ts → oauth-authorization-request-parameters.d.ts} +18 -18
  30. package/dist/oauth-authorization-request-parameters.d.ts.map +1 -0
  31. package/dist/{oauth-authentication-request-parameters.js → oauth-authorization-request-parameters.js} +16 -17
  32. package/dist/oauth-authorization-request-parameters.js.map +1 -0
  33. package/dist/oauth-authorization-request-query.d.ts +128 -0
  34. package/dist/oauth-authorization-request-query.d.ts.map +1 -0
  35. package/dist/oauth-authorization-request-query.js +13 -0
  36. package/dist/oauth-authorization-request-query.js.map +1 -0
  37. package/dist/oauth-authorization-request-uri.d.ts +10 -0
  38. package/dist/oauth-authorization-request-uri.d.ts.map +1 -0
  39. package/dist/oauth-authorization-request-uri.js +9 -0
  40. package/dist/oauth-authorization-request-uri.js.map +1 -0
  41. package/dist/oauth-authorization-server-metadata.d.ts +16 -16
  42. package/dist/oauth-authorization-server-metadata.d.ts.map +1 -1
  43. package/dist/oauth-authorization-server-metadata.js +5 -1
  44. package/dist/oauth-authorization-server-metadata.js.map +1 -1
  45. package/dist/oauth-client-credentials-grant-token-request.d.ts +10 -0
  46. package/dist/oauth-client-credentials-grant-token-request.d.ts.map +1 -0
  47. package/dist/oauth-client-credentials-grant-token-request.js +8 -0
  48. package/dist/oauth-client-credentials-grant-token-request.js.map +1 -0
  49. package/dist/oauth-client-credentials.d.ts +18 -2
  50. package/dist/oauth-client-credentials.d.ts.map +1 -1
  51. package/dist/oauth-client-credentials.js +8 -2
  52. package/dist/oauth-client-credentials.js.map +1 -1
  53. package/dist/oauth-client-id-discoverable.d.ts +3 -2
  54. package/dist/oauth-client-id-discoverable.d.ts.map +1 -1
  55. package/dist/oauth-client-id-discoverable.js +22 -20
  56. package/dist/oauth-client-id-discoverable.js.map +1 -1
  57. package/dist/oauth-client-id-loopback.d.ts +10 -3
  58. package/dist/oauth-client-id-loopback.d.ts.map +1 -1
  59. package/dist/oauth-client-id-loopback.js +59 -23
  60. package/dist/oauth-client-id-loopback.js.map +1 -1
  61. package/dist/oauth-client-metadata.d.ts +91 -91
  62. package/dist/oauth-client-metadata.d.ts.map +1 -1
  63. package/dist/oauth-client-metadata.js +2 -1
  64. package/dist/oauth-client-metadata.js.map +1 -1
  65. package/dist/oauth-code-challenge-method.d.ts +3 -0
  66. package/dist/oauth-code-challenge-method.d.ts.map +1 -0
  67. package/dist/oauth-code-challenge-method.js +6 -0
  68. package/dist/oauth-code-challenge-method.js.map +1 -0
  69. package/dist/oauth-introspection-response.d.ts +20 -0
  70. package/dist/oauth-introspection-response.d.ts.map +1 -0
  71. package/dist/oauth-introspection-response.js +3 -0
  72. package/dist/oauth-introspection-response.js.map +1 -0
  73. package/dist/oauth-issuer-identifier.d.ts +2 -1
  74. package/dist/oauth-issuer-identifier.d.ts.map +1 -1
  75. package/dist/oauth-issuer-identifier.js +13 -12
  76. package/dist/oauth-issuer-identifier.js.map +1 -1
  77. package/dist/oauth-par-response.d.ts +3 -0
  78. package/dist/oauth-par-response.d.ts.map +1 -1
  79. package/dist/oauth-par-response.js +1 -0
  80. package/dist/oauth-par-response.js.map +1 -1
  81. package/dist/oauth-password-grant-token-request.d.ts +16 -0
  82. package/dist/oauth-password-grant-token-request.d.ts.map +1 -0
  83. package/dist/oauth-password-grant-token-request.js +10 -0
  84. package/dist/oauth-password-grant-token-request.js.map +1 -0
  85. package/dist/oauth-protected-resource-metadata.d.ts +2 -2
  86. package/dist/oauth-refresh-token-grant-token-request.d.ts +13 -0
  87. package/dist/oauth-refresh-token-grant-token-request.d.ts.map +1 -0
  88. package/dist/oauth-refresh-token-grant-token-request.js +10 -0
  89. package/dist/oauth-refresh-token-grant-token-request.js.map +1 -0
  90. package/dist/oauth-refresh-token.d.ts +4 -0
  91. package/dist/oauth-refresh-token.d.ts.map +1 -0
  92. package/dist/oauth-refresh-token.js +6 -0
  93. package/dist/oauth-refresh-token.js.map +1 -0
  94. package/dist/oauth-request-uri.d.ts +4 -0
  95. package/dist/oauth-request-uri.d.ts.map +1 -0
  96. package/dist/oauth-request-uri.js +6 -0
  97. package/dist/oauth-request-uri.js.map +1 -0
  98. package/dist/oauth-scope.d.ts +10 -0
  99. package/dist/oauth-scope.d.ts.map +1 -0
  100. package/dist/oauth-scope.js +16 -0
  101. package/dist/oauth-scope.js.map +1 -0
  102. package/dist/oauth-token-identification.d.ts +13 -0
  103. package/dist/oauth-token-identification.d.ts.map +1 -0
  104. package/dist/oauth-token-identification.js +11 -0
  105. package/dist/oauth-token-identification.js.map +1 -0
  106. package/dist/oauth-token-request.d.ts +46 -0
  107. package/dist/oauth-token-request.d.ts.map +1 -0
  108. package/dist/oauth-token-request.js +15 -0
  109. package/dist/oauth-token-request.js.map +1 -0
  110. package/dist/oauth-token-response.d.ts +3 -6
  111. package/dist/oauth-token-response.d.ts.map +1 -1
  112. package/dist/oauth-token-response.js +4 -2
  113. package/dist/oauth-token-response.js.map +1 -1
  114. package/dist/util.d.ts +2 -1
  115. package/dist/util.d.ts.map +1 -1
  116. package/dist/util.js +36 -6
  117. package/dist/util.js.map +1 -1
  118. package/package.json +2 -2
  119. package/src/atproto-loopback-client-metadata.ts +7 -20
  120. package/src/constants.ts +0 -16
  121. package/src/index.ts +18 -5
  122. package/src/oauth-access-token.ts +4 -0
  123. package/src/oauth-authorization-code-grant-token-request.ts +18 -0
  124. package/src/oauth-authorization-request-jar.ts +16 -0
  125. package/src/oauth-authorization-request-par.ts +13 -0
  126. package/src/{oauth-authentication-request-parameters.ts → oauth-authorization-request-parameters.ts} +21 -22
  127. package/src/oauth-authorization-request-query.ts +15 -0
  128. package/src/oauth-authorization-request-uri.ts +11 -0
  129. package/src/oauth-authorization-server-metadata.ts +5 -1
  130. package/src/oauth-client-credentials-grant-token-request.ts +9 -0
  131. package/src/oauth-client-credentials.ts +21 -1
  132. package/src/oauth-client-id-discoverable.ts +29 -26
  133. package/src/oauth-client-id-loopback.ts +78 -30
  134. package/src/oauth-client-metadata.ts +2 -1
  135. package/src/oauth-code-challenge-method.ts +3 -0
  136. package/src/oauth-introspection-response.ts +23 -0
  137. package/src/oauth-issuer-identifier.ts +17 -12
  138. package/src/oauth-par-response.ts +1 -0
  139. package/src/oauth-password-grant-token-request.ts +11 -0
  140. package/src/oauth-refresh-token-grant-token-request.ts +11 -0
  141. package/src/oauth-refresh-token.ts +4 -0
  142. package/src/oauth-request-uri.ts +5 -0
  143. package/src/oauth-scope.ts +15 -0
  144. package/src/oauth-token-identification.ts +12 -0
  145. package/src/oauth-token-request.ts +14 -0
  146. package/src/oauth-token-response.ts +4 -2
  147. package/src/util.ts +41 -1
  148. package/tsconfig.build.tsbuildinfo +1 -0
  149. package/dist/access-token.d.ts +0 -4
  150. package/dist/access-token.d.ts.map +0 -1
  151. package/dist/access-token.js +0 -6
  152. package/dist/access-token.js.map +0 -1
  153. package/dist/oauth-authentication-request-parameters.d.ts.map +0 -1
  154. package/dist/oauth-authentication-request-parameters.js.map +0 -1
  155. package/dist/oauth-client-id-url.d.ts +0 -3
  156. package/dist/oauth-client-id-url.d.ts.map +0 -1
  157. package/dist/oauth-client-id-url.js +0 -21
  158. package/dist/oauth-client-id-url.js.map +0 -1
  159. package/dist/oauth-client-identification.d.ts +0 -31
  160. package/dist/oauth-client-identification.d.ts.map +0 -1
  161. package/dist/oauth-client-identification.js +0 -12
  162. package/dist/oauth-client-identification.js.map +0 -1
  163. package/src/access-token.ts +0 -4
  164. package/src/oauth-client-id-url.ts +0 -25
  165. package/src/oauth-client-identification.ts +0 -14
@@ -1,12 +1,15 @@
1
- import { parseOAuthClientIdUrl } from './oauth-client-id-url.js'
2
1
  import { OAuthClientId } from './oauth-client-id.js'
2
+ import { OAuthScope, oauthScopeSchema } from './oauth-scope.js'
3
+ import { isLoopbackHost, safeUrl } from './util.js'
4
+
5
+ const OAUTH_CLIENT_ID_LOOPBACK_URL = 'http://localhost'
3
6
 
4
7
  export type OAuthClientIdLoopback = OAuthClientId &
5
- `http://localhost${'' | `${'/' | '?' | '#'}${string}`}`
8
+ `${typeof OAUTH_CLIENT_ID_LOOPBACK_URL}${'' | '/'}${'' | `?${string}`}`
6
9
 
7
- export function isOAuthClientIdLoopback<C extends OAuthClientId>(
8
- clientId: C,
9
- ): clientId is C & OAuthClientIdLoopback {
10
+ export function isOAuthClientIdLoopback(
11
+ clientId: string,
12
+ ): clientId is OAuthClientIdLoopback {
10
13
  try {
11
14
  parseOAuthLoopbackClientId(clientId)
12
15
  return true
@@ -15,44 +18,89 @@ export function isOAuthClientIdLoopback<C extends OAuthClientId>(
15
18
  }
16
19
  }
17
20
 
18
- export function parseOAuthLoopbackClientId(clientId: OAuthClientId): URL {
19
- const url = parseOAuthClientIdUrl(clientId)
20
-
21
- // Optimization: cheap checks first
21
+ export function assertOAuthLoopbackClientId(
22
+ clientId: string,
23
+ ): asserts clientId is OAuthClientIdLoopback {
24
+ void parseOAuthLoopbackClientId(clientId)
25
+ }
22
26
 
23
- if (url.protocol !== 'http:') {
24
- throw new TypeError('Loopback ClientID must use the "http:" protocol')
27
+ // @TODO: should we turn this into a zod schema? (more coherent error with other
28
+ // validation functions)
29
+ export function parseOAuthLoopbackClientId(clientId: string): {
30
+ scope?: OAuthScope
31
+ redirect_uris?: [string, ...string[]]
32
+ } {
33
+ if (!clientId.startsWith(OAUTH_CLIENT_ID_LOOPBACK_URL)) {
34
+ throw new TypeError(
35
+ `Loopback ClientID must start with "${OAUTH_CLIENT_ID_LOOPBACK_URL}"`,
36
+ )
37
+ } else if (clientId.includes('#', OAUTH_CLIENT_ID_LOOPBACK_URL.length)) {
38
+ throw new TypeError('Loopback ClientID must not contain a hash component')
25
39
  }
26
40
 
27
- if (url.hostname !== 'localhost') {
28
- throw new TypeError('Loopback ClientID must use the "localhost" hostname')
41
+ const queryStringIdx =
42
+ clientId.length > OAUTH_CLIENT_ID_LOOPBACK_URL.length &&
43
+ clientId[OAUTH_CLIENT_ID_LOOPBACK_URL.length] === '/'
44
+ ? OAUTH_CLIENT_ID_LOOPBACK_URL.length + 1
45
+ : OAUTH_CLIENT_ID_LOOPBACK_URL.length
46
+
47
+ if (clientId.length === queryStringIdx) {
48
+ return {} // no query string to parse
29
49
  }
30
50
 
31
- if (url.hash) {
32
- throw new TypeError('Loopback ClientID must not contain a fragment')
51
+ if (clientId[queryStringIdx] !== '?') {
52
+ throw new TypeError('Loopback ClientID must not contain a path component')
33
53
  }
34
54
 
35
- if (url.username || url.password) {
36
- throw new TypeError('Loopback ClientID must not contain credentials')
55
+ const searchParams = new URLSearchParams(clientId.slice(queryStringIdx + 1))
56
+
57
+ for (const name of searchParams.keys()) {
58
+ if (name !== 'redirect_uri' && name !== 'scope') {
59
+ throw new TypeError(`Invalid query parameter "${name}" in client ID`)
60
+ }
37
61
  }
38
62
 
39
- if (url.port) {
40
- throw new TypeError('Loopback ClientID must not contain a port')
63
+ const scope = searchParams.get('scope') ?? undefined
64
+ if (scope != null) {
65
+ if (searchParams.getAll('scope').length > 1) {
66
+ throw new TypeError(
67
+ 'Loopback ClientID must contain at most one scope query parameter',
68
+ )
69
+ } else if (!oauthScopeSchema.safeParse(scope).success) {
70
+ throw new TypeError('Invalid scope query parameter in client ID')
71
+ }
41
72
  }
42
73
 
43
- // Note: url.pathname === '/' is allowed for loopback URIs
74
+ const redirect_uris = searchParams.has('redirect_uri')
75
+ ? (searchParams.getAll('redirect_uri') as [string, ...string[]])
76
+ : undefined
44
77
 
45
- if (url.pathname !== '/' && url.pathname.endsWith('/')) {
46
- throw new TypeError('Loopback ClientID must not end with a trailing slash')
78
+ if (redirect_uris) {
79
+ for (const uri of redirect_uris) {
80
+ const url = safeUrl(uri)
81
+ if (!url) {
82
+ throw new TypeError(`Invalid redirect_uri in client ID: ${uri}`)
83
+ }
84
+ if (url.protocol !== 'http:') {
85
+ throw new TypeError(
86
+ `Loopback ClientID must use "http:" redirect_uri's (got ${uri})`,
87
+ )
88
+ }
89
+ if (url.hostname === 'localhost') {
90
+ throw new TypeError(
91
+ `Loopback ClientID must not use "localhost" as redirect_uri hostname (got ${uri})`,
92
+ )
93
+ }
94
+ if (!isLoopbackHost(url.hostname)) {
95
+ throw new TypeError(
96
+ `Loopback ClientID must use loopback addresses as redirect_uri's (got ${uri})`,
97
+ )
98
+ }
99
+ }
47
100
  }
48
101
 
49
- if (url.pathname.includes('//')) {
50
- throw new TypeError(
51
- `Loopback ClientID must not contain any double slashes in its path`,
52
- )
102
+ return {
103
+ scope,
104
+ redirect_uris,
53
105
  }
54
-
55
- // Note: Query string is allowed
56
-
57
- return url
58
106
  }
@@ -5,6 +5,7 @@ import { oauthClientIdSchema } from './oauth-client-id.js'
5
5
  import { oauthEndpointAuthMethod } from './oauth-endpoint-auth-method.js'
6
6
  import { oauthGrantTypeSchema } from './oauth-grant-type.js'
7
7
  import { oauthResponseTypeSchema } from './oauth-response-type.js'
8
+ import { oauthScopeSchema } from './oauth-scope.js'
8
9
 
9
10
  // https://openid.net/specs/openid-connect-registration-1_0.html
10
11
  // https://datatracker.ietf.org/doc/html/rfc7591
@@ -22,7 +23,7 @@ export const oauthClientMetadataSchema = z.object({
22
23
  // > If omitted, the default behavior is that the client will use only the
23
24
  // > "authorization_code" Grant Type.
24
25
  .default(['authorization_code']),
25
- scope: z.string().optional(),
26
+ scope: oauthScopeSchema.optional(),
26
27
  token_endpoint_auth_method: oauthEndpointAuthMethod
27
28
  .default('none')
28
29
  .optional(),
@@ -0,0 +1,3 @@
1
+ import { z } from 'zod'
2
+
3
+ export const oauthCodeChallengeMethodSchema = z.enum(['S256', 'plain'])
@@ -0,0 +1,23 @@
1
+ import { OAuthAuthorizationDetails } from './oauth-authorization-details.js'
2
+ import { OAuthTokenType } from './oauth-token-type.js'
3
+
4
+ // https://datatracker.ietf.org/doc/html/rfc7662#section-2.2
5
+ export type OAuthIntrospectionResponse =
6
+ | { active: false }
7
+ | {
8
+ active: true
9
+
10
+ scope?: string
11
+ client_id?: string
12
+ username?: string
13
+ token_type?: OAuthTokenType
14
+ authorization_details?: OAuthAuthorizationDetails
15
+
16
+ aud?: string | [string, ...string[]]
17
+ exp?: number
18
+ iat?: number
19
+ iss?: string
20
+ jti?: string
21
+ nbf?: number
22
+ sub?: string
23
+ }
@@ -1,10 +1,9 @@
1
1
  import { z } from 'zod'
2
- import { ALLOW_UNSECURE_ORIGINS } from './constants.js'
3
2
  import { safeUrl } from './util.js'
4
3
 
5
4
  export const oauthIssuerIdentifierSchema = z
6
5
  .string()
7
- .superRefine((value, ctx) => {
6
+ .superRefine((value, ctx): value is `${'http' | 'https'}://${string}` => {
8
7
  // Validate the issuer (MIX-UP attacks)
9
8
 
10
9
  if (value.endsWith('/')) {
@@ -12,25 +11,24 @@ export const oauthIssuerIdentifierSchema = z
12
11
  code: z.ZodIssueCode.custom,
13
12
  message: 'Issuer URL must not end with a slash',
14
13
  })
14
+ return false
15
15
  }
16
16
 
17
17
  const url = safeUrl(value)
18
18
  if (!url) {
19
- return ctx.addIssue({
19
+ ctx.addIssue({
20
20
  code: z.ZodIssueCode.custom,
21
21
  message: 'Invalid url',
22
22
  })
23
+ return false
23
24
  }
24
25
 
25
- if (url.protocol !== 'https:') {
26
- if (ALLOW_UNSECURE_ORIGINS && url.protocol === 'http:') {
27
- // We'll allow HTTP in development mode
28
- } else {
29
- ctx.addIssue({
30
- code: z.ZodIssueCode.custom,
31
- message: 'Issuer must be an HTTPS URL',
32
- })
33
- }
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
34
32
  }
35
33
 
36
34
  if (url.username || url.password) {
@@ -38,6 +36,7 @@ export const oauthIssuerIdentifierSchema = z
38
36
  code: z.ZodIssueCode.custom,
39
37
  message: 'Issuer URL must not contain a username or password',
40
38
  })
39
+ return false
41
40
  }
42
41
 
43
42
  if (url.hash || url.search) {
@@ -45,6 +44,7 @@ export const oauthIssuerIdentifierSchema = z
45
44
  code: z.ZodIssueCode.custom,
46
45
  message: 'Issuer URL must not contain a query or fragment',
47
46
  })
47
+ return false
48
48
  }
49
49
 
50
50
  const canonicalValue = url.pathname === '/' ? url.origin : url.href
@@ -53,5 +53,10 @@ export const oauthIssuerIdentifierSchema = z
53
53
  code: z.ZodIssueCode.custom,
54
54
  message: 'Issuer URL must be in the canonical form',
55
55
  })
56
+ return false
56
57
  }
58
+
59
+ return true
57
60
  })
61
+
62
+ export type OAuthIssuerIdentifier = z.infer<typeof oauthIssuerIdentifierSchema>
@@ -2,6 +2,7 @@ import { z } from 'zod'
2
2
 
3
3
  export const oauthParResponseSchema = z.object({
4
4
  request_uri: z.string(),
5
+ expires_in: z.number().int().positive(),
5
6
  })
6
7
 
7
8
  export type OAuthParResponse = z.infer<typeof oauthParResponseSchema>
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod'
2
+
3
+ export const oauthPasswordGrantTokenRequestSchema = z.object({
4
+ grant_type: z.literal('password'),
5
+ username: z.string(),
6
+ password: z.string(),
7
+ })
8
+
9
+ export type OAuthPasswordGrantTokenRequest = z.infer<
10
+ typeof oauthPasswordGrantTokenRequestSchema
11
+ >
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod'
2
+ import { oauthRefreshTokenSchema } from './oauth-refresh-token.js'
3
+
4
+ export const oauthRefreshTokenGrantTokenRequestSchema = z.object({
5
+ grant_type: z.literal('refresh_token'),
6
+ refresh_token: oauthRefreshTokenSchema,
7
+ })
8
+
9
+ export type OAuthRefreshTokenGrantTokenRequest = z.infer<
10
+ typeof oauthRefreshTokenGrantTokenRequestSchema
11
+ >
@@ -0,0 +1,4 @@
1
+ import { z } from 'zod'
2
+
3
+ export const oauthRefreshTokenSchema = z.string().min(1)
4
+ export type OAuthRefreshToken = z.infer<typeof oauthRefreshTokenSchema>
@@ -0,0 +1,5 @@
1
+ import { z } from 'zod'
2
+
3
+ export const oauthRequestUriSchema = z.string()
4
+
5
+ export type OAuthRequestUri = z.infer<typeof oauthRequestUriSchema>
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod'
2
+
3
+ /**
4
+ * A space separated list of most non-control ASCII characters except backslash
5
+ * and double quote.
6
+ *
7
+ * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-11#section-1.4.1}
8
+ */
9
+ export const oauthScopeSchema = z
10
+ .string()
11
+ // scope = scope-token *( SP scope-token )
12
+ // scope-token = 1*( %x21 / %x23-5B / %x5D-7E )
13
+ .regex(/^[\x21\x23-\x5B\x5D-\x7E]+(?: [\x21\x23-\x5B\x5D-\x7E]+)*$/)
14
+
15
+ export type OAuthScope = z.infer<typeof oauthScopeSchema>
@@ -0,0 +1,12 @@
1
+ import { z } from 'zod'
2
+ import { oauthAccessTokenSchema } from './oauth-access-token.js'
3
+ import { oauthRefreshTokenSchema } from './oauth-refresh-token.js'
4
+
5
+ export const oauthTokenIdentificationSchema = z.object({
6
+ token: z.union([oauthAccessTokenSchema, oauthRefreshTokenSchema]),
7
+ token_type_hint: z.enum(['access_token', 'refresh_token']).optional(),
8
+ })
9
+
10
+ export type OAuthTokenIdentification = z.infer<
11
+ typeof oauthTokenIdentificationSchema
12
+ >
@@ -0,0 +1,14 @@
1
+ import { z } from 'zod'
2
+ import { oauthAuthorizationCodeGrantTokenRequestSchema } from './oauth-authorization-code-grant-token-request.js'
3
+ import { oauthClientCredentialsGrantTokenRequestSchema } from './oauth-client-credentials-grant-token-request.js'
4
+ import { oauthPasswordGrantTokenRequestSchema } from './oauth-password-grant-token-request.js'
5
+ import { oauthRefreshTokenGrantTokenRequestSchema } from './oauth-refresh-token-grant-token-request.js'
6
+
7
+ export const oauthTokenRequestSchema = z.discriminatedUnion('grant_type', [
8
+ oauthAuthorizationCodeGrantTokenRequestSchema,
9
+ oauthRefreshTokenGrantTokenRequestSchema,
10
+ oauthPasswordGrantTokenRequestSchema,
11
+ oauthClientCredentialsGrantTokenRequestSchema,
12
+ ])
13
+
14
+ export type OAuthTokenRequest = z.infer<typeof oauthTokenRequestSchema>
@@ -9,13 +9,15 @@ import { oauthTokenTypeSchema } from './oauth-token-type.js'
9
9
  */
10
10
  export const oauthTokenResponseSchema = z
11
11
  .object({
12
+ // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
12
13
  access_token: z.string(),
13
14
  token_type: oauthTokenTypeSchema,
14
- issuer: z.string().url().optional(),
15
15
  scope: z.string().optional(),
16
- id_token: signedJwtSchema.optional(),
17
16
  refresh_token: z.string().optional(),
18
17
  expires_in: z.number().optional(),
18
+ // https://openid.net/specs/openid-connect-core-1_0.html#TokenResponse
19
+ id_token: signedJwtSchema.optional(),
20
+ // https://datatracker.ietf.org/doc/html/rfc9396#name-enriched-authorization-deta
19
21
  authorization_details: oauthAuthorizationDetailsSchema.optional(),
20
22
  })
21
23
  // https://www.rfc-editor.org/rfc/rfc6749.html#section-5.1
package/src/util.ts CHANGED
@@ -1,4 +1,4 @@
1
- export function isIP(hostname: string) {
1
+ export function isHostnameIP(hostname: string) {
2
2
  // IPv4
3
3
  if (hostname.match(/^\d+\.\d+\.\d+\.\d+$/)) return true
4
4
 
@@ -26,3 +26,43 @@ export function safeUrl(input: URL | string): URL | null {
26
26
  return null
27
27
  }
28
28
  }
29
+
30
+ export function extractUrlPath(url) {
31
+ // Extracts the path from a URL, without relying on the URL constructor
32
+ // (because it normalizes the URL)
33
+ const endOfProtocol = url.startsWith('https://')
34
+ ? 8
35
+ : url.startsWith('http://')
36
+ ? 7
37
+ : -1
38
+ if (endOfProtocol === -1) {
39
+ throw new TypeError('URL must use the "https:" or "http:" protocol')
40
+ }
41
+
42
+ const hashIdx = url.indexOf('#', endOfProtocol)
43
+ const questionIdx = url.indexOf('?', endOfProtocol)
44
+
45
+ const queryStrIdx =
46
+ questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx)
47
+ ? questionIdx
48
+ : -1
49
+
50
+ const pathEnd =
51
+ hashIdx === -1
52
+ ? queryStrIdx === -1
53
+ ? url.length
54
+ : queryStrIdx
55
+ : queryStrIdx === -1
56
+ ? hashIdx
57
+ : Math.min(hashIdx, queryStrIdx)
58
+
59
+ const slashIdx = url.indexOf('/', endOfProtocol)
60
+
61
+ const pathStart = slashIdx === -1 || slashIdx > pathEnd ? pathEnd : slashIdx
62
+
63
+ if (endOfProtocol === pathStart) {
64
+ throw new TypeError('URL must contain a host')
65
+ }
66
+
67
+ return url.substring(pathStart, pathEnd)
68
+ }
@@ -0,0 +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,4 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const accessTokenSchema: z.ZodString;
3
- export type AccessToken = z.infer<typeof accessTokenSchema>;
4
- //# sourceMappingURL=access-token.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"access-token.d.ts","sourceRoot":"","sources":["../src/access-token.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAEvB,eAAO,MAAM,iBAAiB,aAAoB,CAAA;AAClD,MAAM,MAAM,WAAW,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,iBAAiB,CAAC,CAAA"}
@@ -1,6 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.accessTokenSchema = void 0;
4
- const zod_1 = require("zod");
5
- exports.accessTokenSchema = zod_1.z.string().min(1);
6
- //# sourceMappingURL=access-token.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"access-token.js","sourceRoot":"","sources":["../src/access-token.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEV,QAAA,iBAAiB,GAAG,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth-authentication-request-parameters.d.ts","sourceRoot":"","sources":["../src/oauth-authentication-request-parameters.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AASvB;;GAEG;AACH,eAAO,MAAM,0CAA0C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAiErD;;;;;OAKG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAKH,CAAA;AAEF;;GAEG;AACH,MAAM,MAAM,oCAAoC,GAAG,CAAC,CAAC,KAAK,CACxD,OAAO,0CAA0C,CAClD,CAAA"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth-authentication-request-parameters.js","sourceRoot":"","sources":["../src/oauth-authentication-request-parameters.ts"],"names":[],"mappings":";;;AAAA,sCAA8C;AAC9C,6BAAuB;AAEvB,qFAAkF;AAClF,6DAA0D;AAC1D,qEAAkE;AAClE,yEAAsE;AACtE,2EAAwE;AACxE,+DAA4D;AAE5D;;GAEG;AACU,QAAA,0CAA0C,GAAG,OAAC,CAAC,MAAM,CAAC;IACjE,SAAS,EAAE,wCAAmB;IAE9B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,KAAK,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAC5B,QAAQ,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IAE/B,aAAa,EAAE,gDAAuB;IAEtC,kCAAkC;IAClC,aAAa,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,UAAU,EAAE,WAAW,CAAC,CAAC,CAAC,QAAQ,EAAE;IAEpE,OAAO;IACP,cAAc,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACrC,qBAAqB,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,QAAQ,EAAE;IAE3E,YAAY,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IAEzC,+EAA+E;IAC/E,gDAAgD;IAChD,+CAA+C;IAC/C,sEAAsE;IACtE,KAAK,EAAE,OAAC;SACL,MAAM,EAAE;SACR,KAAK,CAAC,oDAAoD,CAAC;SAC3D,QAAQ,EAAE;IAEb,OAAO;IAEP,0EAA0E;IAC1E,wEAAwE;IACxE,2EAA2E;IAC3E,6EAA6E;IAC7E,4EAA4E;IAC5E,yEAAyE;IACzE,2CAA2C;IAC3C,OAAO,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAE3C,MAAM,EAAE,OAAC;SACN,MAAM,CACL,0CAAoB,EACpB,OAAC,CAAC,MAAM,CACN,oDAAyB,EACzB,OAAC,CAAC,KAAK,CAAC,CAAC,OAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE,sDAA0B,CAAC,CAAC,CACvD,CACF;SACA,QAAQ,EAAE;IAEb,8EAA8E;IAC9E,uCAAuC;IACvC,iDAAiD;IAEjD,UAAU,EAAE,OAAC,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,QAAQ,EAAE;IAExC,UAAU,EAAE,OAAC;SACV,MAAM,EAAE;SACR,KAAK,CAAC,gDAAgD,CAAC,CAAC,cAAc;SACtE,QAAQ,EAAE;IAEb,iEAAiE;IACjE,aAAa,EAAE,qBAAe,CAAC,QAAQ,EAAE;IAEzC,oCAAoC;IACpC,OAAO,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,QAAQ,EAAE;IAEtD;;;;;OAKG;IACH,MAAM,EAAE,OAAC,CAAC,IAAI,CAAC,CAAC,MAAM,EAAE,OAAO,EAAE,SAAS,EAAE,gBAAgB,CAAC,CAAC,CAAC,QAAQ,EAAE;IAEzE,gDAAgD;IAChD,qBAAqB,EAAE,gEAA+B,CAAC,QAAQ,EAAE;CAClE,CAAC,CAAA"}
@@ -1,3 +0,0 @@
1
- import { OAuthClientId } from './oauth-client-id.js';
2
- export declare function parseOAuthClientIdUrl(clientId: OAuthClientId): URL;
3
- //# sourceMappingURL=oauth-client-id-url.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth-client-id-url.d.ts","sourceRoot":"","sources":["../src/oauth-client-id-url.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEpD,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,aAAa,GAAG,GAAG,CAsBlE"}
@@ -1,21 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.parseOAuthClientIdUrl = void 0;
4
- function parseOAuthClientIdUrl(clientId) {
5
- if (clientId.endsWith('/')) {
6
- throw new TypeError('ClientID must not end with a trailing slash');
7
- }
8
- const url = new URL(clientId);
9
- if (url.protocol !== 'https:' && url.protocol !== 'http:') {
10
- throw new TypeError('ClientID must use the "https:" or "http:" protocol');
11
- }
12
- url.searchParams.sort();
13
- // URL constructor normalizes the URL, so we need to compare the canonical form
14
- const canonicalUri = url.pathname === '/' ? url.origin + url.search : url.href;
15
- if (canonicalUri !== clientId) {
16
- throw new TypeError(`ClientID must be in canonical form ("${canonicalUri}", got "${clientId}")`);
17
- }
18
- return url;
19
- }
20
- exports.parseOAuthClientIdUrl = parseOAuthClientIdUrl;
21
- //# sourceMappingURL=oauth-client-id-url.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth-client-id-url.js","sourceRoot":"","sources":["../src/oauth-client-id-url.ts"],"names":[],"mappings":";;;AAEA,SAAgB,qBAAqB,CAAC,QAAuB;IAC3D,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;QAC3B,MAAM,IAAI,SAAS,CAAC,6CAA6C,CAAC,CAAA;IACpE,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,QAAQ,CAAC,CAAA;IAE7B,IAAI,GAAG,CAAC,QAAQ,KAAK,QAAQ,IAAI,GAAG,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;QAC1D,MAAM,IAAI,SAAS,CAAC,oDAAoD,CAAC,CAAA;IAC3E,CAAC;IAED,GAAG,CAAC,YAAY,CAAC,IAAI,EAAE,CAAA;IAEvB,+EAA+E;IAC/E,MAAM,YAAY,GAAG,GAAG,CAAC,QAAQ,KAAK,GAAG,CAAC,CAAC,CAAC,GAAG,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC,GAAG,CAAC,IAAI,CAAA;IAC9E,IAAI,YAAY,KAAK,QAAQ,EAAE,CAAC;QAC9B,MAAM,IAAI,SAAS,CACjB,wCAAwC,YAAY,WAAW,QAAQ,IAAI,CAC5E,CAAA;IACH,CAAC;IAED,OAAO,GAAG,CAAA;AACZ,CAAC;AAtBD,sDAsBC"}
@@ -1,31 +0,0 @@
1
- import { z } from 'zod';
2
- export declare const oauthClientIdentificationSchema: z.ZodUnion<[z.ZodUnion<[z.ZodObject<{
3
- client_id: z.ZodString;
4
- client_assertion_type: z.ZodLiteral<"urn:ietf:params:oauth:client-assertion-type:jwt-bearer">;
5
- client_assertion: z.ZodEffects<z.ZodEffects<z.ZodString, string, string>, `${string}.${string}.${string}`, string>;
6
- }, "strip", z.ZodTypeAny, {
7
- client_id: string;
8
- client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
9
- client_assertion: `${string}.${string}.${string}`;
10
- }, {
11
- client_id: string;
12
- client_assertion_type: "urn:ietf:params:oauth:client-assertion-type:jwt-bearer";
13
- client_assertion: string;
14
- }>, z.ZodObject<{
15
- client_id: z.ZodString;
16
- client_secret: z.ZodString;
17
- }, "strip", z.ZodTypeAny, {
18
- client_id: string;
19
- client_secret: string;
20
- }, {
21
- client_id: string;
22
- client_secret: string;
23
- }>]>, z.ZodObject<{
24
- client_id: z.ZodString;
25
- }, "strip", z.ZodTypeAny, {
26
- client_id: string;
27
- }, {
28
- client_id: string;
29
- }>]>;
30
- export type OAuthClientIdentification = z.infer<typeof oauthClientIdentificationSchema>;
31
- //# sourceMappingURL=oauth-client-identification.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth-client-identification.d.ts","sourceRoot":"","sources":["../src/oauth-client-identification.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAA;AAKvB,eAAO,MAAM,+BAA+B;;;;;;;;;;;;;;;;;;;;;;;;;;;IAI1C,CAAA;AAEF,MAAM,MAAM,yBAAyB,GAAG,CAAC,CAAC,KAAK,CAC7C,OAAO,+BAA+B,CACvC,CAAA"}
@@ -1,12 +0,0 @@
1
- "use strict";
2
- Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.oauthClientIdentificationSchema = void 0;
4
- const zod_1 = require("zod");
5
- const oauth_client_id_js_1 = require("./oauth-client-id.js");
6
- const oauth_client_credentials_js_1 = require("./oauth-client-credentials.js");
7
- exports.oauthClientIdentificationSchema = zod_1.z.union([
8
- oauth_client_credentials_js_1.oauthClientCredentialsSchema,
9
- // Must be last since it is less specific
10
- zod_1.z.object({ client_id: oauth_client_id_js_1.oauthClientIdSchema }),
11
- ]);
12
- //# sourceMappingURL=oauth-client-identification.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"oauth-client-identification.js","sourceRoot":"","sources":["../src/oauth-client-identification.ts"],"names":[],"mappings":";;;AAAA,6BAAuB;AAEvB,6DAA0D;AAC1D,+EAA4E;AAE/D,QAAA,+BAA+B,GAAG,OAAC,CAAC,KAAK,CAAC;IACrD,0DAA4B;IAC5B,yCAAyC;IACzC,OAAC,CAAC,MAAM,CAAC,EAAE,SAAS,EAAE,wCAAmB,EAAE,CAAC;CAC7C,CAAC,CAAA"}
@@ -1,4 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- export const accessTokenSchema = z.string().min(1)
4
- export type AccessToken = z.infer<typeof accessTokenSchema>
@@ -1,25 +0,0 @@
1
- import { OAuthClientId } from './oauth-client-id.js'
2
-
3
- export function parseOAuthClientIdUrl(clientId: OAuthClientId): URL {
4
- if (clientId.endsWith('/')) {
5
- throw new TypeError('ClientID must not end with a trailing slash')
6
- }
7
-
8
- const url = new URL(clientId)
9
-
10
- if (url.protocol !== 'https:' && url.protocol !== 'http:') {
11
- throw new TypeError('ClientID must use the "https:" or "http:" protocol')
12
- }
13
-
14
- url.searchParams.sort()
15
-
16
- // URL constructor normalizes the URL, so we need to compare the canonical form
17
- const canonicalUri = url.pathname === '/' ? url.origin + url.search : url.href
18
- if (canonicalUri !== clientId) {
19
- throw new TypeError(
20
- `ClientID must be in canonical form ("${canonicalUri}", got "${clientId}")`,
21
- )
22
- }
23
-
24
- return url
25
- }
@@ -1,14 +0,0 @@
1
- import { z } from 'zod'
2
-
3
- import { oauthClientIdSchema } from './oauth-client-id.js'
4
- import { oauthClientCredentialsSchema } from './oauth-client-credentials.js'
5
-
6
- export const oauthClientIdentificationSchema = z.union([
7
- oauthClientCredentialsSchema,
8
- // Must be last since it is less specific
9
- z.object({ client_id: oauthClientIdSchema }),
10
- ])
11
-
12
- export type OAuthClientIdentification = z.infer<
13
- typeof oauthClientIdentificationSchema
14
- >