@draftlab/auth 0.4.1 → 0.6.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 (104) hide show
  1. package/dist/adapters/{node.js → node.mjs} +2 -4
  2. package/dist/{allow.js → allow.mjs} +1 -1
  3. package/dist/{client.d.ts → client.d.mts} +47 -4
  4. package/dist/{client.js → client.mjs} +81 -10
  5. package/dist/{core.d.ts → core.d.mts} +10 -10
  6. package/dist/{core.js → core.mjs} +104 -56
  7. package/dist/index.d.mts +2 -0
  8. package/dist/index.mjs +3 -0
  9. package/dist/{keys.d.ts → keys.d.mts} +1 -1
  10. package/dist/{keys.js → keys.mjs} +6 -8
  11. package/dist/{pkce.js → pkce.mjs} +5 -10
  12. package/dist/plugin/{builder.d.ts → builder.d.mts} +1 -1
  13. package/dist/plugin/{manager.d.ts → manager.d.mts} +2 -2
  14. package/dist/plugin/{manager.js → manager.mjs} +1 -1
  15. package/dist/plugin/{plugin.d.ts → plugin.d.mts} +1 -1
  16. package/dist/plugin/{types.d.ts → types.d.mts} +1 -1
  17. package/dist/provider/apple.d.mts +105 -0
  18. package/dist/provider/apple.mjs +151 -0
  19. package/dist/provider/{code.d.ts → code.d.mts} +1 -1
  20. package/dist/provider/{code.js → code.mjs} +2 -3
  21. package/dist/provider/{discord.d.ts → discord.d.mts} +2 -2
  22. package/dist/provider/{discord.js → discord.mjs} +59 -1
  23. package/dist/provider/{facebook.d.ts → facebook.d.mts} +2 -2
  24. package/dist/provider/{facebook.js → facebook.mjs} +57 -1
  25. package/dist/provider/{github.d.ts → github.d.mts} +2 -2
  26. package/dist/provider/{github.js → github.mjs} +79 -1
  27. package/dist/provider/gitlab.d.mts +100 -0
  28. package/dist/provider/gitlab.mjs +128 -0
  29. package/dist/provider/{google.d.ts → google.d.mts} +2 -2
  30. package/dist/provider/{google.js → google.mjs} +45 -1
  31. package/dist/provider/{linkedin.d.ts → linkedin.d.mts} +2 -2
  32. package/dist/provider/{linkedin.js → linkedin.mjs} +57 -1
  33. package/dist/provider/{magiclink.d.ts → magiclink.d.mts} +1 -1
  34. package/dist/provider/{magiclink.js → magiclink.mjs} +4 -6
  35. package/dist/provider/{microsoft.d.ts → microsoft.d.mts} +2 -2
  36. package/dist/provider/{microsoft.js → microsoft.mjs} +68 -1
  37. package/dist/provider/{oauth2.d.ts → oauth2.d.mts} +1 -1
  38. package/dist/provider/{oauth2.js → oauth2.mjs} +4 -4
  39. package/dist/provider/{passkey.d.ts → passkey.d.mts} +1 -1
  40. package/dist/provider/{passkey.js → passkey.mjs} +8 -13
  41. package/dist/provider/{password.d.ts → password.d.mts} +1 -1
  42. package/dist/provider/{password.js → password.mjs} +31 -44
  43. package/dist/provider/{provider.d.ts → provider.d.mts} +1 -1
  44. package/dist/provider/reddit.d.mts +101 -0
  45. package/dist/provider/reddit.mjs +114 -0
  46. package/dist/provider/slack.d.mts +108 -0
  47. package/dist/provider/slack.mjs +125 -0
  48. package/dist/provider/spotify.d.mts +107 -0
  49. package/dist/provider/spotify.mjs +122 -0
  50. package/dist/provider/{totp.d.ts → totp.d.mts} +1 -1
  51. package/dist/provider/{totp.js → totp.mjs} +51 -14
  52. package/dist/provider/twitch.d.mts +102 -0
  53. package/dist/provider/twitch.mjs +118 -0
  54. package/dist/{random.js → random.mjs} +1 -2
  55. package/dist/revocation.d.mts +55 -0
  56. package/dist/revocation.mjs +63 -0
  57. package/dist/storage/{memory.d.ts → memory.d.mts} +1 -1
  58. package/dist/storage/{memory.js → memory.mjs} +3 -5
  59. package/dist/storage/{storage.d.ts → storage.d.mts} +27 -10
  60. package/dist/storage/storage.mjs +104 -0
  61. package/dist/storage/{turso.d.ts → turso.d.mts} +1 -1
  62. package/dist/storage/{turso.js → turso.mjs} +1 -1
  63. package/dist/storage/{unstorage.d.ts → unstorage.d.mts} +1 -1
  64. package/dist/storage/{unstorage.js → unstorage.mjs} +11 -4
  65. package/dist/{subject.d.ts → subject.d.mts} +1 -1
  66. package/dist/ui/{base.d.ts → base.d.mts} +1 -1
  67. package/dist/ui/{base.js → base.mjs} +1 -1
  68. package/dist/ui/{code.d.ts → code.d.mts} +1 -1
  69. package/dist/ui/{code.js → code.mjs} +3 -4
  70. package/dist/ui/{magiclink.d.ts → magiclink.d.mts} +1 -1
  71. package/dist/ui/{magiclink.js → magiclink.mjs} +3 -4
  72. package/dist/ui/{passkey.d.ts → passkey.d.mts} +1 -1
  73. package/dist/ui/{passkey.js → passkey.mjs} +2 -2
  74. package/dist/ui/{password.d.ts → password.d.mts} +1 -1
  75. package/dist/ui/{password.js → password.mjs} +3 -4
  76. package/dist/ui/{select.d.ts → select.d.mts} +1 -1
  77. package/dist/ui/{select.js → select.mjs} +2 -2
  78. package/dist/ui/{totp.d.ts → totp.d.mts} +1 -1
  79. package/dist/ui/{totp.js → totp.mjs} +2 -2
  80. package/dist/{util.js → util.mjs} +2 -5
  81. package/package.json +17 -16
  82. package/dist/index.d.ts +0 -2
  83. package/dist/index.js +0 -3
  84. package/dist/storage/storage.js +0 -62
  85. /package/dist/adapters/{node.d.ts → node.d.mts} +0 -0
  86. /package/dist/{allow.d.ts → allow.d.mts} +0 -0
  87. /package/dist/{error.d.ts → error.d.mts} +0 -0
  88. /package/dist/{error.js → error.mjs} +0 -0
  89. /package/dist/{pkce.d.ts → pkce.d.mts} +0 -0
  90. /package/dist/plugin/{builder.js → builder.mjs} +0 -0
  91. /package/dist/plugin/{plugin.js → plugin.mjs} +0 -0
  92. /package/dist/plugin/{types.js → types.mjs} +0 -0
  93. /package/dist/provider/{provider.js → provider.mjs} +0 -0
  94. /package/dist/{random.d.ts → random.d.mts} +0 -0
  95. /package/dist/{subject.js → subject.mjs} +0 -0
  96. /package/dist/themes/{theme.d.ts → theme.d.mts} +0 -0
  97. /package/dist/themes/{theme.js → theme.mjs} +0 -0
  98. /package/dist/{types.d.ts → types.d.mts} +0 -0
  99. /package/dist/{types.js → types.mjs} +0 -0
  100. /package/dist/ui/{form.d.ts → form.d.mts} +0 -0
  101. /package/dist/ui/{form.js → form.mjs} +0 -0
  102. /package/dist/ui/{icon.d.ts → icon.d.mts} +0 -0
  103. /package/dist/ui/{icon.js → icon.mjs} +0 -0
  104. /package/dist/{util.d.ts → util.d.mts} +0 -0
@@ -0,0 +1,128 @@
1
+ import { Oauth2Provider } from "./oauth2.mjs";
2
+
3
+ //#region src/provider/gitlab.ts
4
+ /**
5
+ * GitLab authentication provider for Draft Auth.
6
+ * Implements OAuth 2.0 flow for authenticating users with their GitLab accounts.
7
+ *
8
+ * ## Quick Setup
9
+ *
10
+ * ```ts
11
+ * import { GitlabProvider } from "@draftlab/auth/provider/gitlab"
12
+ *
13
+ * export default issuer({
14
+ * providers: {
15
+ * gitlab: GitlabProvider({
16
+ * clientID: process.env.GITLAB_CLIENT_ID,
17
+ * clientSecret: process.env.GITLAB_CLIENT_SECRET,
18
+ * scopes: ["read_user", "read_api"]
19
+ * })
20
+ * }
21
+ * })
22
+ * ```
23
+ *
24
+ * ## Common Scopes
25
+ *
26
+ * - `read_user` - Access user profile
27
+ * - `read_api` - Read-access to the API
28
+ * - `read_repository` - Access to project repositories
29
+ * - `write_repository` - Write access to repositories
30
+ * - `api` - Full API access
31
+ * - `read_user_email` - Access user email
32
+ *
33
+ * ## Self-Hosted GitLab
34
+ *
35
+ * For self-hosted GitLab instances, you can override the endpoint URLs:
36
+ *
37
+ * ```ts
38
+ * const selfHostedGitlab = Oauth2Provider({
39
+ * clientID: process.env.GITLAB_CLIENT_ID,
40
+ * clientSecret: process.env.GITLAB_CLIENT_SECRET,
41
+ * scopes: ["read_user"],
42
+ * type: "gitlab",
43
+ * endpoint: {
44
+ * authorization: "https://your-gitlab.com/oauth/authorize",
45
+ * token: "https://your-gitlab.com/oauth/token"
46
+ * }
47
+ * })
48
+ * ```
49
+ *
50
+ * ## User Data Access
51
+ *
52
+ * ```ts
53
+ * success: async (ctx, value) => {
54
+ * if (value.provider === "gitlab") {
55
+ * const accessToken = value.tokenset.access
56
+ *
57
+ * // Fetch user information
58
+ * const userResponse = await fetch('https://gitlab.com/api/v4/user', {
59
+ * headers: { Authorization: `Bearer ${accessToken}` }
60
+ * })
61
+ * const user = await userResponse.json()
62
+ *
63
+ * // User info: id, username, email, name, avatar_url
64
+ * }
65
+ * }
66
+ * ```
67
+ *
68
+ * @packageDocumentation
69
+ */
70
+ /**
71
+ * Creates a GitLab OAuth 2.0 authentication provider.
72
+ * Allows users to authenticate using their GitLab accounts (gitlab.com or self-hosted).
73
+ *
74
+ * @param config - GitLab OAuth 2.0 configuration
75
+ * @returns OAuth 2.0 provider configured for GitLab
76
+ *
77
+ * @example
78
+ * ```ts
79
+ * // Basic GitLab.com authentication
80
+ * const basicGitlab = GitlabProvider({
81
+ * clientID: process.env.GITLAB_CLIENT_ID,
82
+ * clientSecret: process.env.GITLAB_CLIENT_SECRET
83
+ * })
84
+ *
85
+ * // GitLab with read access
86
+ * const gitlabWithRead = GitlabProvider({
87
+ * clientID: process.env.GITLAB_CLIENT_ID,
88
+ * clientSecret: process.env.GITLAB_CLIENT_SECRET,
89
+ * scopes: ["read_user", "read_api"]
90
+ * })
91
+ *
92
+ * // Using the access token to fetch user data
93
+ * export default issuer({
94
+ * providers: { gitlab: gitlabWithRead },
95
+ * success: async (ctx, value) => {
96
+ * if (value.provider === "gitlab") {
97
+ * const token = value.tokenset.access
98
+ *
99
+ * const userRes = await fetch('https://gitlab.com/api/v4/user', {
100
+ * headers: { Authorization: `Bearer ${token}` }
101
+ * })
102
+ * const user = await userRes.json()
103
+ *
104
+ * return ctx.subject("user", {
105
+ * gitlabId: user.id,
106
+ * username: user.username,
107
+ * email: user.email,
108
+ * name: user.name,
109
+ * avatar: user.avatar_url
110
+ * })
111
+ * }
112
+ * }
113
+ * })
114
+ * ```
115
+ */
116
+ const GitlabProvider = (config) => {
117
+ return Oauth2Provider({
118
+ ...config,
119
+ type: "gitlab",
120
+ endpoint: {
121
+ authorization: "https://gitlab.com/oauth/authorize",
122
+ token: "https://gitlab.com/oauth/token"
123
+ }
124
+ });
125
+ };
126
+
127
+ //#endregion
128
+ export { GitlabProvider };
@@ -1,5 +1,5 @@
1
- import { Provider } from "./provider.js";
2
- import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
1
+ import { Provider } from "./provider.mjs";
2
+ import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
3
3
 
4
4
  //#region src/provider/google.d.ts
5
5
 
@@ -1,7 +1,51 @@
1
- import { Oauth2Provider } from "./oauth2.js";
1
+ import { Oauth2Provider } from "./oauth2.mjs";
2
2
 
3
3
  //#region src/provider/google.ts
4
4
  /**
5
+ * Google OAuth 2.0 authentication provider for Draft Auth.
6
+ * Provides access tokens for calling Google APIs on behalf of users.
7
+ *
8
+ * ## Quick Setup
9
+ *
10
+ * ```ts
11
+ * import { GoogleProvider } from "@draftlab/auth/provider/google"
12
+ *
13
+ * export default issuer({
14
+ * providers: {
15
+ * google: GoogleProvider({
16
+ * clientID: process.env.GOOGLE_CLIENT_ID,
17
+ * clientSecret: process.env.GOOGLE_CLIENT_SECRET,
18
+ * scopes: ["profile", "email", "https://www.googleapis.com/auth/calendar.readonly"]
19
+ * })
20
+ * }
21
+ * })
22
+ * ```
23
+ *
24
+ * ## Configuration Options
25
+ *
26
+ * - Access tokens for Google API calls
27
+ * - Refresh tokens for long-lived access
28
+ * - Support for offline access
29
+ * - Custom scopes for specific Google services
30
+ *
31
+ * ## User Data Access
32
+ *
33
+ * ```ts
34
+ * success: async (ctx, value) => {
35
+ * if (value.provider === "google") {
36
+ * // Access token for API calls: value.tokenset.access
37
+ * // Refresh token (if requested): value.tokenset.refresh
38
+ * // Use the access token to call Google APIs
39
+ * const response = await fetch('https://www.googleapis.com/oauth2/v2/userinfo', {
40
+ * headers: { Authorization: `Bearer ${value.tokenset.access}` }
41
+ * })
42
+ * }
43
+ * }
44
+ * ```
45
+ *
46
+ * @packageDocumentation
47
+ */
48
+ /**
5
49
  * Creates a Google OAuth 2.0 authentication provider.
6
50
  * Use this when you need access tokens to call Google APIs on behalf of the user.
7
51
  *
@@ -1,5 +1,5 @@
1
- import { Provider } from "./provider.js";
2
- import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
1
+ import { Provider } from "./provider.mjs";
2
+ import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
3
3
 
4
4
  //#region src/provider/linkedin.d.ts
5
5
 
@@ -1,7 +1,63 @@
1
- import { Oauth2Provider } from "./oauth2.js";
1
+ import { Oauth2Provider } from "./oauth2.mjs";
2
2
 
3
3
  //#region src/provider/linkedin.ts
4
4
  /**
5
+ * LinkedIn OAuth 2.0 authentication provider for Draft Auth.
6
+ * Provides access tokens for calling LinkedIn APIs on behalf of users.
7
+ *
8
+ * ## Quick Setup
9
+ *
10
+ * ```ts
11
+ * import { LinkedInProvider } from "@draftlab/auth/provider/linkedin"
12
+ *
13
+ * export default issuer({
14
+ * providers: {
15
+ * linkedin: LinkedInProvider({
16
+ * clientID: process.env.LINKEDIN_CLIENT_ID,
17
+ * clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
18
+ * scopes: ["r_liteprofile", "r_emailaddress", "w_member_social"]
19
+ * })
20
+ * }
21
+ * })
22
+ * ```
23
+ *
24
+ * ## Common Scopes
25
+ *
26
+ * - `r_liteprofile` - Access to basic profile information
27
+ * - `r_emailaddress` - Access to user's email address
28
+ * - `r_basicprofile` - Access to full profile information (deprecated)
29
+ * - `w_member_social` - Share content on behalf of user
30
+ * - `r_organization_social` - Access to organization social content
31
+ * - `rw_organization_admin` - Manage organization pages
32
+ *
33
+ * ## User Data Access
34
+ *
35
+ * ```ts
36
+ * success: async (ctx, value) => {
37
+ * if (value.provider === "linkedin") {
38
+ * const accessToken = value.tokenset.access
39
+ *
40
+ * // Fetch user profile
41
+ * const profileResponse = await fetch('https://api.linkedin.com/v2/people/~', {
42
+ * headers: { Authorization: `Bearer ${accessToken}` }
43
+ * })
44
+ * const profile = await profileResponse.json()
45
+ *
46
+ * // Fetch user email (requires r_emailaddress scope)
47
+ * const emailResponse = await fetch('https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', {
48
+ * headers: { Authorization: `Bearer ${accessToken}` }
49
+ * })
50
+ * const emailData = await emailResponse.json()
51
+ *
52
+ * // User info: profile.localizedFirstName + profile.localizedLastName
53
+ * // Email: emailData.elements[0]['handle~'].emailAddress
54
+ * }
55
+ * }
56
+ * ```
57
+ *
58
+ * @packageDocumentation
59
+ */
60
+ /**
5
61
  * Creates a LinkedIn OAuth 2.0 authentication provider.
6
62
  * Use this when you need access tokens to call LinkedIn APIs on behalf of the user.
7
63
  *
@@ -1,4 +1,4 @@
1
- import { Provider } from "./provider.js";
1
+ import { Provider } from "./provider.mjs";
2
2
 
3
3
  //#region src/provider/magiclink.d.ts
4
4
 
@@ -1,4 +1,4 @@
1
- import { generateUnbiasedDigits, timingSafeCompare } from "../random.js";
1
+ import { generateUnbiasedDigits, timingSafeCompare } from "../random.mjs";
2
2
 
3
3
  //#region src/provider/magiclink.ts
4
4
  /**
@@ -41,8 +41,7 @@ const MagicLinkProvider = (config) => {
41
41
  const action = formData.get("action")?.toString();
42
42
  if (action === "request" || action === "resend") {
43
43
  const token = generateToken();
44
- const formEntries = Object.fromEntries(formData);
45
- const { action: _,...claims } = formEntries;
44
+ const { action: _, ...claims } = Object.fromEntries(formData);
46
45
  const baseUrl = new URL(c.request.url).origin;
47
46
  const magicUrl = new URL(`/auth/${ctx.name}/verify`, baseUrl);
48
47
  magicUrl.searchParams.set("token", token);
@@ -69,13 +68,12 @@ const MagicLinkProvider = (config) => {
69
68
  if (!timingSafeCompare(storedState.token, token)) return transition(c, { type: "start" }, void 0, { type: "invalid_link" });
70
69
  const urlClaims = {};
71
70
  for (const [key, value] of url.searchParams) if (key !== "token" && value) urlClaims[key] = value;
72
- const claimsMatch = Object.keys(storedState.claims).every((key) => {
71
+ if (!Object.keys(storedState.claims).every((key) => {
73
72
  const urlValue = urlClaims[key];
74
73
  const storedValue = storedState.claims[key];
75
74
  if (!urlValue || !storedValue) return false;
76
75
  return timingSafeCompare(storedValue, urlValue);
77
- });
78
- if (!claimsMatch) return transition(c, { type: "start" }, void 0, { type: "invalid_link" });
76
+ })) return transition(c, { type: "start" }, void 0, { type: "invalid_link" });
79
77
  await ctx.unset(c, "provider");
80
78
  return await ctx.success(c, { claims: storedState.claims });
81
79
  });
@@ -1,5 +1,5 @@
1
- import { Provider } from "./provider.js";
2
- import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
1
+ import { Provider } from "./provider.mjs";
2
+ import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.mjs";
3
3
 
4
4
  //#region src/provider/microsoft.d.ts
5
5
 
@@ -1,7 +1,74 @@
1
- import { Oauth2Provider } from "./oauth2.js";
1
+ import { Oauth2Provider } from "./oauth2.mjs";
2
2
 
3
3
  //#region src/provider/microsoft.ts
4
4
  /**
5
+ * Microsoft OAuth 2.0 authentication provider for Draft Auth.
6
+ * Supports Microsoft personal accounts, work accounts, and Azure AD.
7
+ * Provides access tokens for calling Microsoft Graph APIs on behalf of users.
8
+ *
9
+ * ## Quick Setup
10
+ *
11
+ * ```ts
12
+ * import { MicrosoftProvider } from "@draftlab/auth/provider/microsoft"
13
+ *
14
+ * export default issuer({
15
+ * providers: {
16
+ * microsoft: MicrosoftProvider({
17
+ * tenant: "common", // or specific tenant ID
18
+ * clientID: process.env.MICROSOFT_CLIENT_ID,
19
+ * clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
20
+ * scopes: ["openid", "profile", "email", "User.Read"]
21
+ * })
22
+ * }
23
+ * })
24
+ * ```
25
+ *
26
+ * ## Tenant Configuration
27
+ *
28
+ * - `common` - Both personal and work/school accounts
29
+ * - `organizations` - Work/school accounts only
30
+ * - `consumers` - Personal Microsoft accounts only
31
+ * - `{tenant-id}` - Specific Azure AD tenant only
32
+ *
33
+ * ## Common Scopes
34
+ *
35
+ * - `openid` - Basic OpenID Connect sign-in
36
+ * - `profile` - User's basic profile information
37
+ * - `email` - User's email address
38
+ * - `User.Read` - Read user's profile via Microsoft Graph
39
+ * - `Mail.Read` - Read user's mail
40
+ * - `Calendars.Read` - Read user's calendars
41
+ * - `Files.Read` - Read user's files in OneDrive
42
+ * - `Sites.Read.All` - Read SharePoint sites
43
+ * - `Directory.Read.All` - Read directory data (requires admin consent)
44
+ *
45
+ * ## User Data Access
46
+ *
47
+ * ```ts
48
+ * success: async (ctx, value) => {
49
+ * if (value.provider === "microsoft") {
50
+ * const accessToken = value.tokenset.access
51
+ *
52
+ * // Fetch user profile via Microsoft Graph
53
+ * const userResponse = await fetch('https://graph.microsoft.com/v1.0/me', {
54
+ * headers: { Authorization: `Bearer ${accessToken}` }
55
+ * })
56
+ * const user = await userResponse.json()
57
+ *
58
+ * // Fetch user photo (requires User.Read scope)
59
+ * const photoResponse = await fetch('https://graph.microsoft.com/v1.0/me/photo/$value', {
60
+ * headers: { Authorization: `Bearer ${accessToken}` }
61
+ * })
62
+ * const photoBlob = await photoResponse.blob()
63
+ *
64
+ * // User info: user.displayName, user.mail, user.userPrincipalName
65
+ * }
66
+ * }
67
+ * ```
68
+ *
69
+ * @packageDocumentation
70
+ */
71
+ /**
5
72
  * Creates a Microsoft OAuth 2.0 authentication provider.
6
73
  * Use this when you need access tokens to call Microsoft Graph APIs on behalf of the user.
7
74
  *
@@ -1,4 +1,4 @@
1
- import { Provider } from "./provider.js";
1
+ import { Provider } from "./provider.mjs";
2
2
 
3
3
  //#region src/provider/oauth2.d.ts
4
4
 
@@ -1,7 +1,7 @@
1
- import { getRelativeUrl } from "../util.js";
2
- import { OauthError } from "../error.js";
3
- import { generatePKCE } from "../pkce.js";
4
- import { generateSecureToken, timingSafeCompare } from "../random.js";
1
+ import { getRelativeUrl } from "../util.mjs";
2
+ import { OauthError } from "../error.mjs";
3
+ import { generatePKCE } from "../pkce.mjs";
4
+ import { generateSecureToken, timingSafeCompare } from "../random.mjs";
5
5
 
6
6
  //#region src/provider/oauth2.ts
7
7
  /**
@@ -1,4 +1,4 @@
1
- import { Provider } from "./provider.js";
1
+ import { Provider } from "./provider.mjs";
2
2
  import { AuthenticatorSelectionCriteria, AuthenticatorTransportFuture, Base64URLString, CredentialDeviceType } from "@simplewebauthn/server";
3
3
 
4
4
  //#region src/provider/passkey.d.ts
@@ -1,4 +1,4 @@
1
- import { Storage } from "../storage/storage.js";
1
+ import { Storage } from "../storage/storage.mjs";
2
2
  import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthenticationResponse, verifyRegistrationResponse } from "@simplewebauthn/server";
3
3
 
4
4
  //#region src/provider/passkey.ts
@@ -12,8 +12,7 @@ import { generateAuthenticationOptions, generateRegistrationOptions, verifyAuthe
12
12
  const uint8ArrayToBase64Url = (bytes) => {
13
13
  let str = "";
14
14
  for (const charCode of bytes) str += String.fromCharCode(charCode);
15
- const base64String = btoa(str);
16
- return base64String.replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
15
+ return btoa(str).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
17
16
  };
18
17
  /**
19
18
  * Converts a Base64URL encoded string back to a Uint8Array.
@@ -150,8 +149,7 @@ const PasskeyProvider = (config) => {
150
149
  const username = c.query("username") || userId;
151
150
  let user = await getStoredUserById(userId);
152
151
  if (config.userCanRegisterPasskey) {
153
- const isAllowed = await config.userCanRegisterPasskey(userId, c.request);
154
- if (!isAllowed) return c.json({ error: copy.error_user_not_allowed }, { status: 403 });
152
+ if (!await config.userCanRegisterPasskey(userId, c.request)) return c.json({ error: copy.error_user_not_allowed }, { status: 403 });
155
153
  }
156
154
  if (!user) {
157
155
  user = {
@@ -252,14 +250,12 @@ const PasskeyProvider = (config) => {
252
250
  if (!rpID) return c.json({ error: "RP ID for authentication is required." }, { status: 400 });
253
251
  const userForAuth = await getStoredUserById(userId);
254
252
  if (!userForAuth) return c.json({ error: "User not found for authentication." }, { status: 404 });
255
- const userPasskeys = await getStoredUserPasskeys(userForAuth.id);
256
- const allowCredentialsList = userPasskeys.map((pk) => ({
257
- id: pk.id,
258
- transports: pk.transports
259
- }));
260
253
  const authOptions = await generateAuthenticationOptions({
261
254
  rpID,
262
- allowCredentials: allowCredentialsList,
255
+ allowCredentials: (await getStoredUserPasskeys(userForAuth.id)).map((pk) => ({
256
+ id: pk.id,
257
+ transports: pk.transports
258
+ })),
263
259
  userVerification: authenticatorSelection?.userVerification ?? "preferred",
264
260
  timeout
265
261
  });
@@ -290,7 +286,7 @@ const PasskeyProvider = (config) => {
290
286
  if (!publicKey || typeof counter !== "number" || !transports) return c.json({ error: "Passkey not found for authentication." }, { status: 400 });
291
287
  const challenge = authOptions.challenge;
292
288
  if (!challenge) return c.json({ error: "Authentication challenge not found." }, { status: 400 });
293
- const verification = await verifyAuthenticationResponse({
289
+ const { verified, authenticationInfo } = await verifyAuthenticationResponse({
294
290
  response: body,
295
291
  expectedChallenge: challenge,
296
292
  expectedOrigin: origin || "",
@@ -302,7 +298,6 @@ const PasskeyProvider = (config) => {
302
298
  transports
303
299
  }
304
300
  });
305
- const { verified, authenticationInfo } = verification;
306
301
  if (verified) {
307
302
  await updateStoredPasskeyCounter(user.id, passkey.id, authenticationInfo.newCounter);
308
303
  return ctx.success(c, {
@@ -1,4 +1,4 @@
1
- import { Provider } from "./provider.js";
1
+ import { Provider } from "./provider.mjs";
2
2
  import { StandardSchemaV1 } from "@standard-schema/spec";
3
3
 
4
4
  //#region src/provider/password.d.ts
@@ -1,7 +1,7 @@
1
- import { getRelativeUrl } from "../util.js";
2
- import { UnknownStateError } from "../error.js";
3
- import { generateUnbiasedDigits, timingSafeCompare } from "../random.js";
4
- import { Storage } from "../storage/storage.js";
1
+ import { getRelativeUrl } from "../util.mjs";
2
+ import { UnknownStateError } from "../error.mjs";
3
+ import { generateUnbiasedDigits, timingSafeCompare } from "../random.mjs";
4
+ import { Storage } from "../storage/storage.mjs";
5
5
  import * as jose from "jose";
6
6
  import { randomBytes, scrypt, timingSafeEqual } from "node:crypto";
7
7
  import { TextEncoder } from "node:util";
@@ -123,12 +123,11 @@ const PasswordProvider = (config) => {
123
123
  message: validationError
124
124
  });
125
125
  }
126
- const existingUser = await Storage.get(ctx.storage, [
126
+ if (await Storage.get(ctx.storage, [
127
127
  "email",
128
128
  email,
129
129
  "password"
130
- ]);
131
- if (existingUser) return transition(provider, { type: "email_taken" });
130
+ ])) return transition(provider, { type: "email_taken" });
132
131
  const code = generateCode();
133
132
  await config.sendCode(email, code);
134
133
  return transition({
@@ -151,12 +150,11 @@ const PasswordProvider = (config) => {
151
150
  if (action === "verify" && provider.type === "code") {
152
151
  const code = formData.get("code")?.toString();
153
152
  if (!(code && timingSafeCompare(code, provider.code))) return transition(provider, { type: "invalid_code" });
154
- const existingUser = await Storage.get(ctx.storage, [
153
+ if (await Storage.get(ctx.storage, [
155
154
  "email",
156
155
  provider.email,
157
156
  "password"
158
- ]);
159
- if (existingUser) return transition({ type: "start" }, { type: "email_taken" });
157
+ ])) return transition({ type: "start" }, { type: "email_taken" });
160
158
  await Storage.set(ctx.storage, [
161
159
  "email",
162
160
  provider.email,
@@ -170,10 +168,9 @@ const PasswordProvider = (config) => {
170
168
  * GET /change - Display password change form
171
169
  */
172
170
  routes.get("/change", async (c) => {
173
- const redirect = c.query("redirect_uri") || getRelativeUrl(c, "/authorize");
174
171
  const state = {
175
172
  type: "start",
176
- redirect
173
+ redirect: c.query("redirect_uri") || getRelativeUrl(c, "/authorize")
177
174
  };
178
175
  await ctx.set(c, "provider", 3600 * 24, state);
179
176
  return ctx.forward(c, await config.change(c.request, state));
@@ -215,12 +212,11 @@ const PasswordProvider = (config) => {
215
212
  });
216
213
  }
217
214
  if (action === "update" && provider.type === "update") {
218
- const existingPassword = await Storage.get(ctx.storage, [
215
+ if (!await Storage.get(ctx.storage, [
219
216
  "email",
220
217
  provider.email,
221
218
  "password"
222
- ]);
223
- if (!existingPassword) return c.redirect(provider.redirect, 302);
219
+ ])) return c.redirect(provider.redirect, 302);
224
220
  const password = formData.get("password")?.toString();
225
221
  const repeat = formData.get("repeat")?.toString();
226
222
  if (!password) return transition(provider, { type: "invalid_password" });
@@ -274,8 +270,7 @@ const PBKDF2Hasher = (opts) => {
274
270
  const iterations = opts?.iterations ?? 6e5;
275
271
  return {
276
272
  async hash(password) {
277
- const encoder = new TextEncoder();
278
- const passwordBytes = encoder.encode(password);
273
+ const passwordBytes = new TextEncoder().encode(password);
279
274
  const salt = crypto.getRandomValues(new Uint8Array(16));
280
275
  const keyMaterial = await crypto.subtle.importKey("raw", passwordBytes, "PBKDF2", false, ["deriveBits"]);
281
276
  const hashBuffer = await crypto.subtle.deriveBits({
@@ -284,17 +279,14 @@ const PBKDF2Hasher = (opts) => {
284
279
  salt,
285
280
  iterations
286
281
  }, keyMaterial, 256);
287
- const hashBase64 = jose.base64url.encode(new Uint8Array(hashBuffer));
288
- const saltBase64 = jose.base64url.encode(salt);
289
282
  return {
290
- hash: hashBase64,
291
- salt: saltBase64,
283
+ hash: jose.base64url.encode(new Uint8Array(hashBuffer)),
284
+ salt: jose.base64url.encode(salt),
292
285
  iterations
293
286
  };
294
287
  },
295
288
  async verify(password, compare) {
296
- const encoder = new TextEncoder();
297
- const passwordBytes = encoder.encode(password);
289
+ const passwordBytes = new TextEncoder().encode(password);
298
290
  const salt = jose.base64url.decode(compare.salt);
299
291
  const keyMaterial = await crypto.subtle.importKey("raw", passwordBytes, "PBKDF2", false, ["deriveBits"]);
300
292
  const hashBuffer = await crypto.subtle.deriveBits({
@@ -303,8 +295,7 @@ const PBKDF2Hasher = (opts) => {
303
295
  salt,
304
296
  iterations: compare.iterations
305
297
  }, keyMaterial, 256);
306
- const hashBase64 = jose.base64url.encode(new Uint8Array(hashBuffer));
307
- return timingSafeCompare(hashBase64, compare.hash);
298
+ return timingSafeCompare(jose.base64url.encode(new Uint8Array(hashBuffer)), compare.hash);
308
299
  }
309
300
  };
310
301
  };
@@ -324,21 +315,18 @@ const ScryptHasher = (opts) => {
324
315
  async hash(password) {
325
316
  const salt = randomBytes(16);
326
317
  const keyLength = 32;
327
- const derivedKey = await new Promise((resolve, reject) => {
328
- scrypt(password, salt, keyLength, {
329
- N,
330
- r,
331
- p
332
- }, (err, derivedKey$1) => {
333
- if (err) reject(err);
334
- else resolve(derivedKey$1);
335
- });
336
- });
337
- const hashBase64 = derivedKey.toString("base64");
338
- const saltBase64 = salt.toString("base64");
339
318
  return {
340
- hash: hashBase64,
341
- salt: saltBase64,
319
+ hash: (await new Promise((resolve, reject) => {
320
+ scrypt(password, salt, keyLength, {
321
+ N,
322
+ r,
323
+ p
324
+ }, (err, derivedKey) => {
325
+ if (err) reject(err);
326
+ else resolve(derivedKey);
327
+ });
328
+ })).toString("base64"),
329
+ salt: salt.toString("base64"),
342
330
  N,
343
331
  r,
344
332
  p
@@ -347,17 +335,16 @@ const ScryptHasher = (opts) => {
347
335
  async verify(password, compare) {
348
336
  const salt = Buffer.from(compare.salt, "base64");
349
337
  const keyLength = 32;
350
- const derivedKey = await new Promise((resolve, reject) => {
338
+ return timingSafeEqual(await new Promise((resolve, reject) => {
351
339
  scrypt(password, salt, keyLength, {
352
340
  N: compare.N,
353
341
  r: compare.r,
354
342
  p: compare.p
355
- }, (err, derivedKey$1) => {
343
+ }, (err, derivedKey) => {
356
344
  if (err) reject(err);
357
- else resolve(derivedKey$1);
345
+ else resolve(derivedKey);
358
346
  });
359
- });
360
- return timingSafeEqual(derivedKey, Buffer.from(compare.hash, "base64"));
347
+ }), Buffer.from(compare.hash, "base64"));
361
348
  }
362
349
  };
363
350
  };
@@ -1,4 +1,4 @@
1
- import { StorageAdapter } from "../storage/storage.js";
1
+ import { StorageAdapter } from "../storage/storage.mjs";
2
2
  import { Router } from "@draftlab/auth-router";
3
3
  import { RouterContext } from "@draftlab/auth-router/types";
4
4