@atproto/oauth-types 0.1.4 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
- >