@ereo/auth 0.1.6

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.
@@ -0,0 +1,191 @@
1
+ /**
2
+ * @ereo/auth/providers - Built-in authentication providers
3
+ *
4
+ * Provides credential-based and OAuth authentication providers.
5
+ */
6
+ import type { AuthProvider, User, Session } from '../auth';
7
+ /** Credentials provider configuration */
8
+ export interface CredentialsConfig {
9
+ /** Provider display name */
10
+ name?: string;
11
+ /** Custom provider ID (default: 'credentials') */
12
+ id?: string;
13
+ /** Authorize function - receives credentials, returns user or null */
14
+ authorize: (credentials: Record<string, unknown>) => Promise<User | null>;
15
+ }
16
+ /** OAuth provider base configuration */
17
+ export interface OAuthConfig {
18
+ /** OAuth client ID */
19
+ clientId: string;
20
+ /** OAuth client secret */
21
+ clientSecret: string;
22
+ /** Redirect URI (callback URL) */
23
+ redirectUri?: string;
24
+ /** OAuth scopes to request */
25
+ scope?: string[];
26
+ /** Authorization URL */
27
+ authorizationUrl?: string;
28
+ /** Token URL */
29
+ tokenUrl?: string;
30
+ /** User info URL */
31
+ userInfoUrl?: string;
32
+ }
33
+ /** GitHub OAuth configuration */
34
+ export interface GitHubConfig extends OAuthConfig {
35
+ /** Allow sign-in with GitHub Enterprise */
36
+ enterprise?: {
37
+ baseUrl: string;
38
+ };
39
+ }
40
+ /** Google OAuth configuration */
41
+ export interface GoogleConfig extends OAuthConfig {
42
+ /** Enable Google Workspace domain restriction */
43
+ hostedDomain?: string;
44
+ }
45
+ /** Discord OAuth configuration */
46
+ export interface DiscordConfig extends OAuthConfig {
47
+ /** Discord guild ID to require membership */
48
+ guildId?: string;
49
+ }
50
+ /** Generic OAuth provider configuration */
51
+ export interface GenericOAuthConfig extends OAuthConfig {
52
+ /** Provider ID */
53
+ id: string;
54
+ /** Provider display name */
55
+ name: string;
56
+ /** Profile URL for fetching user data */
57
+ profileUrl?: string;
58
+ /** Function to map provider profile to User */
59
+ profile?: (profile: Record<string, unknown>, tokens: OAuthTokens) => User | Promise<User>;
60
+ }
61
+ /** OAuth tokens returned from token exchange */
62
+ export interface OAuthTokens {
63
+ access_token: string;
64
+ token_type: string;
65
+ refresh_token?: string;
66
+ expires_in?: number;
67
+ scope?: string;
68
+ id_token?: string;
69
+ }
70
+ /**
71
+ * Credentials provider (email/password or custom authentication).
72
+ *
73
+ * @example
74
+ * ```typescript
75
+ * credentials({
76
+ * authorize: async ({ email, password }) => {
77
+ * const user = await db.users.findByEmail(email);
78
+ * if (user && await bcrypt.compare(password, user.passwordHash)) {
79
+ * return { id: user.id, email: user.email, roles: user.roles };
80
+ * }
81
+ * return null;
82
+ * }
83
+ * })
84
+ * ```
85
+ */
86
+ export declare function credentials(config: CredentialsConfig): AuthProvider;
87
+ /**
88
+ * GitHub OAuth provider.
89
+ *
90
+ * @example
91
+ * ```typescript
92
+ * github({
93
+ * clientId: process.env.GITHUB_CLIENT_ID,
94
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET,
95
+ * })
96
+ * ```
97
+ */
98
+ export declare function github(config: GitHubConfig): AuthProvider;
99
+ /**
100
+ * Google OAuth provider.
101
+ *
102
+ * @example
103
+ * ```typescript
104
+ * google({
105
+ * clientId: process.env.GOOGLE_CLIENT_ID,
106
+ * clientSecret: process.env.GOOGLE_CLIENT_SECRET,
107
+ * })
108
+ * ```
109
+ */
110
+ export declare function google(config: GoogleConfig): AuthProvider;
111
+ /**
112
+ * Discord OAuth provider.
113
+ *
114
+ * @example
115
+ * ```typescript
116
+ * discord({
117
+ * clientId: process.env.DISCORD_CLIENT_ID,
118
+ * clientSecret: process.env.DISCORD_CLIENT_SECRET,
119
+ * })
120
+ * ```
121
+ */
122
+ export declare function discord(config: DiscordConfig): AuthProvider;
123
+ /**
124
+ * Generic OAuth provider for any OAuth 2.0 service.
125
+ *
126
+ * @example
127
+ * ```typescript
128
+ * oauth({
129
+ * id: 'custom',
130
+ * name: 'Custom OAuth',
131
+ * clientId: process.env.OAUTH_CLIENT_ID,
132
+ * clientSecret: process.env.OAUTH_CLIENT_SECRET,
133
+ * authorizationUrl: 'https://example.com/oauth/authorize',
134
+ * tokenUrl: 'https://example.com/oauth/token',
135
+ * userInfoUrl: 'https://example.com/api/user',
136
+ * profile: (profile) => ({
137
+ * id: profile.sub,
138
+ * email: profile.email,
139
+ * name: profile.name,
140
+ * }),
141
+ * })
142
+ * ```
143
+ */
144
+ export declare function oauth(config: GenericOAuthConfig): AuthProvider;
145
+ /** Mock provider configuration */
146
+ export interface MockConfig {
147
+ /** Pre-configured session to return */
148
+ session?: Session;
149
+ /** Pre-configured user to return */
150
+ user?: User;
151
+ /** Delay before returning (for testing loading states) */
152
+ delay?: number;
153
+ }
154
+ /**
155
+ * Simple mock provider for development and testing.
156
+ *
157
+ * @example
158
+ * ```typescript
159
+ * mock({
160
+ * user: { id: 'test-123', email: 'test@example.com', roles: ['admin'] }
161
+ * })
162
+ * ```
163
+ */
164
+ export declare function mock(config?: MockConfig): AuthProvider;
165
+ /** API Key provider configuration */
166
+ export interface ApiKeyConfig {
167
+ /** Function to validate API key and return user */
168
+ validate: (apiKey: string) => Promise<User | null>;
169
+ /** Header name to check (default: 'x-api-key') */
170
+ header?: string;
171
+ /** Query parameter name to check */
172
+ queryParam?: string;
173
+ }
174
+ /**
175
+ * API Key authentication provider.
176
+ *
177
+ * @example
178
+ * ```typescript
179
+ * apiKey({
180
+ * validate: async (key) => {
181
+ * const apiKey = await db.apiKeys.findByKey(key);
182
+ * if (apiKey && !apiKey.expired) {
183
+ * return { id: apiKey.userId, roles: apiKey.scopes };
184
+ * }
185
+ * return null;
186
+ * }
187
+ * })
188
+ * ```
189
+ */
190
+ export declare function apiKey(config: ApiKeyConfig): AuthProvider;
191
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAM3D,yCAAyC;AACzC,MAAM,WAAW,iBAAiB;IAChC,4BAA4B;IAC5B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,kDAAkD;IAClD,EAAE,CAAC,EAAE,MAAM,CAAC;IACZ,sEAAsE;IACtE,SAAS,EAAE,CAAC,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;CAC3E;AAED,wCAAwC;AACxC,MAAM,WAAW,WAAW;IAC1B,sBAAsB;IACtB,QAAQ,EAAE,MAAM,CAAC;IACjB,0BAA0B;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,kCAAkC;IAClC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,8BAA8B;IAC9B,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,wBAAwB;IACxB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,gBAAgB;IAChB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,oBAAoB;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,2CAA2C;IAC3C,UAAU,CAAC,EAAE;QACX,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH;AAED,iCAAiC;AACjC,MAAM,WAAW,YAAa,SAAQ,WAAW;IAC/C,iDAAiD;IACjD,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,kCAAkC;AAClC,MAAM,WAAW,aAAc,SAAQ,WAAW;IAChD,6CAA6C;IAC7C,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,2CAA2C;AAC3C,MAAM,WAAW,kBAAmB,SAAQ,WAAW;IACrD,kBAAkB;IAClB,EAAE,EAAE,MAAM,CAAC;IACX,4BAA4B;IAC5B,IAAI,EAAE,MAAM,CAAC;IACb,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,+CAA+C;IAC/C,OAAO,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,WAAW,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC3F;AAED,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAMD;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,iBAAiB,GAAG,YAAY,CAOnE;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CA+IzD;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAuHzD;AAMD;;;;;;;;;;GAUG;AACH,wBAAgB,OAAO,CAAC,MAAM,EAAE,aAAa,GAAG,YAAY,CAyI3D;AAMD;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,wBAAgB,KAAK,CAAC,MAAM,EAAE,kBAAkB,GAAG,YAAY,CAoI9D;AAMD,kCAAkC;AAClC,MAAM,WAAW,UAAU;IACzB,uCAAuC;IACvC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oCAAoC;IACpC,IAAI,CAAC,EAAE,IAAI,CAAC;IACZ,0DAA0D;IAC1D,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,YAAY,CAoCtD;AAMD,qCAAqC;AACrC,MAAM,WAAW,YAAY;IAC3B,mDAAmD;IACnD,QAAQ,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,GAAG,IAAI,CAAC,CAAC;IACnD,kDAAkD;IAClD,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oCAAoC;IACpC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,MAAM,CAAC,MAAM,EAAE,YAAY,GAAG,YAAY,CAazD"}
@@ -0,0 +1,495 @@
1
+ // @bun
2
+ // src/providers/index.ts
3
+ function credentials(config) {
4
+ return {
5
+ id: config.id || "credentials",
6
+ name: config.name || "Credentials",
7
+ type: "credentials",
8
+ authorize: config.authorize
9
+ };
10
+ }
11
+ function github(config) {
12
+ const baseUrl = config.enterprise?.baseUrl || "https://github.com";
13
+ const apiBaseUrl = config.enterprise?.baseUrl ? `${config.enterprise.baseUrl}/api/v3` : "https://api.github.com";
14
+ const authorizationUrl = config.authorizationUrl || `${baseUrl}/login/oauth/authorize`;
15
+ const tokenUrl = config.tokenUrl || `${baseUrl}/login/oauth/access_token`;
16
+ const userInfoUrl = config.userInfoUrl || `${apiBaseUrl}/user`;
17
+ const scope = config.scope || ["read:user", "user:email"];
18
+ return {
19
+ id: "github",
20
+ name: "GitHub",
21
+ type: "oauth",
22
+ async authorize(credentials2) {
23
+ if (credentials2.user) {
24
+ return credentials2.user;
25
+ }
26
+ const code = credentials2.code;
27
+ if (!code)
28
+ return null;
29
+ const tokenResponse = await fetch(tokenUrl, {
30
+ method: "POST",
31
+ headers: {
32
+ Accept: "application/json",
33
+ "Content-Type": "application/json"
34
+ },
35
+ body: JSON.stringify({
36
+ client_id: config.clientId,
37
+ client_secret: config.clientSecret,
38
+ code,
39
+ redirect_uri: config.redirectUri
40
+ })
41
+ });
42
+ const tokenData = await tokenResponse.json();
43
+ if (!tokenData.access_token)
44
+ return null;
45
+ const userResponse = await fetch(userInfoUrl, {
46
+ headers: {
47
+ Authorization: `token ${tokenData.access_token}`,
48
+ "User-Agent": "EreoJS-Auth",
49
+ Accept: "application/json"
50
+ }
51
+ });
52
+ const user = await userResponse.json();
53
+ let email = user.email;
54
+ if (!email) {
55
+ const emailsResponse = await fetch(`${apiBaseUrl}/user/emails`, {
56
+ headers: {
57
+ Authorization: `token ${tokenData.access_token}`,
58
+ "User-Agent": "EreoJS-Auth",
59
+ Accept: "application/json"
60
+ }
61
+ });
62
+ const emails = await emailsResponse.json();
63
+ const primaryEmail = emails.find((e) => e.primary && e.verified);
64
+ email = primaryEmail?.email || emails[0]?.email || null;
65
+ }
66
+ return {
67
+ id: String(user.id),
68
+ email: email || undefined,
69
+ name: user.name || user.login,
70
+ avatar: user.avatar_url,
71
+ username: user.login
72
+ };
73
+ },
74
+ getAuthorizationUrl(state, redirectUri) {
75
+ const params = new URLSearchParams({
76
+ client_id: config.clientId,
77
+ redirect_uri: redirectUri,
78
+ scope: scope.join(" "),
79
+ state
80
+ });
81
+ return `${authorizationUrl}?${params.toString()}`;
82
+ },
83
+ async handleCallback(params) {
84
+ const { code, redirectUri } = params;
85
+ const tokenResponse = await fetch(tokenUrl, {
86
+ method: "POST",
87
+ headers: {
88
+ Accept: "application/json",
89
+ "Content-Type": "application/json"
90
+ },
91
+ body: JSON.stringify({
92
+ client_id: config.clientId,
93
+ client_secret: config.clientSecret,
94
+ code,
95
+ redirect_uri: redirectUri
96
+ })
97
+ });
98
+ const tokenData = await tokenResponse.json();
99
+ if (!tokenData.access_token)
100
+ return null;
101
+ const userResponse = await fetch(userInfoUrl, {
102
+ headers: {
103
+ Authorization: `token ${tokenData.access_token}`,
104
+ "User-Agent": "EreoJS-Auth",
105
+ Accept: "application/json"
106
+ }
107
+ });
108
+ const user = await userResponse.json();
109
+ let email = user.email;
110
+ if (!email) {
111
+ const emailsResponse = await fetch(`${apiBaseUrl}/user/emails`, {
112
+ headers: {
113
+ Authorization: `token ${tokenData.access_token}`,
114
+ "User-Agent": "EreoJS-Auth",
115
+ Accept: "application/json"
116
+ }
117
+ });
118
+ const emails = await emailsResponse.json();
119
+ const primaryEmail = emails.find((e) => e.primary && e.verified);
120
+ email = primaryEmail?.email || emails[0]?.email || null;
121
+ }
122
+ return {
123
+ id: String(user.id),
124
+ email: email || undefined,
125
+ name: user.name || user.login,
126
+ avatar: user.avatar_url,
127
+ username: user.login
128
+ };
129
+ }
130
+ };
131
+ }
132
+ function google(config) {
133
+ const authorizationUrl = config.authorizationUrl || "https://accounts.google.com/o/oauth2/v2/auth";
134
+ const tokenUrl = config.tokenUrl || "https://oauth2.googleapis.com/token";
135
+ const userInfoUrl = config.userInfoUrl || "https://www.googleapis.com/oauth2/v2/userinfo";
136
+ const scope = config.scope || ["openid", "email", "profile"];
137
+ return {
138
+ id: "google",
139
+ name: "Google",
140
+ type: "oauth",
141
+ async authorize(credentials2) {
142
+ if (credentials2.user) {
143
+ return credentials2.user;
144
+ }
145
+ const code = credentials2.code;
146
+ if (!code)
147
+ return null;
148
+ const tokenResponse = await fetch(tokenUrl, {
149
+ method: "POST",
150
+ headers: {
151
+ "Content-Type": "application/x-www-form-urlencoded"
152
+ },
153
+ body: new URLSearchParams({
154
+ code,
155
+ client_id: config.clientId,
156
+ client_secret: config.clientSecret,
157
+ redirect_uri: config.redirectUri || "postmessage",
158
+ grant_type: "authorization_code"
159
+ })
160
+ });
161
+ const tokenData = await tokenResponse.json();
162
+ if (!tokenData.access_token)
163
+ return null;
164
+ const userResponse = await fetch(`${userInfoUrl}?access_token=${tokenData.access_token}`);
165
+ const user = await userResponse.json();
166
+ if (config.hostedDomain && user.hd !== config.hostedDomain) {
167
+ return null;
168
+ }
169
+ return {
170
+ id: user.id,
171
+ email: user.email,
172
+ name: user.name,
173
+ picture: user.picture,
174
+ emailVerified: user.verified_email
175
+ };
176
+ },
177
+ getAuthorizationUrl(state, redirectUri) {
178
+ const params = new URLSearchParams({
179
+ client_id: config.clientId,
180
+ redirect_uri: redirectUri,
181
+ response_type: "code",
182
+ scope: scope.join(" "),
183
+ state,
184
+ access_type: "offline",
185
+ prompt: "consent"
186
+ });
187
+ if (config.hostedDomain) {
188
+ params.set("hd", config.hostedDomain);
189
+ }
190
+ return `${authorizationUrl}?${params.toString()}`;
191
+ },
192
+ async handleCallback(params) {
193
+ const { code, redirectUri } = params;
194
+ const tokenResponse = await fetch(tokenUrl, {
195
+ method: "POST",
196
+ headers: {
197
+ "Content-Type": "application/x-www-form-urlencoded"
198
+ },
199
+ body: new URLSearchParams({
200
+ code,
201
+ client_id: config.clientId,
202
+ client_secret: config.clientSecret,
203
+ redirect_uri: redirectUri,
204
+ grant_type: "authorization_code"
205
+ })
206
+ });
207
+ const tokenData = await tokenResponse.json();
208
+ if (!tokenData.access_token)
209
+ return null;
210
+ const userResponse = await fetch(`${userInfoUrl}?access_token=${tokenData.access_token}`);
211
+ const user = await userResponse.json();
212
+ if (config.hostedDomain && user.hd !== config.hostedDomain) {
213
+ return null;
214
+ }
215
+ return {
216
+ id: user.id,
217
+ email: user.email,
218
+ name: user.name,
219
+ picture: user.picture,
220
+ emailVerified: user.verified_email
221
+ };
222
+ }
223
+ };
224
+ }
225
+ function discord(config) {
226
+ const authorizationUrl = config.authorizationUrl || "https://discord.com/api/oauth2/authorize";
227
+ const tokenUrl = config.tokenUrl || "https://discord.com/api/oauth2/token";
228
+ const userInfoUrl = config.userInfoUrl || "https://discord.com/api/users/@me";
229
+ const scope = config.scope || ["identify", "email"];
230
+ return {
231
+ id: "discord",
232
+ name: "Discord",
233
+ type: "oauth",
234
+ async authorize(credentials2) {
235
+ if (credentials2.user) {
236
+ return credentials2.user;
237
+ }
238
+ const code = credentials2.code;
239
+ if (!code)
240
+ return null;
241
+ const tokenResponse = await fetch(tokenUrl, {
242
+ method: "POST",
243
+ headers: {
244
+ "Content-Type": "application/x-www-form-urlencoded"
245
+ },
246
+ body: new URLSearchParams({
247
+ client_id: config.clientId,
248
+ client_secret: config.clientSecret,
249
+ code,
250
+ grant_type: "authorization_code",
251
+ redirect_uri: config.redirectUri || ""
252
+ })
253
+ });
254
+ const tokenData = await tokenResponse.json();
255
+ if (!tokenData.access_token)
256
+ return null;
257
+ const userResponse = await fetch(userInfoUrl, {
258
+ headers: {
259
+ Authorization: `Bearer ${tokenData.access_token}`
260
+ }
261
+ });
262
+ const user = await userResponse.json();
263
+ if (config.guildId) {
264
+ const guildsResponse = await fetch("https://discord.com/api/users/@me/guilds", {
265
+ headers: {
266
+ Authorization: `Bearer ${tokenData.access_token}`
267
+ }
268
+ });
269
+ const guilds = await guildsResponse.json();
270
+ const isMember = guilds.some((g) => g.id === config.guildId);
271
+ if (!isMember)
272
+ return null;
273
+ }
274
+ const avatarUrl = user.avatar ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png` : null;
275
+ return {
276
+ id: user.id,
277
+ email: user.email,
278
+ name: user.username,
279
+ avatar: avatarUrl || undefined,
280
+ discriminator: user.discriminator
281
+ };
282
+ },
283
+ getAuthorizationUrl(state, redirectUri) {
284
+ const params = new URLSearchParams({
285
+ client_id: config.clientId,
286
+ redirect_uri: redirectUri,
287
+ response_type: "code",
288
+ scope: scope.join(" "),
289
+ state
290
+ });
291
+ return `${authorizationUrl}?${params.toString()}`;
292
+ },
293
+ async handleCallback(params) {
294
+ const { code, redirectUri } = params;
295
+ const tokenResponse = await fetch(tokenUrl, {
296
+ method: "POST",
297
+ headers: {
298
+ "Content-Type": "application/x-www-form-urlencoded"
299
+ },
300
+ body: new URLSearchParams({
301
+ client_id: config.clientId,
302
+ client_secret: config.clientSecret,
303
+ code,
304
+ grant_type: "authorization_code",
305
+ redirect_uri: redirectUri
306
+ })
307
+ });
308
+ const tokenData = await tokenResponse.json();
309
+ if (!tokenData.access_token)
310
+ return null;
311
+ const userResponse = await fetch(userInfoUrl, {
312
+ headers: {
313
+ Authorization: `Bearer ${tokenData.access_token}`
314
+ }
315
+ });
316
+ const user = await userResponse.json();
317
+ if (config.guildId) {
318
+ const guildsResponse = await fetch("https://discord.com/api/users/@me/guilds", {
319
+ headers: {
320
+ Authorization: `Bearer ${tokenData.access_token}`
321
+ }
322
+ });
323
+ const guilds = await guildsResponse.json();
324
+ const isMember = guilds.some((g) => g.id === config.guildId);
325
+ if (!isMember)
326
+ return null;
327
+ }
328
+ const avatarUrl = user.avatar ? `https://cdn.discordapp.com/avatars/${user.id}/${user.avatar}.png` : null;
329
+ return {
330
+ id: user.id,
331
+ email: user.email,
332
+ name: user.username,
333
+ avatar: avatarUrl || undefined,
334
+ discriminator: user.discriminator
335
+ };
336
+ }
337
+ };
338
+ }
339
+ function oauth(config) {
340
+ const scope = config.scope || [];
341
+ return {
342
+ id: config.id,
343
+ name: config.name,
344
+ type: "oauth",
345
+ async authorize(credentials2) {
346
+ if (credentials2.user) {
347
+ return credentials2.user;
348
+ }
349
+ const code = credentials2.code;
350
+ if (!code || !config.tokenUrl)
351
+ return null;
352
+ const tokenResponse = await fetch(config.tokenUrl, {
353
+ method: "POST",
354
+ headers: {
355
+ "Content-Type": "application/x-www-form-urlencoded"
356
+ },
357
+ body: new URLSearchParams({
358
+ client_id: config.clientId,
359
+ client_secret: config.clientSecret,
360
+ code,
361
+ grant_type: "authorization_code",
362
+ redirect_uri: config.redirectUri || ""
363
+ })
364
+ });
365
+ const tokenData = await tokenResponse.json();
366
+ if (!tokenData.access_token)
367
+ return null;
368
+ const userInfoUrl = config.userInfoUrl || config.profileUrl;
369
+ if (!userInfoUrl) {
370
+ return null;
371
+ }
372
+ const userResponse = await fetch(userInfoUrl, {
373
+ headers: {
374
+ Authorization: `Bearer ${tokenData.access_token}`
375
+ }
376
+ });
377
+ const profile = await userResponse.json();
378
+ if (config.profile) {
379
+ return config.profile(profile, tokenData);
380
+ }
381
+ return {
382
+ id: profile.id || profile.sub || profile.user_id,
383
+ email: profile.email,
384
+ name: profile.name || profile.username
385
+ };
386
+ },
387
+ getAuthorizationUrl(state, redirectUri) {
388
+ if (!config.authorizationUrl) {
389
+ throw new Error(`Authorization URL not configured for provider: ${config.id}`);
390
+ }
391
+ const params = new URLSearchParams({
392
+ client_id: config.clientId,
393
+ redirect_uri: redirectUri,
394
+ response_type: "code",
395
+ state
396
+ });
397
+ if (scope.length > 0) {
398
+ params.set("scope", scope.join(" "));
399
+ }
400
+ return `${config.authorizationUrl}?${params.toString()}`;
401
+ },
402
+ async handleCallback(params) {
403
+ const { code, redirectUri } = params;
404
+ if (!config.tokenUrl) {
405
+ throw new Error(`Token URL not configured for provider: ${config.id}`);
406
+ }
407
+ const tokenResponse = await fetch(config.tokenUrl, {
408
+ method: "POST",
409
+ headers: {
410
+ "Content-Type": "application/x-www-form-urlencoded"
411
+ },
412
+ body: new URLSearchParams({
413
+ client_id: config.clientId,
414
+ client_secret: config.clientSecret,
415
+ code,
416
+ grant_type: "authorization_code",
417
+ redirect_uri: redirectUri
418
+ })
419
+ });
420
+ const tokenData = await tokenResponse.json();
421
+ if (!tokenData.access_token)
422
+ return null;
423
+ const userInfoUrl = config.userInfoUrl || config.profileUrl;
424
+ if (!userInfoUrl) {
425
+ return null;
426
+ }
427
+ const userResponse = await fetch(userInfoUrl, {
428
+ headers: {
429
+ Authorization: `Bearer ${tokenData.access_token}`
430
+ }
431
+ });
432
+ const profile = await userResponse.json();
433
+ if (config.profile) {
434
+ return config.profile(profile, tokenData);
435
+ }
436
+ return {
437
+ id: profile.id || profile.sub || profile.user_id,
438
+ email: profile.email,
439
+ name: profile.name || profile.username
440
+ };
441
+ }
442
+ };
443
+ }
444
+ function mock(config) {
445
+ return {
446
+ id: "mock",
447
+ name: "Mock",
448
+ type: "credentials",
449
+ async authorize() {
450
+ if (config?.delay) {
451
+ await new Promise((resolve) => setTimeout(resolve, config.delay));
452
+ }
453
+ if (config?.user) {
454
+ return config.user;
455
+ }
456
+ if (config?.session) {
457
+ return {
458
+ id: config.session.userId,
459
+ email: config.session.email,
460
+ name: config.session.name,
461
+ roles: config.session.roles,
462
+ ...config.session.claims
463
+ };
464
+ }
465
+ return {
466
+ id: "mock-user-123",
467
+ email: "mock@example.com",
468
+ name: "Mock User",
469
+ roles: ["user"]
470
+ };
471
+ }
472
+ };
473
+ }
474
+ function apiKey(config) {
475
+ return {
476
+ id: "api-key",
477
+ name: "API Key",
478
+ type: "credentials",
479
+ async authorize(credentials2) {
480
+ const key = credentials2.apiKey;
481
+ if (!key)
482
+ return null;
483
+ return config.validate(key);
484
+ }
485
+ };
486
+ }
487
+ export {
488
+ oauth,
489
+ mock,
490
+ google,
491
+ github,
492
+ discord,
493
+ credentials,
494
+ apiKey
495
+ };