@_mustachio/openauth 0.10.0 → 0.12.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 (277) hide show
  1. package/dist/esm/adapters/d1/index.js +18 -9
  2. package/dist/esm/adapters/d1/migrations.js +7 -2
  3. package/dist/esm/adapters/d1/session-store.js +39 -0
  4. package/dist/esm/adapters/d1/session.js +2 -2
  5. package/dist/esm/adapters/durable-object/index.js +4 -0
  6. package/dist/esm/adapters/durable-object/session-store.js +42 -0
  7. package/dist/esm/adapters/dynamo/index.js +24 -7
  8. package/dist/esm/adapters/dynamo/session-store.js +49 -0
  9. package/dist/esm/adapters/kms/index.js +6 -2
  10. package/dist/esm/adapters/kv/index.js +6 -2
  11. package/dist/esm/adapters/memory/index.js +24 -7
  12. package/dist/esm/adapters/memory/session-store.js +45 -0
  13. package/dist/esm/adapters/memory/token-store.js +13 -0
  14. package/dist/esm/adapters/postgres/executor.js +2 -2
  15. package/dist/esm/adapters/postgres/index.js +33 -10
  16. package/dist/esm/adapters/postgres/migrations.js +8 -2
  17. package/dist/esm/adapters/postgres/session-store.js +42 -0
  18. package/dist/esm/client.js +35 -28
  19. package/dist/esm/domain/authorize.js +7 -2
  20. package/dist/esm/domain/callback.js +124 -7
  21. package/dist/esm/domain/client-auth.js +2 -2
  22. package/dist/esm/domain/crypto.js +14 -14
  23. package/dist/esm/domain/discovery.js +46 -4
  24. package/dist/esm/domain/dpop.js +122 -0
  25. package/dist/esm/domain/id-token.js +94 -0
  26. package/dist/esm/domain/introspect.js +5 -1
  27. package/dist/esm/domain/jwt.js +30 -2
  28. package/dist/esm/domain/logout.js +75 -0
  29. package/dist/esm/domain/method-dispatch.js +27 -1
  30. package/dist/esm/domain/method-route.js +83 -1
  31. package/dist/esm/domain/par.js +51 -0
  32. package/dist/esm/domain/pkce.js +2 -2
  33. package/dist/esm/domain/refresh.js +12 -1
  34. package/dist/esm/domain/register.js +74 -0
  35. package/dist/esm/domain/revoke.js +2 -2
  36. package/dist/esm/domain/state-envelope.js +2 -2
  37. package/dist/esm/domain/token-exchange.js +1 -0
  38. package/dist/esm/domain/token.js +53 -12
  39. package/dist/esm/domain/userinfo.js +38 -3
  40. package/dist/esm/error.js +8 -8
  41. package/dist/esm/http/cookies.js +19 -2
  42. package/dist/esm/http/errors.js +4 -4
  43. package/dist/esm/http/handlers/authorize.js +92 -21
  44. package/dist/esm/http/handlers/callback.js +7 -1
  45. package/dist/esm/http/handlers/end-session.js +64 -0
  46. package/dist/esm/http/handlers/metadata.js +7 -4
  47. package/dist/esm/http/handlers/method-route.js +47 -2
  48. package/dist/esm/http/handlers/par.js +53 -0
  49. package/dist/esm/http/handlers/register.js +46 -0
  50. package/dist/esm/http/handlers/revocation.js +2 -2
  51. package/dist/esm/http/handlers/token.js +36 -6
  52. package/dist/esm/http/handlers/userinfo.js +35 -29
  53. package/dist/esm/http/middleware/tenant.js +6 -6
  54. package/dist/esm/http/router.js +10 -0
  55. package/dist/esm/http/schemas/authorize.js +8 -2
  56. package/dist/esm/http/schemas/end-session.js +13 -0
  57. package/dist/esm/http/schemas/par.js +9 -0
  58. package/dist/esm/http/schemas/register.js +16 -0
  59. package/dist/esm/http/schemas/revocation.js +2 -2
  60. package/dist/esm/http/schemas/token.js +7 -7
  61. package/dist/esm/index.js +40 -34
  62. package/dist/esm/methods/oauth2-factory.js +2 -2
  63. package/dist/esm/methods/password-hash.js +2 -2
  64. package/dist/esm/methods/providers/index.js +30 -14
  65. package/dist/esm/methods/saml-sp/acs.js +208 -0
  66. package/dist/esm/methods/saml-sp/attributes.js +75 -0
  67. package/dist/esm/methods/saml-sp/authnrequest.js +63 -0
  68. package/dist/esm/methods/saml-sp/cache-provider.js +33 -0
  69. package/dist/esm/methods/saml-sp/cert-rotation.js +13 -0
  70. package/dist/esm/methods/saml-sp/factory.js +70 -0
  71. package/dist/esm/methods/saml-sp/index.js +7 -0
  72. package/dist/esm/methods/saml-sp/metadata.js +69 -0
  73. package/dist/esm/methods/saml-sp/method.js +29 -0
  74. package/dist/esm/methods/saml-sp/parse-idp-metadata.js +102 -0
  75. package/dist/esm/methods/saml-sp/saml-instance.js +49 -0
  76. package/dist/esm/methods/saml-sp/slo-initiate.js +84 -0
  77. package/dist/esm/methods/saml-sp/sls.js +154 -0
  78. package/dist/esm/methods/saml-sp/types.js +0 -0
  79. package/dist/esm/types/error.js +5 -0
  80. package/dist/esm/types/result.js +3 -3
  81. package/dist/esm/ui/forms.js +3 -3
  82. package/dist/esm/ui/index.js +8 -5
  83. package/dist/esm/ui/picker.js +2 -2
  84. package/dist/types/adapters/d1/migrations.d.ts.map +1 -1
  85. package/dist/types/adapters/d1/session-store.d.ts +3 -0
  86. package/dist/types/adapters/d1/session-store.d.ts.map +1 -1
  87. package/dist/types/adapters/durable-object/session-store.d.ts +3 -0
  88. package/dist/types/adapters/durable-object/session-store.d.ts.map +1 -1
  89. package/dist/types/adapters/dynamo/session-store.d.ts +3 -0
  90. package/dist/types/adapters/dynamo/session-store.d.ts.map +1 -1
  91. package/dist/types/adapters/memory/session-store.d.ts +6 -1
  92. package/dist/types/adapters/memory/session-store.d.ts.map +1 -1
  93. package/dist/types/adapters/memory/token-store.d.ts +1 -0
  94. package/dist/types/adapters/memory/token-store.d.ts.map +1 -1
  95. package/dist/types/adapters/postgres/migrations.d.ts +1 -1
  96. package/dist/types/adapters/postgres/migrations.d.ts.map +1 -1
  97. package/dist/types/adapters/postgres/session-store.d.ts +3 -0
  98. package/dist/types/adapters/postgres/session-store.d.ts.map +1 -1
  99. package/dist/types/client.d.ts +19 -0
  100. package/dist/types/client.d.ts.map +1 -1
  101. package/dist/types/domain/authorize.d.ts.map +1 -1
  102. package/dist/types/domain/callback.d.ts +16 -1
  103. package/dist/types/domain/callback.d.ts.map +1 -1
  104. package/dist/types/domain/client-credentials.d.ts +2 -0
  105. package/dist/types/domain/client-credentials.d.ts.map +1 -1
  106. package/dist/types/domain/discovery.d.ts +37 -0
  107. package/dist/types/domain/discovery.d.ts.map +1 -1
  108. package/dist/types/domain/dpop.d.ts +61 -0
  109. package/dist/types/domain/dpop.d.ts.map +1 -0
  110. package/dist/types/domain/id-token.d.ts +83 -0
  111. package/dist/types/domain/id-token.d.ts.map +1 -0
  112. package/dist/types/domain/introspect.d.ts +8 -0
  113. package/dist/types/domain/introspect.d.ts.map +1 -1
  114. package/dist/types/domain/jwt.d.ts +27 -1
  115. package/dist/types/domain/jwt.d.ts.map +1 -1
  116. package/dist/types/domain/logout.d.ts +69 -0
  117. package/dist/types/domain/logout.d.ts.map +1 -0
  118. package/dist/types/domain/method-dispatch.d.ts.map +1 -1
  119. package/dist/types/domain/method-route.d.ts +57 -1
  120. package/dist/types/domain/method-route.d.ts.map +1 -1
  121. package/dist/types/domain/par.d.ts +62 -0
  122. package/dist/types/domain/par.d.ts.map +1 -0
  123. package/dist/types/domain/refresh.d.ts +9 -0
  124. package/dist/types/domain/refresh.d.ts.map +1 -1
  125. package/dist/types/domain/register.d.ts +33 -0
  126. package/dist/types/domain/register.d.ts.map +1 -0
  127. package/dist/types/domain/token-exchange.d.ts +2 -0
  128. package/dist/types/domain/token-exchange.d.ts.map +1 -1
  129. package/dist/types/domain/token.d.ts +41 -1
  130. package/dist/types/domain/token.d.ts.map +1 -1
  131. package/dist/types/domain/userinfo.d.ts +44 -5
  132. package/dist/types/domain/userinfo.d.ts.map +1 -1
  133. package/dist/types/http/context.d.ts +6 -1
  134. package/dist/types/http/context.d.ts.map +1 -1
  135. package/dist/types/http/cookies.d.ts +9 -1
  136. package/dist/types/http/cookies.d.ts.map +1 -1
  137. package/dist/types/http/handlers/authorize.d.ts.map +1 -1
  138. package/dist/types/http/handlers/callback.d.ts.map +1 -1
  139. package/dist/types/http/handlers/end-session.d.ts +3 -0
  140. package/dist/types/http/handlers/end-session.d.ts.map +1 -0
  141. package/dist/types/http/handlers/metadata.d.ts +1 -1
  142. package/dist/types/http/handlers/metadata.d.ts.map +1 -1
  143. package/dist/types/http/handlers/method-route.d.ts.map +1 -1
  144. package/dist/types/http/handlers/par.d.ts +3 -0
  145. package/dist/types/http/handlers/par.d.ts.map +1 -0
  146. package/dist/types/http/handlers/register.d.ts +3 -0
  147. package/dist/types/http/handlers/register.d.ts.map +1 -0
  148. package/dist/types/http/handlers/token.d.ts.map +1 -1
  149. package/dist/types/http/handlers/userinfo.d.ts.map +1 -1
  150. package/dist/types/http/router.d.ts.map +1 -1
  151. package/dist/types/http/schemas/authorize.d.ts +40 -0
  152. package/dist/types/http/schemas/authorize.d.ts.map +1 -1
  153. package/dist/types/http/schemas/end-session.d.ts +31 -0
  154. package/dist/types/http/schemas/end-session.d.ts.map +1 -0
  155. package/dist/types/http/schemas/par.d.ts +20 -0
  156. package/dist/types/http/schemas/par.d.ts.map +1 -0
  157. package/dist/types/http/schemas/register.d.ts +42 -0
  158. package/dist/types/http/schemas/register.d.ts.map +1 -0
  159. package/dist/types/index.d.ts +3 -3
  160. package/dist/types/index.d.ts.map +1 -1
  161. package/dist/types/methods/saml-sp/acs.d.ts +4 -0
  162. package/dist/types/methods/saml-sp/acs.d.ts.map +1 -0
  163. package/dist/types/methods/saml-sp/attributes.d.ts +40 -0
  164. package/dist/types/methods/saml-sp/attributes.d.ts.map +1 -0
  165. package/dist/types/methods/saml-sp/authnrequest.d.ts +4 -0
  166. package/dist/types/methods/saml-sp/authnrequest.d.ts.map +1 -0
  167. package/dist/types/methods/saml-sp/cache-provider.d.ts +38 -0
  168. package/dist/types/methods/saml-sp/cache-provider.d.ts.map +1 -0
  169. package/dist/types/methods/saml-sp/cert-rotation.d.ts +23 -0
  170. package/dist/types/methods/saml-sp/cert-rotation.d.ts.map +1 -0
  171. package/dist/types/methods/saml-sp/factory.d.ts +704 -0
  172. package/dist/types/methods/saml-sp/factory.d.ts.map +1 -0
  173. package/dist/types/methods/saml-sp/index.d.ts +24 -0
  174. package/dist/types/methods/saml-sp/index.d.ts.map +1 -0
  175. package/dist/types/methods/saml-sp/metadata.d.ts +42 -0
  176. package/dist/types/methods/saml-sp/metadata.d.ts.map +1 -0
  177. package/dist/types/methods/saml-sp/method.d.ts +17 -0
  178. package/dist/types/methods/saml-sp/method.d.ts.map +1 -0
  179. package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts +4 -0
  180. package/dist/types/methods/saml-sp/parse-idp-metadata.d.ts.map +1 -0
  181. package/dist/types/methods/saml-sp/saml-instance.d.ts +81 -0
  182. package/dist/types/methods/saml-sp/saml-instance.d.ts.map +1 -0
  183. package/dist/types/methods/saml-sp/slo-initiate.d.ts +4 -0
  184. package/dist/types/methods/saml-sp/slo-initiate.d.ts.map +1 -0
  185. package/dist/types/methods/saml-sp/sls.d.ts +4 -0
  186. package/dist/types/methods/saml-sp/sls.d.ts.map +1 -0
  187. package/dist/types/methods/saml-sp/types.d.ts +204 -0
  188. package/dist/types/methods/saml-sp/types.d.ts.map +1 -0
  189. package/dist/types/ports/audit-log.d.ts +40 -0
  190. package/dist/types/ports/audit-log.d.ts.map +1 -1
  191. package/dist/types/ports/session-store.d.ts +50 -0
  192. package/dist/types/ports/session-store.d.ts.map +1 -1
  193. package/dist/types/ports/token-store.d.ts +12 -0
  194. package/dist/types/ports/token-store.d.ts.map +1 -1
  195. package/dist/types/types/authorization.d.ts +23 -0
  196. package/dist/types/types/authorization.d.ts.map +1 -1
  197. package/dist/types/types/error.d.ts +9 -0
  198. package/dist/types/types/error.d.ts.map +1 -1
  199. package/dist/types/types/flow.d.ts +10 -0
  200. package/dist/types/types/flow.d.ts.map +1 -1
  201. package/dist/types/types/idp.d.ts +162 -1
  202. package/dist/types/types/idp.d.ts.map +1 -1
  203. package/dist/types/types/method.d.ts +109 -0
  204. package/dist/types/types/method.d.ts.map +1 -1
  205. package/dist/types/types/tenant.d.ts +37 -0
  206. package/dist/types/types/tenant.d.ts.map +1 -1
  207. package/dist/types/types/token.d.ts +128 -0
  208. package/dist/types/types/token.d.ts.map +1 -1
  209. package/package.json +9 -1
  210. package/src/adapters/d1/migrations.ts +5 -0
  211. package/src/adapters/d1/session-store.ts +68 -0
  212. package/src/adapters/durable-object/session-store.ts +61 -0
  213. package/src/adapters/dynamo/session-store.ts +63 -0
  214. package/src/adapters/memory/session-store.ts +83 -1
  215. package/src/adapters/memory/token-store.ts +16 -0
  216. package/src/adapters/postgres/migrations.ts +6 -0
  217. package/src/adapters/postgres/session-store.ts +69 -0
  218. package/src/client.ts +32 -0
  219. package/src/domain/authorize.ts +9 -0
  220. package/src/domain/callback.ts +220 -7
  221. package/src/domain/client-credentials.ts +2 -0
  222. package/src/domain/discovery.ts +80 -2
  223. package/src/domain/dpop.ts +293 -0
  224. package/src/domain/id-token.ts +213 -0
  225. package/src/domain/introspect.ts +10 -0
  226. package/src/domain/jwt.ts +80 -1
  227. package/src/domain/logout.ts +180 -0
  228. package/src/domain/method-dispatch.ts +53 -1
  229. package/src/domain/method-route.ts +195 -1
  230. package/src/domain/par.ts +141 -0
  231. package/src/domain/refresh.ts +41 -0
  232. package/src/domain/register.ts +168 -0
  233. package/src/domain/token-exchange.ts +11 -0
  234. package/src/domain/token.ts +134 -8
  235. package/src/domain/userinfo.ts +121 -6
  236. package/src/http/context.ts +10 -0
  237. package/src/http/cookies.ts +19 -1
  238. package/src/http/handlers/authorize.ts +146 -24
  239. package/src/http/handlers/callback.ts +9 -1
  240. package/src/http/handlers/end-session.ts +106 -0
  241. package/src/http/handlers/metadata.ts +7 -2
  242. package/src/http/handlers/method-route.ts +75 -2
  243. package/src/http/handlers/par.ts +84 -0
  244. package/src/http/handlers/register.ts +70 -0
  245. package/src/http/handlers/token.ts +56 -0
  246. package/src/http/handlers/userinfo.ts +62 -38
  247. package/src/http/router.ts +19 -0
  248. package/src/http/schemas/authorize.ts +27 -0
  249. package/src/http/schemas/end-session.ts +23 -0
  250. package/src/http/schemas/par.ts +23 -0
  251. package/src/http/schemas/register.ts +29 -0
  252. package/src/index.ts +21 -1
  253. package/src/methods/saml-sp/acs.ts +399 -0
  254. package/src/methods/saml-sp/attributes.ts +137 -0
  255. package/src/methods/saml-sp/authnrequest.ts +128 -0
  256. package/src/methods/saml-sp/cache-provider.ts +76 -0
  257. package/src/methods/saml-sp/cert-rotation.ts +34 -0
  258. package/src/methods/saml-sp/factory.ts +118 -0
  259. package/src/methods/saml-sp/index.ts +34 -0
  260. package/src/methods/saml-sp/metadata.ts +204 -0
  261. package/src/methods/saml-sp/method.ts +65 -0
  262. package/src/methods/saml-sp/parse-idp-metadata.ts +164 -0
  263. package/src/methods/saml-sp/saml-instance.ts +173 -0
  264. package/src/methods/saml-sp/slo-initiate.ts +177 -0
  265. package/src/methods/saml-sp/sls.ts +306 -0
  266. package/src/methods/saml-sp/types.ts +203 -0
  267. package/src/ports/CONSISTENCY.md +77 -21
  268. package/src/ports/audit-log.ts +42 -0
  269. package/src/ports/session-store.ts +61 -0
  270. package/src/ports/token-store.ts +13 -0
  271. package/src/types/authorization.ts +25 -0
  272. package/src/types/error.ts +23 -0
  273. package/src/types/flow.ts +10 -0
  274. package/src/types/idp.ts +170 -0
  275. package/src/types/method.ts +110 -0
  276. package/src/types/tenant.ts +37 -0
  277. package/src/types/token.ts +135 -0
@@ -1,12 +1,21 @@
1
+ // src/adapters/d1/index.ts
2
+ import { D1AuditLog } from "./audit-log";
3
+ import { D1ConfigStore } from "./config-store";
4
+ import { D1KeyStore } from "./key-store";
5
+ import { D1MethodStore } from "./method-store";
6
+ import { D1SessionStore } from "./session-store";
7
+ import { D1TokenStore } from "./token-store";
8
+ import { INITIAL_SCHEMA_SQL, migrate } from "./migrations";
9
+ import { isSessionsCapable, primarySession } from "./session";
1
10
  export {
2
- primarySession,
3
- migrate,
4
- isSessionsCapable,
5
- INITIAL_SCHEMA_SQL,
6
- D1TokenStore,
7
- D1SessionStore,
8
- D1MethodStore,
9
- D1KeyStore,
11
+ D1AuditLog,
10
12
  D1ConfigStore,
11
- D1AuditLog
13
+ D1KeyStore,
14
+ D1MethodStore,
15
+ D1SessionStore,
16
+ D1TokenStore,
17
+ INITIAL_SCHEMA_SQL,
18
+ isSessionsCapable,
19
+ migrate,
20
+ primarySession
12
21
  };
@@ -57,6 +57,11 @@ var INITIAL_SCHEMA_SQL = [
57
57
  payload TEXT NOT NULL,
58
58
  expires_at INTEGER NOT NULL
59
59
  )`,
60
+ `CREATE TABLE IF NOT EXISTS openauth_scratch (
61
+ scratch_key TEXT PRIMARY KEY,
62
+ value TEXT NOT NULL,
63
+ expires_at INTEGER NOT NULL
64
+ )`,
60
65
  `CREATE TABLE IF NOT EXISTS openauth_audit_events (
61
66
  id INTEGER PRIMARY KEY AUTOINCREMENT,
62
67
  ts INTEGER NOT NULL,
@@ -72,6 +77,6 @@ async function migrate(db) {
72
77
  }
73
78
  }
74
79
  export {
75
- migrate,
76
- INITIAL_SCHEMA_SQL
80
+ INITIAL_SCHEMA_SQL,
81
+ migrate
77
82
  };
@@ -128,6 +128,45 @@ class D1SessionStore {
128
128
  }
129
129
  return ok(undefined);
130
130
  }
131
+ async saveScratch(key, value, ttlMs) {
132
+ if (ttlMs <= 0) {
133
+ return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
134
+ }
135
+ const expiresAt = this.#clock() + ttlMs;
136
+ try {
137
+ await primarySession(this.#db).prepare(`INSERT INTO openauth_scratch (scratch_key, value, expires_at) VALUES (?1, ?2, ?3)
138
+ ON CONFLICT(scratch_key) DO UPDATE SET value = excluded.value, expires_at = excluded.expires_at`).bind(key, value, expiresAt).run();
139
+ } catch (e) {
140
+ return err(authError.internalError("saveScratch: upsert failed", e));
141
+ }
142
+ return ok(undefined);
143
+ }
144
+ async readScratch(key) {
145
+ let row;
146
+ try {
147
+ row = await primarySession(this.#db).prepare(`SELECT value, expires_at FROM openauth_scratch WHERE scratch_key = ?1`).bind(key).first();
148
+ } catch (e) {
149
+ return err(authError.internalError("readScratch: query failed", e));
150
+ }
151
+ if (!row) {
152
+ return err(authError.unknownState(`scratch "${key}" unknown`));
153
+ }
154
+ if (this.#clock() >= Number(row.expires_at)) {
155
+ try {
156
+ await primarySession(this.#db).prepare(`DELETE FROM openauth_scratch WHERE scratch_key = ?1`).bind(key).run();
157
+ } catch {}
158
+ return err(authError.unknownState(`scratch "${key}" expired`));
159
+ }
160
+ return ok(row.value);
161
+ }
162
+ async deleteScratch(key) {
163
+ try {
164
+ await primarySession(this.#db).prepare(`DELETE FROM openauth_scratch WHERE scratch_key = ?1`).bind(key).run();
165
+ } catch (e) {
166
+ return err(authError.internalError("deleteScratch: delete failed", e));
167
+ }
168
+ return ok(undefined);
169
+ }
131
170
  }
132
171
  export {
133
172
  D1SessionStore
@@ -13,6 +13,6 @@ function primarySession(db) {
13
13
  };
14
14
  }
15
15
  export {
16
- primarySession,
17
- isSessionsCapable
16
+ isSessionsCapable,
17
+ primarySession
18
18
  };
@@ -1,3 +1,7 @@
1
+ // src/adapters/durable-object/index.ts
2
+ import {
3
+ DurableObjectSessionStore
4
+ } from "./session-store";
1
5
  export {
2
6
  DurableObjectSessionStore
3
7
  };
@@ -131,9 +131,51 @@ class DurableObjectSessionStore {
131
131
  }
132
132
  return ok(undefined);
133
133
  }
134
+ async saveScratch(key, value, ttlMs) {
135
+ if (ttlMs <= 0) {
136
+ return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
137
+ }
138
+ const stored = {
139
+ value,
140
+ expiresAt: this.#clock() + ttlMs
141
+ };
142
+ try {
143
+ await this.#storage.put(scratchKey(key), stored);
144
+ } catch (e) {
145
+ return err(authError.internalError("saveScratch: put failed", e));
146
+ }
147
+ return ok(undefined);
148
+ }
149
+ async readScratch(key) {
150
+ let stored;
151
+ try {
152
+ stored = await this.#storage.get(scratchKey(key));
153
+ } catch (e) {
154
+ return err(authError.internalError("readScratch: get failed", e));
155
+ }
156
+ if (!stored) {
157
+ return err(authError.unknownState(`scratch "${key}" unknown`));
158
+ }
159
+ if (this.#clock() >= stored.expiresAt) {
160
+ try {
161
+ await this.#storage.delete(scratchKey(key));
162
+ } catch {}
163
+ return err(authError.unknownState(`scratch "${key}" expired`));
164
+ }
165
+ return ok(stored.value);
166
+ }
167
+ async deleteScratch(key) {
168
+ try {
169
+ await this.#storage.delete(scratchKey(key));
170
+ } catch (e) {
171
+ return err(authError.internalError("deleteScratch: delete failed", e));
172
+ }
173
+ return ok(undefined);
174
+ }
134
175
  }
135
176
  var flowKey = (flowId) => `flow:${flowId}`;
136
177
  var sessionKey = (sessionId) => `session:${sessionId}`;
178
+ var scratchKey = (key) => `scratch:${key}`;
137
179
  export {
138
180
  DurableObjectSessionStore
139
181
  };
@@ -1,10 +1,27 @@
1
+ // src/adapters/dynamo/index.ts
2
+ import { DynamoAuditLog } from "./audit-log";
3
+ import {
4
+ DynamoConfigStore
5
+ } from "./config-store";
6
+ import { DynamoKeyStore } from "./key-store";
7
+ import {
8
+ DynamoMethodStore
9
+ } from "./method-store";
10
+ import {
11
+ DynamoSessionStore
12
+ } from "./session-store";
13
+ import { DynamoTokenStore } from "./token-store";
14
+ import {
15
+ DynamoPasskeyCredentialStore
16
+ } from "./passkey-credential-store";
17
+ import { fromDynamoDBClient } from "./sdk";
1
18
  export {
2
- fromDynamoDBClient,
3
- DynamoTokenStore,
4
- DynamoSessionStore,
5
- DynamoPasskeyCredentialStore,
6
- DynamoMethodStore,
7
- DynamoKeyStore,
19
+ DynamoAuditLog,
8
20
  DynamoConfigStore,
9
- DynamoAuditLog
21
+ DynamoKeyStore,
22
+ DynamoMethodStore,
23
+ DynamoPasskeyCredentialStore,
24
+ DynamoSessionStore,
25
+ DynamoTokenStore,
26
+ fromDynamoDBClient
10
27
  };
@@ -152,6 +152,55 @@ class DynamoSessionStore {
152
152
  }
153
153
  return ok(undefined);
154
154
  }
155
+ async saveScratch(key, value, ttlMs) {
156
+ if (ttlMs <= 0) {
157
+ return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
158
+ }
159
+ const expiresAtMs = this.#clock() + ttlMs;
160
+ try {
161
+ await this.#exec.put({
162
+ item: {
163
+ pk: "scratch",
164
+ sk: key,
165
+ value,
166
+ expires_at: expiresAtMs,
167
+ ttl: Math.floor(expiresAtMs / 1000)
168
+ }
169
+ });
170
+ } catch (e) {
171
+ return err(authError.internalError("saveScratch: put failed", e));
172
+ }
173
+ return ok(undefined);
174
+ }
175
+ async readScratch(key) {
176
+ let row;
177
+ try {
178
+ row = await this.#exec.get({
179
+ key: { pk: "scratch", sk: key },
180
+ consistentRead: true
181
+ });
182
+ } catch (e) {
183
+ return err(authError.internalError("readScratch: get failed", e));
184
+ }
185
+ if (!row) {
186
+ return err(authError.unknownState(`scratch "${key}" unknown`));
187
+ }
188
+ if (this.#clock() >= Number(row.expires_at)) {
189
+ try {
190
+ await this.#exec.delete({ key: { pk: "scratch", sk: key } });
191
+ } catch {}
192
+ return err(authError.unknownState(`scratch "${key}" expired`));
193
+ }
194
+ return ok(String(row.value));
195
+ }
196
+ async deleteScratch(key) {
197
+ try {
198
+ await this.#exec.delete({ key: { pk: "scratch", sk: key } });
199
+ } catch (e) {
200
+ return err(authError.internalError("deleteScratch: delete failed", e));
201
+ }
202
+ return ok(undefined);
203
+ }
155
204
  }
156
205
  function parseFlowPayload(raw) {
157
206
  if (typeof raw === "string")
@@ -1,5 +1,9 @@
1
+ // src/adapters/kms/index.ts
2
+ import { inMemoryKmsBackingStore } from "./in-memory-backing";
3
+ import { KmsKeyStore } from "./key-store";
4
+ import { fromKmsClient } from "./sdk";
1
5
  export {
2
- inMemoryKmsBackingStore,
6
+ KmsKeyStore,
3
7
  fromKmsClient,
4
- KmsKeyStore
8
+ inMemoryKmsBackingStore
5
9
  };
@@ -1,5 +1,9 @@
1
+ // src/adapters/kv/index.ts
2
+ import { KvAuditLog } from "./audit-log";
3
+ import { KvConfigStore } from "./config-store";
4
+ import { KvMethodStore } from "./method-store";
1
5
  export {
2
- KvMethodStore,
6
+ KvAuditLog,
3
7
  KvConfigStore,
4
- KvAuditLog
8
+ KvMethodStore
5
9
  };
@@ -1,10 +1,27 @@
1
+ // src/adapters/memory/index.ts
2
+ import { MemoryAuditLog } from "./audit-log";
3
+ import {
4
+ MemoryConfigStore
5
+ } from "./config-store";
6
+ import { MemoryKeyStore } from "./key-store";
7
+ import {
8
+ MemoryMethodStore
9
+ } from "./method-store";
10
+ import {
11
+ MemorySessionStore
12
+ } from "./session-store";
13
+ import { MemoryTokenStore } from "./token-store";
14
+ import {
15
+ MemoryPasskeyCredentialStore
16
+ } from "./passkey-credential-store";
17
+ import { realClock } from "./clock";
1
18
  export {
2
- realClock,
3
- MemoryTokenStore,
4
- MemorySessionStore,
5
- MemoryPasskeyCredentialStore,
6
- MemoryMethodStore,
7
- MemoryKeyStore,
19
+ MemoryAuditLog,
8
20
  MemoryConfigStore,
9
- MemoryAuditLog
21
+ MemoryKeyStore,
22
+ MemoryMethodStore,
23
+ MemoryPasskeyCredentialStore,
24
+ MemorySessionStore,
25
+ MemoryTokenStore,
26
+ realClock
10
27
  };
@@ -7,6 +7,8 @@ class MemorySessionStore {
7
7
  #clock;
8
8
  #flows = new Map;
9
9
  #sessions = new Map;
10
+ #par = new Map;
11
+ #scratch = new Map;
10
12
  constructor(opts = {}) {
11
13
  this.#clock = opts.clock ?? realClock;
12
14
  }
@@ -76,6 +78,49 @@ class MemorySessionStore {
76
78
  this.#sessions.delete(sessionId);
77
79
  return ok(undefined);
78
80
  }
81
+ async savePar(requestUri, payload, ttl) {
82
+ if (ttl <= 0) {
83
+ return err(authError.internalError(`savePar: ttl must be positive, got ${ttl}`));
84
+ }
85
+ this.#par.set(requestUri, {
86
+ record: payload,
87
+ expiresAt: this.#clock() + ttl
88
+ });
89
+ return ok(undefined);
90
+ }
91
+ async consumePar(requestUri) {
92
+ const stored = this.#par.get(requestUri);
93
+ if (!stored) {
94
+ return err(authError.unknownState(`par "${requestUri}" unknown or already consumed`));
95
+ }
96
+ this.#par.delete(requestUri);
97
+ if (this.#clock() >= stored.expiresAt) {
98
+ return err(authError.unknownState(`par "${requestUri}" expired`));
99
+ }
100
+ return ok(stored.record);
101
+ }
102
+ async saveScratch(key, value, ttlMs) {
103
+ if (ttlMs <= 0) {
104
+ return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
105
+ }
106
+ this.#scratch.set(key, { value, expiresAt: this.#clock() + ttlMs });
107
+ return ok(undefined);
108
+ }
109
+ async readScratch(key) {
110
+ const stored = this.#scratch.get(key);
111
+ if (!stored) {
112
+ return err(authError.unknownState(`scratch "${key}" unknown`));
113
+ }
114
+ if (this.#clock() >= stored.expiresAt) {
115
+ this.#scratch.delete(key);
116
+ return err(authError.unknownState(`scratch "${key}" expired`));
117
+ }
118
+ return ok(stored.value);
119
+ }
120
+ async deleteScratch(key) {
121
+ this.#scratch.delete(key);
122
+ return ok(undefined);
123
+ }
79
124
  }
80
125
  export {
81
126
  MemorySessionStore
@@ -8,6 +8,7 @@ class MemoryTokenStore {
8
8
  #clock;
9
9
  #codes = new Map;
10
10
  #refresh = new Map;
11
+ #dpopJtis = new Map;
11
12
  constructor(opts = {}) {
12
13
  this.#clock = opts.clock ?? realClock;
13
14
  }
@@ -85,6 +86,18 @@ class MemoryTokenStore {
85
86
  }
86
87
  return ok(undefined);
87
88
  }
89
+ async recordDpopJti(jti, ttlMs) {
90
+ const now = this.#clock();
91
+ for (const [k, exp] of this.#dpopJtis) {
92
+ if (exp <= now)
93
+ this.#dpopJtis.delete(k);
94
+ }
95
+ if (this.#dpopJtis.has(jti)) {
96
+ return err(authError.invalidGrant(`dpop jti "${jti}" replayed`));
97
+ }
98
+ this.#dpopJtis.set(jti, now + ttlMs);
99
+ return ok(undefined);
100
+ }
88
101
  }
89
102
  export {
90
103
  MemoryTokenStore
@@ -16,6 +16,6 @@ function fromPGlite(db) {
16
16
  };
17
17
  }
18
18
  export {
19
- fromPostgresJs,
20
- fromPGlite
19
+ fromPGlite,
20
+ fromPostgresJs
21
21
  };
@@ -1,13 +1,36 @@
1
- export {
2
- migrate,
3
- fromPostgresJs,
1
+ // src/adapters/postgres/index.ts
2
+ import {
4
3
  fromPGlite,
5
- PostgresTokenStore,
6
- PostgresSessionStore,
7
- PostgresPasskeyCredentialStore,
8
- PostgresMethodStore,
9
- PostgresKeyStore,
10
- PostgresConfigStore,
4
+ fromPostgresJs
5
+ } from "./executor";
6
+ import { INITIAL_SCHEMA_SQL, migrate } from "./migrations";
7
+ import { PostgresAuditLog } from "./audit-log";
8
+ import {
9
+ PostgresConfigStore
10
+ } from "./config-store";
11
+ import { PostgresKeyStore } from "./key-store";
12
+ import {
13
+ PostgresMethodStore
14
+ } from "./method-store";
15
+ import {
16
+ PostgresSessionStore
17
+ } from "./session-store";
18
+ import {
19
+ PostgresTokenStore
20
+ } from "./token-store";
21
+ import {
22
+ PostgresPasskeyCredentialStore
23
+ } from "./passkey-credential-store";
24
+ export {
25
+ INITIAL_SCHEMA_SQL,
11
26
  PostgresAuditLog,
12
- INITIAL_SCHEMA_SQL
27
+ PostgresConfigStore,
28
+ PostgresKeyStore,
29
+ PostgresMethodStore,
30
+ PostgresPasskeyCredentialStore,
31
+ PostgresSessionStore,
32
+ PostgresTokenStore,
33
+ fromPGlite,
34
+ fromPostgresJs,
35
+ migrate
13
36
  };
@@ -75,6 +75,12 @@ CREATE TABLE IF NOT EXISTS openauth_sessions (
75
75
  expires_at bigint NOT NULL
76
76
  );
77
77
 
78
+ CREATE TABLE IF NOT EXISTS openauth_scratch (
79
+ scratch_key text PRIMARY KEY,
80
+ value text NOT NULL,
81
+ expires_at bigint NOT NULL
82
+ );
83
+
78
84
  CREATE TABLE IF NOT EXISTS openauth_audit_events (
79
85
  id bigserial PRIMARY KEY,
80
86
  ts bigint NOT NULL,
@@ -106,6 +112,6 @@ async function migrate(exec) {
106
112
  }
107
113
  }
108
114
  export {
109
- migrate,
110
- INITIAL_SCHEMA_SQL
115
+ INITIAL_SCHEMA_SQL,
116
+ migrate
111
117
  };
@@ -119,6 +119,48 @@ class PostgresSessionStore {
119
119
  }
120
120
  return ok(undefined);
121
121
  }
122
+ async saveScratch(key, value, ttlMs) {
123
+ if (ttlMs <= 0) {
124
+ return err(authError.internalError(`saveScratch: ttlMs must be positive, got ${ttlMs}`));
125
+ }
126
+ const expiresAt = this.#clock() + ttlMs;
127
+ try {
128
+ await this.#exec.query(`INSERT INTO openauth_scratch (scratch_key, value, expires_at)
129
+ VALUES ($1, $2, $3)
130
+ ON CONFLICT (scratch_key)
131
+ DO UPDATE SET value = EXCLUDED.value, expires_at = EXCLUDED.expires_at`, [key, value, expiresAt]);
132
+ } catch (e) {
133
+ return err(authError.internalError("saveScratch: upsert failed", e));
134
+ }
135
+ return ok(undefined);
136
+ }
137
+ async readScratch(key) {
138
+ let row;
139
+ try {
140
+ const result = await this.#exec.query(`SELECT value, expires_at FROM openauth_scratch WHERE scratch_key = $1`, [key]);
141
+ row = result.rows[0];
142
+ } catch (e) {
143
+ return err(authError.internalError("readScratch: query failed", e));
144
+ }
145
+ if (!row) {
146
+ return err(authError.unknownState(`scratch "${key}" unknown`));
147
+ }
148
+ if (this.#clock() >= Number(row.expires_at)) {
149
+ try {
150
+ await this.#exec.query(`DELETE FROM openauth_scratch WHERE scratch_key = $1`, [key]);
151
+ } catch {}
152
+ return err(authError.unknownState(`scratch "${key}" expired`));
153
+ }
154
+ return ok(row.value);
155
+ }
156
+ async deleteScratch(key) {
157
+ try {
158
+ await this.#exec.query(`DELETE FROM openauth_scratch WHERE scratch_key = $1`, [key]);
159
+ } catch (e) {
160
+ return err(authError.internalError("deleteScratch: delete failed", e));
161
+ }
162
+ return ok(undefined);
163
+ }
122
164
  }
123
165
  function parseFlowRecord(raw) {
124
166
  if (typeof raw === "string")
@@ -33,44 +33,49 @@ function createClient(input) {
33
33
  if (cached)
34
34
  return cached;
35
35
  const keyset = await (f || fetch)(wk.jwks_uri).then((r) => r.json());
36
- const result2 = createLocalJWKSet(keyset);
37
- jwksCache.set(issuer, result2);
38
- return result2;
36
+ const result = createLocalJWKSet(keyset);
37
+ jwksCache.set(issuer, result);
38
+ return result;
39
39
  }
40
40
  const result = {
41
41
  async authorize(redirectURI, response, opts) {
42
- const result2 = new URL(issuer + "/authorize");
42
+ const result = new URL(issuer + "/authorize");
43
43
  const challenge = {
44
44
  state: crypto.randomUUID()
45
45
  };
46
- result2.searchParams.set("client_id", input.clientID);
47
- result2.searchParams.set("redirect_uri", redirectURI);
48
- result2.searchParams.set("response_type", response);
49
- result2.searchParams.set("state", challenge.state);
46
+ result.searchParams.set("client_id", input.clientID);
47
+ result.searchParams.set("redirect_uri", redirectURI);
48
+ result.searchParams.set("response_type", response);
49
+ result.searchParams.set("state", challenge.state);
50
50
  if (opts?.provider)
51
- result2.searchParams.set("provider", opts.provider);
51
+ result.searchParams.set("provider", opts.provider);
52
+ if (opts?.scope !== undefined) {
53
+ const scope = Array.isArray(opts.scope) ? opts.scope.join(" ") : opts.scope;
54
+ if (scope)
55
+ result.searchParams.set("scope", scope);
56
+ }
52
57
  if (opts?.pkce && response === "code") {
53
58
  const pkce = await generatePKCE();
54
- result2.searchParams.set("code_challenge_method", "S256");
55
- result2.searchParams.set("code_challenge", pkce.challenge);
59
+ result.searchParams.set("code_challenge_method", "S256");
60
+ result.searchParams.set("code_challenge", pkce.challenge);
56
61
  challenge.verifier = pkce.verifier;
57
62
  }
58
63
  return {
59
64
  challenge,
60
- url: result2.toString()
65
+ url: result.toString()
61
66
  };
62
67
  },
63
68
  async pkce(redirectURI, opts) {
64
- const result2 = new URL(issuer + "/authorize");
69
+ const result = new URL(issuer + "/authorize");
65
70
  if (opts?.provider)
66
- result2.searchParams.set("provider", opts.provider);
67
- result2.searchParams.set("client_id", input.clientID);
68
- result2.searchParams.set("redirect_uri", redirectURI);
69
- result2.searchParams.set("response_type", "code");
71
+ result.searchParams.set("provider", opts.provider);
72
+ result.searchParams.set("client_id", input.clientID);
73
+ result.searchParams.set("redirect_uri", redirectURI);
74
+ result.searchParams.set("response_type", "code");
70
75
  const pkce = await generatePKCE();
71
- result2.searchParams.set("code_challenge_method", "S256");
72
- result2.searchParams.set("code_challenge", pkce.challenge);
73
- return [pkce.verifier, result2.toString()];
76
+ result.searchParams.set("code_challenge_method", "S256");
77
+ result.searchParams.set("code_challenge", pkce.challenge);
78
+ return [pkce.verifier, result.toString()];
74
79
  },
75
80
  async exchange(code, redirectURI, verifier) {
76
81
  const tokens = await f(issuer + "/token", {
@@ -97,7 +102,8 @@ function createClient(input) {
97
102
  tokens: {
98
103
  access: json.access_token,
99
104
  refresh: json.refresh_token,
100
- expiresIn: json.expires_in
105
+ expiresIn: json.expires_in,
106
+ ...typeof json.id_token === "string" ? { idToken: json.id_token } : {}
101
107
  }
102
108
  };
103
109
  },
@@ -136,25 +142,26 @@ function createClient(input) {
136
142
  tokens: {
137
143
  access: json.access_token,
138
144
  refresh: json.refresh_token,
139
- expiresIn: json.expires_in
145
+ expiresIn: json.expires_in,
146
+ ...typeof json.id_token === "string" ? { idToken: json.id_token } : {}
140
147
  }
141
148
  };
142
149
  },
143
150
  async verify(subjects, token, options) {
144
151
  const jwks = await getJWKS();
145
152
  try {
146
- const result2 = await jwtVerify(token, jwks, {
153
+ const result = await jwtVerify(token, jwks, {
147
154
  issuer
148
155
  });
149
- const claim = result2.payload.claim;
156
+ const claim = result.payload.claim;
150
157
  let subjectType;
151
158
  let subjectProperties;
152
159
  if (claim && typeof claim.type === "string") {
153
160
  subjectType = claim.type;
154
161
  subjectProperties = claim.properties;
155
- } else if (result2.payload.mode === "access" && typeof result2.payload.type === "string") {
156
- subjectType = result2.payload.type;
157
- subjectProperties = result2.payload.properties;
162
+ } else if (result.payload.mode === "access" && typeof result.payload.type === "string") {
163
+ subjectType = result.payload.type;
164
+ subjectProperties = result.payload.properties;
158
165
  }
159
166
  if (subjectType === undefined) {
160
167
  return { err: new InvalidSubjectError };
@@ -168,7 +175,7 @@ function createClient(input) {
168
175
  return { err: new InvalidSubjectError };
169
176
  }
170
177
  return {
171
- aud: result2.payload.aud,
178
+ aud: result.payload.aud,
172
179
  subject: {
173
180
  type: subjectType,
174
181
  properties: validated.value