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.
- package/ReadMe.md +230 -16
- package/dist/index.d.ts +18 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +11 -1
- package/dist/index.js.map +1 -1
- package/dist/middleware/express.d.ts +67 -0
- package/dist/middleware/express.d.ts.map +1 -1
- package/dist/middleware/express.js +69 -0
- package/dist/middleware/express.js.map +1 -1
- package/dist/middleware/index.d.ts +2 -2
- package/dist/middleware/index.d.ts.map +1 -1
- package/dist/middleware/index.js +1 -1
- package/dist/middleware/index.js.map +1 -1
- package/dist/strategies/discord.d.ts +99 -0
- package/dist/strategies/discord.d.ts.map +1 -0
- package/dist/strategies/discord.js +85 -0
- package/dist/strategies/discord.js.map +1 -0
- package/dist/strategies/github.d.ts +112 -0
- package/dist/strategies/github.d.ts.map +1 -0
- package/dist/strategies/github.js +110 -0
- package/dist/strategies/github.js.map +1 -0
- package/dist/strategies/google.d.ts +91 -0
- package/dist/strategies/google.d.ts.map +1 -0
- package/dist/strategies/google.js +77 -0
- package/dist/strategies/google.js.map +1 -0
- package/dist/strategies/index.d.ts +16 -0
- package/dist/strategies/index.d.ts.map +1 -1
- package/dist/strategies/index.js +10 -0
- package/dist/strategies/index.js.map +1 -1
- package/dist/strategies/magic-link.d.ts +141 -0
- package/dist/strategies/magic-link.d.ts.map +1 -0
- package/dist/strategies/magic-link.js +186 -0
- package/dist/strategies/magic-link.js.map +1 -0
- package/dist/strategies/microsoft.d.ts +127 -0
- package/dist/strategies/microsoft.d.ts.map +1 -0
- package/dist/strategies/microsoft.js +98 -0
- package/dist/strategies/microsoft.js.map +1 -0
- package/dist/strategies/oauth-base.d.ts +162 -0
- package/dist/strategies/oauth-base.d.ts.map +1 -0
- package/dist/strategies/oauth-base.js +243 -0
- package/dist/strategies/oauth-base.js.map +1 -0
- package/dist/strategies/password.d.ts +69 -6
- package/dist/strategies/password.d.ts.map +1 -1
- package/dist/strategies/password.js +73 -24
- package/dist/strategies/password.js.map +1 -1
- package/dist/strategies/twitter-x.d.ts +130 -0
- package/dist/strategies/twitter-x.d.ts.map +1 -0
- package/dist/strategies/twitter-x.js +275 -0
- package/dist/strategies/twitter-x.js.map +1 -0
- package/dist/strategies/username-password.d.ts +38 -0
- package/dist/strategies/username-password.d.ts.map +1 -0
- package/dist/strategies/username-password.js +61 -0
- package/dist/strategies/username-password.js.map +1 -0
- package/package.json +2 -2
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import type { AuthResult, Strategy, HandshakeCallbacks } from '../types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Configuration options for the Magic Link strategy.
|
|
4
|
+
*/
|
|
5
|
+
export interface MagicLinkOptions {
|
|
6
|
+
/**
|
|
7
|
+
* Base URL for building the callback URL (e.g., 'http://localhost:3000').
|
|
8
|
+
* Required for constructing the full magic link URL.
|
|
9
|
+
*/
|
|
10
|
+
baseUrl: string;
|
|
11
|
+
/**
|
|
12
|
+
* Path for the magic link callback route.
|
|
13
|
+
* @default '/auth/magic/callback'
|
|
14
|
+
*/
|
|
15
|
+
callbackPath?: string;
|
|
16
|
+
/**
|
|
17
|
+
* How many minutes until the token expires.
|
|
18
|
+
* @default 15
|
|
19
|
+
*/
|
|
20
|
+
tokenExpiryMinutes?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Callback to send the magic link to the user.
|
|
23
|
+
* You handle email delivery (e.g., via handshake-email, Postmark, Resend, etc.)
|
|
24
|
+
*
|
|
25
|
+
* @param email - The user's email address
|
|
26
|
+
* @param token - The generated token
|
|
27
|
+
* @param url - The full callback URL with token
|
|
28
|
+
*/
|
|
29
|
+
sendMagicLink: (email: string, token: string, url: string) => Promise<void>;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Result from the send phase of magic link authentication.
|
|
33
|
+
* Returned when initiating a magic link flow.
|
|
34
|
+
*/
|
|
35
|
+
export interface MagicLinkSendResult {
|
|
36
|
+
sent: true;
|
|
37
|
+
email: string;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* Magic Link authentication strategy for passwordless authentication.
|
|
41
|
+
*
|
|
42
|
+
* This strategy uses the Inversion of Control pattern:
|
|
43
|
+
* - Library handles token generation and flow logic
|
|
44
|
+
* - You provide callbacks for storage and email delivery
|
|
45
|
+
*
|
|
46
|
+
* The strategy has two phases:
|
|
47
|
+
* 1. **Send phase** (`magic:send`): Generate token, store it, and send email
|
|
48
|
+
* 2. **Verify phase** (`magic:verify`): Verify token and return account
|
|
49
|
+
*
|
|
50
|
+
* @example
|
|
51
|
+
* ```typescript
|
|
52
|
+
* const hs = new Handshake({
|
|
53
|
+
* findAccount: async (email) => db.accounts.findByEmail(email),
|
|
54
|
+
* storeMagicToken: async (email, token, expiresAt) => {
|
|
55
|
+
* await db.magicTokens.create({ email, token, expiresAt });
|
|
56
|
+
* },
|
|
57
|
+
* verifyMagicToken: async (token) => {
|
|
58
|
+
* const record = await db.magicTokens.findOne({ token });
|
|
59
|
+
* if (!record || record.expiresAt < new Date()) return null;
|
|
60
|
+
* await db.magicTokens.delete({ token }); // One-time use
|
|
61
|
+
* return { email: record.email };
|
|
62
|
+
* },
|
|
63
|
+
* });
|
|
64
|
+
*
|
|
65
|
+
* hs.use(new MagicLinkStrategy({
|
|
66
|
+
* baseUrl: 'http://localhost:3000',
|
|
67
|
+
* sendMagicLink: async (email, token, url) => {
|
|
68
|
+
* await emailService.send(email, 'Your magic link', url);
|
|
69
|
+
* },
|
|
70
|
+
* }));
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
export declare class MagicLinkStrategy<TAccount> implements Strategy<TAccount> {
|
|
74
|
+
readonly name = "magic";
|
|
75
|
+
private readonly baseUrl;
|
|
76
|
+
private readonly callbackPath;
|
|
77
|
+
private readonly tokenExpiryMinutes;
|
|
78
|
+
private readonly sendMagicLink;
|
|
79
|
+
constructor(options: MagicLinkOptions);
|
|
80
|
+
/**
|
|
81
|
+
* Authenticate using the magic link strategy.
|
|
82
|
+
*
|
|
83
|
+
* This method handles two sub-strategies:
|
|
84
|
+
* - `magic:send` - Initiate magic link flow (send email)
|
|
85
|
+
* - `magic:verify` - Verify token and return account
|
|
86
|
+
*
|
|
87
|
+
* @param callbacks - Handshake callbacks
|
|
88
|
+
* @param args - [phase, ...phaseArgs] where phase is 'send' or 'verify'
|
|
89
|
+
*/
|
|
90
|
+
authenticate(callbacks: HandshakeCallbacks<TAccount>, ...args: unknown[]): Promise<AuthResult<TAccount>>;
|
|
91
|
+
/**
|
|
92
|
+
* Handle the send phase: generate token, store it, and send email.
|
|
93
|
+
*/
|
|
94
|
+
private handleSend;
|
|
95
|
+
/**
|
|
96
|
+
* Handle the verify phase: verify token and return account.
|
|
97
|
+
*/
|
|
98
|
+
private handleVerify;
|
|
99
|
+
/**
|
|
100
|
+
* Build the full callback URL with the token.
|
|
101
|
+
*/
|
|
102
|
+
private buildCallbackUrl;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Alias strategies for the two magic link phases.
|
|
106
|
+
* These allow using `hs.authenticate('magic:send', email)` syntax.
|
|
107
|
+
*/
|
|
108
|
+
export declare class MagicLinkSendStrategy<TAccount> implements Strategy<TAccount> {
|
|
109
|
+
private readonly magicLinkStrategy;
|
|
110
|
+
readonly name = "magic:send";
|
|
111
|
+
constructor(magicLinkStrategy: MagicLinkStrategy<TAccount>);
|
|
112
|
+
authenticate(callbacks: HandshakeCallbacks<TAccount>, ...args: unknown[]): Promise<AuthResult<TAccount>>;
|
|
113
|
+
}
|
|
114
|
+
export declare class MagicLinkVerifyStrategy<TAccount> implements Strategy<TAccount> {
|
|
115
|
+
private readonly magicLinkStrategy;
|
|
116
|
+
readonly name = "magic:verify";
|
|
117
|
+
constructor(magicLinkStrategy: MagicLinkStrategy<TAccount>);
|
|
118
|
+
authenticate(callbacks: HandshakeCallbacks<TAccount>, ...args: unknown[]): Promise<AuthResult<TAccount>>;
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* Helper function to register all magic link strategies at once.
|
|
122
|
+
*
|
|
123
|
+
* @example
|
|
124
|
+
* ```typescript
|
|
125
|
+
* import { Handshake, useMagicLink } from 'handshake-auth';
|
|
126
|
+
*
|
|
127
|
+
* const hs = new Handshake({...});
|
|
128
|
+
* useMagicLink(hs, {
|
|
129
|
+
* baseUrl: 'http://localhost:3000',
|
|
130
|
+
* sendMagicLink: async (email, token, url) => {...},
|
|
131
|
+
* });
|
|
132
|
+
*
|
|
133
|
+
* // Now you can use:
|
|
134
|
+
* await hs.authenticate('magic:send', email);
|
|
135
|
+
* await hs.authenticate('magic:verify', token);
|
|
136
|
+
* ```
|
|
137
|
+
*/
|
|
138
|
+
export declare function useMagicLink<TAccount>(hs: {
|
|
139
|
+
use: (strategy: Strategy<TAccount>) => unknown;
|
|
140
|
+
}, options: MagicLinkOptions): MagicLinkStrategy<TAccount>;
|
|
141
|
+
//# sourceMappingURL=magic-link.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"magic-link.d.ts","sourceRoot":"","sources":["../../src/strategies/magic-link.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAEhB;;;OAGG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;IAEtB;;;OAGG;IACH,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAE5B;;;;;;;OAOG;IACH,aAAa,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;CAC7E;AAED;;;GAGG;AACH,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,IAAI,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;CACf;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,iBAAiB,CAAC,QAAQ,CAAE,YAAW,QAAQ,CAAC,QAAQ,CAAC;IACpE,QAAQ,CAAC,IAAI,WAAW;IAExB,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAAS;IACtC,OAAO,CAAC,QAAQ,CAAC,kBAAkB,CAAS;IAC5C,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAoC;gBAEtD,OAAO,EAAE,gBAAgB;IAOrC;;;;;;;;;OASG;IACG,YAAY,CAChB,SAAS,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACvC,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;IAehC;;OAEG;YACW,UAAU;IAqCxB;;OAEG;YACW,YAAY;IAgC1B;;OAEG;IACH,OAAO,CAAC,gBAAgB;CAKzB;AAED;;;GAGG;AACH,qBAAa,qBAAqB,CAAC,QAAQ,CAAE,YAAW,QAAQ,CAAC,QAAQ,CAAC;IAG5D,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAF9C,QAAQ,CAAC,IAAI,gBAAgB;gBAEA,iBAAiB,EAAE,iBAAiB,CAAC,QAAQ,CAAC;IAErE,YAAY,CAChB,SAAS,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACvC,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;CAGjC;AAED,qBAAa,uBAAuB,CAAC,QAAQ,CAAE,YAAW,QAAQ,CAAC,QAAQ,CAAC;IAG9D,OAAO,CAAC,QAAQ,CAAC,iBAAiB;IAF9C,QAAQ,CAAC,IAAI,kBAAkB;gBAEF,iBAAiB,EAAE,iBAAiB,CAAC,QAAQ,CAAC;IAErE,YAAY,CAChB,SAAS,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACvC,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;CAGjC;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EACnC,EAAE,EAAE;IAAE,GAAG,EAAE,CAAC,QAAQ,EAAE,QAAQ,CAAC,QAAQ,CAAC,KAAK,OAAO,CAAA;CAAE,EACtD,OAAO,EAAE,gBAAgB,GACxB,iBAAiB,CAAC,QAAQ,CAAC,CAM7B"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
/**
|
|
3
|
+
* Magic Link authentication strategy for passwordless authentication.
|
|
4
|
+
*
|
|
5
|
+
* This strategy uses the Inversion of Control pattern:
|
|
6
|
+
* - Library handles token generation and flow logic
|
|
7
|
+
* - You provide callbacks for storage and email delivery
|
|
8
|
+
*
|
|
9
|
+
* The strategy has two phases:
|
|
10
|
+
* 1. **Send phase** (`magic:send`): Generate token, store it, and send email
|
|
11
|
+
* 2. **Verify phase** (`magic:verify`): Verify token and return account
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```typescript
|
|
15
|
+
* const hs = new Handshake({
|
|
16
|
+
* findAccount: async (email) => db.accounts.findByEmail(email),
|
|
17
|
+
* storeMagicToken: async (email, token, expiresAt) => {
|
|
18
|
+
* await db.magicTokens.create({ email, token, expiresAt });
|
|
19
|
+
* },
|
|
20
|
+
* verifyMagicToken: async (token) => {
|
|
21
|
+
* const record = await db.magicTokens.findOne({ token });
|
|
22
|
+
* if (!record || record.expiresAt < new Date()) return null;
|
|
23
|
+
* await db.magicTokens.delete({ token }); // One-time use
|
|
24
|
+
* return { email: record.email };
|
|
25
|
+
* },
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* hs.use(new MagicLinkStrategy({
|
|
29
|
+
* baseUrl: 'http://localhost:3000',
|
|
30
|
+
* sendMagicLink: async (email, token, url) => {
|
|
31
|
+
* await emailService.send(email, 'Your magic link', url);
|
|
32
|
+
* },
|
|
33
|
+
* }));
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export class MagicLinkStrategy {
|
|
37
|
+
name = 'magic';
|
|
38
|
+
baseUrl;
|
|
39
|
+
callbackPath;
|
|
40
|
+
tokenExpiryMinutes;
|
|
41
|
+
sendMagicLink;
|
|
42
|
+
constructor(options) {
|
|
43
|
+
this.baseUrl = options.baseUrl;
|
|
44
|
+
this.callbackPath = options.callbackPath ?? '/auth/magic/callback';
|
|
45
|
+
this.tokenExpiryMinutes = options.tokenExpiryMinutes ?? 15;
|
|
46
|
+
this.sendMagicLink = options.sendMagicLink;
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Authenticate using the magic link strategy.
|
|
50
|
+
*
|
|
51
|
+
* This method handles two sub-strategies:
|
|
52
|
+
* - `magic:send` - Initiate magic link flow (send email)
|
|
53
|
+
* - `magic:verify` - Verify token and return account
|
|
54
|
+
*
|
|
55
|
+
* @param callbacks - Handshake callbacks
|
|
56
|
+
* @param args - [phase, ...phaseArgs] where phase is 'send' or 'verify'
|
|
57
|
+
*/
|
|
58
|
+
async authenticate(callbacks, ...args) {
|
|
59
|
+
const [phase, ...phaseArgs] = args;
|
|
60
|
+
if (phase === 'send') {
|
|
61
|
+
return this.handleSend(callbacks, phaseArgs[0]);
|
|
62
|
+
}
|
|
63
|
+
else if (phase === 'verify') {
|
|
64
|
+
return this.handleVerify(callbacks, phaseArgs[0]);
|
|
65
|
+
}
|
|
66
|
+
else {
|
|
67
|
+
return {
|
|
68
|
+
account: null,
|
|
69
|
+
error: `Invalid magic link phase: ${phase}. Use 'send' or 'verify'`,
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* Handle the send phase: generate token, store it, and send email.
|
|
75
|
+
*/
|
|
76
|
+
async handleSend(callbacks, email) {
|
|
77
|
+
if (!email) {
|
|
78
|
+
return { account: null, error: 'Email is required' };
|
|
79
|
+
}
|
|
80
|
+
if (!callbacks.storeMagicToken) {
|
|
81
|
+
return {
|
|
82
|
+
account: null,
|
|
83
|
+
error: 'storeMagicToken callback is required for magic link strategy',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
// Generate token and expiry
|
|
87
|
+
const token = randomUUID();
|
|
88
|
+
const expiresAt = new Date(Date.now() + this.tokenExpiryMinutes * 60 * 1000);
|
|
89
|
+
// Build callback URL
|
|
90
|
+
const url = this.buildCallbackUrl(token);
|
|
91
|
+
// Store the token (user's responsibility)
|
|
92
|
+
await callbacks.storeMagicToken(email, token, expiresAt);
|
|
93
|
+
// Send the email (user's responsibility)
|
|
94
|
+
await this.sendMagicLink(email, token, url);
|
|
95
|
+
// Return special result for send phase
|
|
96
|
+
// We return account: null but no error to indicate success
|
|
97
|
+
// The caller should check for the absence of error
|
|
98
|
+
return {
|
|
99
|
+
account: null,
|
|
100
|
+
// No error means success - email was sent
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
/**
|
|
104
|
+
* Handle the verify phase: verify token and return account.
|
|
105
|
+
*/
|
|
106
|
+
async handleVerify(callbacks, token) {
|
|
107
|
+
if (!token) {
|
|
108
|
+
return { account: null, error: 'Token is required' };
|
|
109
|
+
}
|
|
110
|
+
if (!callbacks.verifyMagicToken) {
|
|
111
|
+
return {
|
|
112
|
+
account: null,
|
|
113
|
+
error: 'verifyMagicToken callback is required for magic link strategy',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
// Verify the token (user's responsibility to check expiry and delete)
|
|
117
|
+
const result = await callbacks.verifyMagicToken(token);
|
|
118
|
+
if (!result) {
|
|
119
|
+
return { account: null, error: 'Invalid or expired token' };
|
|
120
|
+
}
|
|
121
|
+
// Find the account by email
|
|
122
|
+
const account = await callbacks.findAccount(result.email);
|
|
123
|
+
if (!account) {
|
|
124
|
+
return { account: null, error: 'Account not found' };
|
|
125
|
+
}
|
|
126
|
+
return { account, strategy: this.name };
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Build the full callback URL with the token.
|
|
130
|
+
*/
|
|
131
|
+
buildCallbackUrl(token) {
|
|
132
|
+
const base = this.baseUrl.endsWith('/') ? this.baseUrl.slice(0, -1) : this.baseUrl;
|
|
133
|
+
const path = this.callbackPath.startsWith('/') ? this.callbackPath : `/${this.callbackPath}`;
|
|
134
|
+
return `${base}${path}?token=${encodeURIComponent(token)}`;
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Alias strategies for the two magic link phases.
|
|
139
|
+
* These allow using `hs.authenticate('magic:send', email)` syntax.
|
|
140
|
+
*/
|
|
141
|
+
export class MagicLinkSendStrategy {
|
|
142
|
+
magicLinkStrategy;
|
|
143
|
+
name = 'magic:send';
|
|
144
|
+
constructor(magicLinkStrategy) {
|
|
145
|
+
this.magicLinkStrategy = magicLinkStrategy;
|
|
146
|
+
}
|
|
147
|
+
async authenticate(callbacks, ...args) {
|
|
148
|
+
return this.magicLinkStrategy.authenticate(callbacks, 'send', ...args);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
export class MagicLinkVerifyStrategy {
|
|
152
|
+
magicLinkStrategy;
|
|
153
|
+
name = 'magic:verify';
|
|
154
|
+
constructor(magicLinkStrategy) {
|
|
155
|
+
this.magicLinkStrategy = magicLinkStrategy;
|
|
156
|
+
}
|
|
157
|
+
async authenticate(callbacks, ...args) {
|
|
158
|
+
return this.magicLinkStrategy.authenticate(callbacks, 'verify', ...args);
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Helper function to register all magic link strategies at once.
|
|
163
|
+
*
|
|
164
|
+
* @example
|
|
165
|
+
* ```typescript
|
|
166
|
+
* import { Handshake, useMagicLink } from 'handshake-auth';
|
|
167
|
+
*
|
|
168
|
+
* const hs = new Handshake({...});
|
|
169
|
+
* useMagicLink(hs, {
|
|
170
|
+
* baseUrl: 'http://localhost:3000',
|
|
171
|
+
* sendMagicLink: async (email, token, url) => {...},
|
|
172
|
+
* });
|
|
173
|
+
*
|
|
174
|
+
* // Now you can use:
|
|
175
|
+
* await hs.authenticate('magic:send', email);
|
|
176
|
+
* await hs.authenticate('magic:verify', token);
|
|
177
|
+
* ```
|
|
178
|
+
*/
|
|
179
|
+
export function useMagicLink(hs, options) {
|
|
180
|
+
const strategy = new MagicLinkStrategy(options);
|
|
181
|
+
hs.use(strategy);
|
|
182
|
+
hs.use(new MagicLinkSendStrategy(strategy));
|
|
183
|
+
hs.use(new MagicLinkVerifyStrategy(strategy));
|
|
184
|
+
return strategy;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=magic-link.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"magic-link.js","sourceRoot":"","sources":["../../src/strategies/magic-link.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
import type { OAuthProfile } from '../types.js';
|
|
2
|
+
import { OAuthStrategy } from './oauth-base.js';
|
|
3
|
+
/**
|
|
4
|
+
* Microsoft user profile from the Microsoft Graph /me endpoint.
|
|
5
|
+
*/
|
|
6
|
+
export interface MicrosoftProfile {
|
|
7
|
+
id: string;
|
|
8
|
+
displayName?: string;
|
|
9
|
+
givenName?: string;
|
|
10
|
+
surname?: string;
|
|
11
|
+
mail?: string;
|
|
12
|
+
userPrincipalName?: string;
|
|
13
|
+
jobTitle?: string;
|
|
14
|
+
officeLocation?: string;
|
|
15
|
+
preferredLanguage?: string;
|
|
16
|
+
businessPhones?: string[];
|
|
17
|
+
mobilePhone?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Microsoft tenant options.
|
|
21
|
+
* - 'common': Any Microsoft account (personal or work/school)
|
|
22
|
+
* - 'organizations': Work/school accounts only
|
|
23
|
+
* - 'consumers': Personal Microsoft accounts only
|
|
24
|
+
* - Specific tenant ID: Accounts from that tenant only
|
|
25
|
+
*/
|
|
26
|
+
export type MicrosoftTenant = 'common' | 'organizations' | 'consumers' | string;
|
|
27
|
+
/**
|
|
28
|
+
* Configuration options for Microsoft OAuth strategy.
|
|
29
|
+
*/
|
|
30
|
+
export interface MicrosoftStrategyOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Microsoft OAuth client ID (Application ID)
|
|
33
|
+
*/
|
|
34
|
+
clientId: string;
|
|
35
|
+
/**
|
|
36
|
+
* Microsoft OAuth client secret
|
|
37
|
+
*/
|
|
38
|
+
clientSecret: string;
|
|
39
|
+
/**
|
|
40
|
+
* Your callback URL (must match Azure Portal configuration)
|
|
41
|
+
*/
|
|
42
|
+
redirectUri: string;
|
|
43
|
+
/**
|
|
44
|
+
* Azure AD tenant
|
|
45
|
+
* - 'common': Any Microsoft account (default)
|
|
46
|
+
* - 'organizations': Work/school accounts only
|
|
47
|
+
* - 'consumers': Personal Microsoft accounts only
|
|
48
|
+
* - Specific tenant ID: Accounts from that tenant only
|
|
49
|
+
* @default 'common'
|
|
50
|
+
*/
|
|
51
|
+
tenant?: MicrosoftTenant;
|
|
52
|
+
/**
|
|
53
|
+
* OAuth scopes to request
|
|
54
|
+
* @default ['openid', 'email', 'profile', 'User.Read']
|
|
55
|
+
*/
|
|
56
|
+
scopes?: string[];
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Microsoft OAuth2 authentication strategy using Azure AD / Microsoft Identity Platform.
|
|
60
|
+
*
|
|
61
|
+
* Supports multiple tenant configurations:
|
|
62
|
+
* - `common`: Accept any Microsoft account
|
|
63
|
+
* - `organizations`: Only work/school accounts
|
|
64
|
+
* - `consumers`: Only personal Microsoft accounts
|
|
65
|
+
* - Specific tenant ID: Only accounts from that organization
|
|
66
|
+
*
|
|
67
|
+
* @example
|
|
68
|
+
* ```typescript
|
|
69
|
+
* const hs = new Handshake({
|
|
70
|
+
* findAccount: async (email) => db.accounts.findByEmail(email),
|
|
71
|
+
* findOrCreateFromOAuth: async (provider, profile) => {
|
|
72
|
+
* let account = await db.accounts.findByProviderId(provider, profile.id);
|
|
73
|
+
* if (!account) {
|
|
74
|
+
* account = await db.accounts.create({
|
|
75
|
+
* email: profile.email,
|
|
76
|
+
* name: profile.name,
|
|
77
|
+
* providerId: profile.id,
|
|
78
|
+
* provider,
|
|
79
|
+
* });
|
|
80
|
+
* }
|
|
81
|
+
* return account;
|
|
82
|
+
* },
|
|
83
|
+
* });
|
|
84
|
+
*
|
|
85
|
+
* // Accept any Microsoft account
|
|
86
|
+
* hs.use(new MicrosoftStrategy({
|
|
87
|
+
* clientId: process.env.MICROSOFT_CLIENT_ID!,
|
|
88
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
|
|
89
|
+
* redirectUri: 'http://localhost:3000/auth/microsoft/callback',
|
|
90
|
+
* tenant: 'common',
|
|
91
|
+
* }));
|
|
92
|
+
*
|
|
93
|
+
* // Or restrict to a specific organization
|
|
94
|
+
* hs.use(new MicrosoftStrategy({
|
|
95
|
+
* clientId: process.env.MICROSOFT_CLIENT_ID!,
|
|
96
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
|
|
97
|
+
* redirectUri: 'http://localhost:3000/auth/microsoft/callback',
|
|
98
|
+
* tenant: 'your-tenant-id',
|
|
99
|
+
* }));
|
|
100
|
+
*
|
|
101
|
+
* // Routes
|
|
102
|
+
* app.get('/auth/microsoft', async (req, res) => {
|
|
103
|
+
* const result = await hs.authenticate('microsoft', req, res, 'redirect');
|
|
104
|
+
* if ('redirectUrl' in result) {
|
|
105
|
+
* res.redirect(result.redirectUrl);
|
|
106
|
+
* }
|
|
107
|
+
* });
|
|
108
|
+
*
|
|
109
|
+
* app.get('/auth/microsoft/callback', async (req, res) => {
|
|
110
|
+
* const result = await hs.authenticate('microsoft', req, res, 'callback');
|
|
111
|
+
* if (result.account) {
|
|
112
|
+
* login(req, result.account);
|
|
113
|
+
* res.redirect('/dashboard');
|
|
114
|
+
* } else {
|
|
115
|
+
* res.status(401).send(result.error);
|
|
116
|
+
* }
|
|
117
|
+
* });
|
|
118
|
+
* ```
|
|
119
|
+
*/
|
|
120
|
+
export declare class MicrosoftStrategy<TAccount> extends OAuthStrategy<TAccount> {
|
|
121
|
+
constructor(options: MicrosoftStrategyOptions);
|
|
122
|
+
/**
|
|
123
|
+
* Map Microsoft profile to standard OAuthProfile.
|
|
124
|
+
*/
|
|
125
|
+
protected mapProfile(data: unknown, accessToken: string): OAuthProfile;
|
|
126
|
+
}
|
|
127
|
+
//# sourceMappingURL=microsoft.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microsoft.d.ts","sourceRoot":"","sources":["../../src/strategies/microsoft.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,gBAAgB;IAC/B,EAAE,EAAE,MAAM,CAAC;IACX,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,EAAE,CAAC;IAC1B,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED;;;;;;GAMG;AACH,MAAM,MAAM,eAAe,GAAG,QAAQ,GAAG,eAAe,GAAG,WAAW,GAAG,MAAM,CAAC;AAEhF;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC;;OAEG;IACH,QAAQ,EAAE,MAAM,CAAC;IAEjB;;OAEG;IACH,YAAY,EAAE,MAAM,CAAC;IAErB;;OAEG;IACH,WAAW,EAAE,MAAM,CAAC;IAEpB;;;;;;;OAOG;IACH,MAAM,CAAC,EAAE,eAAe,CAAC;IAEzB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,qBAAa,iBAAiB,CAAC,QAAQ,CAAE,SAAQ,aAAa,CAAC,QAAQ,CAAC;gBAC1D,OAAO,EAAE,wBAAwB;IAgB7C;;OAEG;IAEH,SAAS,CAAC,UAAU,CAAC,IAAI,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,YAAY;CAgBvE"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { OAuthStrategy } from './oauth-base.js';
|
|
2
|
+
/**
|
|
3
|
+
* Microsoft OAuth2 authentication strategy using Azure AD / Microsoft Identity Platform.
|
|
4
|
+
*
|
|
5
|
+
* Supports multiple tenant configurations:
|
|
6
|
+
* - `common`: Accept any Microsoft account
|
|
7
|
+
* - `organizations`: Only work/school accounts
|
|
8
|
+
* - `consumers`: Only personal Microsoft accounts
|
|
9
|
+
* - Specific tenant ID: Only accounts from that organization
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```typescript
|
|
13
|
+
* const hs = new Handshake({
|
|
14
|
+
* findAccount: async (email) => db.accounts.findByEmail(email),
|
|
15
|
+
* findOrCreateFromOAuth: async (provider, profile) => {
|
|
16
|
+
* let account = await db.accounts.findByProviderId(provider, profile.id);
|
|
17
|
+
* if (!account) {
|
|
18
|
+
* account = await db.accounts.create({
|
|
19
|
+
* email: profile.email,
|
|
20
|
+
* name: profile.name,
|
|
21
|
+
* providerId: profile.id,
|
|
22
|
+
* provider,
|
|
23
|
+
* });
|
|
24
|
+
* }
|
|
25
|
+
* return account;
|
|
26
|
+
* },
|
|
27
|
+
* });
|
|
28
|
+
*
|
|
29
|
+
* // Accept any Microsoft account
|
|
30
|
+
* hs.use(new MicrosoftStrategy({
|
|
31
|
+
* clientId: process.env.MICROSOFT_CLIENT_ID!,
|
|
32
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
|
|
33
|
+
* redirectUri: 'http://localhost:3000/auth/microsoft/callback',
|
|
34
|
+
* tenant: 'common',
|
|
35
|
+
* }));
|
|
36
|
+
*
|
|
37
|
+
* // Or restrict to a specific organization
|
|
38
|
+
* hs.use(new MicrosoftStrategy({
|
|
39
|
+
* clientId: process.env.MICROSOFT_CLIENT_ID!,
|
|
40
|
+
* clientSecret: process.env.MICROSOFT_CLIENT_SECRET!,
|
|
41
|
+
* redirectUri: 'http://localhost:3000/auth/microsoft/callback',
|
|
42
|
+
* tenant: 'your-tenant-id',
|
|
43
|
+
* }));
|
|
44
|
+
*
|
|
45
|
+
* // Routes
|
|
46
|
+
* app.get('/auth/microsoft', async (req, res) => {
|
|
47
|
+
* const result = await hs.authenticate('microsoft', req, res, 'redirect');
|
|
48
|
+
* if ('redirectUrl' in result) {
|
|
49
|
+
* res.redirect(result.redirectUrl);
|
|
50
|
+
* }
|
|
51
|
+
* });
|
|
52
|
+
*
|
|
53
|
+
* app.get('/auth/microsoft/callback', async (req, res) => {
|
|
54
|
+
* const result = await hs.authenticate('microsoft', req, res, 'callback');
|
|
55
|
+
* if (result.account) {
|
|
56
|
+
* login(req, result.account);
|
|
57
|
+
* res.redirect('/dashboard');
|
|
58
|
+
* } else {
|
|
59
|
+
* res.status(401).send(result.error);
|
|
60
|
+
* }
|
|
61
|
+
* });
|
|
62
|
+
* ```
|
|
63
|
+
*/
|
|
64
|
+
export class MicrosoftStrategy extends OAuthStrategy {
|
|
65
|
+
constructor(options) {
|
|
66
|
+
const tenant = options.tenant ?? 'common';
|
|
67
|
+
super({
|
|
68
|
+
name: 'microsoft',
|
|
69
|
+
clientId: options.clientId,
|
|
70
|
+
clientSecret: options.clientSecret,
|
|
71
|
+
redirectUri: options.redirectUri,
|
|
72
|
+
authorizeUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/authorize`,
|
|
73
|
+
tokenUrl: `https://login.microsoftonline.com/${tenant}/oauth2/v2.0/token`,
|
|
74
|
+
userInfoUrl: 'https://graph.microsoft.com/v1.0/me',
|
|
75
|
+
scopes: options.scopes ?? ['openid', 'email', 'profile', 'User.Read'],
|
|
76
|
+
stateCookieName: 'microsoft_oauth_state',
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Map Microsoft profile to standard OAuthProfile.
|
|
81
|
+
*/
|
|
82
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
|
83
|
+
mapProfile(data, accessToken) {
|
|
84
|
+
const profile = data;
|
|
85
|
+
// Microsoft may provide email in 'mail' or 'userPrincipalName'
|
|
86
|
+
// userPrincipalName is often an email for work/school accounts
|
|
87
|
+
const email = profile.mail ??
|
|
88
|
+
(profile.userPrincipalName?.includes('@') ? profile.userPrincipalName : null);
|
|
89
|
+
return {
|
|
90
|
+
id: profile.id,
|
|
91
|
+
email: email ?? null,
|
|
92
|
+
name: profile.displayName ?? null,
|
|
93
|
+
picture: null, // Microsoft Graph requires separate API call for photo
|
|
94
|
+
raw: profile,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=microsoft.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"microsoft.js","sourceRoot":"","sources":["../../src/strategies/microsoft.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAgEhD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6DG;AACH,MAAM,OAAO,iBAA4B,SAAQ,aAAuB;IACtE,YAAY,OAAiC;QAC3C,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,QAAQ,CAAC;QAE1C,KAAK,CAAC;YACJ,IAAI,EAAE,WAAW;YACjB,QAAQ,EAAE,OAAO,CAAC,QAAQ;YAC1B,YAAY,EAAE,OAAO,CAAC,YAAY;YAClC,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,YAAY,EAAE,qCAAqC,MAAM,wBAAwB;YACjF,QAAQ,EAAE,qCAAqC,MAAM,oBAAoB;YACzE,WAAW,EAAE,qCAAqC;YAClD,MAAM,EAAE,OAAO,CAAC,MAAM,IAAI,CAAC,QAAQ,EAAE,OAAO,EAAE,SAAS,EAAE,WAAW,CAAC;YACrE,eAAe,EAAE,uBAAuB;SACzC,CAAC,CAAC;IACL,CAAC;IAED;;OAEG;IACH,6DAA6D;IACnD,UAAU,CAAC,IAAa,EAAE,WAAmB;QACrD,MAAM,OAAO,GAAG,IAAwB,CAAC;QAEzC,+DAA+D;QAC/D,+DAA+D;QAC/D,MAAM,KAAK,GAAG,OAAO,CAAC,IAAI;YACxB,CAAC,OAAO,CAAC,iBAAiB,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,iBAAiB,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC;QAEhF,OAAO;YACL,EAAE,EAAE,OAAO,CAAC,EAAE;YACd,KAAK,EAAE,KAAK,IAAI,IAAI;YACpB,IAAI,EAAE,OAAO,CAAC,WAAW,IAAI,IAAI;YACjC,OAAO,EAAE,IAAI,EAAE,uDAAuD;YACtE,GAAG,EAAE,OAA6C;SACnD,CAAC;IACJ,CAAC;CACF"}
|