@draftlab/auth 0.15.0 → 0.16.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 (272) hide show
  1. package/dist/esm/allow.js +26 -0
  2. package/dist/esm/client.js +254 -0
  3. package/dist/esm/core.js +597 -0
  4. package/dist/esm/css.d.js +0 -0
  5. package/dist/esm/error.js +88 -0
  6. package/dist/esm/index.js +5 -0
  7. package/dist/esm/keys.js +126 -0
  8. package/dist/esm/mutex.js +53 -0
  9. package/dist/esm/pkce.js +87 -0
  10. package/dist/esm/provider/apple.js +15 -0
  11. package/dist/esm/provider/code.js +62 -0
  12. package/dist/esm/provider/discord.js +15 -0
  13. package/dist/esm/provider/facebook.js +15 -0
  14. package/dist/esm/provider/github.js +15 -0
  15. package/dist/esm/provider/gitlab.js +15 -0
  16. package/dist/esm/provider/google.js +16 -0
  17. package/dist/esm/provider/linkedin.js +15 -0
  18. package/dist/esm/provider/magiclink.js +83 -0
  19. package/dist/esm/provider/microsoft.js +15 -0
  20. package/dist/esm/provider/oauth2.js +130 -0
  21. package/dist/esm/provider/password.js +331 -0
  22. package/dist/esm/provider/provider.js +18 -0
  23. package/dist/esm/provider/reddit.js +15 -0
  24. package/dist/esm/provider/slack.js +15 -0
  25. package/dist/esm/provider/spotify.js +15 -0
  26. package/dist/esm/provider/twitch.js +15 -0
  27. package/dist/esm/provider/vercel.js +17 -0
  28. package/dist/esm/random.js +40 -0
  29. package/dist/esm/revocation.js +27 -0
  30. package/dist/esm/storage/memory.js +110 -0
  31. package/dist/esm/storage/storage.js +56 -0
  32. package/dist/esm/storage/turso.js +93 -0
  33. package/dist/esm/storage/unstorage.js +78 -0
  34. package/dist/esm/subject.js +7 -0
  35. package/dist/esm/themes/theme.js +115 -0
  36. package/dist/esm/toolkit/client.js +119 -0
  37. package/dist/esm/toolkit/index.js +25 -0
  38. package/dist/esm/toolkit/providers/facebook.js +11 -0
  39. package/dist/esm/toolkit/providers/github.js +11 -0
  40. package/dist/esm/toolkit/providers/google.js +11 -0
  41. package/dist/esm/toolkit/providers/strategy.js +0 -0
  42. package/dist/esm/toolkit/storage.js +81 -0
  43. package/dist/esm/toolkit/utils.js +18 -0
  44. package/dist/esm/types.js +0 -0
  45. package/dist/esm/ui/base.js +478 -0
  46. package/dist/esm/ui/code.js +186 -0
  47. package/dist/esm/ui/form.js +46 -0
  48. package/dist/esm/ui/icon.js +242 -0
  49. package/dist/esm/ui/magiclink.js +158 -0
  50. package/dist/esm/ui/password.js +435 -0
  51. package/dist/esm/ui/select.js +102 -0
  52. package/dist/esm/util.js +59 -0
  53. package/dist/{allow.d.mts → types/allow.d.ts} +9 -11
  54. package/dist/types/allow.d.ts.map +1 -0
  55. package/dist/types/client.d.ts +462 -0
  56. package/dist/types/client.d.ts.map +1 -0
  57. package/dist/types/core.d.ts +113 -0
  58. package/dist/types/core.d.ts.map +1 -0
  59. package/dist/{error.d.mts → types/error.d.ts} +95 -97
  60. package/dist/types/error.d.ts.map +1 -0
  61. package/dist/types/index.d.ts +2 -0
  62. package/dist/types/index.d.ts.map +1 -0
  63. package/dist/{keys.d.mts → types/keys.d.ts} +20 -24
  64. package/dist/types/keys.d.ts.map +1 -0
  65. package/dist/types/mutex.d.ts +42 -0
  66. package/dist/types/mutex.d.ts.map +1 -0
  67. package/dist/{pkce.d.mts → types/pkce.d.ts} +10 -11
  68. package/dist/types/pkce.d.ts.map +1 -0
  69. package/dist/types/provider/apple.d.ts +197 -0
  70. package/dist/types/provider/apple.d.ts.map +1 -0
  71. package/dist/types/provider/code.d.ts +288 -0
  72. package/dist/types/provider/code.d.ts.map +1 -0
  73. package/dist/types/provider/discord.d.ts +206 -0
  74. package/dist/types/provider/discord.d.ts.map +1 -0
  75. package/dist/types/provider/facebook.d.ts +200 -0
  76. package/dist/types/provider/facebook.d.ts.map +1 -0
  77. package/dist/types/provider/github.d.ts +220 -0
  78. package/dist/types/provider/github.d.ts.map +1 -0
  79. package/dist/types/provider/gitlab.d.ts +180 -0
  80. package/dist/types/provider/gitlab.d.ts.map +1 -0
  81. package/dist/types/provider/google.d.ts +158 -0
  82. package/dist/types/provider/google.d.ts.map +1 -0
  83. package/dist/types/provider/linkedin.d.ts +190 -0
  84. package/dist/types/provider/linkedin.d.ts.map +1 -0
  85. package/dist/types/provider/magiclink.d.ts +141 -0
  86. package/dist/types/provider/magiclink.d.ts.map +1 -0
  87. package/dist/types/provider/microsoft.d.ts +247 -0
  88. package/dist/types/provider/microsoft.d.ts.map +1 -0
  89. package/dist/types/provider/oauth2.d.ts +229 -0
  90. package/dist/types/provider/oauth2.d.ts.map +1 -0
  91. package/dist/types/provider/password.d.ts +408 -0
  92. package/dist/types/provider/password.d.ts.map +1 -0
  93. package/dist/types/provider/provider.d.ts +226 -0
  94. package/dist/types/provider/provider.d.ts.map +1 -0
  95. package/dist/types/provider/reddit.d.ts +159 -0
  96. package/dist/types/provider/reddit.d.ts.map +1 -0
  97. package/dist/types/provider/slack.d.ts +171 -0
  98. package/dist/types/provider/slack.d.ts.map +1 -0
  99. package/dist/types/provider/spotify.d.ts +168 -0
  100. package/dist/types/provider/spotify.d.ts.map +1 -0
  101. package/dist/types/provider/twitch.d.ts +163 -0
  102. package/dist/types/provider/twitch.d.ts.map +1 -0
  103. package/dist/types/provider/vercel.d.ts +294 -0
  104. package/dist/types/provider/vercel.d.ts.map +1 -0
  105. package/dist/{random.d.mts → types/random.d.ts} +4 -6
  106. package/dist/types/random.d.ts.map +1 -0
  107. package/dist/types/revocation.d.ts +76 -0
  108. package/dist/types/revocation.d.ts.map +1 -0
  109. package/dist/{storage/memory.d.mts → types/storage/memory.d.ts} +17 -21
  110. package/dist/types/storage/memory.d.ts.map +1 -0
  111. package/dist/types/storage/storage.d.ts +177 -0
  112. package/dist/types/storage/storage.d.ts.map +1 -0
  113. package/dist/{storage/turso.d.mts → types/storage/turso.d.ts} +4 -8
  114. package/dist/types/storage/turso.d.ts.map +1 -0
  115. package/dist/{storage/unstorage.d.mts → types/storage/unstorage.d.ts} +12 -11
  116. package/dist/types/storage/unstorage.d.ts.map +1 -0
  117. package/dist/types/subject.d.ts +115 -0
  118. package/dist/types/subject.d.ts.map +1 -0
  119. package/dist/types/themes/theme.d.ts +207 -0
  120. package/dist/types/themes/theme.d.ts.map +1 -0
  121. package/dist/types/toolkit/client.d.ts +235 -0
  122. package/dist/types/toolkit/client.d.ts.map +1 -0
  123. package/dist/types/toolkit/index.d.ts +45 -0
  124. package/dist/types/toolkit/index.d.ts.map +1 -0
  125. package/dist/types/toolkit/providers/facebook.d.ts +8 -0
  126. package/dist/types/toolkit/providers/facebook.d.ts.map +1 -0
  127. package/dist/types/toolkit/providers/github.d.ts +8 -0
  128. package/dist/types/toolkit/providers/github.d.ts.map +1 -0
  129. package/dist/types/toolkit/providers/google.d.ts +8 -0
  130. package/dist/types/toolkit/providers/google.d.ts.map +1 -0
  131. package/dist/types/toolkit/providers/strategy.d.ts +38 -0
  132. package/dist/types/toolkit/providers/strategy.d.ts.map +1 -0
  133. package/dist/{toolkit/storage.d.mts → types/toolkit/storage.d.ts} +37 -39
  134. package/dist/types/toolkit/storage.d.ts.map +1 -0
  135. package/dist/{toolkit/utils.d.mts → types/toolkit/utils.d.ts} +2 -4
  136. package/dist/types/toolkit/utils.d.ts.map +1 -0
  137. package/dist/types/types.d.ts +92 -0
  138. package/dist/types/types.d.ts.map +1 -0
  139. package/dist/types/ui/base.d.ts +18 -0
  140. package/dist/types/ui/base.d.ts.map +1 -0
  141. package/dist/types/ui/code.d.ts +43 -0
  142. package/dist/types/ui/code.d.ts.map +1 -0
  143. package/dist/types/ui/form.d.ts +24 -0
  144. package/dist/types/ui/form.d.ts.map +1 -0
  145. package/dist/types/ui/icon.d.ts +60 -0
  146. package/dist/types/ui/icon.d.ts.map +1 -0
  147. package/dist/types/ui/magiclink.d.ts +41 -0
  148. package/dist/types/ui/magiclink.d.ts.map +1 -0
  149. package/dist/types/ui/password.d.ts +43 -0
  150. package/dist/types/ui/password.d.ts.map +1 -0
  151. package/dist/types/ui/select.d.ts +33 -0
  152. package/dist/types/ui/select.d.ts.map +1 -0
  153. package/dist/{util.d.mts → types/util.d.ts} +11 -13
  154. package/dist/types/util.d.ts.map +1 -0
  155. package/package.json +10 -16
  156. package/dist/adapters/node.d.mts +0 -18
  157. package/dist/adapters/node.mjs +0 -69
  158. package/dist/allow.mjs +0 -63
  159. package/dist/client.d.mts +0 -456
  160. package/dist/client.mjs +0 -283
  161. package/dist/core.d.mts +0 -110
  162. package/dist/core.mjs +0 -595
  163. package/dist/error.mjs +0 -237
  164. package/dist/index.d.mts +0 -2
  165. package/dist/index.mjs +0 -3
  166. package/dist/keys.mjs +0 -146
  167. package/dist/mutex.d.mts +0 -44
  168. package/dist/mutex.mjs +0 -110
  169. package/dist/pkce.mjs +0 -157
  170. package/dist/provider/apple.d.mts +0 -111
  171. package/dist/provider/apple.mjs +0 -164
  172. package/dist/provider/code.d.mts +0 -228
  173. package/dist/provider/code.mjs +0 -246
  174. package/dist/provider/discord.d.mts +0 -146
  175. package/dist/provider/discord.mjs +0 -156
  176. package/dist/provider/facebook.d.mts +0 -142
  177. package/dist/provider/facebook.mjs +0 -150
  178. package/dist/provider/github.d.mts +0 -140
  179. package/dist/provider/github.mjs +0 -169
  180. package/dist/provider/gitlab.d.mts +0 -106
  181. package/dist/provider/gitlab.mjs +0 -147
  182. package/dist/provider/google.d.mts +0 -112
  183. package/dist/provider/google.mjs +0 -109
  184. package/dist/provider/linkedin.d.mts +0 -132
  185. package/dist/provider/linkedin.mjs +0 -142
  186. package/dist/provider/magiclink.d.mts +0 -89
  187. package/dist/provider/magiclink.mjs +0 -143
  188. package/dist/provider/microsoft.d.mts +0 -178
  189. package/dist/provider/microsoft.mjs +0 -177
  190. package/dist/provider/oauth2.d.mts +0 -176
  191. package/dist/provider/oauth2.mjs +0 -222
  192. package/dist/provider/passkey.d.mts +0 -104
  193. package/dist/provider/passkey.mjs +0 -320
  194. package/dist/provider/password.d.mts +0 -412
  195. package/dist/provider/password.mjs +0 -363
  196. package/dist/provider/provider.d.mts +0 -227
  197. package/dist/provider/provider.mjs +0 -44
  198. package/dist/provider/reddit.d.mts +0 -107
  199. package/dist/provider/reddit.mjs +0 -127
  200. package/dist/provider/slack.d.mts +0 -114
  201. package/dist/provider/slack.mjs +0 -138
  202. package/dist/provider/spotify.d.mts +0 -113
  203. package/dist/provider/spotify.mjs +0 -135
  204. package/dist/provider/totp.d.mts +0 -112
  205. package/dist/provider/totp.mjs +0 -191
  206. package/dist/provider/twitch.d.mts +0 -108
  207. package/dist/provider/twitch.mjs +0 -131
  208. package/dist/provider/vercel.d.mts +0 -177
  209. package/dist/provider/vercel.mjs +0 -230
  210. package/dist/random.mjs +0 -86
  211. package/dist/revocation.d.mts +0 -55
  212. package/dist/revocation.mjs +0 -63
  213. package/dist/router/context.d.mts +0 -21
  214. package/dist/router/context.mjs +0 -193
  215. package/dist/router/cookies.d.mts +0 -8
  216. package/dist/router/cookies.mjs +0 -13
  217. package/dist/router/index.d.mts +0 -21
  218. package/dist/router/index.mjs +0 -107
  219. package/dist/router/matcher.d.mts +0 -15
  220. package/dist/router/matcher.mjs +0 -76
  221. package/dist/router/middleware/cors.d.mts +0 -15
  222. package/dist/router/middleware/cors.mjs +0 -114
  223. package/dist/router/safe-request.d.mts +0 -52
  224. package/dist/router/safe-request.mjs +0 -160
  225. package/dist/router/types.d.mts +0 -67
  226. package/dist/router/types.mjs +0 -1
  227. package/dist/router/variables.d.mts +0 -12
  228. package/dist/router/variables.mjs +0 -20
  229. package/dist/storage/memory.mjs +0 -125
  230. package/dist/storage/storage.d.mts +0 -179
  231. package/dist/storage/storage.mjs +0 -104
  232. package/dist/storage/turso.mjs +0 -117
  233. package/dist/storage/unstorage.mjs +0 -103
  234. package/dist/subject.d.mts +0 -62
  235. package/dist/subject.mjs +0 -36
  236. package/dist/themes/theme.d.mts +0 -209
  237. package/dist/themes/theme.mjs +0 -120
  238. package/dist/toolkit/client.d.mts +0 -169
  239. package/dist/toolkit/client.mjs +0 -209
  240. package/dist/toolkit/index.d.mts +0 -9
  241. package/dist/toolkit/index.mjs +0 -9
  242. package/dist/toolkit/providers/facebook.d.mts +0 -12
  243. package/dist/toolkit/providers/facebook.mjs +0 -16
  244. package/dist/toolkit/providers/github.d.mts +0 -12
  245. package/dist/toolkit/providers/github.mjs +0 -16
  246. package/dist/toolkit/providers/google.d.mts +0 -12
  247. package/dist/toolkit/providers/google.mjs +0 -20
  248. package/dist/toolkit/providers/strategy.d.mts +0 -40
  249. package/dist/toolkit/providers/strategy.mjs +0 -1
  250. package/dist/toolkit/storage.mjs +0 -157
  251. package/dist/toolkit/utils.mjs +0 -30
  252. package/dist/types.d.mts +0 -94
  253. package/dist/types.mjs +0 -1
  254. package/dist/ui/base.d.mts +0 -30
  255. package/dist/ui/base.mjs +0 -407
  256. package/dist/ui/code.d.mts +0 -43
  257. package/dist/ui/code.mjs +0 -173
  258. package/dist/ui/form.d.mts +0 -32
  259. package/dist/ui/form.mjs +0 -49
  260. package/dist/ui/icon.d.mts +0 -58
  261. package/dist/ui/icon.mjs +0 -247
  262. package/dist/ui/magiclink.d.mts +0 -41
  263. package/dist/ui/magiclink.mjs +0 -152
  264. package/dist/ui/passkey.d.mts +0 -27
  265. package/dist/ui/passkey.mjs +0 -323
  266. package/dist/ui/password.d.mts +0 -42
  267. package/dist/ui/password.mjs +0 -402
  268. package/dist/ui/select.d.mts +0 -34
  269. package/dist/ui/select.mjs +0 -98
  270. package/dist/ui/totp.d.mts +0 -34
  271. package/dist/ui/totp.mjs +0 -270
  272. package/dist/util.mjs +0 -128
package/dist/error.mjs DELETED
@@ -1,237 +0,0 @@
1
- //#region src/error.ts
2
- /**
3
- * Base OAuth error class for handling standard OAuth error responses.
4
- * Contains both the error code and human-readable description.
5
- */
6
- var OauthError = class extends Error {
7
- /** The OAuth error code as defined in the specification */
8
- error;
9
- /** Human-readable description of the error */
10
- description;
11
- /**
12
- * Creates a new OAuth error with the specified error code and description.
13
- *
14
- * @param error - The OAuth error type
15
- * @param description - Human-readable error description
16
- *
17
- * @example
18
- * ```ts
19
- * throw new OauthError("invalid_grant", "Authorization code has expired")
20
- * ```
21
- */
22
- constructor(error, description) {
23
- super(`${error} - ${description}`);
24
- this.name = "OauthError";
25
- this.error = error;
26
- this.description = description;
27
- }
28
- /**
29
- * Converts the error to a standard OAuth JSON response format.
30
- *
31
- * @returns Object with error and error_description fields
32
- *
33
- * @example
34
- * ```ts
35
- * const oauthError = new OauthError("invalid_request", "Missing parameter")
36
- * return c.json(oauthError.toJSON(), 400)
37
- * ```
38
- */
39
- toJSON() {
40
- return {
41
- error: this.error,
42
- error_description: this.description
43
- };
44
- }
45
- };
46
- /**
47
- * Error thrown when a provider parameter is missing from the authorization request.
48
- * Occurs when multiple providers are configured but no specific provider is selected.
49
- */
50
- var MissingProviderError = class extends OauthError {
51
- /**
52
- * Creates a missing provider error.
53
- * Thrown when the provider query parameter is required but not provided.
54
- */
55
- constructor() {
56
- super("invalid_request", "Must specify `provider` query parameter if `select` callback on issuer is not specified");
57
- this.name = "MissingProviderError";
58
- }
59
- };
60
- /**
61
- * Error thrown when a required parameter is missing from a request.
62
- * Used for validating OAuth request parameters.
63
- */
64
- var MissingParameterError = class extends OauthError {
65
- /** The name of the missing parameter */
66
- parameter;
67
- /**
68
- * Creates a missing parameter error.
69
- *
70
- * @param parameter - The name of the missing parameter
71
- *
72
- * @example
73
- * ```ts
74
- * throw new MissingParameterError("client_id")
75
- * ```
76
- */
77
- constructor(parameter) {
78
- super("invalid_request", `Missing parameter: ${parameter}`);
79
- this.name = "MissingParameterError";
80
- this.parameter = parameter;
81
- }
82
- };
83
- /**
84
- * Error thrown when a client is not authorized to use a specific redirect URI.
85
- * Prevents unauthorized clients from hijacking authorization codes.
86
- */
87
- var UnauthorizedClientError = class extends OauthError {
88
- /** The client ID that attempted unauthorized access */
89
- clientID;
90
- /**
91
- * Creates an unauthorized client error.
92
- *
93
- * @param clientID - The client ID attempting unauthorized access
94
- * @param redirectURI - The unauthorized redirect URI
95
- *
96
- * @example
97
- * ```ts
98
- * throw new UnauthorizedClientError("malicious-client", "https://evil.com/callback")
99
- * ```
100
- */
101
- constructor(clientID, redirectURI) {
102
- super("unauthorized_client", `Client ${clientID} is not authorized to use this redirect_uri: ${redirectURI}`);
103
- this.name = "UnauthorizedClientError";
104
- this.clientID = clientID;
105
- }
106
- };
107
- /**
108
- * Error thrown when the authentication flow is in an unknown or invalid state.
109
- *
110
- * ## Common Causes
111
- * - Session cookies have expired during the authentication flow
112
- * - User switched browsers or devices mid-flow
113
- * - Authentication state was manually tampered with
114
- * - Server session storage was cleared
115
- *
116
- * @example
117
- * ```ts
118
- * // In provider callback handling
119
- * const state = await getAuthState(request)
120
- * if (!state) {
121
- * throw new UnknownStateError()
122
- * }
123
- * ```
124
- */
125
- var UnknownStateError = class extends Error {
126
- /**
127
- * Creates an unknown state error.
128
- * Indicates that the authentication flow cannot continue due to missing state.
129
- */
130
- constructor() {
131
- super("The browser was in an unknown state. This could be because certain cookies expired or the browser was switched in the middle of an authentication flow.");
132
- this.name = "UnknownStateError";
133
- }
134
- };
135
- /**
136
- * Error thrown when a subject (user identifier) is invalid or malformed.
137
- * Used during token verification and subject validation.
138
- *
139
- * @example
140
- * ```ts
141
- * // During token verification
142
- * const subject = extractSubject(token)
143
- * if (!isValidSubject(subject)) {
144
- * throw new InvalidSubjectError()
145
- * }
146
- * ```
147
- */
148
- var InvalidSubjectError = class extends Error {
149
- /**
150
- * Creates an invalid subject error.
151
- */
152
- constructor() {
153
- super("Invalid subject");
154
- this.name = "InvalidSubjectError";
155
- }
156
- };
157
- /**
158
- * Error thrown when a refresh token is invalid, expired, or revoked.
159
- * Occurs during token refresh operations.
160
- *
161
- * @example
162
- * ```ts
163
- * // During token refresh
164
- * try {
165
- * const newTokens = await client.refresh(refreshToken)
166
- * } catch (error) {
167
- * if (error instanceof InvalidRefreshTokenError) {
168
- * // Redirect user to login again
169
- * redirectToLogin()
170
- * }
171
- * }
172
- * ```
173
- */
174
- var InvalidRefreshTokenError = class extends Error {
175
- /**
176
- * Creates an invalid refresh token error.
177
- */
178
- constructor() {
179
- super("Invalid refresh token");
180
- this.name = "InvalidRefreshTokenError";
181
- }
182
- };
183
- /**
184
- * Error thrown when an access token is invalid, expired, or malformed.
185
- * Occurs during token verification and API requests.
186
- *
187
- * @example
188
- * ```ts
189
- * // During API request
190
- * try {
191
- * const user = await api.getUser(accessToken)
192
- * } catch (error) {
193
- * if (error instanceof InvalidAccessTokenError) {
194
- * // Try to refresh the token
195
- * await refreshAccessToken()
196
- * }
197
- * }
198
- * ```
199
- */
200
- var InvalidAccessTokenError = class extends Error {
201
- /**
202
- * Creates an invalid access token error.
203
- */
204
- constructor() {
205
- super("Invalid access token");
206
- this.name = "InvalidAccessTokenError";
207
- }
208
- };
209
- /**
210
- * Error thrown when an authorization code is invalid, expired, or already used.
211
- * Occurs during the token exchange step of the OAuth flow.
212
- *
213
- * @example
214
- * ```ts
215
- * // During authorization code exchange
216
- * try {
217
- * const tokens = await client.exchange(code, redirectUri)
218
- * } catch (error) {
219
- * if (error instanceof InvalidAuthorizationCodeError) {
220
- * // Code may have expired or been used already
221
- * redirectToAuthorize()
222
- * }
223
- * }
224
- * ```
225
- */
226
- var InvalidAuthorizationCodeError = class extends Error {
227
- /**
228
- * Creates an invalid authorization code error.
229
- */
230
- constructor() {
231
- super("Invalid authorization code");
232
- this.name = "InvalidAuthorizationCodeError";
233
- }
234
- };
235
-
236
- //#endregion
237
- export { InvalidAccessTokenError, InvalidAuthorizationCodeError, InvalidRefreshTokenError, InvalidSubjectError, MissingParameterError, MissingProviderError, OauthError, UnauthorizedClientError, UnknownStateError };
package/dist/index.d.mts DELETED
@@ -1,2 +0,0 @@
1
- import { issuer } from "./core.mjs";
2
- export { issuer };
package/dist/index.mjs DELETED
@@ -1,3 +0,0 @@
1
- import { issuer } from "./core.mjs";
2
-
3
- export { issuer };
package/dist/keys.mjs DELETED
@@ -1,146 +0,0 @@
1
- import { Mutex } from "./mutex.mjs";
2
- import { generateSecureToken } from "./random.mjs";
3
- import { Storage } from "./storage/storage.mjs";
4
- import { exportJWK, exportPKCS8, exportSPKI, generateKeyPair, importPKCS8, importSPKI } from "jose";
5
-
6
- //#region src/keys.ts
7
- /**
8
- * Cryptographic key management for JWT signing and encryption operations.
9
- * Handles automatic key generation, rotation, and storage for OAuth operations.
10
- */
11
- /** Elliptic Curve algorithm used for JWT signing operations */
12
- const signingAlg = "ES256";
13
- /** RSA algorithm used for token encryption operations */
14
- const encryptionAlg = "RSA-OAEP-512";
15
- /** Mutex to prevent concurrent key generation (race condition with eventually consistent storage) */
16
- const signingKeyMutex = new Mutex();
17
- const encryptionKeyMutex = new Mutex();
18
- /**
19
- * Loads or generates signing keys for JWT operations.
20
- * Returns existing valid keys, or generates new ones if none are available.
21
- * Keys are automatically sorted by creation date (newest first).
22
- *
23
- * @param storage - Storage adapter for persistent key storage
24
- * @returns Promise resolving to array of available signing key pairs
25
- *
26
- * @example
27
- * ```ts
28
- * const keys = await signingKeys(storage)
29
- * const currentKey = keys[0] // Most recent key
30
- *
31
- * // Use for JWT signing
32
- * const jwt = await new SignJWT(payload)
33
- * .setProtectedHeader({ alg: currentKey.alg, kid: currentKey.id })
34
- * .sign(currentKey.private)
35
- * ```
36
- */
37
- const signingKeys = async (storage) => {
38
- return signingKeyMutex.runExclusive(async () => {
39
- const results = [];
40
- const scanner = Storage.scan(storage, ["signing:key"]);
41
- for await (const [, value] of scanner) try {
42
- const publicKey = await importSPKI(value.publicKey, value.alg, { extractable: true });
43
- const privateKey = await importPKCS8(value.privateKey, value.alg);
44
- const jwk$1 = await exportJWK(publicKey);
45
- jwk$1.kid = value.id;
46
- jwk$1.use = "sig";
47
- results.push({
48
- id: value.id,
49
- alg: signingAlg,
50
- created: new Date(value.created),
51
- expired: value.expired ? new Date(value.expired) : void 0,
52
- public: publicKey,
53
- private: privateKey,
54
- jwk: jwk$1
55
- });
56
- } catch {}
57
- results.sort((a, b) => b.created.getTime() - a.created.getTime());
58
- if (results.filter((item) => !item.expired).length) return results;
59
- const key = await generateKeyPair(signingAlg, { extractable: true });
60
- const serialized = {
61
- id: generateSecureToken(16),
62
- publicKey: await exportSPKI(key.publicKey),
63
- privateKey: await exportPKCS8(key.privateKey),
64
- created: Date.now(),
65
- alg: signingAlg
66
- };
67
- await Storage.set(storage, ["signing:key", serialized.id], serialized);
68
- const jwk = await exportJWK(key.publicKey);
69
- jwk.kid = serialized.id;
70
- jwk.use = "sig";
71
- return [{
72
- id: serialized.id,
73
- alg: signingAlg,
74
- created: new Date(serialized.created),
75
- expired: serialized.expired ? new Date(serialized.expired) : void 0,
76
- public: key.publicKey,
77
- private: key.privateKey,
78
- jwk
79
- }, ...results];
80
- });
81
- };
82
- /**
83
- * Loads or generates encryption keys for token encryption operations.
84
- * Returns existing valid keys, or generates new ones if none are available.
85
- * Keys are automatically sorted by creation date (newest first).
86
- *
87
- * @param storage - Storage adapter for persistent key storage
88
- * @returns Promise resolving to array of available encryption key pairs
89
- *
90
- * @example
91
- * ```ts
92
- * const keys = await encryptionKeys(storage)
93
- * const currentKey = keys[0] // Most recent key
94
- *
95
- * // Use for token encryption
96
- * const encrypted = await new EncryptJWT(payload)
97
- * .setProtectedHeader({ alg: 'RSA-OAEP-512', enc: 'A256GCM' })
98
- * .encrypt(currentKey.public)
99
- * ```
100
- */
101
- const encryptionKeys = async (storage) => {
102
- return encryptionKeyMutex.runExclusive(async () => {
103
- const results = [];
104
- const scanner = Storage.scan(storage, ["encryption:key"]);
105
- for await (const [, value] of scanner) try {
106
- const publicKey = await importSPKI(value.publicKey, value.alg, { extractable: true });
107
- const privateKey = await importPKCS8(value.privateKey, value.alg);
108
- const jwk$1 = await exportJWK(publicKey);
109
- jwk$1.kid = value.id;
110
- results.push({
111
- id: value.id,
112
- alg: encryptionAlg,
113
- created: new Date(value.created),
114
- expired: value.expired ? new Date(value.expired) : void 0,
115
- public: publicKey,
116
- private: privateKey,
117
- jwk: jwk$1
118
- });
119
- } catch {}
120
- results.sort((a, b) => b.created.getTime() - a.created.getTime());
121
- if (results.filter((item) => !item.expired).length) return results;
122
- const key = await generateKeyPair(encryptionAlg, { extractable: true });
123
- const serialized = {
124
- id: generateSecureToken(16),
125
- publicKey: await exportSPKI(key.publicKey),
126
- privateKey: await exportPKCS8(key.privateKey),
127
- created: Date.now(),
128
- alg: encryptionAlg
129
- };
130
- await Storage.set(storage, ["encryption:key", serialized.id], serialized);
131
- const jwk = await exportJWK(key.publicKey);
132
- jwk.kid = serialized.id;
133
- return [{
134
- id: serialized.id,
135
- alg: encryptionAlg,
136
- created: new Date(serialized.created),
137
- expired: serialized.expired ? new Date(serialized.expired) : void 0,
138
- public: key.publicKey,
139
- private: key.privateKey,
140
- jwk
141
- }, ...results];
142
- });
143
- };
144
-
145
- //#endregion
146
- export { encryptionKeys, signingKeys };
package/dist/mutex.d.mts DELETED
@@ -1,44 +0,0 @@
1
- //#region src/mutex.d.ts
2
- /**
3
- * A Mutex (mutual exclusion lock) for async functions.
4
- * It allows only one async task to access a critical section at a time.
5
- *
6
- * @example
7
- * const mutex = new Mutex();
8
- *
9
- * async function criticalSection() {
10
- * await mutex.acquire();
11
- * try {
12
- * // This code section cannot be executed simultaneously
13
- * } finally {
14
- * mutex.release();
15
- * }
16
- * }
17
- */
18
- declare class Mutex {
19
- private semaphore;
20
- /**
21
- * Checks if the mutex is currently locked.
22
- * @returns True if the mutex is locked, false otherwise.
23
- */
24
- get isLocked(): boolean;
25
- /**
26
- * Acquires the mutex, blocking if necessary until it is available.
27
- * @returns A promise that resolves when the mutex is acquired.
28
- */
29
- acquire(): Promise<void>;
30
- /**
31
- * Releases the mutex, allowing another waiting task to proceed.
32
- */
33
- release(): void;
34
- /**
35
- * Runs a function while holding the mutex lock.
36
- * Automatically acquires before and releases after the function execution.
37
- *
38
- * @param fn - The function to execute while holding the lock
39
- * @returns The result of the function
40
- */
41
- runExclusive<T>(fn: () => Promise<T>): Promise<T>;
42
- }
43
- //#endregion
44
- export { Mutex };
package/dist/mutex.mjs DELETED
@@ -1,110 +0,0 @@
1
- //#region src/mutex.ts
2
- /**
3
- * A counting semaphore for async functions that manages available permits.
4
- * Semaphores are mainly used to limit the number of concurrent async tasks.
5
- *
6
- * Each `acquire` operation takes a permit or waits until one is available.
7
- * Each `release` operation adds a permit, potentially allowing a waiting task to proceed.
8
- *
9
- * The semaphore ensures fairness by maintaining a FIFO (First In, First Out) order for acquirers.
10
- */
11
- var Semaphore = class {
12
- /**
13
- * The maximum number of concurrent operations allowed.
14
- */
15
- capacity;
16
- /**
17
- * The number of available permits.
18
- */
19
- available;
20
- deferredTasks = [];
21
- /**
22
- * Creates an instance of Semaphore.
23
- * @param capacity - The maximum number of concurrent operations allowed.
24
- */
25
- constructor(capacity) {
26
- this.capacity = capacity;
27
- this.available = capacity;
28
- }
29
- /**
30
- * Acquires a semaphore, blocking if necessary until one is available.
31
- * @returns A promise that resolves when the semaphore is acquired.
32
- */
33
- async acquire() {
34
- if (this.available > 0) {
35
- this.available--;
36
- return;
37
- }
38
- return new Promise((resolve) => {
39
- this.deferredTasks.push(resolve);
40
- });
41
- }
42
- /**
43
- * Releases a semaphore, allowing one more operation to proceed.
44
- */
45
- release() {
46
- const deferredTask = this.deferredTasks.shift();
47
- if (deferredTask != null) {
48
- deferredTask();
49
- return;
50
- }
51
- if (this.available < this.capacity) this.available++;
52
- }
53
- };
54
- /**
55
- * A Mutex (mutual exclusion lock) for async functions.
56
- * It allows only one async task to access a critical section at a time.
57
- *
58
- * @example
59
- * const mutex = new Mutex();
60
- *
61
- * async function criticalSection() {
62
- * await mutex.acquire();
63
- * try {
64
- * // This code section cannot be executed simultaneously
65
- * } finally {
66
- * mutex.release();
67
- * }
68
- * }
69
- */
70
- var Mutex = class {
71
- semaphore = new Semaphore(1);
72
- /**
73
- * Checks if the mutex is currently locked.
74
- * @returns True if the mutex is locked, false otherwise.
75
- */
76
- get isLocked() {
77
- return this.semaphore.available === 0;
78
- }
79
- /**
80
- * Acquires the mutex, blocking if necessary until it is available.
81
- * @returns A promise that resolves when the mutex is acquired.
82
- */
83
- async acquire() {
84
- return this.semaphore.acquire();
85
- }
86
- /**
87
- * Releases the mutex, allowing another waiting task to proceed.
88
- */
89
- release() {
90
- this.semaphore.release();
91
- }
92
- /**
93
- * Runs a function while holding the mutex lock.
94
- * Automatically acquires before and releases after the function execution.
95
- *
96
- * @param fn - The function to execute while holding the lock
97
- * @returns The result of the function
98
- */
99
- async runExclusive(fn) {
100
- await this.acquire();
101
- try {
102
- return await fn();
103
- } finally {
104
- this.release();
105
- }
106
- }
107
- };
108
-
109
- //#endregion
110
- export { Mutex };
package/dist/pkce.mjs DELETED
@@ -1,157 +0,0 @@
1
- import { base64url } from "jose";
2
-
3
- //#region src/pkce.ts
4
- /**
5
- * Performs a timing-safe comparison of two strings to prevent timing attacks.
6
- * This implementation is platform-agnostic, uses a constant-time algorithm,
7
- * and correctly handles all Unicode characters by operating on their UTF-8 byte representation.
8
- * It always takes a time proportional to the length of the expected string,
9
- * regardless of where the strings differ, making it safe for comparing sensitive values.
10
- *
11
- * @param a - The first string to compare (often the expected, secret value).
12
- * @param b - The second string to compare (often the user-provided value).
13
- * @returns True if the strings are identical, false otherwise.
14
- *
15
- * @example
16
- * ```ts
17
- * // Safe for comparing sensitive values like PKCE verifiers or tokens
18
- * const isValid = await timingSafeCompare(receivedVerifier, expectedChallenge);
19
- *
20
- * // Safe for password hash verification
21
- * const isValidPassword = timingSafeCompare(hashedInput, storedHash);
22
- *
23
- * // Returns false for different types or lengths without leaking timing info
24
- * timingSafeCompare("abc", 123 as any); // false
25
- * timingSafeCompare("abc", "abcd"); // false
26
- * ```
27
- */
28
- const timingSafeCompare = (a, b) => {
29
- if (typeof a !== "string" || typeof b !== "string") return false;
30
- const encoder = new TextEncoder();
31
- const aBytes = encoder.encode(a);
32
- const bBytes = encoder.encode(b);
33
- let diff = aBytes.length ^ bBytes.length;
34
- for (const [i, aByte] of aBytes.entries()) diff |= aByte ^ (bBytes[i] ?? 0);
35
- return diff === 0;
36
- };
37
- /**
38
- * Generates a cryptographically secure code verifier for PKCE.
39
- * The verifier is a URL-safe base64-encoded string of random bytes.
40
- *
41
- * @param length - Length of the random buffer in bytes
42
- * @returns Base64url-encoded verifier string
43
- */
44
- const generateVerifier = (length) => {
45
- const buffer = new Uint8Array(length);
46
- crypto.getRandomValues(buffer);
47
- return base64url.encode(buffer);
48
- };
49
- /**
50
- * Generates a code challenge from a verifier using the specified method.
51
- * For 'S256', applies SHA-256 hash then base64url encoding.
52
- * For 'plain', returns the verifier unchanged (not recommended for production).
53
- *
54
- * @param verifier - The code verifier string
55
- * @param method - Challenge generation method
56
- * @returns Promise resolving to the code challenge string
57
- */
58
- const generateChallenge = async (verifier, method) => {
59
- if (method === "plain") return verifier;
60
- const data = new TextEncoder().encode(verifier);
61
- const hash = await crypto.subtle.digest("SHA-256", data);
62
- return base64url.encode(new Uint8Array(hash));
63
- };
64
- /**
65
- * Generates a complete PKCE challenge for OAuth authorization requests.
66
- * Creates a cryptographically secure verifier and corresponding S256 challenge.
67
- * Validates that the generated verifier meets standard requirements (43-128 characters).
68
- *
69
- * @param length - Length of the random buffer in bytes (32-96 range to generate 43-128 character verifier)
70
- * @returns Promise resolving to PKCE challenge data
71
- *
72
- * @example
73
- * ```ts
74
- * const pkce = await generatePKCE()
75
- *
76
- * // Use challenge in authorization URL
77
- * authUrl.searchParams.set('code_challenge', pkce.challenge)
78
- * authUrl.searchParams.set('code_challenge_method', pkce.method)
79
- *
80
- * // Store verifier for token exchange
81
- * sessionStorage.setItem('code_verifier', pkce.verifier)
82
- * ```
83
- *
84
- * @throws {RangeError} If length is outside valid range or generated verifier doesn't meet requirements
85
- */
86
- const generatePKCE = async (length = 48) => {
87
- if (!Number.isInteger(length) || length < 32 || length > 96) throw new RangeError("Random buffer length must be between 32 and 96 bytes (generates 43-128 character verifier)");
88
- const verifier = generateVerifier(length);
89
- if (verifier.length < 43 || verifier.length > 128) throw new Error("Generated verifier does not meet requirements");
90
- if (!/^[A-Za-z0-9_-]+$/.test(verifier)) throw new Error("Generated verifier is not valid base64url format");
91
- return {
92
- verifier,
93
- challenge: await generateChallenge(verifier, "S256"),
94
- method: "S256"
95
- };
96
- };
97
- /**
98
- * Validates a PKCE code verifier against a previously generated challenge.
99
- * Uses timing-safe comparison and timing normalization to prevent timing attacks.
100
- * All validation paths take the same computational time regardless of input validity,
101
- * making it resistant to timing-based side-channel attacks.
102
- *
103
- * @param verifier - The code verifier received from the client
104
- * @param challenge - The code challenge stored during authorization
105
- * @param method - The challenge method used during generation
106
- * @returns Promise resolving to true if verifier matches challenge
107
- *
108
- * @example
109
- * ```ts
110
- * // During token exchange
111
- * const isValid = await validatePKCE(
112
- * receivedVerifier,
113
- * storedChallenge,
114
- * 'S256'
115
- * )
116
- *
117
- * if (!isValid) {
118
- * throw new Error('Invalid PKCE verifier')
119
- * }
120
- * ```
121
- */
122
- const validatePKCE = async (verifier, challenge, method = "S256") => {
123
- const MIN_PROCESSING_TIME = 50;
124
- const RANDOM_JITTER_MAX = 20;
125
- const startTime = performance.now();
126
- let isValid = false;
127
- let hasEarlyFailure = false;
128
- const normalizedVerifier = String(verifier || "");
129
- const normalizedChallenge = String(challenge || "");
130
- hasEarlyFailure = ![
131
- typeof verifier === "string" && typeof challenge === "string" && verifier && challenge,
132
- normalizedVerifier.length >= 43 && normalizedVerifier.length <= 128,
133
- normalizedChallenge.length >= 43 && normalizedChallenge.length <= 128,
134
- /^[A-Za-z0-9_-]+$/.test(normalizedVerifier),
135
- /^[A-Za-z0-9_-]+$/.test(normalizedChallenge)
136
- ].every(Boolean);
137
- const verifierToUse = hasEarlyFailure ? "dummyverifier_".repeat(6) : normalizedVerifier;
138
- try {
139
- const comparisonResult = timingSafeCompare(await generateChallenge(verifierToUse, method), hasEarlyFailure ? "dummychallenge_".repeat(6) : normalizedChallenge);
140
- isValid = !hasEarlyFailure && comparisonResult;
141
- } catch {
142
- isValid = false;
143
- }
144
- const elapsed = performance.now() - startTime;
145
- const remainingTime = Math.max(0, MIN_PROCESSING_TIME - elapsed);
146
- if (remainingTime > 0 || elapsed < MIN_PROCESSING_TIME) {
147
- const jitterArray = new Uint32Array(1);
148
- crypto.getRandomValues(jitterArray);
149
- const jitter = (jitterArray[0] ?? 0) / 4294967295 * RANDOM_JITTER_MAX;
150
- const totalDelay = Math.max(remainingTime, MIN_PROCESSING_TIME - elapsed) + jitter;
151
- await new Promise((resolve) => setTimeout(resolve, totalDelay));
152
- }
153
- return isValid;
154
- };
155
-
156
- //#endregion
157
- export { generatePKCE, validatePKCE };