@draftlab/auth 0.2.6 → 0.3.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/provider/discord.d.ts +140 -0
- package/dist/provider/discord.js +85 -0
- package/dist/provider/linkedin.d.ts +126 -0
- package/dist/provider/linkedin.js +73 -0
- package/dist/provider/microsoft.d.ts +172 -0
- package/dist/provider/microsoft.js +97 -0
- package/package.json +1 -1
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
|
|
3
|
+
|
|
4
|
+
//#region src/provider/discord.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for Discord OAuth 2.0 provider.
|
|
8
|
+
* Extends the base OAuth 2.0 configuration with Discord-specific documentation.
|
|
9
|
+
*/
|
|
10
|
+
interface DiscordConfig extends Oauth2WrappedConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Discord OAuth 2.0 client ID from Discord Developer Portal.
|
|
13
|
+
* Found in your Discord application settings.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* {
|
|
18
|
+
* clientID: "1234567890123456789"
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
readonly clientID: string;
|
|
23
|
+
/**
|
|
24
|
+
* Discord OAuth 2.0 client secret from Discord Developer Portal.
|
|
25
|
+
* Keep this secure and never expose it to client-side code.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* {
|
|
30
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
readonly clientSecret: string;
|
|
35
|
+
/**
|
|
36
|
+
* Discord OAuth scopes to request access for.
|
|
37
|
+
* Determines what data and actions your app can access.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* {
|
|
42
|
+
* scopes: [
|
|
43
|
+
* "identify", // Basic user information
|
|
44
|
+
* "email", // Email address
|
|
45
|
+
* "guilds", // User's Discord servers
|
|
46
|
+
* "connections" // Connected accounts (Steam, etc.)
|
|
47
|
+
* ]
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
readonly scopes: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Additional query parameters for Discord OAuth authorization.
|
|
54
|
+
* Useful for Discord-specific options like permissions.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* {
|
|
59
|
+
* query: {
|
|
60
|
+
* permissions: "8", // Administrator permission
|
|
61
|
+
* guild_id: "123456789", // Pre-select specific guild
|
|
62
|
+
* disable_guild_select: "true" // Disable guild selection
|
|
63
|
+
* }
|
|
64
|
+
* }
|
|
65
|
+
* ```
|
|
66
|
+
*/
|
|
67
|
+
readonly query?: Record<string, string>;
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Creates a Discord OAuth 2.0 authentication provider.
|
|
71
|
+
* Use this when you need access tokens to call Discord APIs on behalf of the user.
|
|
72
|
+
*
|
|
73
|
+
* @param config - Discord OAuth 2.0 configuration
|
|
74
|
+
* @returns OAuth 2.0 provider configured for Discord
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* // Basic Discord authentication
|
|
79
|
+
* const basicDiscord = DiscordProvider({
|
|
80
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
81
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
82
|
+
* })
|
|
83
|
+
*
|
|
84
|
+
* // Discord with specific scopes
|
|
85
|
+
* const discordWithScopes = DiscordProvider({
|
|
86
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
87
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
88
|
+
* scopes: [
|
|
89
|
+
* "identify",
|
|
90
|
+
* "email",
|
|
91
|
+
* "guilds",
|
|
92
|
+
* "connections"
|
|
93
|
+
* ]
|
|
94
|
+
* })
|
|
95
|
+
*
|
|
96
|
+
* // Discord bot integration
|
|
97
|
+
* const discordBot = DiscordProvider({
|
|
98
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
99
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
100
|
+
* scopes: ["bot", "guilds"],
|
|
101
|
+
* query: {
|
|
102
|
+
* permissions: "2048" // Send Messages permission
|
|
103
|
+
* }
|
|
104
|
+
* })
|
|
105
|
+
*
|
|
106
|
+
* // Using the access token to fetch data
|
|
107
|
+
* export default issuer({
|
|
108
|
+
* providers: { discord: discordWithScopes },
|
|
109
|
+
* success: async (ctx, value) => {
|
|
110
|
+
* if (value.provider === "discord") {
|
|
111
|
+
* const token = value.tokenset.access
|
|
112
|
+
*
|
|
113
|
+
* // Get user profile
|
|
114
|
+
* const userRes = await fetch('https://discord.com/api/users/@me', {
|
|
115
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
116
|
+
* })
|
|
117
|
+
* const user = await userRes.json()
|
|
118
|
+
*
|
|
119
|
+
* // Get user guilds (if guilds scope granted)
|
|
120
|
+
* const guildsRes = await fetch('https://discord.com/api/users/@me/guilds', {
|
|
121
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
122
|
+
* })
|
|
123
|
+
* const guilds = await guildsRes.json()
|
|
124
|
+
*
|
|
125
|
+
* return ctx.subject("user", {
|
|
126
|
+
* discordId: user.id,
|
|
127
|
+
* username: user.username,
|
|
128
|
+
* discriminator: user.discriminator,
|
|
129
|
+
* email: user.email,
|
|
130
|
+
* avatar: user.avatar ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png` : null,
|
|
131
|
+
* guildCount: guilds.length
|
|
132
|
+
* })
|
|
133
|
+
* }
|
|
134
|
+
* }
|
|
135
|
+
* })
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
declare const DiscordProvider: (config: DiscordConfig) => Provider<Oauth2UserData>;
|
|
139
|
+
//#endregion
|
|
140
|
+
export { DiscordConfig, DiscordProvider };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.js";
|
|
2
|
+
|
|
3
|
+
//#region src/provider/discord.ts
|
|
4
|
+
/**
|
|
5
|
+
* Creates a Discord OAuth 2.0 authentication provider.
|
|
6
|
+
* Use this when you need access tokens to call Discord APIs on behalf of the user.
|
|
7
|
+
*
|
|
8
|
+
* @param config - Discord OAuth 2.0 configuration
|
|
9
|
+
* @returns OAuth 2.0 provider configured for Discord
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Basic Discord authentication
|
|
14
|
+
* const basicDiscord = DiscordProvider({
|
|
15
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
16
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* // Discord with specific scopes
|
|
20
|
+
* const discordWithScopes = DiscordProvider({
|
|
21
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
22
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
23
|
+
* scopes: [
|
|
24
|
+
* "identify",
|
|
25
|
+
* "email",
|
|
26
|
+
* "guilds",
|
|
27
|
+
* "connections"
|
|
28
|
+
* ]
|
|
29
|
+
* })
|
|
30
|
+
*
|
|
31
|
+
* // Discord bot integration
|
|
32
|
+
* const discordBot = DiscordProvider({
|
|
33
|
+
* clientID: process.env.DISCORD_CLIENT_ID,
|
|
34
|
+
* clientSecret: process.env.DISCORD_CLIENT_SECRET,
|
|
35
|
+
* scopes: ["bot", "guilds"],
|
|
36
|
+
* query: {
|
|
37
|
+
* permissions: "2048" // Send Messages permission
|
|
38
|
+
* }
|
|
39
|
+
* })
|
|
40
|
+
*
|
|
41
|
+
* // Using the access token to fetch data
|
|
42
|
+
* export default issuer({
|
|
43
|
+
* providers: { discord: discordWithScopes },
|
|
44
|
+
* success: async (ctx, value) => {
|
|
45
|
+
* if (value.provider === "discord") {
|
|
46
|
+
* const token = value.tokenset.access
|
|
47
|
+
*
|
|
48
|
+
* // Get user profile
|
|
49
|
+
* const userRes = await fetch('https://discord.com/api/users/@me', {
|
|
50
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
51
|
+
* })
|
|
52
|
+
* const user = await userRes.json()
|
|
53
|
+
*
|
|
54
|
+
* // Get user guilds (if guilds scope granted)
|
|
55
|
+
* const guildsRes = await fetch('https://discord.com/api/users/@me/guilds', {
|
|
56
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
57
|
+
* })
|
|
58
|
+
* const guilds = await guildsRes.json()
|
|
59
|
+
*
|
|
60
|
+
* return ctx.subject("user", {
|
|
61
|
+
* discordId: user.id,
|
|
62
|
+
* username: user.username,
|
|
63
|
+
* discriminator: user.discriminator,
|
|
64
|
+
* email: user.email,
|
|
65
|
+
* avatar: user.avatar ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png` : null,
|
|
66
|
+
* guildCount: guilds.length
|
|
67
|
+
* })
|
|
68
|
+
* }
|
|
69
|
+
* }
|
|
70
|
+
* })
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
const DiscordProvider = (config) => {
|
|
74
|
+
return Oauth2Provider({
|
|
75
|
+
...config,
|
|
76
|
+
type: "discord",
|
|
77
|
+
endpoint: {
|
|
78
|
+
authorization: "https://discord.com/oauth2/authorize",
|
|
79
|
+
token: "https://discord.com/api/oauth2/token"
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
//#endregion
|
|
85
|
+
export { DiscordProvider };
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
|
|
3
|
+
|
|
4
|
+
//#region src/provider/linkedin.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for LinkedIn OAuth 2.0 provider.
|
|
8
|
+
* Extends the base OAuth 2.0 configuration with LinkedIn-specific documentation.
|
|
9
|
+
*/
|
|
10
|
+
interface LinkedInConfig extends Oauth2WrappedConfig {
|
|
11
|
+
/**
|
|
12
|
+
* LinkedIn OAuth 2.0 client ID from LinkedIn Developer Console.
|
|
13
|
+
* Found in your LinkedIn app settings.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* {
|
|
18
|
+
* clientID: "78abc123456789"
|
|
19
|
+
* }
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
readonly clientID: string;
|
|
23
|
+
/**
|
|
24
|
+
* LinkedIn OAuth 2.0 client secret from LinkedIn Developer Console.
|
|
25
|
+
* Keep this secure and never expose it to client-side code.
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```ts
|
|
29
|
+
* {
|
|
30
|
+
* clientSecret: process.env.LINKEDIN_CLIENT_SECRET
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
readonly clientSecret: string;
|
|
35
|
+
/**
|
|
36
|
+
* LinkedIn OAuth scopes to request access for.
|
|
37
|
+
* Determines what data and actions your app can access.
|
|
38
|
+
*
|
|
39
|
+
* @example
|
|
40
|
+
* ```ts
|
|
41
|
+
* {
|
|
42
|
+
* scopes: [
|
|
43
|
+
* "r_liteprofile", // Basic profile information
|
|
44
|
+
* "r_emailaddress", // Email address
|
|
45
|
+
* "w_member_social", // Share content on behalf of user
|
|
46
|
+
* "r_organization_social" // Organization content access
|
|
47
|
+
* ]
|
|
48
|
+
* }
|
|
49
|
+
* ```
|
|
50
|
+
*/
|
|
51
|
+
readonly scopes: string[];
|
|
52
|
+
/**
|
|
53
|
+
* Additional query parameters for LinkedIn OAuth authorization.
|
|
54
|
+
* Useful for LinkedIn-specific options.
|
|
55
|
+
*
|
|
56
|
+
* @example
|
|
57
|
+
* ```ts
|
|
58
|
+
* {
|
|
59
|
+
* query: {
|
|
60
|
+
* state: "custom-state-value" // Custom state parameter
|
|
61
|
+
* }
|
|
62
|
+
* }
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
65
|
+
readonly query?: Record<string, string>;
|
|
66
|
+
}
|
|
67
|
+
/**
|
|
68
|
+
* Creates a LinkedIn OAuth 2.0 authentication provider.
|
|
69
|
+
* Use this when you need access tokens to call LinkedIn APIs on behalf of the user.
|
|
70
|
+
*
|
|
71
|
+
* @param config - LinkedIn OAuth 2.0 configuration
|
|
72
|
+
* @returns OAuth 2.0 provider configured for LinkedIn
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```ts
|
|
76
|
+
* // Basic LinkedIn authentication
|
|
77
|
+
* const basicLinkedIn = LinkedInProvider({
|
|
78
|
+
* clientID: process.env.LINKEDIN_CLIENT_ID,
|
|
79
|
+
* clientSecret: process.env.LINKEDIN_CLIENT_SECRET
|
|
80
|
+
* })
|
|
81
|
+
*
|
|
82
|
+
* // LinkedIn with specific scopes
|
|
83
|
+
* const linkedInWithScopes = LinkedInProvider({
|
|
84
|
+
* clientID: process.env.LINKEDIN_CLIENT_ID,
|
|
85
|
+
* clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
|
|
86
|
+
* scopes: [
|
|
87
|
+
* "r_liteprofile",
|
|
88
|
+
* "r_emailaddress",
|
|
89
|
+
* "w_member_social"
|
|
90
|
+
* ]
|
|
91
|
+
* })
|
|
92
|
+
*
|
|
93
|
+
* // Using the access token to fetch data
|
|
94
|
+
* export default issuer({
|
|
95
|
+
* providers: { linkedin: linkedInWithScopes },
|
|
96
|
+
* success: async (ctx, value) => {
|
|
97
|
+
* if (value.provider === "linkedin") {
|
|
98
|
+
* const token = value.tokenset.access
|
|
99
|
+
*
|
|
100
|
+
* // Get user profile
|
|
101
|
+
* const profileRes = await fetch('https://api.linkedin.com/v2/people/~', {
|
|
102
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
103
|
+
* })
|
|
104
|
+
* const profile = await profileRes.json()
|
|
105
|
+
*
|
|
106
|
+
* // Get user email
|
|
107
|
+
* const emailRes = await fetch('https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', {
|
|
108
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
109
|
+
* })
|
|
110
|
+
* const emailData = await emailRes.json()
|
|
111
|
+
*
|
|
112
|
+
* return ctx.subject("user", {
|
|
113
|
+
* linkedinId: profile.id,
|
|
114
|
+
* firstName: profile.localizedFirstName,
|
|
115
|
+
* lastName: profile.localizedLastName,
|
|
116
|
+
* email: emailData.elements[0]['handle~'].emailAddress,
|
|
117
|
+
* profileUrl: `https://www.linkedin.com/in/${profile.vanityName || profile.id}`
|
|
118
|
+
* })
|
|
119
|
+
* }
|
|
120
|
+
* }
|
|
121
|
+
* })
|
|
122
|
+
* ```
|
|
123
|
+
*/
|
|
124
|
+
declare const LinkedInProvider: (config: LinkedInConfig) => Provider<Oauth2UserData>;
|
|
125
|
+
//#endregion
|
|
126
|
+
export { LinkedInConfig, LinkedInProvider };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.js";
|
|
2
|
+
|
|
3
|
+
//#region src/provider/linkedin.ts
|
|
4
|
+
/**
|
|
5
|
+
* Creates a LinkedIn OAuth 2.0 authentication provider.
|
|
6
|
+
* Use this when you need access tokens to call LinkedIn APIs on behalf of the user.
|
|
7
|
+
*
|
|
8
|
+
* @param config - LinkedIn OAuth 2.0 configuration
|
|
9
|
+
* @returns OAuth 2.0 provider configured for LinkedIn
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Basic LinkedIn authentication
|
|
14
|
+
* const basicLinkedIn = LinkedInProvider({
|
|
15
|
+
* clientID: process.env.LINKEDIN_CLIENT_ID,
|
|
16
|
+
* clientSecret: process.env.LINKEDIN_CLIENT_SECRET
|
|
17
|
+
* })
|
|
18
|
+
*
|
|
19
|
+
* // LinkedIn with specific scopes
|
|
20
|
+
* const linkedInWithScopes = LinkedInProvider({
|
|
21
|
+
* clientID: process.env.LINKEDIN_CLIENT_ID,
|
|
22
|
+
* clientSecret: process.env.LINKEDIN_CLIENT_SECRET,
|
|
23
|
+
* scopes: [
|
|
24
|
+
* "r_liteprofile",
|
|
25
|
+
* "r_emailaddress",
|
|
26
|
+
* "w_member_social"
|
|
27
|
+
* ]
|
|
28
|
+
* })
|
|
29
|
+
*
|
|
30
|
+
* // Using the access token to fetch data
|
|
31
|
+
* export default issuer({
|
|
32
|
+
* providers: { linkedin: linkedInWithScopes },
|
|
33
|
+
* success: async (ctx, value) => {
|
|
34
|
+
* if (value.provider === "linkedin") {
|
|
35
|
+
* const token = value.tokenset.access
|
|
36
|
+
*
|
|
37
|
+
* // Get user profile
|
|
38
|
+
* const profileRes = await fetch('https://api.linkedin.com/v2/people/~', {
|
|
39
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
40
|
+
* })
|
|
41
|
+
* const profile = await profileRes.json()
|
|
42
|
+
*
|
|
43
|
+
* // Get user email
|
|
44
|
+
* const emailRes = await fetch('https://api.linkedin.com/v2/emailAddress?q=members&projection=(elements*(handle~))', {
|
|
45
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
46
|
+
* })
|
|
47
|
+
* const emailData = await emailRes.json()
|
|
48
|
+
*
|
|
49
|
+
* return ctx.subject("user", {
|
|
50
|
+
* linkedinId: profile.id,
|
|
51
|
+
* firstName: profile.localizedFirstName,
|
|
52
|
+
* lastName: profile.localizedLastName,
|
|
53
|
+
* email: emailData.elements[0]['handle~'].emailAddress,
|
|
54
|
+
* profileUrl: `https://www.linkedin.com/in/${profile.vanityName || profile.id}`
|
|
55
|
+
* })
|
|
56
|
+
* }
|
|
57
|
+
* }
|
|
58
|
+
* })
|
|
59
|
+
* ```
|
|
60
|
+
*/
|
|
61
|
+
const LinkedInProvider = (config) => {
|
|
62
|
+
return Oauth2Provider({
|
|
63
|
+
...config,
|
|
64
|
+
type: "linkedin",
|
|
65
|
+
endpoint: {
|
|
66
|
+
authorization: "https://www.linkedin.com/oauth/v2/authorization",
|
|
67
|
+
token: "https://www.linkedin.com/oauth/v2/accessToken"
|
|
68
|
+
}
|
|
69
|
+
});
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
//#endregion
|
|
73
|
+
export { LinkedInProvider };
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { Provider } from "./provider.js";
|
|
2
|
+
import { Oauth2UserData, Oauth2WrappedConfig } from "./oauth2.js";
|
|
3
|
+
|
|
4
|
+
//#region src/provider/microsoft.d.ts
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Configuration options for Microsoft OAuth 2.0 provider.
|
|
8
|
+
* Extends the base OAuth 2.0 configuration with Microsoft-specific documentation.
|
|
9
|
+
*/
|
|
10
|
+
interface MicrosoftConfig extends Oauth2WrappedConfig {
|
|
11
|
+
/**
|
|
12
|
+
* Microsoft Azure AD tenant ID or tenant type.
|
|
13
|
+
* Determines which types of accounts can sign in.
|
|
14
|
+
*
|
|
15
|
+
* @example
|
|
16
|
+
* ```ts
|
|
17
|
+
* {
|
|
18
|
+
* tenant: "common" // Personal + work/school accounts
|
|
19
|
+
* // or
|
|
20
|
+
* tenant: "organizations" // Work/school accounts only
|
|
21
|
+
* // or
|
|
22
|
+
* tenant: "consumers" // Personal accounts only
|
|
23
|
+
* // or
|
|
24
|
+
* tenant: "12345678-1234-1234-1234-123456789012" // Specific tenant
|
|
25
|
+
* }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
readonly tenant: string;
|
|
29
|
+
/**
|
|
30
|
+
* Microsoft OAuth 2.0 client ID from Azure App Registration.
|
|
31
|
+
* Found in your Azure portal app registration.
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* {
|
|
36
|
+
* clientID: "12345678-1234-1234-1234-123456789012"
|
|
37
|
+
* }
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
readonly clientID: string;
|
|
41
|
+
/**
|
|
42
|
+
* Microsoft OAuth 2.0 client secret from Azure App Registration.
|
|
43
|
+
* Keep this secure and never expose it to client-side code.
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* ```ts
|
|
47
|
+
* {
|
|
48
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET
|
|
49
|
+
* }
|
|
50
|
+
* ```
|
|
51
|
+
*/
|
|
52
|
+
readonly clientSecret: string;
|
|
53
|
+
/**
|
|
54
|
+
* Microsoft OAuth scopes to request access for.
|
|
55
|
+
* Determines what data and actions your app can access via Microsoft Graph.
|
|
56
|
+
*
|
|
57
|
+
* @example
|
|
58
|
+
* ```ts
|
|
59
|
+
* {
|
|
60
|
+
* scopes: [
|
|
61
|
+
* "openid", // OpenID Connect sign-in
|
|
62
|
+
* "profile", // Basic profile
|
|
63
|
+
* "email", // Email address
|
|
64
|
+
* "User.Read", // Read user profile
|
|
65
|
+
* "Mail.Read", // Read user mail
|
|
66
|
+
* "Calendars.Read" // Read user calendars
|
|
67
|
+
* ]
|
|
68
|
+
* }
|
|
69
|
+
* ```
|
|
70
|
+
*/
|
|
71
|
+
readonly scopes: string[];
|
|
72
|
+
/**
|
|
73
|
+
* Additional query parameters for Microsoft OAuth authorization.
|
|
74
|
+
* Useful for Microsoft-specific options like domain hints.
|
|
75
|
+
*
|
|
76
|
+
* @example
|
|
77
|
+
* ```ts
|
|
78
|
+
* {
|
|
79
|
+
* query: {
|
|
80
|
+
* domain_hint: "contoso.com", // Pre-fill domain
|
|
81
|
+
* login_hint: "user@contoso.com", // Pre-fill username
|
|
82
|
+
* prompt: "consent" // Force consent screen
|
|
83
|
+
* }
|
|
84
|
+
* }
|
|
85
|
+
* ```
|
|
86
|
+
*/
|
|
87
|
+
readonly query?: Record<string, string>;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Creates a Microsoft OAuth 2.0 authentication provider.
|
|
91
|
+
* Use this when you need access tokens to call Microsoft Graph APIs on behalf of the user.
|
|
92
|
+
*
|
|
93
|
+
* @param config - Microsoft OAuth 2.0 configuration
|
|
94
|
+
* @returns OAuth 2.0 provider configured for Microsoft
|
|
95
|
+
*
|
|
96
|
+
* @example
|
|
97
|
+
* ```ts
|
|
98
|
+
* // Basic Microsoft authentication (all account types)
|
|
99
|
+
* const basicMicrosoft = MicrosoftProvider({
|
|
100
|
+
* tenant: "common",
|
|
101
|
+
* clientID: process.env.MICROSOFT_CLIENT_ID,
|
|
102
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET
|
|
103
|
+
* })
|
|
104
|
+
*
|
|
105
|
+
* // Work/school accounts only
|
|
106
|
+
* const workMicrosoft = MicrosoftProvider({
|
|
107
|
+
* tenant: "organizations",
|
|
108
|
+
* clientID: process.env.MICROSOFT_CLIENT_ID,
|
|
109
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
|
|
110
|
+
* scopes: [
|
|
111
|
+
* "openid",
|
|
112
|
+
* "profile",
|
|
113
|
+
* "email",
|
|
114
|
+
* "User.Read",
|
|
115
|
+
* "Mail.Read"
|
|
116
|
+
* ]
|
|
117
|
+
* })
|
|
118
|
+
*
|
|
119
|
+
* // Specific tenant with advanced scopes
|
|
120
|
+
* const enterpriseMicrosoft = MicrosoftProvider({
|
|
121
|
+
* tenant: "12345678-1234-1234-1234-123456789012",
|
|
122
|
+
* clientID: process.env.MICROSOFT_CLIENT_ID,
|
|
123
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
|
|
124
|
+
* scopes: [
|
|
125
|
+
* "openid",
|
|
126
|
+
* "profile",
|
|
127
|
+
* "email",
|
|
128
|
+
* "User.Read",
|
|
129
|
+
* "Directory.Read.All",
|
|
130
|
+
* "Sites.Read.All"
|
|
131
|
+
* ],
|
|
132
|
+
* query: {
|
|
133
|
+
* domain_hint: "contoso.com"
|
|
134
|
+
* }
|
|
135
|
+
* })
|
|
136
|
+
*
|
|
137
|
+
* // Using the access token to fetch data
|
|
138
|
+
* export default issuer({
|
|
139
|
+
* providers: { microsoft: workMicrosoft },
|
|
140
|
+
* success: async (ctx, value) => {
|
|
141
|
+
* if (value.provider === "microsoft") {
|
|
142
|
+
* const token = value.tokenset.access
|
|
143
|
+
*
|
|
144
|
+
* // Get user profile from Microsoft Graph
|
|
145
|
+
* const userRes = await fetch('https://graph.microsoft.com/v1.0/me', {
|
|
146
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
147
|
+
* })
|
|
148
|
+
* const user = await userRes.json()
|
|
149
|
+
*
|
|
150
|
+
* // Get user's manager (if available)
|
|
151
|
+
* const managerRes = await fetch('https://graph.microsoft.com/v1.0/me/manager', {
|
|
152
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
153
|
+
* })
|
|
154
|
+
* const manager = await managerRes.json()
|
|
155
|
+
*
|
|
156
|
+
* return ctx.subject("user", {
|
|
157
|
+
* microsoftId: user.id,
|
|
158
|
+
* displayName: user.displayName,
|
|
159
|
+
* email: user.mail || user.userPrincipalName,
|
|
160
|
+
* jobTitle: user.jobTitle,
|
|
161
|
+
* department: user.department,
|
|
162
|
+
* officeLocation: user.officeLocation,
|
|
163
|
+
* managerName: manager?.displayName
|
|
164
|
+
* })
|
|
165
|
+
* }
|
|
166
|
+
* }
|
|
167
|
+
* })
|
|
168
|
+
* ```
|
|
169
|
+
*/
|
|
170
|
+
declare const MicrosoftProvider: (config: MicrosoftConfig) => Provider<Oauth2UserData>;
|
|
171
|
+
//#endregion
|
|
172
|
+
export { MicrosoftConfig, MicrosoftProvider };
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { Oauth2Provider } from "./oauth2.js";
|
|
2
|
+
|
|
3
|
+
//#region src/provider/microsoft.ts
|
|
4
|
+
/**
|
|
5
|
+
* Creates a Microsoft OAuth 2.0 authentication provider.
|
|
6
|
+
* Use this when you need access tokens to call Microsoft Graph APIs on behalf of the user.
|
|
7
|
+
*
|
|
8
|
+
* @param config - Microsoft OAuth 2.0 configuration
|
|
9
|
+
* @returns OAuth 2.0 provider configured for Microsoft
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* // Basic Microsoft authentication (all account types)
|
|
14
|
+
* const basicMicrosoft = MicrosoftProvider({
|
|
15
|
+
* tenant: "common",
|
|
16
|
+
* clientID: process.env.MICROSOFT_CLIENT_ID,
|
|
17
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET
|
|
18
|
+
* })
|
|
19
|
+
*
|
|
20
|
+
* // Work/school accounts only
|
|
21
|
+
* const workMicrosoft = MicrosoftProvider({
|
|
22
|
+
* tenant: "organizations",
|
|
23
|
+
* clientID: process.env.MICROSOFT_CLIENT_ID,
|
|
24
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
|
|
25
|
+
* scopes: [
|
|
26
|
+
* "openid",
|
|
27
|
+
* "profile",
|
|
28
|
+
* "email",
|
|
29
|
+
* "User.Read",
|
|
30
|
+
* "Mail.Read"
|
|
31
|
+
* ]
|
|
32
|
+
* })
|
|
33
|
+
*
|
|
34
|
+
* // Specific tenant with advanced scopes
|
|
35
|
+
* const enterpriseMicrosoft = MicrosoftProvider({
|
|
36
|
+
* tenant: "12345678-1234-1234-1234-123456789012",
|
|
37
|
+
* clientID: process.env.MICROSOFT_CLIENT_ID,
|
|
38
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET,
|
|
39
|
+
* scopes: [
|
|
40
|
+
* "openid",
|
|
41
|
+
* "profile",
|
|
42
|
+
* "email",
|
|
43
|
+
* "User.Read",
|
|
44
|
+
* "Directory.Read.All",
|
|
45
|
+
* "Sites.Read.All"
|
|
46
|
+
* ],
|
|
47
|
+
* query: {
|
|
48
|
+
* domain_hint: "contoso.com"
|
|
49
|
+
* }
|
|
50
|
+
* })
|
|
51
|
+
*
|
|
52
|
+
* // Using the access token to fetch data
|
|
53
|
+
* export default issuer({
|
|
54
|
+
* providers: { microsoft: workMicrosoft },
|
|
55
|
+
* success: async (ctx, value) => {
|
|
56
|
+
* if (value.provider === "microsoft") {
|
|
57
|
+
* const token = value.tokenset.access
|
|
58
|
+
*
|
|
59
|
+
* // Get user profile from Microsoft Graph
|
|
60
|
+
* const userRes = await fetch('https://graph.microsoft.com/v1.0/me', {
|
|
61
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
62
|
+
* })
|
|
63
|
+
* const user = await userRes.json()
|
|
64
|
+
*
|
|
65
|
+
* // Get user's manager (if available)
|
|
66
|
+
* const managerRes = await fetch('https://graph.microsoft.com/v1.0/me/manager', {
|
|
67
|
+
* headers: { Authorization: `Bearer ${token}` }
|
|
68
|
+
* })
|
|
69
|
+
* const manager = await managerRes.json()
|
|
70
|
+
*
|
|
71
|
+
* return ctx.subject("user", {
|
|
72
|
+
* microsoftId: user.id,
|
|
73
|
+
* displayName: user.displayName,
|
|
74
|
+
* email: user.mail || user.userPrincipalName,
|
|
75
|
+
* jobTitle: user.jobTitle,
|
|
76
|
+
* department: user.department,
|
|
77
|
+
* officeLocation: user.officeLocation,
|
|
78
|
+
* managerName: manager?.displayName
|
|
79
|
+
* })
|
|
80
|
+
* }
|
|
81
|
+
* }
|
|
82
|
+
* })
|
|
83
|
+
* ```
|
|
84
|
+
*/
|
|
85
|
+
const MicrosoftProvider = (config) => {
|
|
86
|
+
return Oauth2Provider({
|
|
87
|
+
...config,
|
|
88
|
+
type: "microsoft",
|
|
89
|
+
endpoint: {
|
|
90
|
+
authorization: `https://login.microsoftonline.com/${config.tenant}/oauth2/v2.0/authorize`,
|
|
91
|
+
token: `https://login.microsoftonline.com/${config.tenant}/oauth2/v2.0/token`
|
|
92
|
+
}
|
|
93
|
+
});
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
//#endregion
|
|
97
|
+
export { MicrosoftProvider };
|