@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
@@ -1,177 +0,0 @@
1
- import { Provider } from "./provider.mjs";
2
- import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
3
-
4
- //#region src/provider/vercel.d.ts
5
-
6
- /**
7
- * Configuration options for Vercel OAuth 2.0 + OpenID Connect provider.
8
- * Extends the base OAuth 2.0 configuration with Vercel-specific documentation.
9
- */
10
- interface VercelConfig extends Oauth2WrappedConfig {
11
- /**
12
- * Vercel OAuth App client ID.
13
- * Found in your Vercel App settings under the Authentication tab.
14
- *
15
- * To create an app:
16
- * 1. Go to Team Settings → Apps → Create
17
- * 2. Configure app details and callback URLs
18
- * 3. Copy the Client ID from the Authentication tab
19
- *
20
- * @example
21
- * ```ts
22
- * {
23
- * clientID: "oac_abc123xyz789" // Vercel OAuth App Client ID
24
- * }
25
- * ```
26
- */
27
- readonly clientID: string;
28
- /**
29
- * Vercel OAuth App client secret.
30
- * Generated in your Vercel App settings under the Authentication tab.
31
- * Keep this secure and never expose it to client-side code.
32
- *
33
- * To generate:
34
- * 1. Go to your app's Authentication tab
35
- * 2. Click "Generate Client Secret"
36
- * 3. Copy and store securely (shown only once)
37
- *
38
- * @example
39
- * ```ts
40
- * {
41
- * clientSecret: process.env.VERCEL_CLIENT_SECRET
42
- * }
43
- * ```
44
- */
45
- readonly clientSecret: string;
46
- /**
47
- * OpenID Connect scopes to request.
48
- * Controls what user information is included in the ID Token.
49
- *
50
- * Available scopes (must be enabled in Vercel App dashboard first):
51
- * - `openid`: Required for ID Token issuance
52
- * - `email`: User's email address
53
- * - `profile`: Name, username, and avatar
54
- * - `offline_access`: Refresh token for long-lived access (optional)
55
- *
56
- * **Important**: Enable scopes in: Vercel App → Permissions page
57
- *
58
- * @example
59
- * ```ts
60
- * {
61
- * // Basic scopes (usually sufficient)
62
- * scopes: ["openid", "email", "profile"]
63
- *
64
- * // With refresh token support (enable offline_access in dashboard first)
65
- * scopes: ["openid", "email", "profile", "offline_access"]
66
- * }
67
- * ```
68
- */
69
- readonly scopes: string[];
70
- /**
71
- * Additional query parameters for Vercel OAuth authorization.
72
- * Useful for customizing the authorization flow.
73
- *
74
- * @example
75
- * ```ts
76
- * {
77
- * query: {
78
- * prompt: "consent" // Force consent screen every time
79
- * }
80
- * }
81
- * ```
82
- */
83
- readonly query?: Record<string, string>;
84
- }
85
- /**
86
- * Creates a Vercel OAuth 2.0 + OpenID Connect authentication provider.
87
- * Implements "Sign in with Vercel" for user authentication.
88
- *
89
- * This provider uses the standard OAuth 2.0 Authorization Code Grant flow
90
- * with PKCE (Proof Key for Code Exchange) for enhanced security.
91
- *
92
- * @param config - Vercel OAuth 2.0 configuration
93
- * @returns OAuth 2.0 provider configured for Vercel
94
- *
95
- * @example
96
- * ```ts
97
- * // Basic Vercel authentication (email + profile)
98
- * const basicVercel = VercelProvider({
99
- * clientID: process.env.VERCEL_CLIENT_ID,
100
- * clientSecret: process.env.VERCEL_CLIENT_SECRET,
101
- * scopes: ["openid", "email", "profile"]
102
- * })
103
- *
104
- * // Vercel with refresh token support
105
- * const vercelWithRefresh = VercelProvider({
106
- * clientID: process.env.VERCEL_CLIENT_ID,
107
- * clientSecret: process.env.VERCEL_CLIENT_SECRET,
108
- * scopes: ["openid", "email", "profile", "offline_access"]
109
- * })
110
- *
111
- * // Minimal setup (only user ID in ID Token)
112
- * const minimalVercel = VercelProvider({
113
- * clientID: process.env.VERCEL_CLIENT_ID,
114
- * clientSecret: process.env.VERCEL_CLIENT_SECRET,
115
- * scopes: ["openid"] // Only sub claim in ID Token
116
- * })
117
- *
118
- * // Using the tokens in your app
119
- * export default issuer({
120
- * providers: { vercel: vercelWithRefresh },
121
- * success: async (ctx, value) => {
122
- * if (value.provider === "vercel") {
123
- * const idToken = value.tokenset.raw.id_token as string | undefined
124
- * const accessToken = value.tokenset.access
125
- * const refreshToken = value.tokenset.refresh
126
- *
127
- * if (idToken) {
128
- * // Decode ID Token to access user claims
129
- * // (Already validated by oauth2.ts - signature, issuer, audience, exp)
130
- * const claims = JSON.parse(
131
- * Buffer.from(idToken.split('.')[1], 'base64').toString()
132
- * )
133
- *
134
- * // Claims available (depending on scopes):
135
- * // - sub: Vercel user ID (always present)
136
- * // - email: user@example.com (if email scope)
137
- * // - name: "John Doe" (if profile scope)
138
- * // - picture: "https://..." (if profile scope)
139
- * // - preferred_username: "johndoe" (if profile scope)
140
- *
141
- * // Optionally call Vercel API for more data
142
- * const userRes = await fetch('https://api.vercel.com/v2/user', {
143
- * headers: { Authorization: `Bearer ${accessToken}` }
144
- * })
145
- * const user = await userRes.json()
146
- *
147
- * // Get user's teams
148
- * const teamsRes = await fetch('https://api.vercel.com/v2/teams', {
149
- * headers: { Authorization: `Bearer ${accessToken}` }
150
- * })
151
- * const teams = await teamsRes.json()
152
- *
153
- * return ctx.subject("user", {
154
- * vercelId: claims.sub,
155
- * email: claims.email,
156
- * name: claims.name,
157
- * username: claims.preferred_username,
158
- * avatar: claims.picture,
159
- * teamCount: teams.teams?.length || 0
160
- * })
161
- * }
162
- * }
163
- * }
164
- * })
165
- * ```
166
- *
167
- * @remarks
168
- * - Requires creating a Vercel App in Team Settings → Apps
169
- * - PKCE is enabled by default for enhanced security
170
- * - ID Token is automatically validated (signature, issuer, audience, expiration)
171
- * - Access tokens expire after 1 hour
172
- * - Refresh tokens rotate on each use and last 30 days
173
- * - The `openid` scope is required for ID Token issuance
174
- */
175
- declare const VercelProvider: (config: VercelConfig) => Provider<Oauth2UserData>;
176
- //#endregion
177
- export { VercelConfig, VercelProvider };
@@ -1,230 +0,0 @@
1
- import { Oauth2Provider } from "./oauth2.mjs";
2
-
3
- //#region src/provider/vercel.ts
4
- /**
5
- * Vercel OAuth 2.0 + OpenID Connect authentication provider for Draft Auth.
6
- * Implements "Sign in with Vercel" for user authentication.
7
- *
8
- * ## Quick Setup
9
- *
10
- * ```ts
11
- * import { VercelProvider } from "@draftlab/auth/provider/vercel"
12
- *
13
- * export default issuer({
14
- * basePath: "/auth", // Important for callback URL
15
- * providers: {
16
- * vercel: VercelProvider({
17
- * clientID: process.env.VERCEL_CLIENT_ID,
18
- * clientSecret: process.env.VERCEL_CLIENT_SECRET,
19
- * scopes: ["openid", "email", "profile"]
20
- * })
21
- * }
22
- * })
23
- * ```
24
- *
25
- * **Callback URL Pattern**: `{baseURL}{basePath}/{provider}/callback`
26
- * - Example: `http://localhost:3000/auth/vercel/callback`
27
- * - Production: `https://yourapp.com/auth/vercel/callback`
28
- *
29
- * ## Creating a Vercel App
30
- *
31
- * Before using this provider, create a Vercel App in your dashboard:
32
- *
33
- * 1. Go to **Team Settings** → **Apps** → **Create**
34
- * 2. Fill in app name, description, and logo
35
- * 3. Add **Authorization Callback URLs**:
36
- * - Development: `http://localhost:3000/auth/vercel/callback`
37
- * - Production: `https://yourapp.com/auth/vercel/callback`
38
- * - Pattern: `{baseURL}{basePath}/{provider}/callback`
39
- * 4. Configure **Scopes** in the app's Permissions page:
40
- * - ✅ openid (Required)
41
- * - ✅ email
42
- * - ✅ profile
43
- * - ✅ offline_access (optional, for refresh tokens)
44
- * 5. Generate a **Client Secret** in the Authentication tab
45
- * 6. Copy the **Client ID** and **Client Secret**
46
- *
47
- * **Important**: You must enable the scopes in the Vercel App dashboard before requesting them!
48
- *
49
- * ## Available Scopes
50
- *
51
- * - `openid` - **Required**. Enables ID Token issuance for user identification
52
- * - `email` - Access user's email address in ID Token
53
- * - `profile` - Access user's name, username, and avatar in ID Token
54
- * - `offline_access` - Issue a Refresh Token for long-lived access (30 days)
55
- *
56
- * ## Tokens Returned
57
- *
58
- * - **ID Token**: Signed JWT with user identity claims (verified automatically)
59
- * - **Access Token**: Bearer token for Vercel API calls (1 hour duration)
60
- * - **Refresh Token**: Rotates on each use (30 days, requires offline_access scope)
61
- *
62
- * ## User Data Access
63
- *
64
- * ```ts
65
- * success: async (ctx, value) => {
66
- * if (value.provider === "vercel") {
67
- * // ID Token is automatically validated (signature, issuer, audience, expiration)
68
- * const idToken = value.tokenset.raw.id_token as string | undefined
69
- * const accessToken = value.tokenset.access
70
- * const refreshToken = value.tokenset.refresh
71
- *
72
- * // Decode ID Token to access user claims
73
- * if (idToken) {
74
- * const claims = JSON.parse(
75
- * Buffer.from(idToken.split('.')[1], 'base64').toString()
76
- * )
77
- *
78
- * // Claims available (depending on scopes):
79
- * // - sub: Unique Vercel user ID (always present)
80
- * // - email: User's email (if email scope granted)
81
- * // - name: User's full name (if profile scope granted)
82
- * // - picture: Avatar URL (if profile scope granted)
83
- * // - preferred_username: Vercel username (if profile scope granted)
84
- *
85
- * return ctx.subject("user", {
86
- * email: claims.email || claims.sub
87
- * })
88
- * }
89
- * }
90
- * }
91
- * ```
92
- *
93
- * ## Calling Vercel API
94
- *
95
- * Use the access token to call Vercel's REST API:
96
- *
97
- * ```ts
98
- * // Get user information
99
- * const userRes = await fetch('https://api.vercel.com/v2/user', {
100
- * headers: { Authorization: `Bearer ${accessToken}` }
101
- * })
102
- *
103
- * // Get user's teams
104
- * const teamsRes = await fetch('https://api.vercel.com/v2/teams', {
105
- * headers: { Authorization: `Bearer ${accessToken}` }
106
- * })
107
- *
108
- * // Get projects (requires appropriate permissions)
109
- * const projectsRes = await fetch('https://api.vercel.com/v9/projects', {
110
- * headers: { Authorization: `Bearer ${accessToken}` }
111
- * })
112
- * ```
113
- *
114
- * ## Consent Page
115
- *
116
- * The first time a user signs in, Vercel shows a consent page with:
117
- * - Your app's name and logo
118
- * - Requested scopes and permissions
119
- * - Allow/Cancel buttons
120
- *
121
- * If the user grants access, they're redirected back with an authorization code.
122
- * If they cancel, they're redirected with an error parameter.
123
- *
124
- * @packageDocumentation
125
- */
126
- /**
127
- * Creates a Vercel OAuth 2.0 + OpenID Connect authentication provider.
128
- * Implements "Sign in with Vercel" for user authentication.
129
- *
130
- * This provider uses the standard OAuth 2.0 Authorization Code Grant flow
131
- * with PKCE (Proof Key for Code Exchange) for enhanced security.
132
- *
133
- * @param config - Vercel OAuth 2.0 configuration
134
- * @returns OAuth 2.0 provider configured for Vercel
135
- *
136
- * @example
137
- * ```ts
138
- * // Basic Vercel authentication (email + profile)
139
- * const basicVercel = VercelProvider({
140
- * clientID: process.env.VERCEL_CLIENT_ID,
141
- * clientSecret: process.env.VERCEL_CLIENT_SECRET,
142
- * scopes: ["openid", "email", "profile"]
143
- * })
144
- *
145
- * // Vercel with refresh token support
146
- * const vercelWithRefresh = VercelProvider({
147
- * clientID: process.env.VERCEL_CLIENT_ID,
148
- * clientSecret: process.env.VERCEL_CLIENT_SECRET,
149
- * scopes: ["openid", "email", "profile", "offline_access"]
150
- * })
151
- *
152
- * // Minimal setup (only user ID in ID Token)
153
- * const minimalVercel = VercelProvider({
154
- * clientID: process.env.VERCEL_CLIENT_ID,
155
- * clientSecret: process.env.VERCEL_CLIENT_SECRET,
156
- * scopes: ["openid"] // Only sub claim in ID Token
157
- * })
158
- *
159
- * // Using the tokens in your app
160
- * export default issuer({
161
- * providers: { vercel: vercelWithRefresh },
162
- * success: async (ctx, value) => {
163
- * if (value.provider === "vercel") {
164
- * const idToken = value.tokenset.raw.id_token as string | undefined
165
- * const accessToken = value.tokenset.access
166
- * const refreshToken = value.tokenset.refresh
167
- *
168
- * if (idToken) {
169
- * // Decode ID Token to access user claims
170
- * // (Already validated by oauth2.ts - signature, issuer, audience, exp)
171
- * const claims = JSON.parse(
172
- * Buffer.from(idToken.split('.')[1], 'base64').toString()
173
- * )
174
- *
175
- * // Claims available (depending on scopes):
176
- * // - sub: Vercel user ID (always present)
177
- * // - email: user@example.com (if email scope)
178
- * // - name: "John Doe" (if profile scope)
179
- * // - picture: "https://..." (if profile scope)
180
- * // - preferred_username: "johndoe" (if profile scope)
181
- *
182
- * // Optionally call Vercel API for more data
183
- * const userRes = await fetch('https://api.vercel.com/v2/user', {
184
- * headers: { Authorization: `Bearer ${accessToken}` }
185
- * })
186
- * const user = await userRes.json()
187
- *
188
- * // Get user's teams
189
- * const teamsRes = await fetch('https://api.vercel.com/v2/teams', {
190
- * headers: { Authorization: `Bearer ${accessToken}` }
191
- * })
192
- * const teams = await teamsRes.json()
193
- *
194
- * return ctx.subject("user", {
195
- * vercelId: claims.sub,
196
- * email: claims.email,
197
- * name: claims.name,
198
- * username: claims.preferred_username,
199
- * avatar: claims.picture,
200
- * teamCount: teams.teams?.length || 0
201
- * })
202
- * }
203
- * }
204
- * }
205
- * })
206
- * ```
207
- *
208
- * @remarks
209
- * - Requires creating a Vercel App in Team Settings → Apps
210
- * - PKCE is enabled by default for enhanced security
211
- * - ID Token is automatically validated (signature, issuer, audience, expiration)
212
- * - Access tokens expire after 1 hour
213
- * - Refresh tokens rotate on each use and last 30 days
214
- * - The `openid` scope is required for ID Token issuance
215
- */
216
- const VercelProvider = (config) => {
217
- return Oauth2Provider({
218
- ...config,
219
- type: "vercel",
220
- endpoint: {
221
- authorization: "https://vercel.com/oauth/authorize",
222
- token: "https://api.vercel.com/login/oauth/token",
223
- jwks: "https://vercel.com/.well-known/jwks.json"
224
- },
225
- pkce: true
226
- });
227
- };
228
-
229
- //#endregion
230
- export { VercelProvider };
package/dist/random.mjs DELETED
@@ -1,86 +0,0 @@
1
- import { timingSafeEqual } from "node:crypto";
2
-
3
- //#region src/random.ts
4
- /**
5
- * Cryptographic utilities for secure random generation and comparison operations.
6
- * These functions are designed to prevent timing attacks and provide unbiased randomness.
7
- */
8
- /**
9
- * Generates a cryptographically secure token with enhanced entropy.
10
- * Uses Web Crypto API and provides 256 bits of entropy
11
- *
12
- * @param length - Length of random data in bytes (default: 32 for 256-bit security)
13
- * @returns Base64url-encoded secure token
14
- *
15
- * @example
16
- * ```ts
17
- * const authCode = generateSecureToken()
18
- * // Returns: "7B8kJ9mN3pQ2rS5tU8vW0xY1zA3bC6dE9fG2hI5jK8lM" (example)
19
- *
20
- * const refreshToken = generateSecureToken(24) // 192-bit token
21
- * // Returns: "4A7bC9dF2gH5iJ8kL1mN4pQ7rS0tU" (example)
22
- * ```
23
- */
24
- const generateSecureToken = (length = 32) => {
25
- if (length <= 0 || !Number.isInteger(length)) throw new RangeError("Token length must be a positive integer");
26
- const randomBytes$1 = new Uint8Array(length);
27
- crypto.getRandomValues(randomBytes$1);
28
- return btoa(String.fromCharCode.apply(null, Array.from(randomBytes$1))).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
29
- };
30
- /**
31
- * Generates a cryptographically secure string of random digits without modulo bias.
32
- * Uses rejection sampling to ensure each digit (0-9) has an equal probability of being selected.
33
- *
34
- * @param length - Number of digits to generate (must be positive)
35
- * @returns String containing exactly the specified number of random digits
36
- *
37
- * @example
38
- * ```ts
39
- * const pinCode = generateUnbiasedDigits(6)
40
- * // Returns: "492847" (example - actual result is random)
41
- *
42
- * const shortCode = generateUnbiasedDigits(4)
43
- * // Returns: "7291" (example - actual result is random)
44
- * ```
45
- *
46
- * @throws {RangeError} If length is not a positive number
47
- */
48
- const generateUnbiasedDigits = (length) => {
49
- if (length <= 0 || !Number.isInteger(length)) throw new RangeError("Length must be a positive integer");
50
- const result = [];
51
- while (result.length < length) {
52
- const buffer = crypto.getRandomValues(new Uint8Array(length * 2));
53
- for (const byte of buffer) if (byte < 250 && result.length < length) result.push(byte % 10);
54
- }
55
- return result.join("");
56
- };
57
- /**
58
- * Performs a timing-safe comparison of two strings to prevent timing attacks.
59
- * Always takes the same amount of time regardless of where the strings differ,
60
- * making it safe for comparing sensitive values like tokens or passwords.
61
- *
62
- * @param a - First string to compare
63
- * @param b - Second string to compare
64
- * @returns True if strings are identical, false otherwise
65
- *
66
- * @example
67
- * ```ts
68
- * // Safe for comparing sensitive values
69
- * const isValidToken = timingSafeCompare(userToken, expectedToken)
70
- *
71
- * // Safe for password verification
72
- * const isValidPassword = timingSafeCompare(hashedInput, storedHash)
73
- *
74
- * // Returns false for different types or lengths
75
- * timingSafeCompare("abc", 123 as any) // false
76
- * timingSafeCompare("abc", "abcd") // false
77
- * ```
78
- */
79
- const timingSafeCompare = (a, b) => {
80
- if (typeof a !== "string" || typeof b !== "string") return false;
81
- if (a.length !== b.length) return false;
82
- return timingSafeEqual(Buffer.from(a), Buffer.from(b));
83
- };
84
-
85
- //#endregion
86
- export { generateSecureToken, generateUnbiasedDigits, timingSafeCompare };
@@ -1,55 +0,0 @@
1
- import { StorageAdapter } from "./storage/storage.mjs";
2
-
3
- //#region src/revocation.d.ts
4
-
5
- /**
6
- * Data stored for a revoked token.
7
- * Tracks when the token was revoked and when it naturally expires.
8
- */
9
- interface RevocationRecord {
10
- /** Timestamp when the token was revoked (milliseconds) */
11
- revokedAt: number;
12
- /** Timestamp when the token naturally expires (milliseconds) */
13
- expiresAt: number;
14
- }
15
- /**
16
- * Token revocation manager.
17
- * Provides methods to revoke tokens and check if a token has been revoked.
18
- */
19
- declare const Revocation: {
20
- /**
21
- * Revokes a token, preventing it from being used even if not yet expired.
22
- *
23
- * @param storage - Storage adapter to use
24
- * @param token - The token to revoke (access or refresh token)
25
- * @param expiresAt - When the token naturally expires (milliseconds since epoch)
26
- * @returns Promise that resolves when revocation is stored
27
- *
28
- * @example
29
- * ```ts
30
- * // Revoke a refresh token on logout
31
- * await Revocation.revoke(storage, refreshToken, expiresAt)
32
- * ```
33
- */
34
- readonly revoke: (storage: StorageAdapter, token: string, expiresAt: number) => Promise<void>;
35
- /**
36
- * Checks if a token has been revoked.
37
- * Returns false if token is not in revocation list (never revoked or already expired).
38
- *
39
- * @param storage - Storage adapter to use
40
- * @param token - The token to check
41
- * @returns Promise resolving to true if token is revoked, false otherwise
42
- *
43
- * @example
44
- * ```ts
45
- * // Check if token was revoked before using it
46
- * const isRevoked = await Revocation.isRevoked(storage, accessToken)
47
- * if (isRevoked) {
48
- * throw new InvalidAccessTokenError()
49
- * }
50
- * ```
51
- */
52
- readonly isRevoked: (storage: StorageAdapter, token: string) => Promise<boolean>;
53
- };
54
- //#endregion
55
- export { Revocation, RevocationRecord };
@@ -1,63 +0,0 @@
1
- import { Storage } from "./storage/storage.mjs";
2
- import { createHash } from "node:crypto";
3
-
4
- //#region src/revocation.ts
5
- /**
6
- * Token revocation management for Draft Auth.
7
- * Handles blacklisting of revoked tokens to prevent their use.
8
- *
9
- * ## Overview
10
- *
11
- * Revocation allows users to invalidate specific tokens before their natural expiration.
12
- * This is essential for logout functionality and security in case of token compromise.
13
- *
14
- * ## Storage Structure
15
- *
16
- * Revoked tokens are stored with their expiration time to allow automatic cleanup:
17
- * ```
18
- * revocation:token:{tokenHash} → { revokedAt: timestamp, expiresAt: timestamp }
19
- * ```
20
- *
21
- * ## Security Considerations
22
- *
23
- * - Revoked tokens are checked on every use
24
- * - Storage automatically cleans up expired revocations
25
- * - Hash tokens for storage to reduce memory usage
26
- * - Use constant-time comparison for hash verification
27
- *
28
- * @packageDocumentation
29
- */
30
- /**
31
- * Hashes a token for storage.
32
- * Uses SHA-256 to reduce storage size and prevent exposure of full token value.
33
- *
34
- * @param token - The token to hash
35
- * @returns SHA-256 hash of the token
36
- *
37
- * @internal
38
- */
39
- const hashToken = (token) => {
40
- return createHash("sha256").update(token).digest("hex");
41
- };
42
- /**
43
- * Token revocation manager.
44
- * Provides methods to revoke tokens and check if a token has been revoked.
45
- */
46
- const Revocation = {
47
- revoke: async (storage, token, expiresAt) => {
48
- const key = ["revocation:token", hashToken(token)];
49
- const record = {
50
- revokedAt: Date.now(),
51
- expiresAt
52
- };
53
- const ttlSeconds = Math.ceil((expiresAt - Date.now()) / 1e3);
54
- await Storage.set(storage, key, record, Math.max(1, ttlSeconds));
55
- },
56
- isRevoked: async (storage, token) => {
57
- const key = ["revocation:token", hashToken(token)];
58
- return !!await Storage.get(storage, key);
59
- }
60
- };
61
-
62
- //#endregion
63
- export { Revocation };
@@ -1,21 +0,0 @@
1
- import { RouterContext, VariableMap } from "./types.mjs";
2
-
3
- //#region src/router/context.d.ts
4
- declare class ContextBuilder<TVariables extends VariableMap = VariableMap> {
5
- private readonly request;
6
- private readonly matchedParams;
7
- private readonly searchParams;
8
- private readonly cookies;
9
- private readonly variableManager;
10
- private readonly responseHeaders;
11
- private status;
12
- private finalized;
13
- private cachedFormData;
14
- private formDataPromise;
15
- private bodyText;
16
- constructor(request: Request, matchedParams: Record<string, string>, initialVariables?: Partial<TVariables>);
17
- build<TParams extends Record<string, string>>(): RouterContext<TParams, TVariables>;
18
- newResponse(body?: BodyInit, init?: ResponseInit): Response;
19
- }
20
- //#endregion
21
- export { ContextBuilder };