@draftlab/auth 0.4.0 → 0.5.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.
- package/dist/adapters/{node.js → node.mjs} +2 -4
- package/dist/{allow.js → allow.mjs} +1 -1
- package/dist/{client.d.ts → client.d.mts} +2 -2
- package/dist/{client.js → client.mjs} +55 -10
- package/dist/{core.d.ts → core.d.mts} +10 -10
- package/dist/{core.js → core.mjs} +72 -55
- package/dist/index.d.mts +2 -0
- package/dist/index.mjs +3 -0
- package/dist/{keys.d.ts → keys.d.mts} +1 -1
- package/dist/{keys.js → keys.mjs} +6 -8
- package/dist/{pkce.js → pkce.mjs} +5 -10
- package/dist/plugin/{builder.d.ts → builder.d.mts} +1 -1
- package/dist/plugin/{manager.d.ts → manager.d.mts} +2 -2
- package/dist/plugin/{manager.js → manager.mjs} +1 -1
- package/dist/plugin/{plugin.d.ts → plugin.d.mts} +1 -1
- package/dist/plugin/plugin.mjs +1 -0
- package/dist/plugin/{types.d.ts → types.d.mts} +1 -1
- package/dist/provider/{code.d.ts → code.d.mts} +1 -1
- package/dist/provider/{code.js → code.mjs} +2 -3
- package/dist/provider/{discord.d.ts → discord.d.mts} +2 -2
- package/dist/provider/{discord.js → discord.mjs} +59 -1
- package/dist/provider/{facebook.d.ts → facebook.d.mts} +2 -2
- package/dist/provider/{facebook.js → facebook.mjs} +57 -1
- package/dist/provider/{github.d.ts → github.d.mts} +2 -2
- package/dist/provider/{github.js → github.mjs} +79 -1
- package/dist/provider/{google.d.ts → google.d.mts} +2 -2
- package/dist/provider/{google.js → google.mjs} +45 -1
- package/dist/provider/{linkedin.d.ts → linkedin.d.mts} +2 -2
- package/dist/provider/{linkedin.js → linkedin.mjs} +57 -1
- package/dist/provider/{magiclink.d.ts → magiclink.d.mts} +1 -1
- package/dist/provider/{magiclink.js → magiclink.mjs} +4 -6
- package/dist/provider/{microsoft.d.ts → microsoft.d.mts} +2 -2
- package/dist/provider/{microsoft.js → microsoft.mjs} +68 -1
- package/dist/provider/{oauth2.d.ts → oauth2.d.mts} +1 -1
- package/dist/provider/{oauth2.js → oauth2.mjs} +4 -4
- package/dist/provider/{passkey.d.ts → passkey.d.mts} +1 -1
- package/dist/provider/{passkey.js → passkey.mjs} +8 -13
- package/dist/provider/{password.d.ts → password.d.mts} +1 -1
- package/dist/provider/{password.js → password.mjs} +31 -44
- package/dist/provider/{provider.d.ts → provider.d.mts} +1 -1
- package/dist/provider/{totp.d.ts → totp.d.mts} +1 -1
- package/dist/provider/{totp.js → totp.mjs} +51 -14
- package/dist/{random.js → random.mjs} +1 -2
- package/dist/storage/{memory.d.ts → memory.d.mts} +1 -1
- package/dist/storage/{memory.js → memory.mjs} +3 -5
- package/dist/storage/{storage.d.ts → storage.d.mts} +27 -10
- package/dist/storage/storage.mjs +104 -0
- package/dist/storage/{turso.d.ts → turso.d.mts} +1 -1
- package/dist/storage/{turso.js → turso.mjs} +1 -1
- package/dist/storage/{unstorage.d.ts → unstorage.d.mts} +1 -1
- package/dist/storage/{unstorage.js → unstorage.mjs} +11 -4
- package/dist/{subject.d.ts → subject.d.mts} +1 -1
- package/dist/types.mjs +1 -0
- package/dist/ui/{base.d.ts → base.d.mts} +1 -1
- package/dist/ui/{base.js → base.mjs} +1 -1
- package/dist/ui/{code.d.ts → code.d.mts} +1 -1
- package/dist/ui/{code.js → code.mjs} +3 -4
- package/dist/ui/{magiclink.d.ts → magiclink.d.mts} +1 -1
- package/dist/ui/{magiclink.js → magiclink.mjs} +3 -4
- package/dist/ui/{passkey.d.ts → passkey.d.mts} +1 -1
- package/dist/ui/{passkey.js → passkey.mjs} +2 -2
- package/dist/ui/{password.d.ts → password.d.mts} +1 -1
- package/dist/ui/{password.js → password.mjs} +3 -4
- package/dist/ui/{select.d.ts → select.d.mts} +1 -1
- package/dist/ui/{select.js → select.mjs} +2 -2
- package/dist/ui/{totp.d.ts → totp.d.mts} +1 -1
- package/dist/ui/{totp.js → totp.mjs} +2 -2
- package/dist/{util.js → util.mjs} +2 -5
- package/package.json +18 -17
- package/dist/index.d.ts +0 -2
- package/dist/index.js +0 -3
- package/dist/plugin/plugin.js +0 -0
- package/dist/storage/storage.js +0 -62
- package/dist/types.js +0 -0
- /package/dist/adapters/{node.d.ts → node.d.mts} +0 -0
- /package/dist/{allow.d.ts → allow.d.mts} +0 -0
- /package/dist/{error.d.ts → error.d.mts} +0 -0
- /package/dist/{error.js → error.mjs} +0 -0
- /package/dist/{pkce.d.ts → pkce.d.mts} +0 -0
- /package/dist/plugin/{builder.js → builder.mjs} +0 -0
- /package/dist/plugin/{types.js → types.mjs} +0 -0
- /package/dist/provider/{provider.js → provider.mjs} +0 -0
- /package/dist/{random.d.ts → random.d.mts} +0 -0
- /package/dist/{subject.js → subject.mjs} +0 -0
- /package/dist/themes/{theme.d.ts → theme.d.mts} +0 -0
- /package/dist/themes/{theme.js → theme.mjs} +0 -0
- /package/dist/{types.d.ts → types.d.mts} +0 -0
- /package/dist/ui/{form.d.ts → form.d.mts} +0 -0
- /package/dist/ui/{form.js → form.mjs} +0 -0
- /package/dist/ui/{icon.d.ts → icon.d.mts} +0 -0
- /package/dist/ui/{icon.js → icon.mjs} +0 -0
- /package/dist/{util.d.ts → util.d.mts} +0 -0
|
@@ -1,7 +1,63 @@
|
|
|
1
|
-
import { Oauth2Provider } from "./oauth2.
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/provider/facebook.ts
|
|
4
4
|
/**
|
|
5
|
+
* Facebook OAuth 2.0 authentication provider for Draft Auth.
|
|
6
|
+
* Provides access tokens for calling Facebook Graph API on behalf of users.
|
|
7
|
+
*
|
|
8
|
+
* ## Quick Setup
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { FacebookProvider } from "@draftlab/auth/provider/facebook"
|
|
12
|
+
*
|
|
13
|
+
* export default issuer({
|
|
14
|
+
* providers: {
|
|
15
|
+
* facebook: FacebookProvider({
|
|
16
|
+
* clientID: process.env.FACEBOOK_APP_ID,
|
|
17
|
+
* clientSecret: process.env.FACEBOOK_APP_SECRET,
|
|
18
|
+
* scopes: ["email", "public_profile", "user_friends"]
|
|
19
|
+
* })
|
|
20
|
+
* }
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ## Configuration Options
|
|
25
|
+
*
|
|
26
|
+
* - Access tokens for Facebook Graph API calls
|
|
27
|
+
* - Support for various Facebook permissions
|
|
28
|
+
* - Access to user data, posts, friends, etc.
|
|
29
|
+
*
|
|
30
|
+
* ## Common Facebook Permissions
|
|
31
|
+
*
|
|
32
|
+
* - `public_profile` - Basic profile information (name, picture, etc.)
|
|
33
|
+
* - `email` - User's email address
|
|
34
|
+
* - `user_friends` - List of user's friends who also use your app
|
|
35
|
+
* - `user_posts` - User's posts on their timeline
|
|
36
|
+
* - `user_photos` - User's photos and albums
|
|
37
|
+
* - `pages_read_engagement` - Read engagement data for Pages
|
|
38
|
+
*
|
|
39
|
+
* ## User Data Access
|
|
40
|
+
*
|
|
41
|
+
* ```ts
|
|
42
|
+
* success: async (ctx, value) => {
|
|
43
|
+
* if (value.provider === "facebook") {
|
|
44
|
+
* const accessToken = value.tokenset.access
|
|
45
|
+
*
|
|
46
|
+
* // Fetch user profile from Graph API
|
|
47
|
+
* const profileResponse = await fetch(
|
|
48
|
+
* `https://graph.facebook.com/me?fields=id,name,email,picture&access_token=${accessToken}`
|
|
49
|
+
* )
|
|
50
|
+
* const profile = await profileResponse.json()
|
|
51
|
+
*
|
|
52
|
+
* // User info: `${profile.name} (${profile.email})`
|
|
53
|
+
* // Facebook ID: profile.id
|
|
54
|
+
* }
|
|
55
|
+
* }
|
|
56
|
+
* ```
|
|
57
|
+
*
|
|
58
|
+
* @packageDocumentation
|
|
59
|
+
*/
|
|
60
|
+
/**
|
|
5
61
|
* Creates a Facebook OAuth 2.0 authentication provider.
|
|
6
62
|
* Use this when you need access tokens to call Facebook Graph API on behalf of the user.
|
|
7
63
|
*
|
|
@@ -1,7 +1,85 @@
|
|
|
1
|
-
import { Oauth2Provider } from "./oauth2.
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.mjs";
|
|
2
2
|
|
|
3
3
|
//#region src/provider/github.ts
|
|
4
4
|
/**
|
|
5
|
+
* GitHub authentication provider for Draft Auth.
|
|
6
|
+
* Implements OAuth 2.0 flow for authenticating users with their GitHub accounts.
|
|
7
|
+
*
|
|
8
|
+
* ## Quick Setup
|
|
9
|
+
*
|
|
10
|
+
* ```ts
|
|
11
|
+
* import { GithubProvider } from "@draftlab/auth/provider/github"
|
|
12
|
+
*
|
|
13
|
+
* export default issuer({
|
|
14
|
+
* providers: {
|
|
15
|
+
* github: GithubProvider({
|
|
16
|
+
* clientID: process.env.GITHUB_CLIENT_ID,
|
|
17
|
+
* clientSecret: process.env.GITHUB_CLIENT_SECRET,
|
|
18
|
+
* scopes: ["user:email", "read:user"]
|
|
19
|
+
* })
|
|
20
|
+
* }
|
|
21
|
+
* })
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* ## GitHub App vs OAuth App
|
|
25
|
+
*
|
|
26
|
+
* This provider works with both GitHub OAuth Apps and GitHub Apps:
|
|
27
|
+
*
|
|
28
|
+
* ### OAuth App (Recommended for user authentication)
|
|
29
|
+
* ```ts
|
|
30
|
+
* GithubProvider({
|
|
31
|
+
* clientID: "your-oauth-app-client-id",
|
|
32
|
+
* clientSecret: "your-oauth-app-client-secret",
|
|
33
|
+
* scopes: ["user:email", "read:user"]
|
|
34
|
+
* })
|
|
35
|
+
* ```
|
|
36
|
+
*
|
|
37
|
+
* ### GitHub App (For organization-level integrations)
|
|
38
|
+
* ```ts
|
|
39
|
+
* GithubProvider({
|
|
40
|
+
* clientID: "your-github-app-client-id",
|
|
41
|
+
* clientSecret: "your-github-app-client-secret",
|
|
42
|
+
* scopes: ["user:email", "read:user", "repo"]
|
|
43
|
+
* })
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* ## Common Scopes
|
|
47
|
+
*
|
|
48
|
+
* - `user:email` - Access user's email addresses
|
|
49
|
+
* - `read:user` - Read user profile information
|
|
50
|
+
* - `repo` - Access public and private repositories
|
|
51
|
+
* - `public_repo` - Access public repositories only
|
|
52
|
+
* - `read:org` - Read organization membership
|
|
53
|
+
* - `gist` - Create and update gists
|
|
54
|
+
*
|
|
55
|
+
* ## User Data Access
|
|
56
|
+
*
|
|
57
|
+
* ```ts
|
|
58
|
+
* success: async (ctx, value) => {
|
|
59
|
+
* if (value.provider === "github") {
|
|
60
|
+
* const accessToken = value.tokenset.access
|
|
61
|
+
*
|
|
62
|
+
* // Fetch user information
|
|
63
|
+
* const userResponse = await fetch('https://api.github.com/user', {
|
|
64
|
+
* headers: { Authorization: `Bearer ${accessToken}` }
|
|
65
|
+
* })
|
|
66
|
+
* const user = await userResponse.json()
|
|
67
|
+
*
|
|
68
|
+
* // Fetch user emails (requires user:email scope)
|
|
69
|
+
* const emailsResponse = await fetch('https://api.github.com/user/emails', {
|
|
70
|
+
* headers: { Authorization: `Bearer ${accessToken}` }
|
|
71
|
+
* })
|
|
72
|
+
* const emails = await emailsResponse.json()
|
|
73
|
+
*
|
|
74
|
+
* // User info: `${user.login} (${user.name})`
|
|
75
|
+
* // Primary email: emails.find(e => e.primary)?.email
|
|
76
|
+
* }
|
|
77
|
+
* }
|
|
78
|
+
* ```
|
|
79
|
+
*
|
|
80
|
+
* @packageDocumentation
|
|
81
|
+
*/
|
|
82
|
+
/**
|
|
5
83
|
* Creates a GitHub OAuth 2.0 authentication provider.
|
|
6
84
|
* Supports both GitHub OAuth Apps and GitHub Apps for user authentication.
|
|
7
85
|
*
|
|
@@ -1,7 +1,51 @@
|
|
|
1
|
-
import { Oauth2Provider } from "./oauth2.
|
|
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.
|
|
2
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.
|
|
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.
|
|
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 { generateUnbiasedDigits, timingSafeCompare } from "../random.
|
|
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
|
|
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
|
-
|
|
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.
|
|
2
|
-
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.
|
|
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.
|
|
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,7 +1,7 @@
|
|
|
1
|
-
import { getRelativeUrl } from "../util.
|
|
2
|
-
import { OauthError } from "../error.
|
|
3
|
-
import { generatePKCE } from "../pkce.
|
|
4
|
-
import { generateSecureToken, timingSafeCompare } from "../random.
|
|
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 { Storage } from "../storage/storage.
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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
|
|
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, {
|