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,162 @@
1
+ import type { Request, Response } from 'express';
2
+ import type { AuthResult, Strategy, HandshakeCallbacks, OAuthProfile } from '../types.js';
3
+ /**
4
+ * Configuration for OAuth providers.
5
+ */
6
+ export interface OAuthConfig {
7
+ /**
8
+ * Strategy name (e.g., 'google', 'github', 'discord')
9
+ */
10
+ name: string;
11
+ /**
12
+ * OAuth client ID from the provider
13
+ */
14
+ clientId: string;
15
+ /**
16
+ * OAuth client secret from the provider
17
+ */
18
+ clientSecret: string;
19
+ /**
20
+ * URL for the provider's authorization endpoint
21
+ */
22
+ authorizeUrl: string;
23
+ /**
24
+ * URL for the provider's token endpoint
25
+ */
26
+ tokenUrl: string;
27
+ /**
28
+ * URL for fetching user profile information
29
+ */
30
+ userInfoUrl: string;
31
+ /**
32
+ * OAuth scopes to request
33
+ */
34
+ scopes: string[];
35
+ /**
36
+ * Your callback URL that the provider will redirect to
37
+ */
38
+ redirectUri: string;
39
+ /**
40
+ * Cookie name for storing OAuth state (CSRF protection)
41
+ * @default 'oauth_state'
42
+ */
43
+ stateCookieName?: string;
44
+ /**
45
+ * Additional authorization parameters to send
46
+ */
47
+ authorizationParams?: Record<string, string>;
48
+ }
49
+ /**
50
+ * Result from the redirect phase of OAuth authentication.
51
+ */
52
+ export interface OAuthRedirectResult {
53
+ redirectUrl: string;
54
+ }
55
+ /**
56
+ * Token response from OAuth provider.
57
+ */
58
+ export interface OAuthTokenResponse {
59
+ access_token: string;
60
+ token_type: string;
61
+ expires_in?: number;
62
+ refresh_token?: string;
63
+ scope?: string;
64
+ }
65
+ /**
66
+ * Combined result type for OAuth authentication.
67
+ * Can be either a redirect URL (for initiation) or an auth result (for callback).
68
+ */
69
+ export type OAuthAuthResult<TAccount> = AuthResult<TAccount> | OAuthRedirectResult;
70
+ /**
71
+ * Base class for OAuth2 authentication strategies.
72
+ *
73
+ * This class implements the standard OAuth2 authorization code flow:
74
+ * 1. **Redirect phase**: Generate state, build authorization URL, return redirect
75
+ * 2. **Callback phase**: Verify state, exchange code for token, fetch profile
76
+ *
77
+ * Subclasses should extend this and implement `mapProfile` to transform
78
+ * the provider's profile response into a standard OAuthProfile.
79
+ *
80
+ * @example
81
+ * ```typescript
82
+ * class GoogleStrategy<TAccount> extends OAuthStrategy<TAccount> {
83
+ * constructor(options: GoogleOptions) {
84
+ * super({
85
+ * name: 'google',
86
+ * authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
87
+ * tokenUrl: 'https://oauth2.googleapis.com/token',
88
+ * userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',
89
+ * scopes: ['openid', 'email', 'profile'],
90
+ * ...options,
91
+ * });
92
+ * }
93
+ *
94
+ * protected mapProfile(data: unknown): OAuthProfile {
95
+ * const profile = data as GoogleProfile;
96
+ * return {
97
+ * id: profile.sub,
98
+ * email: profile.email ?? null,
99
+ * name: profile.name ?? null,
100
+ * picture: profile.picture ?? null,
101
+ * raw: profile,
102
+ * };
103
+ * }
104
+ * }
105
+ * ```
106
+ */
107
+ export declare abstract class OAuthStrategy<TAccount> implements Strategy<TAccount> {
108
+ readonly name: string;
109
+ protected readonly clientId: string;
110
+ protected readonly clientSecret: string;
111
+ protected readonly authorizeUrl: string;
112
+ protected readonly tokenUrl: string;
113
+ protected readonly userInfoUrl: string;
114
+ protected readonly scopes: string[];
115
+ protected readonly redirectUri: string;
116
+ protected readonly stateCookieName: string;
117
+ protected readonly authorizationParams: Record<string, string>;
118
+ constructor(config: OAuthConfig);
119
+ /**
120
+ * Authenticate using OAuth.
121
+ *
122
+ * @param callbacks - Handshake callbacks (findOrCreateFromOAuth is used)
123
+ * @param args - [req, res, phase] where phase is 'redirect' or 'callback'
124
+ * @returns Either a redirect URL or an auth result
125
+ */
126
+ authenticate(callbacks: HandshakeCallbacks<TAccount>, ...args: unknown[]): Promise<AuthResult<TAccount>>;
127
+ /**
128
+ * Handle the redirect phase: generate state and return authorization URL.
129
+ */
130
+ protected handleRedirect(_req: Request, res: Response): AuthResult<TAccount> & OAuthRedirectResult;
131
+ /**
132
+ * Handle the callback phase: verify state, exchange code, fetch profile.
133
+ */
134
+ protected handleCallback(callbacks: HandshakeCallbacks<TAccount>, req: Request, res: Response): Promise<AuthResult<TAccount>>;
135
+ /**
136
+ * Exchange authorization code for access token.
137
+ */
138
+ protected exchangeCodeForToken(code: string): Promise<OAuthTokenResponse | {
139
+ error: string;
140
+ }>;
141
+ /**
142
+ * Fetch user profile from the provider.
143
+ * Subclasses can override this for providers with non-standard profile endpoints.
144
+ */
145
+ protected fetchUserProfile(accessToken: string): Promise<OAuthProfile | {
146
+ error: string;
147
+ }>;
148
+ /**
149
+ * Map the provider's profile response to a standard OAuthProfile.
150
+ * Subclasses must implement this to handle provider-specific profile formats.
151
+ *
152
+ * @param data - Raw profile data from the provider
153
+ * @param accessToken - Access token (some providers need this for additional API calls)
154
+ * @returns Standardized OAuthProfile
155
+ */
156
+ protected abstract mapProfile(data: unknown, accessToken: string): OAuthProfile | Promise<OAuthProfile>;
157
+ }
158
+ /**
159
+ * Type guard to check if a result is an OAuth redirect result.
160
+ */
161
+ export declare function isOAuthRedirect<TAccount>(result: AuthResult<TAccount> | OAuthRedirectResult): result is OAuthRedirectResult & AuthResult<TAccount>;
162
+ //# sourceMappingURL=oauth-base.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-base.d.ts","sourceRoot":"","sources":["../../src/strategies/oauth-base.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,243 @@
1
+ import { randomBytes } from 'crypto';
2
+ /**
3
+ * Base class for OAuth2 authentication strategies.
4
+ *
5
+ * This class implements the standard OAuth2 authorization code flow:
6
+ * 1. **Redirect phase**: Generate state, build authorization URL, return redirect
7
+ * 2. **Callback phase**: Verify state, exchange code for token, fetch profile
8
+ *
9
+ * Subclasses should extend this and implement `mapProfile` to transform
10
+ * the provider's profile response into a standard OAuthProfile.
11
+ *
12
+ * @example
13
+ * ```typescript
14
+ * class GoogleStrategy<TAccount> extends OAuthStrategy<TAccount> {
15
+ * constructor(options: GoogleOptions) {
16
+ * super({
17
+ * name: 'google',
18
+ * authorizeUrl: 'https://accounts.google.com/o/oauth2/v2/auth',
19
+ * tokenUrl: 'https://oauth2.googleapis.com/token',
20
+ * userInfoUrl: 'https://openidconnect.googleapis.com/v1/userinfo',
21
+ * scopes: ['openid', 'email', 'profile'],
22
+ * ...options,
23
+ * });
24
+ * }
25
+ *
26
+ * protected mapProfile(data: unknown): OAuthProfile {
27
+ * const profile = data as GoogleProfile;
28
+ * return {
29
+ * id: profile.sub,
30
+ * email: profile.email ?? null,
31
+ * name: profile.name ?? null,
32
+ * picture: profile.picture ?? null,
33
+ * raw: profile,
34
+ * };
35
+ * }
36
+ * }
37
+ * ```
38
+ */
39
+ export class OAuthStrategy {
40
+ name;
41
+ clientId;
42
+ clientSecret;
43
+ authorizeUrl;
44
+ tokenUrl;
45
+ userInfoUrl;
46
+ scopes;
47
+ redirectUri;
48
+ stateCookieName;
49
+ authorizationParams;
50
+ constructor(config) {
51
+ this.name = config.name;
52
+ this.clientId = config.clientId;
53
+ this.clientSecret = config.clientSecret;
54
+ this.authorizeUrl = config.authorizeUrl;
55
+ this.tokenUrl = config.tokenUrl;
56
+ this.userInfoUrl = config.userInfoUrl;
57
+ this.scopes = config.scopes;
58
+ this.redirectUri = config.redirectUri;
59
+ this.stateCookieName = config.stateCookieName ?? 'oauth_state';
60
+ this.authorizationParams = config.authorizationParams ?? {};
61
+ }
62
+ /**
63
+ * Authenticate using OAuth.
64
+ *
65
+ * @param callbacks - Handshake callbacks (findOrCreateFromOAuth is used)
66
+ * @param args - [req, res, phase] where phase is 'redirect' or 'callback'
67
+ * @returns Either a redirect URL or an auth result
68
+ */
69
+ async authenticate(callbacks, ...args) {
70
+ const [req, res, phase] = args;
71
+ if (phase === 'redirect') {
72
+ return this.handleRedirect(req, res);
73
+ }
74
+ else if (phase === 'callback') {
75
+ return this.handleCallback(callbacks, req, res);
76
+ }
77
+ else {
78
+ return {
79
+ account: null,
80
+ error: `Invalid OAuth phase: ${phase}. Use 'redirect' or 'callback'`,
81
+ };
82
+ }
83
+ }
84
+ /**
85
+ * Handle the redirect phase: generate state and return authorization URL.
86
+ */
87
+ handleRedirect(_req, res) {
88
+ // Generate random state for CSRF protection
89
+ const state = randomBytes(32).toString('hex');
90
+ // Store state in a cookie (will be verified on callback)
91
+ // Using httpOnly and sameSite for security
92
+ res.cookie(this.stateCookieName, state, {
93
+ httpOnly: true,
94
+ secure: process.env.NODE_ENV === 'production',
95
+ sameSite: 'lax',
96
+ maxAge: 10 * 60 * 1000, // 10 minutes
97
+ });
98
+ // Build authorization URL
99
+ const params = new URLSearchParams({
100
+ client_id: this.clientId,
101
+ redirect_uri: this.redirectUri,
102
+ response_type: 'code',
103
+ scope: this.scopes.join(' '),
104
+ state,
105
+ ...this.authorizationParams,
106
+ });
107
+ const redirectUrl = `${this.authorizeUrl}?${params.toString()}`;
108
+ // Return special result with redirect URL
109
+ // The caller should check for redirectUrl and redirect
110
+ return {
111
+ account: null,
112
+ redirectUrl,
113
+ };
114
+ }
115
+ /**
116
+ * Handle the callback phase: verify state, exchange code, fetch profile.
117
+ */
118
+ async handleCallback(callbacks, req, res) {
119
+ // Get state and code from query params
120
+ const { state: returnedState, code, error, error_description } = req.query;
121
+ // Check for OAuth error response
122
+ if (error) {
123
+ return {
124
+ account: null,
125
+ error: error_description || error,
126
+ };
127
+ }
128
+ // Verify state matches what we stored in the cookie
129
+ const storedState = req.cookies?.[this.stateCookieName];
130
+ // Clear the state cookie
131
+ res.clearCookie(this.stateCookieName);
132
+ if (!storedState || storedState !== returnedState) {
133
+ return {
134
+ account: null,
135
+ error: 'Invalid OAuth state. Possible CSRF attack or expired session.',
136
+ };
137
+ }
138
+ if (!code) {
139
+ return {
140
+ account: null,
141
+ error: 'No authorization code received',
142
+ };
143
+ }
144
+ // Exchange code for token
145
+ const tokenResult = await this.exchangeCodeForToken(code);
146
+ if ('error' in tokenResult) {
147
+ return {
148
+ account: null,
149
+ error: tokenResult.error,
150
+ };
151
+ }
152
+ // Fetch user profile
153
+ const profileResult = await this.fetchUserProfile(tokenResult.access_token);
154
+ if ('error' in profileResult) {
155
+ return {
156
+ account: null,
157
+ error: profileResult.error,
158
+ };
159
+ }
160
+ // Check for required callback
161
+ if (!callbacks.findOrCreateFromOAuth) {
162
+ return {
163
+ account: null,
164
+ error: 'findOrCreateFromOAuth callback is required for OAuth strategies',
165
+ };
166
+ }
167
+ // Let the application handle account creation/lookup
168
+ const account = await callbacks.findOrCreateFromOAuth(this.name, profileResult);
169
+ return {
170
+ account,
171
+ strategy: this.name,
172
+ };
173
+ }
174
+ /**
175
+ * Exchange authorization code for access token.
176
+ */
177
+ async exchangeCodeForToken(code) {
178
+ try {
179
+ const params = new URLSearchParams({
180
+ client_id: this.clientId,
181
+ client_secret: this.clientSecret,
182
+ code,
183
+ grant_type: 'authorization_code',
184
+ redirect_uri: this.redirectUri,
185
+ });
186
+ const response = await fetch(this.tokenUrl, {
187
+ method: 'POST',
188
+ headers: {
189
+ 'Content-Type': 'application/x-www-form-urlencoded',
190
+ Accept: 'application/json',
191
+ },
192
+ body: params.toString(),
193
+ });
194
+ if (!response.ok) {
195
+ const errorData = await response.json().catch(() => ({}));
196
+ return {
197
+ error: errorData.error_description ||
198
+ errorData.error ||
199
+ `Token exchange failed: ${response.status}`,
200
+ };
201
+ }
202
+ return (await response.json());
203
+ }
204
+ catch (err) {
205
+ return {
206
+ error: `Token exchange failed: ${err instanceof Error ? err.message : 'Unknown error'}`,
207
+ };
208
+ }
209
+ }
210
+ /**
211
+ * Fetch user profile from the provider.
212
+ * Subclasses can override this for providers with non-standard profile endpoints.
213
+ */
214
+ async fetchUserProfile(accessToken) {
215
+ try {
216
+ const response = await fetch(this.userInfoUrl, {
217
+ headers: {
218
+ Authorization: `Bearer ${accessToken}`,
219
+ Accept: 'application/json',
220
+ },
221
+ });
222
+ if (!response.ok) {
223
+ return {
224
+ error: `Failed to fetch user profile: ${response.status}`,
225
+ };
226
+ }
227
+ const data = await response.json();
228
+ return this.mapProfile(data, accessToken);
229
+ }
230
+ catch (err) {
231
+ return {
232
+ error: `Failed to fetch user profile: ${err instanceof Error ? err.message : 'Unknown error'}`,
233
+ };
234
+ }
235
+ }
236
+ }
237
+ /**
238
+ * Type guard to check if a result is an OAuth redirect result.
239
+ */
240
+ export function isOAuthRedirect(result) {
241
+ return 'redirectUrl' in result;
242
+ }
243
+ //# sourceMappingURL=oauth-base.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth-base.js","sourceRoot":"","sources":["../../src/strategies/oauth-base.ts"],"names":[],"mappings":"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"}
@@ -1,17 +1,80 @@
1
1
  import type { AuthResult, Strategy, HandshakeCallbacks } from '../types.js';
2
2
  /**
3
- * Password authentication strategy.
3
+ * Options for the simple password strategy.
4
+ */
5
+ export interface PasswordStrategyOptions {
6
+ /**
7
+ * Callback to verify the password.
8
+ * You handle how the password is verified (e.g., compare to env var, bcrypt hash).
9
+ *
10
+ * @param password - The password to verify
11
+ * @returns true if valid, false otherwise
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // Simple env var comparison
16
+ * verifyPassword: async (password) => password === process.env.APP_PASSWORD
17
+ *
18
+ * // Bcrypt comparison against hashed env var
19
+ * verifyPassword: async (password) => bcrypt.compare(password, process.env.APP_PASSWORD_HASH)
20
+ * ```
21
+ */
22
+ verifyPassword: (password: string) => Promise<boolean> | boolean;
23
+ }
24
+ /**
25
+ * Simple password-only authentication strategy.
26
+ *
27
+ * Authenticates with just a password (no username/email). Useful for:
28
+ * - Self-hosted apps with a single admin password
29
+ * - Simple internal tools not exposed to the internet
30
+ * - Quick prototyping
31
+ *
32
+ * The strategy doesn't use findAccount since there's no user lookup -
33
+ * it just verifies the password. On success, it returns a minimal account
34
+ * object that you provide.
35
+ *
36
+ * @example
37
+ * ```typescript
38
+ * // Simple password check against env var
39
+ * hs.use(new PasswordStrategy({
40
+ * verifyPassword: async (password) => password === process.env.ADMIN_PASSWORD,
41
+ * }));
42
+ *
43
+ * // With bcrypt
44
+ * hs.use(new PasswordStrategy({
45
+ * verifyPassword: async (password) => {
46
+ * return bcrypt.compare(password, process.env.ADMIN_PASSWORD_HASH!);
47
+ * },
48
+ * }));
49
+ *
50
+ * // In your login route
51
+ * app.post('/login', async (req, res) => {
52
+ * const result = await hs.authenticate('password', req.body.password);
53
+ * if (result.account) {
54
+ * login(req, result.account);
55
+ * res.redirect('/dashboard');
56
+ * } else {
57
+ * res.status(401).send('Invalid password');
58
+ * }
59
+ * });
60
+ * ```
4
61
  *
5
- * Authenticates users with an identifier (e.g., email) and password.
6
- * Uses the findAccount and verifyPassword callbacks from Handshake.
62
+ * @example HTML form
63
+ * ```html
64
+ * <form method="POST" action="/login">
65
+ * <input name="password" type="password" placeholder="Password" required />
66
+ * <button type="submit">Login</button>
67
+ * </form>
68
+ * ```
7
69
  */
8
70
  export declare class PasswordStrategy<TAccount> implements Strategy<TAccount> {
9
71
  readonly name = "password";
72
+ private readonly verifyPassword;
73
+ constructor(options: PasswordStrategyOptions);
10
74
  /**
11
- * Authenticate with identifier and password.
75
+ * Authenticate with password only.
12
76
  *
13
- * @param callbacks - Handshake callbacks (findAccount, verifyPassword)
14
- * @param identifier - The account identifier (e.g., email)
77
+ * @param callbacks - Handshake callbacks (not used by this strategy)
15
78
  * @param password - The password to verify
16
79
  * @returns Authentication result with account or error
17
80
  */
@@ -1 +1 @@
1
- {"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/strategies/password.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE5E;;;;;GAKG;AACH,qBAAa,gBAAgB,CAAC,QAAQ,CAAE,YAAW,QAAQ,CAAC,QAAQ,CAAC;IACnE,QAAQ,CAAC,IAAI,cAAc;IAE3B;;;;;;;OAOG;IACG,YAAY,CAChB,SAAS,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACvC,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;CAgCjC"}
1
+ {"version":3,"file":"password.d.ts","sourceRoot":"","sources":["../../src/strategies/password.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC;AAE5E;;GAEG;AACH,MAAM,WAAW,uBAAuB;IACtC;;;;;;;;;;;;;;;OAeG;IACH,cAAc,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC;CAClE;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,qBAAa,gBAAgB,CAAC,QAAQ,CAAE,YAAW,QAAQ,CAAC,QAAQ,CAAC;IACnE,QAAQ,CAAC,IAAI,cAAc;IAE3B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAA4C;gBAE/D,OAAO,EAAE,uBAAuB;IAI5C;;;;;;OAMG;IACG,YAAY,CAChB,SAAS,EAAE,kBAAkB,CAAC,QAAQ,CAAC,EACvC,GAAG,IAAI,EAAE,OAAO,EAAE,GACjB,OAAO,CAAC,UAAU,CAAC,QAAQ,CAAC,CAAC;CAsCjC"}
@@ -1,41 +1,90 @@
1
1
  /**
2
- * Password authentication strategy.
2
+ * Simple password-only authentication strategy.
3
3
  *
4
- * Authenticates users with an identifier (e.g., email) and password.
5
- * Uses the findAccount and verifyPassword callbacks from Handshake.
4
+ * Authenticates with just a password (no username/email). Useful for:
5
+ * - Self-hosted apps with a single admin password
6
+ * - Simple internal tools not exposed to the internet
7
+ * - Quick prototyping
8
+ *
9
+ * The strategy doesn't use findAccount since there's no user lookup -
10
+ * it just verifies the password. On success, it returns a minimal account
11
+ * object that you provide.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * // Simple password check against env var
16
+ * hs.use(new PasswordStrategy({
17
+ * verifyPassword: async (password) => password === process.env.ADMIN_PASSWORD,
18
+ * }));
19
+ *
20
+ * // With bcrypt
21
+ * hs.use(new PasswordStrategy({
22
+ * verifyPassword: async (password) => {
23
+ * return bcrypt.compare(password, process.env.ADMIN_PASSWORD_HASH!);
24
+ * },
25
+ * }));
26
+ *
27
+ * // In your login route
28
+ * app.post('/login', async (req, res) => {
29
+ * const result = await hs.authenticate('password', req.body.password);
30
+ * if (result.account) {
31
+ * login(req, result.account);
32
+ * res.redirect('/dashboard');
33
+ * } else {
34
+ * res.status(401).send('Invalid password');
35
+ * }
36
+ * });
37
+ * ```
38
+ *
39
+ * @example HTML form
40
+ * ```html
41
+ * <form method="POST" action="/login">
42
+ * <input name="password" type="password" placeholder="Password" required />
43
+ * <button type="submit">Login</button>
44
+ * </form>
45
+ * ```
6
46
  */
7
47
  export class PasswordStrategy {
8
48
  name = 'password';
49
+ verifyPassword;
50
+ constructor(options) {
51
+ this.verifyPassword = options.verifyPassword;
52
+ }
9
53
  /**
10
- * Authenticate with identifier and password.
54
+ * Authenticate with password only.
11
55
  *
12
- * @param callbacks - Handshake callbacks (findAccount, verifyPassword)
13
- * @param identifier - The account identifier (e.g., email)
56
+ * @param callbacks - Handshake callbacks (not used by this strategy)
14
57
  * @param password - The password to verify
15
58
  * @returns Authentication result with account or error
16
59
  */
17
60
  async authenticate(callbacks, ...args) {
18
- const [identifier, password] = args;
19
- if (!identifier || !password) {
20
- return { account: null, error: 'Identifier and password are required' };
61
+ const [password] = args;
62
+ if (!password) {
63
+ return { account: null, error: 'Password is required' };
21
64
  }
22
- if (!callbacks.verifyPassword) {
23
- return { account: null, error: 'verifyPassword callback is required for password strategy' };
65
+ // Verify the password using the provided callback
66
+ const isValid = await this.verifyPassword(password);
67
+ if (!isValid) {
68
+ return { account: null, error: 'Invalid password' };
24
69
  }
25
- // Find the account
26
- const account = await callbacks.findAccount(identifier);
27
- // Always call verifyPassword even if account is null to prevent timing attacks.
28
- // The verifyPassword callback should handle null accounts appropriately
29
- // (e.g., by doing a dummy comparison that takes the same time).
30
- // However, since the callback signature requires an account, we'll check first
31
- // and rely on the overall operation timing being similar.
32
- if (!account) {
33
- return { account: null, error: 'Invalid credentials' };
70
+ // For password-only auth, we don't have a real account from the database.
71
+ // The user needs to provide a findAccount callback or we return a placeholder.
72
+ // Let's check if findAccount can find an account with a special identifier.
73
+ // Or we can just return a generic "authenticated" account.
74
+ // Try to find a default account (e.g., "admin" or the first account)
75
+ // If no findAccount provided or it returns null, create a minimal account marker
76
+ let account = null;
77
+ try {
78
+ // Try to get the default/admin account
79
+ account = await callbacks.findAccount('admin');
34
80
  }
35
- // Verify the password
36
- const isValid = await callbacks.verifyPassword(account, password);
37
- if (!isValid) {
38
- return { account: null, error: 'Invalid credentials' };
81
+ catch {
82
+ // findAccount might not be suitable for this use case
83
+ }
84
+ if (!account) {
85
+ // Return a minimal marker that indicates authentication succeeded
86
+ // The user can check for this in their app
87
+ account = { id: 'password-auth', authenticated: true };
39
88
  }
40
89
  return { account, strategy: this.name };
41
90
  }
@@ -1 +1 @@
1
- {"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/strategies/password.ts"],"names":[],"mappings":"AAEA;;;;;GAKG;AACH,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAG,UAAU,CAAC;IAE3B;;;;;;;OAOG;IACH,KAAK,CAAC,YAAY,CAChB,SAAuC,EACvC,GAAG,IAAe;QAElB,MAAM,CAAC,UAAU,EAAE,QAAQ,CAAC,GAAG,IAAwB,CAAC;QAExD,IAAI,CAAC,UAAU,IAAI,CAAC,QAAQ,EAAE,CAAC;YAC7B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,sCAAsC,EAAE,CAAC;QAC1E,CAAC;QAED,IAAI,CAAC,SAAS,CAAC,cAAc,EAAE,CAAC;YAC9B,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,2DAA2D,EAAE,CAAC;QAC/F,CAAC;QAED,mBAAmB;QACnB,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,UAAU,CAAC,CAAC;QAExD,gFAAgF;QAChF,wEAAwE;QACxE,gEAAgE;QAChE,+EAA+E;QAC/E,0DAA0D;QAC1D,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;QACzD,CAAC;QAED,sBAAsB;QACtB,MAAM,OAAO,GAAG,MAAM,SAAS,CAAC,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAC;QAElE,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,qBAAqB,EAAE,CAAC;QACzD,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC1C,CAAC;CACF"}
1
+ {"version":3,"file":"password.js","sourceRoot":"","sources":["../../src/strategies/password.ts"],"names":[],"mappings":"AAyBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;AACH,MAAM,OAAO,gBAAgB;IAClB,IAAI,GAAG,UAAU,CAAC;IAEV,cAAc,CAA4C;IAE3E,YAAY,OAAgC;QAC1C,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,CAAC;IAC/C,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,YAAY,CAChB,SAAuC,EACvC,GAAG,IAAe;QAElB,MAAM,CAAC,QAAQ,CAAC,GAAG,IAAgB,CAAC;QAEpC,IAAI,CAAC,QAAQ,EAAE,CAAC;YACd,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,sBAAsB,EAAE,CAAC;QAC1D,CAAC;QAED,kDAAkD;QAClD,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,cAAc,CAAC,QAAQ,CAAC,CAAC;QAEpD,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,KAAK,EAAE,kBAAkB,EAAE,CAAC;QACtD,CAAC;QAED,0EAA0E;QAC1E,+EAA+E;QAC/E,4EAA4E;QAC5E,2DAA2D;QAE3D,qEAAqE;QACrE,iFAAiF;QACjF,IAAI,OAAO,GAAoB,IAAI,CAAC;QAEpC,IAAI,CAAC;YACH,uCAAuC;YACvC,OAAO,GAAG,MAAM,SAAS,CAAC,WAAW,CAAC,OAAO,CAAC,CAAC;QACjD,CAAC;QAAC,MAAM,CAAC;YACP,sDAAsD;QACxD,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,kEAAkE;YAClE,2CAA2C;YAC3C,OAAO,GAAG,EAAE,EAAE,EAAE,eAAe,EAAE,aAAa,EAAE,IAAI,EAAyB,CAAC;QAChF,CAAC;QAED,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;IAC1C,CAAC;CACF"}