handshake-auth 0.1.0 → 0.2.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 (54) hide show
  1. package/ReadMe.md +230 -16
  2. package/dist/index.d.ts +18 -2
  3. package/dist/index.d.ts.map +1 -1
  4. package/dist/index.js +11 -1
  5. package/dist/index.js.map +1 -1
  6. package/dist/middleware/express.d.ts +67 -0
  7. package/dist/middleware/express.d.ts.map +1 -1
  8. package/dist/middleware/express.js +69 -0
  9. package/dist/middleware/express.js.map +1 -1
  10. package/dist/middleware/index.d.ts +2 -2
  11. package/dist/middleware/index.d.ts.map +1 -1
  12. package/dist/middleware/index.js +1 -1
  13. package/dist/middleware/index.js.map +1 -1
  14. package/dist/strategies/discord.d.ts +99 -0
  15. package/dist/strategies/discord.d.ts.map +1 -0
  16. package/dist/strategies/discord.js +85 -0
  17. package/dist/strategies/discord.js.map +1 -0
  18. package/dist/strategies/github.d.ts +112 -0
  19. package/dist/strategies/github.d.ts.map +1 -0
  20. package/dist/strategies/github.js +110 -0
  21. package/dist/strategies/github.js.map +1 -0
  22. package/dist/strategies/google.d.ts +91 -0
  23. package/dist/strategies/google.d.ts.map +1 -0
  24. package/dist/strategies/google.js +77 -0
  25. package/dist/strategies/google.js.map +1 -0
  26. package/dist/strategies/index.d.ts +16 -0
  27. package/dist/strategies/index.d.ts.map +1 -1
  28. package/dist/strategies/index.js +10 -0
  29. package/dist/strategies/index.js.map +1 -1
  30. package/dist/strategies/magic-link.d.ts +141 -0
  31. package/dist/strategies/magic-link.d.ts.map +1 -0
  32. package/dist/strategies/magic-link.js +186 -0
  33. package/dist/strategies/magic-link.js.map +1 -0
  34. package/dist/strategies/microsoft.d.ts +127 -0
  35. package/dist/strategies/microsoft.d.ts.map +1 -0
  36. package/dist/strategies/microsoft.js +98 -0
  37. package/dist/strategies/microsoft.js.map +1 -0
  38. package/dist/strategies/oauth-base.d.ts +162 -0
  39. package/dist/strategies/oauth-base.d.ts.map +1 -0
  40. package/dist/strategies/oauth-base.js +243 -0
  41. package/dist/strategies/oauth-base.js.map +1 -0
  42. package/dist/strategies/password.d.ts +69 -6
  43. package/dist/strategies/password.d.ts.map +1 -1
  44. package/dist/strategies/password.js +73 -24
  45. package/dist/strategies/password.js.map +1 -1
  46. package/dist/strategies/twitter-x.d.ts +130 -0
  47. package/dist/strategies/twitter-x.d.ts.map +1 -0
  48. package/dist/strategies/twitter-x.js +275 -0
  49. package/dist/strategies/twitter-x.js.map +1 -0
  50. package/dist/strategies/username-password.d.ts +38 -0
  51. package/dist/strategies/username-password.d.ts.map +1 -0
  52. package/dist/strategies/username-password.js +61 -0
  53. package/dist/strategies/username-password.js.map +1 -0
  54. package/package.json +2 -2
@@ -0,0 +1,85 @@
1
+ import { OAuthStrategy } from './oauth-base.js';
2
+ /**
3
+ * Discord OAuth2 authentication strategy.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const hs = new Handshake({
8
+ * findAccount: async (email) => db.accounts.findByEmail(email),
9
+ * findOrCreateFromOAuth: async (provider, profile) => {
10
+ * let account = await db.accounts.findByProviderId(provider, profile.id);
11
+ * if (!account) {
12
+ * account = await db.accounts.create({
13
+ * email: profile.email,
14
+ * name: profile.name,
15
+ * providerId: profile.id,
16
+ * provider,
17
+ * });
18
+ * }
19
+ * return account;
20
+ * },
21
+ * });
22
+ *
23
+ * hs.use(new DiscordStrategy({
24
+ * clientId: process.env.DISCORD_CLIENT_ID!,
25
+ * clientSecret: process.env.DISCORD_CLIENT_SECRET!,
26
+ * redirectUri: 'http://localhost:3000/auth/discord/callback',
27
+ * }));
28
+ *
29
+ * // Routes
30
+ * app.get('/auth/discord', async (req, res) => {
31
+ * const result = await hs.authenticate('discord', req, res, 'redirect');
32
+ * if ('redirectUrl' in result) {
33
+ * res.redirect(result.redirectUrl);
34
+ * }
35
+ * });
36
+ *
37
+ * app.get('/auth/discord/callback', async (req, res) => {
38
+ * const result = await hs.authenticate('discord', req, res, 'callback');
39
+ * if (result.account) {
40
+ * login(req, result.account);
41
+ * res.redirect('/dashboard');
42
+ * } else {
43
+ * res.status(401).send(result.error);
44
+ * }
45
+ * });
46
+ * ```
47
+ */
48
+ export class DiscordStrategy extends OAuthStrategy {
49
+ constructor(options) {
50
+ super({
51
+ name: 'discord',
52
+ clientId: options.clientId,
53
+ clientSecret: options.clientSecret,
54
+ redirectUri: options.redirectUri,
55
+ authorizeUrl: 'https://discord.com/api/oauth2/authorize',
56
+ tokenUrl: 'https://discord.com/api/oauth2/token',
57
+ userInfoUrl: 'https://discord.com/api/users/@me',
58
+ scopes: options.scopes ?? ['identify', 'email'],
59
+ stateCookieName: 'discord_oauth_state',
60
+ });
61
+ }
62
+ /**
63
+ * Map Discord profile to standard OAuthProfile.
64
+ */
65
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
+ mapProfile(data, accessToken) {
67
+ const profile = data;
68
+ // Build avatar URL if avatar hash exists
69
+ let picture = null;
70
+ if (profile.avatar) {
71
+ const ext = profile.avatar.startsWith('a_') ? 'gif' : 'png';
72
+ picture = `https://cdn.discordapp.com/avatars/${profile.id}/${profile.avatar}.${ext}`;
73
+ }
74
+ // Use global_name (display name) or fall back to username
75
+ const name = profile.global_name ?? profile.username;
76
+ return {
77
+ id: profile.id,
78
+ email: profile.email ?? null,
79
+ name,
80
+ picture,
81
+ raw: profile,
82
+ };
83
+ }
84
+ }
85
+ //# sourceMappingURL=discord.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"discord.js","sourceRoot":"","sources":["../../src/strategies/discord.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAkDhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,OAAO,eAA0B,SAAQ,aAAuB;IACpE,YAAY,OAA+B;QACzC,KAAK,CAAC;YACJ,IAAI,EAAE,SAAS;YACf,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,YAAY,EAAE,0CAA0C;YACxD,QAAQ,EAAE,sCAAsC;YAChD,WAAW,EAAE,mCAAmC;YAChD,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,OAAO,CAAC;YAC/C,eAAe,EAAE,qBAAqB;SACvC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,6DAA6D;IACnD,UAAU,CAAC,IAAa,EAAE,WAAmB;QACrD,MAAM,OAAO,GAAG,IAAsB,CAAC;QAEvC,yCAAyC;QACzC,IAAI,OAAO,GAAkB,IAAI,CAAC;QAClC,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,OAAO,CAAC,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC;YAC5D,OAAO,GAAG,sCAAsC,OAAO,CAAC,EAAE,IAAI,OAAO,CAAC,MAAM,IAAI,GAAG,EAAE,CAAC;QACxF,CAAC;QAED,0DAA0D;QAC1D,MAAM,IAAI,GAAG,OAAO,CAAC,WAAW,IAAI,OAAO,CAAC,QAAQ,CAAC;QAErD,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;YAC5B,IAAI;YACJ,OAAO;YACP,GAAG,EAAE,OAA6C;SACnD,CAAC;IACJ,CAAC;CACF"}
@@ -0,0 +1,112 @@
1
+ import type { OAuthProfile } from '../types.js';
2
+ import { OAuthStrategy } from './oauth-base.js';
3
+ /**
4
+ * GitHub user profile from the /user endpoint.
5
+ */
6
+ export interface GitHubProfile {
7
+ id: number;
8
+ login: string;
9
+ name?: string | null;
10
+ email?: string | null;
11
+ avatar_url?: string;
12
+ bio?: string | null;
13
+ company?: string | null;
14
+ location?: string | null;
15
+ blog?: string | null;
16
+ twitter_username?: string | null;
17
+ html_url?: string;
18
+ }
19
+ /**
20
+ * GitHub email object from the /user/emails endpoint.
21
+ */
22
+ export interface GitHubEmail {
23
+ email: string;
24
+ primary: boolean;
25
+ verified: boolean;
26
+ visibility: string | null;
27
+ }
28
+ /**
29
+ * Configuration options for GitHub OAuth strategy.
30
+ */
31
+ export interface GitHubStrategyOptions {
32
+ /**
33
+ * GitHub OAuth client ID
34
+ */
35
+ clientId: string;
36
+ /**
37
+ * GitHub OAuth client secret
38
+ */
39
+ clientSecret: string;
40
+ /**
41
+ * Your callback URL (must match GitHub OAuth App configuration)
42
+ */
43
+ redirectUri: string;
44
+ /**
45
+ * OAuth scopes to request
46
+ * @default ['read:user', 'user:email']
47
+ */
48
+ scopes?: string[];
49
+ }
50
+ /**
51
+ * GitHub OAuth2 authentication strategy.
52
+ *
53
+ * Note: GitHub may not include the user's email in the profile response.
54
+ * This strategy automatically fetches the primary email from the /user/emails
55
+ * endpoint when needed.
56
+ *
57
+ * @example
58
+ * ```typescript
59
+ * const hs = new Handshake({
60
+ * findAccount: async (email) => db.accounts.findByEmail(email),
61
+ * findOrCreateFromOAuth: async (provider, profile) => {
62
+ * let account = await db.accounts.findByProviderId(provider, profile.id);
63
+ * if (!account) {
64
+ * account = await db.accounts.create({
65
+ * email: profile.email,
66
+ * name: profile.name,
67
+ * providerId: profile.id,
68
+ * provider,
69
+ * });
70
+ * }
71
+ * return account;
72
+ * },
73
+ * });
74
+ *
75
+ * hs.use(new GitHubStrategy({
76
+ * clientId: process.env.GITHUB_CLIENT_ID!,
77
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
78
+ * redirectUri: 'http://localhost:3000/auth/github/callback',
79
+ * }));
80
+ *
81
+ * // Routes
82
+ * app.get('/auth/github', async (req, res) => {
83
+ * const result = await hs.authenticate('github', req, res, 'redirect');
84
+ * if ('redirectUrl' in result) {
85
+ * res.redirect(result.redirectUrl);
86
+ * }
87
+ * });
88
+ *
89
+ * app.get('/auth/github/callback', async (req, res) => {
90
+ * const result = await hs.authenticate('github', req, res, 'callback');
91
+ * if (result.account) {
92
+ * login(req, result.account);
93
+ * res.redirect('/dashboard');
94
+ * } else {
95
+ * res.status(401).send(result.error);
96
+ * }
97
+ * });
98
+ * ```
99
+ */
100
+ export declare class GitHubStrategy<TAccount> extends OAuthStrategy<TAccount> {
101
+ constructor(options: GitHubStrategyOptions);
102
+ /**
103
+ * Map GitHub profile to standard OAuthProfile.
104
+ * Fetches email from /user/emails if not in profile.
105
+ */
106
+ protected mapProfile(data: unknown, accessToken: string): Promise<OAuthProfile>;
107
+ /**
108
+ * Fetch the user's primary email from GitHub's /user/emails endpoint.
109
+ */
110
+ private fetchPrimaryEmail;
111
+ }
112
+ //# sourceMappingURL=github.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.d.ts","sourceRoot":"","sources":["../../src/strategies/github.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,IAAI,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,gBAAgB,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED;;GAEG;AACH,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;CAC3B;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,qBAAa,cAAc,CAAC,QAAQ,CAAE,SAAQ,aAAa,CAAC,QAAQ,CAAC;gBACvD,OAAO,EAAE,qBAAqB;IAc1C;;;OAGG;cACa,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC;IAmBrF;;OAEG;YACW,iBAAiB;CAqBhC"}
@@ -0,0 +1,110 @@
1
+ import { OAuthStrategy } from './oauth-base.js';
2
+ /**
3
+ * GitHub OAuth2 authentication strategy.
4
+ *
5
+ * Note: GitHub may not include the user's email in the profile response.
6
+ * This strategy automatically fetches the primary email from the /user/emails
7
+ * endpoint when needed.
8
+ *
9
+ * @example
10
+ * ```typescript
11
+ * const hs = new Handshake({
12
+ * findAccount: async (email) => db.accounts.findByEmail(email),
13
+ * findOrCreateFromOAuth: async (provider, profile) => {
14
+ * let account = await db.accounts.findByProviderId(provider, profile.id);
15
+ * if (!account) {
16
+ * account = await db.accounts.create({
17
+ * email: profile.email,
18
+ * name: profile.name,
19
+ * providerId: profile.id,
20
+ * provider,
21
+ * });
22
+ * }
23
+ * return account;
24
+ * },
25
+ * });
26
+ *
27
+ * hs.use(new GitHubStrategy({
28
+ * clientId: process.env.GITHUB_CLIENT_ID!,
29
+ * clientSecret: process.env.GITHUB_CLIENT_SECRET!,
30
+ * redirectUri: 'http://localhost:3000/auth/github/callback',
31
+ * }));
32
+ *
33
+ * // Routes
34
+ * app.get('/auth/github', async (req, res) => {
35
+ * const result = await hs.authenticate('github', req, res, 'redirect');
36
+ * if ('redirectUrl' in result) {
37
+ * res.redirect(result.redirectUrl);
38
+ * }
39
+ * });
40
+ *
41
+ * app.get('/auth/github/callback', async (req, res) => {
42
+ * const result = await hs.authenticate('github', req, res, 'callback');
43
+ * if (result.account) {
44
+ * login(req, result.account);
45
+ * res.redirect('/dashboard');
46
+ * } else {
47
+ * res.status(401).send(result.error);
48
+ * }
49
+ * });
50
+ * ```
51
+ */
52
+ export class GitHubStrategy extends OAuthStrategy {
53
+ constructor(options) {
54
+ super({
55
+ name: 'github',
56
+ clientId: options.clientId,
57
+ clientSecret: options.clientSecret,
58
+ redirectUri: options.redirectUri,
59
+ authorizeUrl: 'https://github.com/login/oauth/authorize',
60
+ tokenUrl: 'https://github.com/login/oauth/access_token',
61
+ userInfoUrl: 'https://api.github.com/user',
62
+ scopes: options.scopes ?? ['read:user', 'user:email'],
63
+ stateCookieName: 'github_oauth_state',
64
+ });
65
+ }
66
+ /**
67
+ * Map GitHub profile to standard OAuthProfile.
68
+ * Fetches email from /user/emails if not in profile.
69
+ */
70
+ async mapProfile(data, accessToken) {
71
+ const profile = data;
72
+ // GitHub may not include email in the profile
73
+ // Fetch from /user/emails endpoint if needed
74
+ let email = profile.email;
75
+ if (!email) {
76
+ email = await this.fetchPrimaryEmail(accessToken);
77
+ }
78
+ return {
79
+ id: String(profile.id),
80
+ email: email ?? null,
81
+ name: profile.name ?? profile.login,
82
+ picture: profile.avatar_url ?? null,
83
+ raw: profile,
84
+ };
85
+ }
86
+ /**
87
+ * Fetch the user's primary email from GitHub's /user/emails endpoint.
88
+ */
89
+ async fetchPrimaryEmail(accessToken) {
90
+ try {
91
+ const response = await fetch('https://api.github.com/user/emails', {
92
+ headers: {
93
+ Authorization: `Bearer ${accessToken}`,
94
+ Accept: 'application/json',
95
+ 'User-Agent': 'handshake-auth',
96
+ },
97
+ });
98
+ if (!response.ok) {
99
+ return null;
100
+ }
101
+ const emails = (await response.json());
102
+ const primary = emails.find((e) => e.primary && e.verified);
103
+ return primary?.email ?? emails.find((e) => e.verified)?.email ?? null;
104
+ }
105
+ catch {
106
+ return null;
107
+ }
108
+ }
109
+ }
110
+ //# sourceMappingURL=github.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github.js","sourceRoot":"","sources":["../../src/strategies/github.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAuDhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiDG;AACH,MAAM,OAAO,cAAyB,SAAQ,aAAuB;IACnE,YAAY,OAA8B;QACxC,KAAK,CAAC;YACJ,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,YAAY,EAAE,0CAA0C;YACxD,QAAQ,EAAE,6CAA6C;YACvD,WAAW,EAAE,6BAA6B;YAC1C,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,WAAW,EAAE,YAAY,CAAC;YACrD,eAAe,EAAE,oBAAoB;SACtC,CAAC,CAAC;IACL,CAAC;IAED;;;OAGG;IACO,KAAK,CAAC,UAAU,CAAC,IAAa,EAAE,WAAmB;QAC3D,MAAM,OAAO,GAAG,IAAqB,CAAC;QAEtC,8CAA8C;QAC9C,6CAA6C;QAC7C,IAAI,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC;QAC1B,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,KAAK,GAAG,MAAM,IAAI,CAAC,iBAAiB,CAAC,WAAW,CAAC,CAAC;QACpD,CAAC;QAED,OAAO;YACL,EAAE,EAAE,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC;YACtB,KAAK,EAAE,KAAK,IAAI,IAAI;YACpB,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,OAAO,CAAC,KAAK;YACnC,OAAO,EAAE,OAAO,CAAC,UAAU,IAAI,IAAI;YACnC,GAAG,EAAE,OAA6C;SACnD,CAAC;IACJ,CAAC;IAED;;OAEG;IACK,KAAK,CAAC,iBAAiB,CAAC,WAAmB;QACjD,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,oCAAoC,EAAE;gBACjE,OAAO,EAAE;oBACP,aAAa,EAAE,UAAU,WAAW,EAAE;oBACtC,MAAM,EAAE,kBAAkB;oBAC1B,YAAY,EAAE,gBAAgB;iBAC/B;aACF,CAAC,CAAC;YAEH,IAAI,CAAC,QAAQ,CAAC,EAAE,EAAE,CAAC;gBACjB,OAAO,IAAI,CAAC;YACd,CAAC;YAED,MAAM,MAAM,GAAG,CAAC,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAkB,CAAC;YACxD,MAAM,OAAO,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,QAAQ,CAAC,CAAC;YAC5D,OAAO,OAAO,EAAE,KAAK,IAAI,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,KAAK,IAAI,IAAI,CAAC;QACzE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,IAAI,CAAC;QACd,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,91 @@
1
+ import type { OAuthProfile } from '../types.js';
2
+ import { OAuthStrategy } from './oauth-base.js';
3
+ /**
4
+ * Google user profile from the userinfo endpoint.
5
+ */
6
+ export interface GoogleProfile {
7
+ sub: string;
8
+ name?: string;
9
+ given_name?: string;
10
+ family_name?: string;
11
+ picture?: string;
12
+ email?: string;
13
+ email_verified?: boolean;
14
+ locale?: string;
15
+ }
16
+ /**
17
+ * Configuration options for Google OAuth strategy.
18
+ */
19
+ export interface GoogleStrategyOptions {
20
+ /**
21
+ * Google OAuth client ID
22
+ */
23
+ clientId: string;
24
+ /**
25
+ * Google OAuth client secret
26
+ */
27
+ clientSecret: string;
28
+ /**
29
+ * Your callback URL (must match Google Cloud Console configuration)
30
+ */
31
+ redirectUri: string;
32
+ /**
33
+ * OAuth scopes to request
34
+ * @default ['openid', 'email', 'profile']
35
+ */
36
+ scopes?: string[];
37
+ }
38
+ /**
39
+ * Google OAuth2 authentication strategy using OpenID Connect.
40
+ *
41
+ * @example
42
+ * ```typescript
43
+ * const hs = new Handshake({
44
+ * findAccount: async (email) => db.accounts.findByEmail(email),
45
+ * findOrCreateFromOAuth: async (provider, profile) => {
46
+ * let account = await db.accounts.findByProviderId(provider, profile.id);
47
+ * if (!account) {
48
+ * account = await db.accounts.create({
49
+ * email: profile.email,
50
+ * name: profile.name,
51
+ * providerId: profile.id,
52
+ * provider,
53
+ * });
54
+ * }
55
+ * return account;
56
+ * },
57
+ * });
58
+ *
59
+ * hs.use(new GoogleStrategy({
60
+ * clientId: process.env.GOOGLE_CLIENT_ID!,
61
+ * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
62
+ * redirectUri: 'http://localhost:3000/auth/google/callback',
63
+ * }));
64
+ *
65
+ * // Routes
66
+ * app.get('/auth/google', async (req, res) => {
67
+ * const result = await hs.authenticate('google', req, res, 'redirect');
68
+ * if ('redirectUrl' in result) {
69
+ * res.redirect(result.redirectUrl);
70
+ * }
71
+ * });
72
+ *
73
+ * app.get('/auth/google/callback', async (req, res) => {
74
+ * const result = await hs.authenticate('google', req, res, 'callback');
75
+ * if (result.account) {
76
+ * login(req, result.account);
77
+ * res.redirect('/dashboard');
78
+ * } else {
79
+ * res.status(401).send(result.error);
80
+ * }
81
+ * });
82
+ * ```
83
+ */
84
+ export declare class GoogleStrategy<TAccount> extends OAuthStrategy<TAccount> {
85
+ constructor(options: GoogleStrategyOptions);
86
+ /**
87
+ * Map Google profile to standard OAuthProfile.
88
+ */
89
+ protected mapProfile(data: unknown, accessToken: string): OAuthProfile;
90
+ }
91
+ //# sourceMappingURL=google.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.d.ts","sourceRoot":"","sources":["../../src/strategies/google.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAEhD;;GAEG;AACH,MAAM,WAAW,aAAa;IAC5B,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,qBAAqB;IACpC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,qBAAa,cAAc,CAAC,QAAQ,CAAE,SAAQ,aAAa,CAAC,QAAQ,CAAC;gBACvD,OAAO,EAAE,qBAAqB;IAc1C;;OAEG;IAEH,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY;CAUvE"}
@@ -0,0 +1,77 @@
1
+ import { OAuthStrategy } from './oauth-base.js';
2
+ /**
3
+ * Google OAuth2 authentication strategy using OpenID Connect.
4
+ *
5
+ * @example
6
+ * ```typescript
7
+ * const hs = new Handshake({
8
+ * findAccount: async (email) => db.accounts.findByEmail(email),
9
+ * findOrCreateFromOAuth: async (provider, profile) => {
10
+ * let account = await db.accounts.findByProviderId(provider, profile.id);
11
+ * if (!account) {
12
+ * account = await db.accounts.create({
13
+ * email: profile.email,
14
+ * name: profile.name,
15
+ * providerId: profile.id,
16
+ * provider,
17
+ * });
18
+ * }
19
+ * return account;
20
+ * },
21
+ * });
22
+ *
23
+ * hs.use(new GoogleStrategy({
24
+ * clientId: process.env.GOOGLE_CLIENT_ID!,
25
+ * clientSecret: process.env.GOOGLE_CLIENT_SECRET!,
26
+ * redirectUri: 'http://localhost:3000/auth/google/callback',
27
+ * }));
28
+ *
29
+ * // Routes
30
+ * app.get('/auth/google', async (req, res) => {
31
+ * const result = await hs.authenticate('google', req, res, 'redirect');
32
+ * if ('redirectUrl' in result) {
33
+ * res.redirect(result.redirectUrl);
34
+ * }
35
+ * });
36
+ *
37
+ * app.get('/auth/google/callback', async (req, res) => {
38
+ * const result = await hs.authenticate('google', req, res, 'callback');
39
+ * if (result.account) {
40
+ * login(req, result.account);
41
+ * res.redirect('/dashboard');
42
+ * } else {
43
+ * res.status(401).send(result.error);
44
+ * }
45
+ * });
46
+ * ```
47
+ */
48
+ export class GoogleStrategy extends OAuthStrategy {
49
+ constructor(options) {
50
+ super({
51
+ name: 'google',
52
+ clientId: options.clientId,
53
+ clientSecret: options.clientSecret,
54
+ redirectUri: options.redirectUri,
55
+ authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
56
+ tokenUrl: 'https://oauth2.googleapis.com/token',
57
+ userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',
58
+ scopes: options.scopes ?? ['openid', 'email', 'profile'],
59
+ stateCookieName: 'google_oauth_state',
60
+ });
61
+ }
62
+ /**
63
+ * Map Google profile to standard OAuthProfile.
64
+ */
65
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
66
+ mapProfile(data, accessToken) {
67
+ const profile = data;
68
+ return {
69
+ id: profile.sub,
70
+ email: profile.email ?? null,
71
+ name: profile.name ?? null,
72
+ picture: profile.picture ?? null,
73
+ raw: profile,
74
+ };
75
+ }
76
+ }
77
+ //# sourceMappingURL=google.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"google.js","sourceRoot":"","sources":["../../src/strategies/google.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AA0ChD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,OAAO,cAAyB,SAAQ,aAAuB;IACnE,YAAY,OAA8B;QACxC,KAAK,CAAC;YACJ,IAAI,EAAE,QAAQ;YACd,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,YAAY,EAAE,8CAA8C;YAC5D,QAAQ,EAAE,qCAAqC;YAC/C,WAAW,EAAE,kDAAkD;YAC/D,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,CAAC;YACxD,eAAe,EAAE,oBAAoB;SACtC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,6DAA6D;IACnD,UAAU,CAAC,IAAa,EAAE,WAAmB;QACrD,MAAM,OAAO,GAAG,IAAqB,CAAC;QACtC,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,GAAG;YACf,KAAK,EAAE,OAAO,CAAC,KAAK,IAAI,IAAI;YAC5B,IAAI,EAAE,OAAO,CAAC,IAAI,IAAI,IAAI;YAC1B,OAAO,EAAE,OAAO,CAAC,OAAO,IAAI,IAAI;YAChC,GAAG,EAAE,OAA6C;SACnD,CAAC;IACJ,CAAC;CACF"}
@@ -1,2 +1,18 @@
1
1
  export { PasswordStrategy } from './password.js';
2
+ export type { PasswordStrategyOptions } from './password.js';
3
+ export { UsernamePasswordStrategy } from './username-password.js';
4
+ export { MagicLinkStrategy, MagicLinkSendStrategy, MagicLinkVerifyStrategy, useMagicLink, } from './magic-link.js';
5
+ export type { MagicLinkOptions, MagicLinkSendResult } from './magic-link.js';
6
+ export { OAuthStrategy, isOAuthRedirect } from './oauth-base.js';
7
+ export type { OAuthConfig, OAuthRedirectResult, OAuthTokenResponse, OAuthAuthResult, } from './oauth-base.js';
8
+ export { GoogleStrategy } from './google.js';
9
+ export type { GoogleProfile, GoogleStrategyOptions } from './google.js';
10
+ export { GitHubStrategy } from './github.js';
11
+ export type { GitHubProfile, GitHubEmail, GitHubStrategyOptions } from './github.js';
12
+ export { DiscordStrategy } from './discord.js';
13
+ export type { DiscordProfile, DiscordStrategyOptions } from './discord.js';
14
+ export { MicrosoftStrategy } from './microsoft.js';
15
+ export type { MicrosoftProfile, MicrosoftTenant, MicrosoftStrategyOptions } from './microsoft.js';
16
+ export { TwitterXStrategy } from './twitter-x.js';
17
+ export type { TwitterXProfile, TwitterXStrategyOptions } from './twitter-x.js';
2
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AACjD,YAAY,EAAE,uBAAuB,EAAE,MAAM,eAAe,CAAC;AAC7D,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,GACb,MAAM,iBAAiB,CAAC;AACzB,YAAY,EAAE,gBAAgB,EAAE,mBAAmB,EAAE,MAAM,iBAAiB,CAAC;AAG7E,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AACjE,YAAY,EACV,WAAW,EACX,mBAAmB,EACnB,kBAAkB,EAClB,eAAe,GAChB,MAAM,iBAAiB,CAAC;AAGzB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAExE,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAC7C,YAAY,EAAE,aAAa,EAAE,WAAW,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAErF,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAC/C,YAAY,EAAE,cAAc,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAE3E,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AACnD,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,wBAAwB,EAAE,MAAM,gBAAgB,CAAC;AAElG,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AAClD,YAAY,EAAE,eAAe,EAAE,uBAAuB,EAAE,MAAM,gBAAgB,CAAC"}
@@ -1,2 +1,12 @@
1
1
  export { PasswordStrategy } from './password.js';
2
+ export { UsernamePasswordStrategy } from './username-password.js';
3
+ export { MagicLinkStrategy, MagicLinkSendStrategy, MagicLinkVerifyStrategy, useMagicLink, } from './magic-link.js';
4
+ // OAuth base
5
+ export { OAuthStrategy, isOAuthRedirect } from './oauth-base.js';
6
+ // OAuth providers
7
+ export { GoogleStrategy } from './google.js';
8
+ export { GitHubStrategy } from './github.js';
9
+ export { DiscordStrategy } from './discord.js';
10
+ export { MicrosoftStrategy } from './microsoft.js';
11
+ export { TwitterXStrategy } from './twitter-x.js';
2
12
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/strategies/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAE,wBAAwB,EAAE,MAAM,wBAAwB,CAAC;AAClE,OAAO,EACL,iBAAiB,EACjB,qBAAqB,EACrB,uBAAuB,EACvB,YAAY,GACb,MAAM,iBAAiB,CAAC;AAGzB,aAAa;AACb,OAAO,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,iBAAiB,CAAC;AAQjE,kBAAkB;AAClB,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAG7C,OAAO,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAG/C,OAAO,EAAE,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAGnD,OAAO,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC"}