@atproto/oauth-types 0.1.3 → 0.1.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (156) hide show
  1. package/CHANGELOG.md +36 -0
  2. package/dist/atproto-loopback-client-metadata.d.ts.map +1 -1
  3. package/dist/atproto-loopback-client-metadata.js +5 -17
  4. package/dist/atproto-loopback-client-metadata.js.map +1 -1
  5. package/dist/index.d.ts +18 -5
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.js +18 -5
  8. package/dist/index.js.map +1 -1
  9. package/dist/oauth-access-token.d.ts +4 -0
  10. package/dist/oauth-access-token.d.ts.map +1 -0
  11. package/dist/oauth-access-token.js +6 -0
  12. package/dist/oauth-access-token.js.map +1 -0
  13. package/dist/oauth-authorization-code-grant-token-request.d.ts +20 -0
  14. package/dist/oauth-authorization-code-grant-token-request.d.ts.map +1 -0
  15. package/dist/oauth-authorization-code-grant-token-request.js +17 -0
  16. package/dist/oauth-authorization-code-grant-token-request.js.map +1 -0
  17. package/dist/oauth-authorization-request-jar.d.ts +16 -0
  18. package/dist/oauth-authorization-request-jar.d.ts.map +1 -0
  19. package/dist/oauth-authorization-request-jar.js +15 -0
  20. package/dist/oauth-authorization-request-jar.js.map +1 -0
  21. package/dist/oauth-authorization-request-par.d.ts +122 -0
  22. package/dist/oauth-authorization-request-par.d.ts.map +1 -0
  23. package/dist/oauth-authorization-request-par.js +11 -0
  24. package/dist/oauth-authorization-request-par.js.map +1 -0
  25. package/dist/{oauth-authentication-request-parameters.d.ts → oauth-authorization-request-parameters.d.ts} +16 -16
  26. package/dist/oauth-authorization-request-parameters.d.ts.map +1 -0
  27. package/dist/{oauth-authentication-request-parameters.js → oauth-authorization-request-parameters.js} +17 -25
  28. package/dist/oauth-authorization-request-parameters.js.map +1 -0
  29. package/dist/oauth-authorization-request-query.d.ts +128 -0
  30. package/dist/oauth-authorization-request-query.d.ts.map +1 -0
  31. package/dist/oauth-authorization-request-query.js +13 -0
  32. package/dist/oauth-authorization-request-query.js.map +1 -0
  33. package/dist/oauth-authorization-request-uri.d.ts +10 -0
  34. package/dist/oauth-authorization-request-uri.d.ts.map +1 -0
  35. package/dist/oauth-authorization-request-uri.js +9 -0
  36. package/dist/oauth-authorization-request-uri.js.map +1 -0
  37. package/dist/oauth-authorization-server-metadata.d.ts +10 -10
  38. package/dist/oauth-authorization-server-metadata.d.ts.map +1 -1
  39. package/dist/oauth-authorization-server-metadata.js +5 -1
  40. package/dist/oauth-authorization-server-metadata.js.map +1 -1
  41. package/dist/oauth-client-credentials-grant-token-request.d.ts +10 -0
  42. package/dist/oauth-client-credentials-grant-token-request.d.ts.map +1 -0
  43. package/dist/oauth-client-credentials-grant-token-request.js +8 -0
  44. package/dist/oauth-client-credentials-grant-token-request.js.map +1 -0
  45. package/dist/oauth-client-credentials.d.ts +18 -2
  46. package/dist/oauth-client-credentials.d.ts.map +1 -1
  47. package/dist/oauth-client-credentials.js +8 -2
  48. package/dist/oauth-client-credentials.js.map +1 -1
  49. package/dist/oauth-client-id-discoverable.d.ts +3 -2
  50. package/dist/oauth-client-id-discoverable.d.ts.map +1 -1
  51. package/dist/oauth-client-id-discoverable.js +21 -18
  52. package/dist/oauth-client-id-discoverable.js.map +1 -1
  53. package/dist/oauth-client-id-loopback.d.ts +10 -3
  54. package/dist/oauth-client-id-loopback.d.ts.map +1 -1
  55. package/dist/oauth-client-id-loopback.js +58 -21
  56. package/dist/oauth-client-id-loopback.js.map +1 -1
  57. package/dist/oauth-client-metadata.d.ts +1 -1
  58. package/dist/oauth-client-metadata.d.ts.map +1 -1
  59. package/dist/oauth-client-metadata.js +2 -1
  60. package/dist/oauth-client-metadata.js.map +1 -1
  61. package/dist/oauth-code-challenge-method.d.ts +3 -0
  62. package/dist/oauth-code-challenge-method.d.ts.map +1 -0
  63. package/dist/oauth-code-challenge-method.js +6 -0
  64. package/dist/oauth-code-challenge-method.js.map +1 -0
  65. package/dist/oauth-introspection-response.d.ts +20 -0
  66. package/dist/oauth-introspection-response.d.ts.map +1 -0
  67. package/dist/oauth-introspection-response.js +3 -0
  68. package/dist/oauth-introspection-response.js.map +1 -0
  69. package/dist/oauth-par-response.d.ts +3 -0
  70. package/dist/oauth-par-response.d.ts.map +1 -1
  71. package/dist/oauth-par-response.js +1 -0
  72. package/dist/oauth-par-response.js.map +1 -1
  73. package/dist/oauth-password-grant-token-request.d.ts +16 -0
  74. package/dist/oauth-password-grant-token-request.d.ts.map +1 -0
  75. package/dist/oauth-password-grant-token-request.js +10 -0
  76. package/dist/oauth-password-grant-token-request.js.map +1 -0
  77. package/dist/oauth-refresh-token-grant-token-request.d.ts +16 -0
  78. package/dist/oauth-refresh-token-grant-token-request.d.ts.map +1 -0
  79. package/dist/oauth-refresh-token-grant-token-request.js +12 -0
  80. package/dist/oauth-refresh-token-grant-token-request.js.map +1 -0
  81. package/dist/oauth-refresh-token.d.ts +4 -0
  82. package/dist/oauth-refresh-token.d.ts.map +1 -0
  83. package/dist/oauth-refresh-token.js +6 -0
  84. package/dist/oauth-refresh-token.js.map +1 -0
  85. package/dist/oauth-request-uri.d.ts +4 -0
  86. package/dist/oauth-request-uri.d.ts.map +1 -0
  87. package/dist/oauth-request-uri.js +6 -0
  88. package/dist/oauth-request-uri.js.map +1 -0
  89. package/dist/oauth-response-type.js +2 -2
  90. package/dist/oauth-response-type.js.map +1 -1
  91. package/dist/oauth-scope.d.ts +10 -0
  92. package/dist/oauth-scope.d.ts.map +1 -0
  93. package/dist/oauth-scope.js +16 -0
  94. package/dist/oauth-scope.js.map +1 -0
  95. package/dist/oauth-token-identification.d.ts +13 -0
  96. package/dist/oauth-token-identification.d.ts.map +1 -0
  97. package/dist/oauth-token-identification.js +11 -0
  98. package/dist/oauth-token-identification.js.map +1 -0
  99. package/dist/oauth-token-request.d.ts +49 -0
  100. package/dist/oauth-token-request.d.ts.map +1 -0
  101. package/dist/oauth-token-request.js +15 -0
  102. package/dist/oauth-token-request.js.map +1 -0
  103. package/dist/oauth-token-response.d.ts +0 -3
  104. package/dist/oauth-token-response.d.ts.map +1 -1
  105. package/dist/oauth-token-response.js +0 -1
  106. package/dist/oauth-token-response.js.map +1 -1
  107. package/dist/util.d.ts +2 -1
  108. package/dist/util.d.ts.map +1 -1
  109. package/dist/util.js +34 -3
  110. package/dist/util.js.map +1 -1
  111. package/package.json +1 -1
  112. package/src/atproto-loopback-client-metadata.ts +9 -23
  113. package/src/index.ts +18 -5
  114. package/src/oauth-access-token.ts +4 -0
  115. package/src/oauth-authorization-code-grant-token-request.ts +18 -0
  116. package/src/oauth-authorization-request-jar.ts +16 -0
  117. package/src/oauth-authorization-request-par.ts +13 -0
  118. package/src/{oauth-authentication-request-parameters.ts → oauth-authorization-request-parameters.ts} +22 -31
  119. package/src/oauth-authorization-request-query.ts +15 -0
  120. package/src/oauth-authorization-request-uri.ts +11 -0
  121. package/src/oauth-authorization-server-metadata.ts +5 -1
  122. package/src/oauth-client-credentials-grant-token-request.ts +9 -0
  123. package/src/oauth-client-credentials.ts +21 -1
  124. package/src/oauth-client-id-discoverable.ts +29 -26
  125. package/src/oauth-client-id-loopback.ts +78 -30
  126. package/src/oauth-client-metadata.ts +2 -1
  127. package/src/oauth-code-challenge-method.ts +3 -0
  128. package/src/oauth-introspection-response.ts +23 -0
  129. package/src/oauth-par-response.ts +1 -0
  130. package/src/oauth-password-grant-token-request.ts +11 -0
  131. package/src/oauth-refresh-token-grant-token-request.ts +13 -0
  132. package/src/oauth-refresh-token.ts +4 -0
  133. package/src/oauth-request-uri.ts +5 -0
  134. package/src/oauth-response-type.ts +2 -2
  135. package/src/oauth-scope.ts +15 -0
  136. package/src/oauth-token-identification.ts +12 -0
  137. package/src/oauth-token-request.ts +14 -0
  138. package/src/oauth-token-response.ts +0 -1
  139. package/src/util.ts +41 -1
  140. package/dist/access-token.d.ts +0 -4
  141. package/dist/access-token.d.ts.map +0 -1
  142. package/dist/access-token.js +0 -6
  143. package/dist/access-token.js.map +0 -1
  144. package/dist/oauth-authentication-request-parameters.d.ts.map +0 -1
  145. package/dist/oauth-authentication-request-parameters.js.map +0 -1
  146. package/dist/oauth-client-id-url.d.ts +0 -3
  147. package/dist/oauth-client-id-url.d.ts.map +0 -1
  148. package/dist/oauth-client-id-url.js +0 -21
  149. package/dist/oauth-client-id-url.js.map +0 -1
  150. package/dist/oauth-client-identification.d.ts +0 -31
  151. package/dist/oauth-client-identification.d.ts.map +0 -1
  152. package/dist/oauth-client-identification.js +0 -12
  153. package/dist/oauth-client-identification.js.map +0 -1
  154. package/src/access-token.ts +0 -4
  155. package/src/oauth-client-id-url.ts +0 -25
  156. package/src/oauth-client-identification.ts +0 -14
@@ -1 +1 @@
1
- {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,wBAAgB,IAAI,CAAC,QAAQ,EAAE,MAAM,WAQpC;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAA;AAE9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,YAAY,CAElE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAG1D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,IAAI,CAMvD"}
1
+ {"version":3,"file":"util.d.ts","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,WAQ5C;AAED,MAAM,MAAM,YAAY,GAAG,WAAW,GAAG,WAAW,GAAG,OAAO,CAAA;AAE9D,wBAAgB,cAAc,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,YAAY,CAElE;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,OAAO,CAG1D;AAED,wBAAgB,OAAO,CAAC,KAAK,EAAE,GAAG,GAAG,MAAM,GAAG,GAAG,GAAG,IAAI,CAMvD;AAED,wBAAgB,cAAc,CAAC,GAAG,KAAA,OAsCjC"}
package/dist/util.js CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.safeUrl = exports.isLoopbackUrl = exports.isLoopbackHost = exports.isIP = void 0;
4
- function isIP(hostname) {
3
+ exports.extractUrlPath = exports.safeUrl = exports.isLoopbackUrl = exports.isLoopbackHost = exports.isHostnameIP = void 0;
4
+ function isHostnameIP(hostname) {
5
5
  // IPv4
6
6
  if (hostname.match(/^\d+\.\d+\.\d+\.\d+$/))
7
7
  return true;
@@ -10,7 +10,7 @@ function isIP(hostname) {
10
10
  return true;
11
11
  return false;
12
12
  }
13
- exports.isIP = isIP;
13
+ exports.isHostnameIP = isHostnameIP;
14
14
  function isLoopbackHost(host) {
15
15
  return host === 'localhost' || host === '127.0.0.1' || host === '[::1]';
16
16
  }
@@ -29,4 +29,35 @@ function safeUrl(input) {
29
29
  }
30
30
  }
31
31
  exports.safeUrl = safeUrl;
32
+ function extractUrlPath(url) {
33
+ // Extracts the path from a URL, without relying on the URL constructor
34
+ // (because it normalizes the URL)
35
+ const endOfProtocol = url.startsWith('https://')
36
+ ? 8
37
+ : url.startsWith('http://')
38
+ ? 7
39
+ : -1;
40
+ if (endOfProtocol === -1) {
41
+ throw new TypeError('URL must use the "https:" or "http:" protocol');
42
+ }
43
+ const hashIdx = url.indexOf('#', endOfProtocol);
44
+ const questionIdx = url.indexOf('?', endOfProtocol);
45
+ const queryStrIdx = questionIdx !== -1 && (hashIdx === -1 || questionIdx < hashIdx)
46
+ ? questionIdx
47
+ : -1;
48
+ const pathEnd = hashIdx === -1
49
+ ? queryStrIdx === -1
50
+ ? url.length
51
+ : queryStrIdx
52
+ : queryStrIdx === -1
53
+ ? hashIdx
54
+ : Math.min(hashIdx, queryStrIdx);
55
+ const slashIdx = url.indexOf('/', endOfProtocol);
56
+ const pathStart = slashIdx === -1 || slashIdx > pathEnd ? pathEnd : slashIdx;
57
+ if (endOfProtocol === pathStart) {
58
+ throw new TypeError('URL must contain a host');
59
+ }
60
+ return url.substring(pathStart, pathEnd);
61
+ }
62
+ exports.extractUrlPath = extractUrlPath;
32
63
  //# sourceMappingURL=util.js.map
package/dist/util.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAAA,SAAgB,IAAI,CAAC,QAAgB;IACnC,OAAO;IACP,IAAI,QAAQ,CAAC,KAAK,CAAC,sBAAsB,CAAC;QAAE,OAAO,IAAI,CAAA;IAEvD,OAAO;IACP,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAEnE,OAAO,KAAK,CAAA;AACd,CAAC;AARD,oBAQC;AAID,SAAgB,cAAc,CAAC,IAAa;IAC1C,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,OAAO,CAAA;AACzE,CAAC;AAFD,wCAEC;AAED,SAAgB,aAAa,CAAC,KAAmB;IAC/C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAC9D,OAAO,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AACrC,CAAC;AAHD,sCAGC;AAED,SAAgB,OAAO,CAAC,KAAmB;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAND,0BAMC"}
1
+ {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":";;;AAAA,SAAgB,YAAY,CAAC,QAAgB;IAC3C,OAAO;IACP,IAAI,QAAQ,CAAC,KAAK,CAAC,sBAAsB,CAAC;QAAE,OAAO,IAAI,CAAA;IAEvD,OAAO;IACP,IAAI,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;QAAE,OAAO,IAAI,CAAA;IAEnE,OAAO,KAAK,CAAA;AACd,CAAC;AARD,oCAQC;AAID,SAAgB,cAAc,CAAC,IAAa;IAC1C,OAAO,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,WAAW,IAAI,IAAI,KAAK,OAAO,CAAA;AACzE,CAAC;AAFD,wCAEC;AAED,SAAgB,aAAa,CAAC,KAAmB;IAC/C,MAAM,GAAG,GAAG,OAAO,KAAK,KAAK,QAAQ,CAAC,CAAC,CAAC,IAAI,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,KAAK,CAAA;IAC9D,OAAO,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;AACrC,CAAC;AAHD,sCAGC;AAED,SAAgB,OAAO,CAAC,KAAmB;IACzC,IAAI,CAAC;QACH,OAAO,IAAI,GAAG,CAAC,KAAK,CAAC,CAAA;IACvB,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAND,0BAMC;AAED,SAAgB,cAAc,CAAC,GAAG;IAChC,uEAAuE;IACvE,kCAAkC;IAClC,MAAM,aAAa,GAAG,GAAG,CAAC,UAAU,CAAC,UAAU,CAAC;QAC9C,CAAC,CAAC,CAAC;QACH,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC;YACzB,CAAC,CAAC,CAAC;YACH,CAAC,CAAC,CAAC,CAAC,CAAA;IACR,IAAI,aAAa,KAAK,CAAC,CAAC,EAAE,CAAC;QACzB,MAAM,IAAI,SAAS,CAAC,+CAA+C,CAAC,CAAA;IACtE,CAAC;IAED,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IAC/C,MAAM,WAAW,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IAEnD,MAAM,WAAW,GACf,WAAW,KAAK,CAAC,CAAC,IAAI,CAAC,OAAO,KAAK,CAAC,CAAC,IAAI,WAAW,GAAG,OAAO,CAAC;QAC7D,CAAC,CAAC,WAAW;QACb,CAAC,CAAC,CAAC,CAAC,CAAA;IAER,MAAM,OAAO,GACX,OAAO,KAAK,CAAC,CAAC;QACZ,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC;YAClB,CAAC,CAAC,GAAG,CAAC,MAAM;YACZ,CAAC,CAAC,WAAW;QACf,CAAC,CAAC,WAAW,KAAK,CAAC,CAAC;YAClB,CAAC,CAAC,OAAO;YACT,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,OAAO,EAAE,WAAW,CAAC,CAAA;IAEtC,MAAM,QAAQ,GAAG,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,aAAa,CAAC,CAAA;IAEhD,MAAM,SAAS,GAAG,QAAQ,KAAK,CAAC,CAAC,IAAI,QAAQ,GAAG,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAA;IAE5E,IAAI,aAAa,KAAK,SAAS,EAAE,CAAC;QAChC,MAAM,IAAI,SAAS,CAAC,yBAAyB,CAAC,CAAA;IAChD,CAAC;IAED,OAAO,GAAG,CAAC,SAAS,CAAC,SAAS,EAAE,OAAO,CAAC,CAAA;AAC1C,CAAC;AAtCD,wCAsCC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@atproto/oauth-types",
3
- "version": "0.1.3",
3
+ "version": "0.1.5",
4
4
  "license": "MIT",
5
5
  "description": "OAuth typing & validation library",
6
6
  "keywords": [
@@ -1,35 +1,21 @@
1
- import { isOAuthClientIdLoopback } from './oauth-client-id-loopback.js'
1
+ import { parseOAuthLoopbackClientId } from './oauth-client-id-loopback.js'
2
2
  import { OAuthClientMetadataInput } from './oauth-client-metadata.js'
3
- import { parseOAuthClientIdUrl } from './oauth-client-id-url.js'
4
3
 
5
4
  export function atprotoLoopbackClientMetadata(
6
5
  clientId: string,
7
6
  ): OAuthClientMetadataInput {
8
- if (!isOAuthClientIdLoopback(clientId)) {
9
- throw new TypeError(`Invalid loopback client ID ${clientId}`)
10
- }
11
-
12
- const { origin, pathname, searchParams } = parseOAuthClientIdUrl(clientId)
13
-
14
- for (const name of searchParams.keys()) {
15
- if (name !== 'redirect_uri') {
16
- throw new TypeError(`Invalid query parameter ${name} in client ID`)
17
- }
18
- }
19
- const redirectUris = searchParams.getAll('redirect_uri')
7
+ const {
8
+ scope = 'atproto',
9
+ redirect_uris = [`http://127.0.0.1/`, `http://[::1]/`],
10
+ } = parseOAuthLoopbackClientId(clientId)
20
11
 
21
12
  return {
22
13
  client_id: clientId,
14
+ scope,
15
+ redirect_uris,
23
16
  client_name: 'Loopback client',
24
- response_types: ['code id_token', 'code'],
25
- grant_types: ['authorization_code', 'implicit', 'refresh_token'],
26
- scope: 'openid profile offline_access',
27
- redirect_uris: (redirectUris.length
28
- ? redirectUris
29
- : (['127.0.0.1', '[::1]'] as const).map(
30
- (ip) =>
31
- Object.assign(new URL(pathname, origin), { hostname: ip }).href,
32
- )) as [string, ...string[]],
17
+ response_types: ['code'],
18
+ grant_types: ['authorization_code', 'refresh_token'],
33
19
  token_endpoint_auth_method: 'none',
34
20
  application_type: 'native',
35
21
  dpop_bound_access_tokens: true,
package/src/index.ts CHANGED
@@ -1,25 +1,38 @@
1
1
  export * from './constants.js'
2
2
  export * from './util.js'
3
3
 
4
- export * from './access-token.js'
5
4
  export * from './atproto-loopback-client-metadata.js'
6
- export * from './oauth-client-id-discoverable.js'
7
- export * from './oauth-client-id-loopback.js'
8
- export * from './oauth-authentication-request-parameters.js'
5
+ export * from './oauth-access-token.js'
6
+ export * from './oauth-authorization-code-grant-token-request.js'
9
7
  export * from './oauth-authorization-details.js'
8
+ export * from './oauth-authorization-request-jar.js'
9
+ export * from './oauth-authorization-request-par.js'
10
+ export * from './oauth-authorization-request-parameters.js'
11
+ export * from './oauth-authorization-request-query.js'
12
+ export * from './oauth-authorization-request-uri.js'
10
13
  export * from './oauth-authorization-server-metadata.js'
14
+ export * from './oauth-client-credentials-grant-token-request.js'
11
15
  export * from './oauth-client-credentials.js'
16
+ export * from './oauth-client-id-discoverable.js'
17
+ export * from './oauth-client-id-loopback.js'
12
18
  export * from './oauth-client-id.js'
13
- export * from './oauth-client-identification.js'
14
19
  export * from './oauth-client-metadata.js'
15
20
  export * from './oauth-endpoint-auth-method.js'
16
21
  export * from './oauth-endpoint-name.js'
17
22
  export * from './oauth-grant-type.js'
23
+ export * from './oauth-introspection-response.js'
18
24
  export * from './oauth-issuer-identifier.js'
19
25
  export * from './oauth-par-response.js'
26
+ export * from './oauth-password-grant-token-request.js'
20
27
  export * from './oauth-protected-resource-metadata.js'
28
+ export * from './oauth-refresh-token-grant-token-request.js'
29
+ export * from './oauth-refresh-token.js'
30
+ export * from './oauth-request-uri.js'
21
31
  export * from './oauth-response-mode.js'
22
32
  export * from './oauth-response-type.js'
33
+ export * from './oauth-scope.js'
34
+ export * from './oauth-token-identification.js'
35
+ export * from './oauth-token-request.js'
23
36
  export * from './oauth-token-response.js'
24
37
  export * from './oauth-token-type.js'
25
38
  export * from './oidc-claims-parameter.js'
@@ -0,0 +1,4 @@
1
+ import { z } from 'zod'
2
+
3
+ export const oauthAccessTokenSchema = z.string().min(1)
4
+ export type OAuthAccessToken = z.infer<typeof oauthAccessTokenSchema>
@@ -0,0 +1,18 @@
1
+ import { z } from 'zod'
2
+
3
+ export const oauthAuthorizationCodeGrantTokenRequestSchema = z.object({
4
+ grant_type: z.literal('authorization_code'),
5
+ code: z.string().min(1),
6
+ redirect_uri: z.string().url(),
7
+ /** @see {@link https://datatracker.ietf.org/doc/html/rfc7636#section-4.1} */
8
+ code_verifier: z
9
+ .string()
10
+ .min(43)
11
+ .max(128)
12
+ .regex(/^[a-zA-Z0-9-._~]+$/)
13
+ .optional(),
14
+ })
15
+
16
+ export type OAuthAuthorizationCodeGrantTokenRequest = z.infer<
17
+ typeof oauthAuthorizationCodeGrantTokenRequestSchema
18
+ >
@@ -0,0 +1,16 @@
1
+ import { signedJwtSchema, unsignedJwtSchema } from '@atproto/jwk'
2
+ import { z } from 'zod'
3
+
4
+ export const oauthAuthorizationRequestJarSchema = z.object({
5
+ /**
6
+ * AuthorizationRequest inside a JWT:
7
+ * - "iat" is required and **MUST** be less than one minute
8
+ *
9
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc9101}
10
+ */
11
+ request: z.union([signedJwtSchema, unsignedJwtSchema]),
12
+ })
13
+
14
+ export type OAuthAuthorizationRequestJar = z.infer<
15
+ typeof oauthAuthorizationRequestJarSchema
16
+ >
@@ -0,0 +1,13 @@
1
+ import { z } from 'zod'
2
+
3
+ import { oauthAuthorizationRequestJarSchema } from './oauth-authorization-request-jar.js'
4
+ import { oauthAuthorizationRequestParametersSchema } from './oauth-authorization-request-parameters.js'
5
+
6
+ export const oauthAuthorizationRequestParSchema = z.union([
7
+ oauthAuthorizationRequestParametersSchema,
8
+ oauthAuthorizationRequestJarSchema,
9
+ ])
10
+
11
+ export type OAuthAuthorizationRequestPar = z.infer<
12
+ typeof oauthAuthorizationRequestParSchema
13
+ >
@@ -3,6 +3,9 @@ import { z } from 'zod'
3
3
 
4
4
  import { oauthAuthorizationDetailsSchema } from './oauth-authorization-details.js'
5
5
  import { oauthClientIdSchema } from './oauth-client-id.js'
6
+ import { oauthCodeChallengeMethodSchema } from './oauth-code-challenge-method.js'
7
+ import { oauthResponseTypeSchema } from './oauth-response-type.js'
8
+ import { oauthScopeSchema } from './oauth-scope.js'
6
9
  import { oidcClaimsParameterSchema } from './oidc-claims-parameter.js'
7
10
  import { oidcClaimsPropertiesSchema } from './oidc-claims-properties.js'
8
11
  import { oidcEntityTypeSchema } from './oidc-entity-type.js'
@@ -10,44 +13,32 @@ import { oidcEntityTypeSchema } from './oidc-entity-type.js'
10
13
  /**
11
14
  * @see {@link https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest | OIDC}
12
15
  */
13
- export const oauthAuthenticationRequestParametersSchema = z.object({
16
+ export const oauthAuthorizationRequestParametersSchema = z.object({
14
17
  client_id: oauthClientIdSchema,
15
-
16
18
  state: z.string().optional(),
17
- nonce: z.string().optional(),
18
- dpop_jkt: z.string().optional(),
19
-
20
- response_type: z.enum([
21
- // OAuth2 (https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-10#section-4.1.1)
22
- 'code',
23
- 'token',
24
-
25
- // OIDC (https://openid.net/specs/oauth-v2-multiple-response-types-1_0.html)
26
- 'id_token',
27
- 'none',
28
- 'code token',
29
- 'code id_token',
30
- 'id_token token',
31
- 'code id_token token',
32
- ]),
33
-
34
- // Default depend on response_type
35
- response_mode: z.enum(['query', 'fragment', 'form_post']).optional(),
19
+ redirect_uri: z.string().url().optional(),
20
+ scope: oauthScopeSchema.optional(),
21
+ response_type: oauthResponseTypeSchema,
36
22
 
37
23
  // PKCE
24
+
38
25
  code_challenge: z.string().optional(),
39
- code_challenge_method: z.enum(['S256', 'plain']).default('S256').optional(),
26
+ code_challenge_method: oauthCodeChallengeMethodSchema
27
+ .default('S256')
28
+ .optional(),
40
29
 
41
- redirect_uri: z.string().url().optional(),
30
+ // DPOP
42
31
 
43
- // email profile openid (other?)
44
- scope: z
45
- .string()
46
- .regex(/^[a-zA-Z0-9_]+( [a-zA-Z0-9_]+)*$/)
47
- .optional(),
32
+ // https://datatracker.ietf.org/doc/html/rfc9449#section-12.3
33
+ dpop_jkt: z.string().optional(),
48
34
 
49
35
  // OIDC
50
36
 
37
+ // Default depend on response_type
38
+ response_mode: z.enum(['query', 'fragment', 'form_post']).optional(),
39
+
40
+ nonce: z.string().optional(),
41
+
51
42
  // Specifies the allowable elapsed time in seconds since the last time the
52
43
  // End-User was actively authenticated by the OP. If the elapsed time is
53
44
  // greater than this value, the OP MUST attempt to actively re-authenticate
@@ -97,8 +88,8 @@ export const oauthAuthenticationRequestParametersSchema = z.object({
97
88
  })
98
89
 
99
90
  /**
100
- * @see {oauthAuthenticationRequestParametersSchema}
91
+ * @see {oauthAuthorizationRequestParametersSchema}
101
92
  */
102
- export type OAuthAuthenticationRequestParameters = z.infer<
103
- typeof oauthAuthenticationRequestParametersSchema
93
+ export type OAuthAuthorizationRequestParameters = z.infer<
94
+ typeof oauthAuthorizationRequestParametersSchema
104
95
  >
@@ -0,0 +1,15 @@
1
+ import { z } from 'zod'
2
+
3
+ import { oauthAuthorizationRequestJarSchema } from './oauth-authorization-request-jar.js'
4
+ import { oauthAuthorizationRequestParametersSchema } from './oauth-authorization-request-parameters.js'
5
+ import { oauthAuthorizationRequestUriSchema } from './oauth-authorization-request-uri.js'
6
+
7
+ export const oauthAuthorizationRequestQuerySchema = z.union([
8
+ oauthAuthorizationRequestParametersSchema,
9
+ oauthAuthorizationRequestJarSchema,
10
+ oauthAuthorizationRequestUriSchema,
11
+ ])
12
+
13
+ export type OAuthAuthorizationRequestQuery = z.infer<
14
+ typeof oauthAuthorizationRequestQuerySchema
15
+ >
@@ -0,0 +1,11 @@
1
+ import { z } from 'zod'
2
+
3
+ import { oauthRequestUriSchema } from './oauth-request-uri.js'
4
+
5
+ export const oauthAuthorizationRequestUriSchema = z.object({
6
+ request_uri: oauthRequestUriSchema,
7
+ })
8
+
9
+ export type OAuthAuthorizationRequestUri = z.infer<
10
+ typeof oauthAuthorizationRequestUriSchema
11
+ >
@@ -1,5 +1,6 @@
1
1
  import { z } from 'zod'
2
2
 
3
+ import { oauthCodeChallengeMethodSchema } from './oauth-code-challenge-method.js'
3
4
  import { oauthIssuerIdentifierSchema } from './oauth-issuer-identifier.js'
4
5
 
5
6
  /**
@@ -19,7 +20,10 @@ export const oauthAuthorizationServerMetadataSchema = z.object({
19
20
  response_types_supported: z.array(z.string()).optional(),
20
21
  response_modes_supported: z.array(z.string()).optional(),
21
22
  grant_types_supported: z.array(z.string()).optional(),
22
- code_challenge_methods_supported: z.array(z.string()).min(1).optional(),
23
+ code_challenge_methods_supported: z
24
+ .array(oauthCodeChallengeMethodSchema)
25
+ .min(1)
26
+ .optional(),
23
27
  ui_locales_supported: z.array(z.string()).optional(),
24
28
  id_token_signing_alg_values_supported: z.array(z.string()).optional(),
25
29
  display_values_supported: z.array(z.string()).optional(),
@@ -0,0 +1,9 @@
1
+ import { z } from 'zod'
2
+
3
+ export const oauthClientCredentialsGrantTokenRequestSchema = z.object({
4
+ grant_type: z.literal('client_credentials'),
5
+ })
6
+
7
+ export type OAuthClientCredentialsGrantTokenRequest = z.infer<
8
+ typeof oauthClientCredentialsGrantTokenRequestSchema
9
+ >
@@ -14,19 +14,39 @@ export const oauthClientCredentialsJwtBearerSchema = z.object({
14
14
  * - The JWT MAY contain a "jti" (JWT ID) claim that provides a unique identifier for the token.
15
15
  * - Note that the authorization server may reject JWTs with an "exp" claim value that is unreasonably far in the future.
16
16
  *
17
- * @see {@link https://datatracker.ietf.org/doc/html/draft-ietf-oauth-jwt-bearer-11#section-3}
17
+ * @see {@link https://datatracker.ietf.org/doc/html/rfc7523#section-3}
18
18
  */
19
19
  client_assertion: signedJwtSchema,
20
20
  })
21
21
 
22
+ export type OAuthClientCredentialsJwtBearer = z.infer<
23
+ typeof oauthClientCredentialsJwtBearerSchema
24
+ >
25
+
22
26
  export const oauthClientCredentialsSecretPostSchema = z.object({
23
27
  client_id: oauthClientIdSchema,
24
28
  client_secret: z.string(),
25
29
  })
26
30
 
31
+ export type OAuthClientCredentialsSecretPost = z.infer<
32
+ typeof oauthClientCredentialsSecretPostSchema
33
+ >
34
+
35
+ export const oauthClientCredentialsNoneSchema = z.object({
36
+ client_id: oauthClientIdSchema,
37
+ })
38
+
39
+ export type OAuthClientCredentialsNone = z.infer<
40
+ typeof oauthClientCredentialsNoneSchema
41
+ >
42
+
43
+ //
44
+
27
45
  export const oauthClientCredentialsSchema = z.union([
28
46
  oauthClientCredentialsJwtBearerSchema,
29
47
  oauthClientCredentialsSecretPostSchema,
48
+ // Must be last since it is less specific
49
+ oauthClientCredentialsNoneSchema,
30
50
  ])
31
51
 
32
52
  export type OAuthClientCredentials = z.infer<
@@ -1,15 +1,14 @@
1
- import { parseOAuthClientIdUrl } from './oauth-client-id-url.js'
2
1
  import { OAuthClientId } from './oauth-client-id.js'
3
- import { isIP } from './util.js'
2
+ import { extractUrlPath, isHostnameIP } from './util.js'
4
3
 
5
4
  /**
6
5
  * @see {@link https://drafts.aaronpk.com/draft-parecki-oauth-client-id-metadata-document/draft-parecki-oauth-client-id-metadata-document.html}
7
6
  */
8
7
  export type OAuthClientIdDiscoverable = OAuthClientId & `https://${string}`
9
8
 
10
- export function isOAuthClientIdDiscoverable<C extends OAuthClientId>(
11
- clientId: C,
12
- ): clientId is C & OAuthClientIdDiscoverable {
9
+ export function isOAuthClientIdDiscoverable(
10
+ clientId: string,
11
+ ): clientId is OAuthClientIdDiscoverable {
13
12
  try {
14
13
  parseOAuthDiscoverableClientId(clientId)
15
14
  return true
@@ -18,48 +17,52 @@ export function isOAuthClientIdDiscoverable<C extends OAuthClientId>(
18
17
  }
19
18
  }
20
19
 
21
- export function parseOAuthDiscoverableClientId(clientId: OAuthClientId): URL {
22
- const url = parseOAuthClientIdUrl(clientId)
23
-
24
- // Optimization: cheap checks first
20
+ export function assertOAuthDiscoverableClientId(
21
+ value: string,
22
+ ): asserts value is OAuthClientIdDiscoverable {
23
+ void parseOAuthDiscoverableClientId(value)
24
+ }
25
25
 
26
- if (url.hostname === 'localhost') {
27
- throw new TypeError('ClientID must not be a loopback hostname')
28
- }
26
+ export function parseOAuthDiscoverableClientId(clientId: string): URL {
27
+ const url = new URL(clientId)
29
28
 
30
29
  if (url.protocol !== 'https:') {
31
30
  throw new TypeError('ClientID must use the "https:" protocol')
32
31
  }
33
32
 
33
+ if (url.username || url.password) {
34
+ throw new TypeError('ClientID must not contain credentials')
35
+ }
36
+
34
37
  if (url.hash) {
35
38
  throw new TypeError('ClientID must not contain a fragment')
36
39
  }
37
40
 
38
- if (url.username || url.password) {
39
- throw new TypeError('ClientID must not contain credentials')
41
+ if (url.hostname === 'localhost') {
42
+ throw new TypeError('ClientID hostname must not be "localhost"')
40
43
  }
41
44
 
42
45
  if (url.pathname === '/') {
43
46
  throw new TypeError(
44
- 'ClientID must contain a path (e.g. "/client-metadata")',
47
+ 'ClientID must contain a path component (e.g. "/client-metadata.json")',
45
48
  )
46
49
  }
47
50
 
48
- if (url.pathname !== '/' && url.pathname.endsWith('/')) {
49
- throw new TypeError('ClientID must not end with a trailing slash')
51
+ if (url.pathname.endsWith('/')) {
52
+ throw new TypeError('ClientID path must not end with a trailing slash')
50
53
  }
51
54
 
52
- if (url.pathname.includes('//')) {
53
- throw new TypeError(
54
- `ClientID must not contain any double slashes in its path`,
55
- )
55
+ if (isHostnameIP(url.hostname)) {
56
+ throw new TypeError('ClientID hostname must not be an IP address')
56
57
  }
57
58
 
58
- // Note: Query string is allowed
59
- // Note: no restriction on the port for non-loopback URIs
60
-
61
- if (isIP(url.hostname)) {
62
- throw new TypeError('ClientID must not be an IP address')
59
+ // URL constructor normalizes the URL, so we extract the path manually to
60
+ // avoid normalization, then compare it to the normalized path to ensure
61
+ // that the URL does not contain path traversal or other unexpected characters
62
+ if (extractUrlPath(clientId) !== url.pathname) {
63
+ throw new TypeError(
64
+ `ClientID must be in canonical form ("${url.href}", got "${clientId}")`,
65
+ )
63
66
  }
64
67
 
65
68
  return url
@@ -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(),