@yannvr/auth 1.0.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 (45) hide show
  1. package/dist/client/auth-context.d.ts +18 -0
  2. package/dist/client/auth-context.d.ts.map +1 -0
  3. package/dist/client/auth-context.js +26 -0
  4. package/dist/client/auth-context.js.map +1 -0
  5. package/dist/client/index.d.ts +4 -0
  6. package/dist/client/index.d.ts.map +1 -0
  7. package/dist/client/index.js +3 -0
  8. package/dist/client/index.js.map +1 -0
  9. package/dist/client/use-passkey-login.d.ts +24 -0
  10. package/dist/client/use-passkey-login.d.ts.map +1 -0
  11. package/dist/client/use-passkey-login.js +92 -0
  12. package/dist/client/use-passkey-login.js.map +1 -0
  13. package/dist/server/challenge-store.d.ts +13 -0
  14. package/dist/server/challenge-store.d.ts.map +1 -0
  15. package/dist/server/challenge-store.js +36 -0
  16. package/dist/server/challenge-store.js.map +1 -0
  17. package/dist/server/cookie.d.ts +4 -0
  18. package/dist/server/cookie.d.ts.map +1 -0
  19. package/dist/server/cookie.js +20 -0
  20. package/dist/server/cookie.js.map +1 -0
  21. package/dist/server/index.d.ts +9 -0
  22. package/dist/server/index.d.ts.map +1 -0
  23. package/dist/server/index.js +8 -0
  24. package/dist/server/index.js.map +1 -0
  25. package/dist/server/jwt.d.ts +4 -0
  26. package/dist/server/jwt.d.ts.map +1 -0
  27. package/dist/server/jwt.js +13 -0
  28. package/dist/server/jwt.js.map +1 -0
  29. package/dist/server/middleware.d.ts +12 -0
  30. package/dist/server/middleware.d.ts.map +1 -0
  31. package/dist/server/middleware.js +34 -0
  32. package/dist/server/middleware.js.map +1 -0
  33. package/dist/server/require-auth.d.ts +25 -0
  34. package/dist/server/require-auth.d.ts.map +1 -0
  35. package/dist/server/require-auth.js +43 -0
  36. package/dist/server/require-auth.js.map +1 -0
  37. package/dist/server/webauthn.d.ts +21 -0
  38. package/dist/server/webauthn.d.ts.map +1 -0
  39. package/dist/server/webauthn.js +199 -0
  40. package/dist/server/webauthn.js.map +1 -0
  41. package/dist/types.d.ts +98 -0
  42. package/dist/types.d.ts.map +1 -0
  43. package/dist/types.js +3 -0
  44. package/dist/types.js.map +1 -0
  45. package/package.json +38 -0
@@ -0,0 +1,18 @@
1
+ import React from 'react';
2
+ import type { AuthUser } from '../types';
3
+ interface AuthContextValue {
4
+ user: AuthUser | null;
5
+ isLoggedIn: boolean;
6
+ /** Call after a successful passkey flow to update client state */
7
+ setUser: (user: AuthUser | null) => void;
8
+ logout: () => Promise<void>;
9
+ }
10
+ interface AuthProviderProps {
11
+ /** Server-rendered initial user — pass from layout.tsx server component */
12
+ initialUser?: AuthUser | null;
13
+ children: React.ReactNode;
14
+ }
15
+ export declare function AuthProvider({ initialUser, children }: AuthProviderProps): import("react/jsx-runtime").JSX.Element;
16
+ export declare function useAuth(): AuthContextValue;
17
+ export {};
18
+ //# sourceMappingURL=auth-context.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-context.d.ts","sourceRoot":"","sources":["../../src/client/auth-context.tsx"],"names":[],"mappings":"AAEA,OAAO,KAA2D,MAAM,OAAO,CAAA;AAC/E,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,UAAU,gBAAgB;IACxB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,UAAU,EAAE,OAAO,CAAA;IACnB,kEAAkE;IAClE,OAAO,EAAE,CAAC,IAAI,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,MAAM,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAC5B;AAID,UAAU,iBAAiB;IACzB,2EAA2E;IAC3E,WAAW,CAAC,EAAE,QAAQ,GAAG,IAAI,CAAA;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAC1B;AAED,wBAAgB,YAAY,CAAC,EAAE,WAAkB,EAAE,QAAQ,EAAE,EAAE,iBAAiB,2CAmB/E;AAED,wBAAgB,OAAO,IAAI,gBAAgB,CAI1C"}
@@ -0,0 +1,26 @@
1
+ 'use client';
2
+ import { jsx as _jsx } from "react/jsx-runtime";
3
+ import { createContext, useCallback, useContext, useState } from 'react';
4
+ const AuthContext = createContext(null);
5
+ export function AuthProvider({ initialUser = null, children }) {
6
+ const [user, setUser] = useState(initialUser);
7
+ const logout = useCallback(async () => {
8
+ try {
9
+ await fetch('/api/auth/logout', { method: 'POST' });
10
+ }
11
+ catch (_a) {
12
+ // best effort
13
+ }
14
+ setUser(null);
15
+ // Hard redirect to login — ensures server state is cleared too
16
+ window.location.href = '/login';
17
+ }, []);
18
+ return (_jsx(AuthContext.Provider, { value: { user, isLoggedIn: !!user, setUser, logout }, children: children }));
19
+ }
20
+ export function useAuth() {
21
+ const ctx = useContext(AuthContext);
22
+ if (!ctx)
23
+ throw new Error('useAuth must be used inside <AuthProvider>');
24
+ return ctx;
25
+ }
26
+ //# sourceMappingURL=auth-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auth-context.js","sourceRoot":"","sources":["../../src/client/auth-context.tsx"],"names":[],"mappings":"AAAA,YAAY,CAAA;;AAEZ,OAAc,EAAE,aAAa,EAAE,WAAW,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAW/E,MAAM,WAAW,GAAG,aAAa,CAA0B,IAAI,CAAC,CAAA;AAQhE,MAAM,UAAU,YAAY,CAAC,EAAE,WAAW,GAAG,IAAI,EAAE,QAAQ,EAAqB;IAC9E,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,GAAG,QAAQ,CAAkB,WAAW,CAAC,CAAA;IAE9D,MAAM,MAAM,GAAG,WAAW,CAAC,KAAK,IAAI,EAAE;QACpC,IAAI,CAAC;YACH,MAAM,KAAK,CAAC,kBAAkB,EAAE,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,CAAA;QACrD,CAAC;QAAC,WAAM,CAAC;YACP,cAAc;QAChB,CAAC;QACD,OAAO,CAAC,IAAI,CAAC,CAAA;QACb,+DAA+D;QAC/D,MAAM,CAAC,QAAQ,CAAC,IAAI,GAAG,QAAQ,CAAA;IACjC,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,OAAO,CACL,KAAC,WAAW,CAAC,QAAQ,IAAC,KAAK,EAAE,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC,CAAC,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,YACvE,QAAQ,GACY,CACxB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,OAAO;IACrB,MAAM,GAAG,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACnC,IAAI,CAAC,GAAG;QAAE,MAAM,IAAI,KAAK,CAAC,4CAA4C,CAAC,CAAA;IACvE,OAAO,GAAG,CAAA;AACZ,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { AuthProvider, useAuth } from './auth-context';
2
+ export { usePasskeyLogin } from './use-passkey-login';
3
+ export type { AuthUser } from '../types';
4
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA;AACrD,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,3 @@
1
+ export { AuthProvider, useAuth } from './auth-context';
2
+ export { usePasskeyLogin } from './use-passkey-login';
3
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,gBAAgB,CAAA;AACtD,OAAO,EAAE,eAAe,EAAE,MAAM,qBAAqB,CAAA"}
@@ -0,0 +1,24 @@
1
+ import type { AuthUser } from '../types';
2
+ type Step = 'idle' | 'loading' | 'success' | 'error';
3
+ interface PasskeyLoginState {
4
+ step: Step;
5
+ error: string | null;
6
+ user: AuthUser | null;
7
+ isSupported: boolean;
8
+ }
9
+ interface PasskeyLoginActions {
10
+ register: (email: string, name?: string) => Promise<AuthUser | null>;
11
+ authenticate: (email: string) => Promise<AuthUser | null>;
12
+ reset: () => void;
13
+ }
14
+ /**
15
+ * Headless hook — manages the full WebAuthn state machine.
16
+ * The server sets an HttpOnly session cookie on success; the hook returns the user
17
+ * for the caller to update React auth context.
18
+ *
19
+ * @example
20
+ * const { step, error, register, authenticate, isSupported } = usePasskeyLogin()
21
+ */
22
+ export declare function usePasskeyLogin(): PasskeyLoginState & PasskeyLoginActions;
23
+ export {};
24
+ //# sourceMappingURL=use-passkey-login.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-passkey-login.d.ts","sourceRoot":"","sources":["../../src/client/use-passkey-login.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AAExC,KAAK,IAAI,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,CAAA;AAEpD,UAAU,iBAAiB;IACzB,IAAI,EAAE,IAAI,CAAA;IACV,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,IAAI,EAAE,QAAQ,GAAG,IAAI,CAAA;IACrB,WAAW,EAAE,OAAO,CAAA;CACrB;AAED,UAAU,mBAAmB;IAC3B,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACpE,YAAY,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAAA;IACzD,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED;;;;;;;GAOG;AACH,wBAAgB,eAAe,IAAI,iBAAiB,GAAG,mBAAmB,CAmFzE"}
@@ -0,0 +1,92 @@
1
+ 'use client';
2
+ import { useState, useCallback } from 'react';
3
+ import { startRegistration, startAuthentication } from '@simplewebauthn/browser';
4
+ /**
5
+ * Headless hook — manages the full WebAuthn state machine.
6
+ * The server sets an HttpOnly session cookie on success; the hook returns the user
7
+ * for the caller to update React auth context.
8
+ *
9
+ * @example
10
+ * const { step, error, register, authenticate, isSupported } = usePasskeyLogin()
11
+ */
12
+ export function usePasskeyLogin() {
13
+ const [step, setStep] = useState('idle');
14
+ const [error, setError] = useState(null);
15
+ const [user, setUser] = useState(null);
16
+ const isSupported = typeof window !== 'undefined' &&
17
+ 'credentials' in navigator &&
18
+ 'create' in navigator.credentials;
19
+ const register = useCallback(async (email, name) => {
20
+ var _a, _b;
21
+ setStep('loading');
22
+ setError(null);
23
+ try {
24
+ const optRes = await fetch('/api/auth/passkey/register', {
25
+ method: 'POST',
26
+ headers: { 'Content-Type': 'application/json' },
27
+ body: JSON.stringify({ email, name }),
28
+ });
29
+ if (!optRes.ok)
30
+ throw new Error((_a = (await optRes.json()).error) !== null && _a !== void 0 ? _a : 'Registration failed');
31
+ const { registrationOptions } = await optRes.json();
32
+ const credential = await startRegistration(registrationOptions);
33
+ const verifyRes = await fetch('/api/auth/passkey/verify-registration', {
34
+ method: 'POST',
35
+ headers: { 'Content-Type': 'application/json' },
36
+ body: JSON.stringify({ email, credential }),
37
+ });
38
+ if (!verifyRes.ok)
39
+ throw new Error((_b = (await verifyRes.json()).error) !== null && _b !== void 0 ? _b : 'Verification failed');
40
+ const { user: loggedInUser } = await verifyRes.json();
41
+ setUser(loggedInUser);
42
+ setStep('success');
43
+ return loggedInUser;
44
+ }
45
+ catch (err) {
46
+ const msg = err instanceof Error ? err.message : 'Registration failed';
47
+ setError(msg);
48
+ setStep('error');
49
+ return null;
50
+ }
51
+ }, []);
52
+ const authenticate = useCallback(async (email) => {
53
+ var _a, _b;
54
+ setStep('loading');
55
+ setError(null);
56
+ try {
57
+ const optRes = await fetch('/api/auth/passkey/authenticate', {
58
+ method: 'POST',
59
+ headers: { 'Content-Type': 'application/json' },
60
+ body: JSON.stringify({ email }),
61
+ });
62
+ if (!optRes.ok)
63
+ throw new Error((_a = (await optRes.json()).error) !== null && _a !== void 0 ? _a : 'Authentication failed');
64
+ const { authenticationOptions } = await optRes.json();
65
+ const credential = await startAuthentication(authenticationOptions);
66
+ const verifyRes = await fetch('/api/auth/passkey/verify-authentication', {
67
+ method: 'POST',
68
+ headers: { 'Content-Type': 'application/json' },
69
+ body: JSON.stringify({ email, credential }),
70
+ });
71
+ if (!verifyRes.ok)
72
+ throw new Error((_b = (await verifyRes.json()).error) !== null && _b !== void 0 ? _b : 'Verification failed');
73
+ const { user: loggedInUser } = await verifyRes.json();
74
+ setUser(loggedInUser);
75
+ setStep('success');
76
+ return loggedInUser;
77
+ }
78
+ catch (err) {
79
+ const msg = err instanceof Error ? err.message : 'Authentication failed';
80
+ setError(msg);
81
+ setStep('error');
82
+ return null;
83
+ }
84
+ }, []);
85
+ const reset = useCallback(() => {
86
+ setStep('idle');
87
+ setError(null);
88
+ setUser(null);
89
+ }, []);
90
+ return { step, error, user, isSupported, register, authenticate, reset };
91
+ }
92
+ //# sourceMappingURL=use-passkey-login.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"use-passkey-login.js","sourceRoot":"","sources":["../../src/client/use-passkey-login.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ declare class ChallengeStore {
2
+ private store;
3
+ private cleanupTimer;
4
+ constructor();
5
+ set(key: string, challenge: string, ttlMs?: number): void;
6
+ get(key: string): string | null;
7
+ delete(key: string): void;
8
+ private cleanup;
9
+ destroy(): void;
10
+ }
11
+ export declare const challengeStore: ChallengeStore;
12
+ export {};
13
+ //# sourceMappingURL=challenge-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-store.d.ts","sourceRoot":"","sources":["../../src/server/challenge-store.ts"],"names":[],"mappings":"AAKA,cAAM,cAAc;IAClB,OAAO,CAAC,KAAK,CAAoC;IACjD,OAAO,CAAC,YAAY,CAAgB;;IAMpC,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,KAAK,SAAS,GAAG,IAAI;IAIzD,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI;IAU/B,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB,OAAO,CAAC,OAAO;IAOf,OAAO,IAAI,IAAI;CAIhB;AAGD,eAAO,MAAM,cAAc,gBAAuB,CAAA"}
@@ -0,0 +1,36 @@
1
+ class ChallengeStore {
2
+ constructor() {
3
+ this.store = new Map();
4
+ this.cleanupTimer = setInterval(() => this.cleanup(), 5 * 60 * 1000);
5
+ }
6
+ set(key, challenge, ttlMs = 60000) {
7
+ this.store.set(key, { challenge, expiresAt: Date.now() + ttlMs });
8
+ }
9
+ get(key) {
10
+ const entry = this.store.get(key);
11
+ if (!entry)
12
+ return null;
13
+ if (Date.now() > entry.expiresAt) {
14
+ this.store.delete(key);
15
+ return null;
16
+ }
17
+ return entry.challenge;
18
+ }
19
+ delete(key) {
20
+ this.store.delete(key);
21
+ }
22
+ cleanup() {
23
+ const now = Date.now();
24
+ for (const [key, entry] of this.store.entries()) {
25
+ if (now > entry.expiresAt)
26
+ this.store.delete(key);
27
+ }
28
+ }
29
+ destroy() {
30
+ clearInterval(this.cleanupTimer);
31
+ this.store.clear();
32
+ }
33
+ }
34
+ // Module-level singleton — one instance per Node.js process
35
+ export const challengeStore = new ChallengeStore();
36
+ //# sourceMappingURL=challenge-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"challenge-store.js","sourceRoot":"","sources":["../../src/server/challenge-store.ts"],"names":[],"mappings":"AAKA,MAAM,cAAc;IAIlB;QAHQ,UAAK,GAAG,IAAI,GAAG,EAA0B,CAAA;QAI/C,IAAI,CAAC,YAAY,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,OAAO,EAAE,EAAE,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,CAAA;IACtE,CAAC;IAED,GAAG,CAAC,GAAW,EAAE,SAAiB,EAAE,KAAK,GAAG,KAAM;QAChD,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,EAAE,CAAC,CAAA;IACnE,CAAC;IAED,GAAG,CAAC,GAAW;QACb,MAAM,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;QACjC,IAAI,CAAC,KAAK;YAAE,OAAO,IAAI,CAAA;QACvB,IAAI,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC,SAAS,EAAE,CAAC;YACjC,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;YACtB,OAAO,IAAI,CAAA;QACb,CAAC;QACD,OAAO,KAAK,CAAC,SAAS,CAAA;IACxB,CAAC;IAED,MAAM,CAAC,GAAW;QAChB,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;IACxB,CAAC;IAEO,OAAO;QACb,MAAM,GAAG,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QACtB,KAAK,MAAM,CAAC,GAAG,EAAE,KAAK,CAAC,IAAI,IAAI,CAAC,KAAK,CAAC,OAAO,EAAE,EAAE,CAAC;YAChD,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS;gBAAE,IAAI,CAAC,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;QACnD,CAAC;IACH,CAAC;IAED,OAAO;QACL,aAAa,CAAC,IAAI,CAAC,YAAY,CAAC,CAAA;QAChC,IAAI,CAAC,KAAK,CAAC,KAAK,EAAE,CAAA;IACpB,CAAC;CACF;AAED,4DAA4D;AAC5D,MAAM,CAAC,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAA"}
@@ -0,0 +1,4 @@
1
+ import { NextResponse } from 'next/server';
2
+ export declare function setSessionCookie(response: NextResponse, token: string): void;
3
+ export declare function clearSessionCookie(response: NextResponse): void;
4
+ //# sourceMappingURL=cookie.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie.d.ts","sourceRoot":"","sources":["../../src/server/cookie.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG1C,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,CAQ5E;AAED,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,YAAY,GAAG,IAAI,CAQ/D"}
@@ -0,0 +1,20 @@
1
+ import { SESSION_COOKIE, SESSION_MAX_AGE_SECONDS } from '../types';
2
+ export function setSessionCookie(response, token) {
3
+ response.cookies.set(SESSION_COOKIE, token, {
4
+ httpOnly: true,
5
+ secure: process.env.NODE_ENV === 'production',
6
+ sameSite: 'lax',
7
+ maxAge: SESSION_MAX_AGE_SECONDS,
8
+ path: '/',
9
+ });
10
+ }
11
+ export function clearSessionCookie(response) {
12
+ response.cookies.set(SESSION_COOKIE, '', {
13
+ httpOnly: true,
14
+ secure: process.env.NODE_ENV === 'production',
15
+ sameSite: 'lax',
16
+ maxAge: 0,
17
+ path: '/',
18
+ });
19
+ }
20
+ //# sourceMappingURL=cookie.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cookie.js","sourceRoot":"","sources":["../../src/server/cookie.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,cAAc,EAAE,uBAAuB,EAAE,MAAM,UAAU,CAAA;AAElE,MAAM,UAAU,gBAAgB,CAAC,QAAsB,EAAE,KAAa;IACpE,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,KAAK,EAAE;QAC1C,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAC7C,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,uBAAuB;QAC/B,IAAI,EAAE,GAAG;KACV,CAAC,CAAA;AACJ,CAAC;AAED,MAAM,UAAU,kBAAkB,CAAC,QAAsB;IACvD,QAAQ,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,EAAE,EAAE;QACvC,QAAQ,EAAE,IAAI;QACd,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,QAAQ,KAAK,YAAY;QAC7C,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,CAAC;QACT,IAAI,EAAE,GAAG;KACV,CAAC,CAAA;AACJ,CAAC"}
@@ -0,0 +1,9 @@
1
+ export { signToken, verifyToken } from './jwt';
2
+ export { setSessionCookie, clearSessionCookie } from './cookie';
3
+ export { challengeStore } from './challenge-store';
4
+ export { createWebAuthnConfig } from './webauthn';
5
+ export { handlePasskeyRegister, handlePasskeyVerifyRegistration, handlePasskeyAuthenticate, handlePasskeyVerifyAuthentication, } from './webauthn';
6
+ export { createAuthMiddleware } from './middleware';
7
+ export { requireAuth, getServerUser, unauthorized } from './require-auth';
8
+ export type { WebAuthnConfig, AuthUser, JWTPayload, AuthPrismaClient } from '../types';
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,EACL,qBAAqB,EACrB,+BAA+B,EAC/B,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA;AACzE,YAAY,EAAE,cAAc,EAAE,QAAQ,EAAE,UAAU,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA"}
@@ -0,0 +1,8 @@
1
+ export { signToken, verifyToken } from './jwt';
2
+ export { setSessionCookie, clearSessionCookie } from './cookie';
3
+ export { challengeStore } from './challenge-store';
4
+ export { createWebAuthnConfig } from './webauthn';
5
+ export { handlePasskeyRegister, handlePasskeyVerifyRegistration, handlePasskeyAuthenticate, handlePasskeyVerifyAuthentication, } from './webauthn';
6
+ export { createAuthMiddleware } from './middleware';
7
+ export { requireAuth, getServerUser, unauthorized } from './require-auth';
8
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAC9C,OAAO,EAAE,gBAAgB,EAAE,kBAAkB,EAAE,MAAM,UAAU,CAAA;AAC/D,OAAO,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,EAAE,oBAAoB,EAAE,MAAM,YAAY,CAAA;AACjD,OAAO,EACL,qBAAqB,EACrB,+BAA+B,EAC/B,yBAAyB,EACzB,iCAAiC,GAClC,MAAM,YAAY,CAAA;AACnB,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAA;AACnD,OAAO,EAAE,WAAW,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAA"}
@@ -0,0 +1,4 @@
1
+ import type { JWTPayload } from '../types';
2
+ export declare function signToken(payload: Omit<JWTPayload, 'iat' | 'exp'>, secret: string): string;
3
+ export declare function verifyToken(token: string, secret: string): JWTPayload | null;
4
+ //# sourceMappingURL=jwt.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.d.ts","sourceRoot":"","sources":["../../src/server/jwt.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C,wBAAgB,SAAS,CAAC,OAAO,EAAE,IAAI,CAAC,UAAU,EAAE,KAAK,GAAG,KAAK,CAAC,EAAE,MAAM,EAAE,MAAM,GAAG,MAAM,CAE1F;AAED,wBAAgB,WAAW,CAAC,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,UAAU,GAAG,IAAI,CAM5E"}
@@ -0,0 +1,13 @@
1
+ import jwt from 'jsonwebtoken';
2
+ export function signToken(payload, secret) {
3
+ return jwt.sign(payload, secret, { expiresIn: '7d' });
4
+ }
5
+ export function verifyToken(token, secret) {
6
+ try {
7
+ return jwt.verify(token, secret);
8
+ }
9
+ catch (_a) {
10
+ return null;
11
+ }
12
+ }
13
+ //# sourceMappingURL=jwt.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"jwt.js","sourceRoot":"","sources":["../../src/server/jwt.ts"],"names":[],"mappings":"AAAA,OAAO,GAAG,MAAM,cAAc,CAAA;AAG9B,MAAM,UAAU,SAAS,CAAC,OAAwC,EAAE,MAAc;IAChF,OAAO,GAAG,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;AACvD,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,KAAa,EAAE,MAAc;IACvD,IAAI,CAAC;QACH,OAAO,GAAG,CAAC,MAAM,CAAC,KAAK,EAAE,MAAM,CAAe,CAAA;IAChD,CAAC;IAAC,WAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
@@ -0,0 +1,12 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ interface AuthMiddlewareConfig {
3
+ /** Path prefixes that skip auth. Defaults to ['/login', '/api/auth', '/_next', '/favicon.ico'] */
4
+ publicPaths?: string[];
5
+ /** Where to redirect unauthenticated requests. Defaults to '/login' */
6
+ loginPath?: string;
7
+ /** JWT secret. Defaults to process.env.JWT_SECRET */
8
+ jwtSecret?: string;
9
+ }
10
+ export declare function createAuthMiddleware(config?: AuthMiddlewareConfig): (request: NextRequest) => Promise<NextResponse>;
11
+ export {};
12
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAIvD,UAAU,oBAAoB;IAC5B,kGAAkG;IAClG,WAAW,CAAC,EAAE,MAAM,EAAE,CAAA;IACtB,uEAAuE;IACvE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,qDAAqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,GAAE,oBAAyB,IAOnC,SAAS,WAAW,KAAG,OAAO,CAAC,YAAY,CAAC,CAkC9E"}
@@ -0,0 +1,34 @@
1
+ import { NextResponse } from 'next/server';
2
+ import { SESSION_COOKIE } from '../types';
3
+ import { verifyToken } from './jwt';
4
+ export function createAuthMiddleware(config = {}) {
5
+ const { publicPaths = ['/login', '/api/auth', '/_next', '/favicon.ico'], loginPath = '/login', jwtSecret, } = config;
6
+ return async function middleware(request) {
7
+ var _a;
8
+ const { pathname } = request.nextUrl;
9
+ if (publicPaths.some((p) => pathname.startsWith(p))) {
10
+ return NextResponse.next();
11
+ }
12
+ const secret = jwtSecret !== null && jwtSecret !== void 0 ? jwtSecret : process.env.JWT_SECRET;
13
+ if (!secret) {
14
+ console.error('[auth] JWT_SECRET is not set');
15
+ return NextResponse.redirect(new URL(loginPath, request.url));
16
+ }
17
+ const token = (_a = request.cookies.get(SESSION_COOKIE)) === null || _a === void 0 ? void 0 : _a.value;
18
+ if (!token) {
19
+ return NextResponse.redirect(new URL(loginPath, request.url));
20
+ }
21
+ const payload = verifyToken(token, secret);
22
+ if (!payload) {
23
+ const response = NextResponse.redirect(new URL(loginPath, request.url));
24
+ response.cookies.delete(SESSION_COOKIE);
25
+ return response;
26
+ }
27
+ // Forward user identity to server components and API routes via headers
28
+ const headers = new Headers(request.headers);
29
+ headers.set('x-user-id', payload.userId);
30
+ headers.set('x-user-email', payload.email);
31
+ return NextResponse.next({ request: { headers } });
32
+ };
33
+ }
34
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA,OAAO,EAAe,YAAY,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAWnC,MAAM,UAAU,oBAAoB,CAAC,SAA+B,EAAE;IACpE,MAAM,EACJ,WAAW,GAAG,CAAC,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,cAAc,CAAC,EAC/D,SAAS,GAAG,QAAQ,EACpB,SAAS,GACV,GAAG,MAAM,CAAA;IAEV,OAAO,KAAK,UAAU,UAAU,CAAC,OAAoB;;QACnD,MAAM,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC,OAAO,CAAA;QAEpC,IAAI,WAAW,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACpD,OAAO,YAAY,CAAC,IAAI,EAAE,CAAA;QAC5B,CAAC;QAED,MAAM,MAAM,GAAG,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAA;QAClD,IAAI,CAAC,MAAM,EAAE,CAAC;YACZ,OAAO,CAAC,KAAK,CAAC,8BAA8B,CAAC,CAAA;YAC7C,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;QAC/D,CAAC;QAED,MAAM,KAAK,GAAG,MAAA,OAAO,CAAC,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,KAAK,CAAA;QAExD,IAAI,CAAC,KAAK,EAAE,CAAC;YACX,OAAO,YAAY,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;QAC/D,CAAC;QAED,MAAM,OAAO,GAAG,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;QAE1C,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,QAAQ,GAAG,YAAY,CAAC,QAAQ,CAAC,IAAI,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;YACvE,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,cAAc,CAAC,CAAA;YACvC,OAAO,QAAQ,CAAA;QACjB,CAAC;QAED,wEAAwE;QACxE,MAAM,OAAO,GAAG,IAAI,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;QAC5C,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;QACxC,OAAO,CAAC,GAAG,CAAC,cAAc,EAAE,OAAO,CAAC,KAAK,CAAC,CAAA;QAE1C,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,EAAE,OAAO,EAAE,EAAE,CAAC,CAAA;IACpD,CAAC,CAAA;AACH,CAAC"}
@@ -0,0 +1,25 @@
1
+ import { NextResponse } from 'next/server';
2
+ import type { JWTPayload } from '../types';
3
+ /**
4
+ * For use in API route handlers (POST/PUT/DELETE etc.)
5
+ * Reads the session cookie and verifies the JWT.
6
+ * Returns the payload or null if unauthenticated.
7
+ *
8
+ * @example
9
+ * export async function POST(req: Request) {
10
+ * const user = await requireAuth()
11
+ * if (!user) return unauthorized()
12
+ * // ...
13
+ * }
14
+ */
15
+ export declare function requireAuth(jwtSecret?: string): Promise<JWTPayload | null>;
16
+ /**
17
+ * For use in Server Components — reads the user injected by middleware headers.
18
+ * Faster than requireAuth() as it skips JWT verification (middleware already did it).
19
+ */
20
+ export declare function getServerUser(): Promise<{
21
+ userId: string;
22
+ email: string;
23
+ } | null>;
24
+ export declare function unauthorized(): NextResponse;
25
+ //# sourceMappingURL=require-auth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-auth.d.ts","sourceRoot":"","sources":["../../src/server/require-auth.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAG1C,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,UAAU,CAAA;AAE1C;;;;;;;;;;;GAWG;AACH,wBAAsB,WAAW,CAAC,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,GAAG,IAAI,CAAC,CAShF;AAED;;;GAGG;AACH,wBAAsB,aAAa,IAAI,OAAO,CAAC;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,GAAG,IAAI,CAAC,CAMvF;AAED,wBAAgB,YAAY,IAAI,YAAY,CAE3C"}
@@ -0,0 +1,43 @@
1
+ import { cookies, headers } from 'next/headers';
2
+ import { NextResponse } from 'next/server';
3
+ import { SESSION_COOKIE } from '../types';
4
+ import { verifyToken } from './jwt';
5
+ /**
6
+ * For use in API route handlers (POST/PUT/DELETE etc.)
7
+ * Reads the session cookie and verifies the JWT.
8
+ * Returns the payload or null if unauthenticated.
9
+ *
10
+ * @example
11
+ * export async function POST(req: Request) {
12
+ * const user = await requireAuth()
13
+ * if (!user) return unauthorized()
14
+ * // ...
15
+ * }
16
+ */
17
+ export async function requireAuth(jwtSecret) {
18
+ var _a;
19
+ const secret = jwtSecret !== null && jwtSecret !== void 0 ? jwtSecret : process.env.JWT_SECRET;
20
+ if (!secret)
21
+ return null;
22
+ const cookieStore = await cookies();
23
+ const token = (_a = cookieStore.get(SESSION_COOKIE)) === null || _a === void 0 ? void 0 : _a.value;
24
+ if (!token)
25
+ return null;
26
+ return verifyToken(token, secret);
27
+ }
28
+ /**
29
+ * For use in Server Components — reads the user injected by middleware headers.
30
+ * Faster than requireAuth() as it skips JWT verification (middleware already did it).
31
+ */
32
+ export async function getServerUser() {
33
+ const headerStore = await headers();
34
+ const userId = headerStore.get('x-user-id');
35
+ const email = headerStore.get('x-user-email');
36
+ if (!userId || !email)
37
+ return null;
38
+ return { userId, email };
39
+ }
40
+ export function unauthorized() {
41
+ return NextResponse.json({ error: 'Unauthorized' }, { status: 401 });
42
+ }
43
+ //# sourceMappingURL=require-auth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"require-auth.js","sourceRoot":"","sources":["../../src/server/require-auth.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAA;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AAC1C,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAA;AACzC,OAAO,EAAE,WAAW,EAAE,MAAM,OAAO,CAAA;AAGnC;;;;;;;;;;;GAWG;AACH,MAAM,CAAC,KAAK,UAAU,WAAW,CAAC,SAAkB;;IAClD,MAAM,MAAM,GAAG,SAAS,aAAT,SAAS,cAAT,SAAS,GAAI,OAAO,CAAC,GAAG,CAAC,UAAU,CAAA;IAClD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAA;IAExB,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAA;IACnC,MAAM,KAAK,GAAG,MAAA,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,0CAAE,KAAK,CAAA;IACpD,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAEvB,OAAO,WAAW,CAAC,KAAK,EAAE,MAAM,CAAC,CAAA;AACnC,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa;IACjC,MAAM,WAAW,GAAG,MAAM,OAAO,EAAE,CAAA;IACnC,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,WAAW,CAAC,CAAA;IAC3C,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,CAAC,cAAc,CAAC,CAAA;IAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,KAAK;QAAE,OAAO,IAAI,CAAA;IAClC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,CAAA;AAC1B,CAAC;AAED,MAAM,UAAU,YAAY;IAC1B,OAAO,YAAY,CAAC,IAAI,CAAC,EAAE,KAAK,EAAE,cAAc,EAAE,EAAE,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC,CAAA;AACtE,CAAC"}
@@ -0,0 +1,21 @@
1
+ import { NextRequest, NextResponse } from 'next/server';
2
+ import type { WebAuthnConfig, AuthPrismaClient } from '../types';
3
+ export declare function createWebAuthnConfig(config: {
4
+ rpName: string;
5
+ rpID: string;
6
+ /** Full origin URL e.g. https://intel.hyperdrift.io or http://localhost:3000 */
7
+ origin: string;
8
+ }): WebAuthnConfig;
9
+ interface HandlerConfig {
10
+ webAuthnConfig: WebAuthnConfig;
11
+ prisma: AuthPrismaClient;
12
+ }
13
+ interface VerifyConfig extends HandlerConfig {
14
+ jwtSecret?: string;
15
+ }
16
+ export declare function handlePasskeyRegister(request: NextRequest, { webAuthnConfig, prisma }: HandlerConfig): Promise<NextResponse>;
17
+ export declare function handlePasskeyVerifyRegistration(request: NextRequest, { webAuthnConfig, prisma, jwtSecret }: VerifyConfig): Promise<NextResponse>;
18
+ export declare function handlePasskeyAuthenticate(request: NextRequest, { webAuthnConfig, prisma }: HandlerConfig): Promise<NextResponse>;
19
+ export declare function handlePasskeyVerifyAuthentication(request: NextRequest, { webAuthnConfig, prisma, jwtSecret }: VerifyConfig): Promise<NextResponse>;
20
+ export {};
21
+ //# sourceMappingURL=webauthn.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn.d.ts","sourceRoot":"","sources":["../../src/server/webauthn.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,aAAa,CAAA;AACvD,OAAO,KAAK,EAAE,cAAc,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAA;AAMhE,wBAAgB,oBAAoB,CAAC,MAAM,EAAE;IAC3C,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,EAAE,MAAM,CAAA;IACZ,gFAAgF;IAChF,MAAM,EAAE,MAAM,CAAA;CACf,GAAG,cAAc,CAOjB;AAED,UAAU,aAAa;IACrB,cAAc,EAAE,cAAc,CAAA;IAC9B,MAAM,EAAE,gBAAgB,CAAA;CACzB;AAED,UAAU,YAAa,SAAQ,aAAa;IAC1C,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAID,wBAAsB,qBAAqB,CACzC,OAAO,EAAE,WAAW,EACpB,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,aAAa,GACxC,OAAO,CAAC,YAAY,CAAC,CA0CvB;AAID,wBAAsB,+BAA+B,CACnD,OAAO,EAAE,WAAW,EACpB,EAAE,cAAc,EAAE,MAAM,EAAE,SAAmC,EAAE,EAAE,YAAY,GAC5E,OAAO,CAAC,YAAY,CAAC,CA6DvB;AAID,wBAAsB,yBAAyB,CAC7C,OAAO,EAAE,WAAW,EACpB,EAAE,cAAc,EAAE,MAAM,EAAE,EAAE,aAAa,GACxC,OAAO,CAAC,YAAY,CAAC,CA+BvB;AAID,wBAAsB,iCAAiC,CACrD,OAAO,EAAE,WAAW,EACpB,EAAE,cAAc,EAAE,MAAM,EAAE,SAAmC,EAAE,EAAE,YAAY,GAC5E,OAAO,CAAC,YAAY,CAAC,CA+DvB"}
@@ -0,0 +1,199 @@
1
+ import { generateRegistrationOptions, verifyRegistrationResponse, generateAuthenticationOptions, verifyAuthenticationResponse, } from '@simplewebauthn/server';
2
+ import { NextResponse } from 'next/server';
3
+ import { SESSION_MAX_AGE_SECONDS } from '../types';
4
+ import { signToken } from './jwt';
5
+ import { setSessionCookie } from './cookie';
6
+ import { challengeStore } from './challenge-store';
7
+ export function createWebAuthnConfig(config) {
8
+ return {
9
+ rpName: config.rpName,
10
+ rpID: config.rpID,
11
+ origin: config.origin,
12
+ expectedOrigin: config.origin,
13
+ };
14
+ }
15
+ // ── POST /api/auth/passkey/register ───────────────────────────────────────────
16
+ export async function handlePasskeyRegister(request, { webAuthnConfig, prisma }) {
17
+ var _a, _b;
18
+ try {
19
+ const { email, name } = await request.json();
20
+ if (!email)
21
+ return NextResponse.json({ error: 'Email is required' }, { status: 400 });
22
+ let user = await prisma.user.findUnique({ where: { email }, include: { passkeys: true } });
23
+ if (!user) {
24
+ user = await prisma.user.create({
25
+ data: { email, name: name !== null && name !== void 0 ? name : email.split('@')[0] },
26
+ include: { passkeys: true },
27
+ });
28
+ }
29
+ const passkeys = (_a = user.passkeys) !== null && _a !== void 0 ? _a : [];
30
+ const options = await generateRegistrationOptions({
31
+ rpName: webAuthnConfig.rpName,
32
+ rpID: webAuthnConfig.rpID,
33
+ userID: new TextEncoder().encode(user.id),
34
+ userName: user.email,
35
+ userDisplayName: (_b = user.name) !== null && _b !== void 0 ? _b : user.email,
36
+ attestationType: 'none',
37
+ excludeCredentials: passkeys.map((pk) => ({
38
+ id: pk.credentialId,
39
+ type: 'public-key',
40
+ transports: pk.transports,
41
+ })),
42
+ authenticatorSelection: {
43
+ residentKey: 'preferred',
44
+ userVerification: 'preferred',
45
+ authenticatorAttachment: 'platform',
46
+ },
47
+ });
48
+ challengeStore.set(email, options.challenge);
49
+ return NextResponse.json({ registrationOptions: options, userId: user.id });
50
+ }
51
+ catch (err) {
52
+ console.error('[auth] passkey register error:', err);
53
+ return NextResponse.json({ error: 'Failed to initiate passkey registration' }, { status: 500 });
54
+ }
55
+ }
56
+ // ── POST /api/auth/passkey/verify-registration ────────────────────────────────
57
+ export async function handlePasskeyVerifyRegistration(request, { webAuthnConfig, prisma, jwtSecret = process.env.JWT_SECRET }) {
58
+ var _a, _b;
59
+ try {
60
+ const { email, credential } = await request.json();
61
+ if (!email || !credential) {
62
+ return NextResponse.json({ error: 'Email and credential are required' }, { status: 400 });
63
+ }
64
+ const expectedChallenge = challengeStore.get(email);
65
+ if (!expectedChallenge) {
66
+ return NextResponse.json({ error: 'Challenge expired — please try again' }, { status: 400 });
67
+ }
68
+ const user = await prisma.user.findUnique({ where: { email } });
69
+ if (!user)
70
+ return NextResponse.json({ error: 'User not found' }, { status: 404 });
71
+ const verification = await verifyRegistrationResponse({
72
+ response: credential,
73
+ expectedChallenge,
74
+ expectedOrigin: webAuthnConfig.expectedOrigin,
75
+ expectedRPID: webAuthnConfig.rpID,
76
+ requireUserVerification: true,
77
+ });
78
+ if (!verification.verified || !verification.registrationInfo) {
79
+ return NextResponse.json({ error: 'Passkey verification failed' }, { status: 400 });
80
+ }
81
+ const { credential: cred, credentialDeviceType, credentialBackedUp } = verification.registrationInfo;
82
+ await prisma.passkey.create({
83
+ data: {
84
+ userId: user.id,
85
+ credentialId: Buffer.from(cred.id).toString('base64url'),
86
+ publicKey: Buffer.from(cred.publicKey),
87
+ counter: BigInt((_a = cred.counter) !== null && _a !== void 0 ? _a : 0),
88
+ deviceType: credentialDeviceType !== null && credentialDeviceType !== void 0 ? credentialDeviceType : 'unknown',
89
+ backedUp: credentialBackedUp !== null && credentialBackedUp !== void 0 ? credentialBackedUp : false,
90
+ transports: (_b = cred.transports) !== null && _b !== void 0 ? _b : [],
91
+ },
92
+ });
93
+ challengeStore.delete(email);
94
+ const token = signToken({ userId: user.id, email: user.email }, jwtSecret);
95
+ await prisma.session.create({
96
+ data: {
97
+ userId: user.id,
98
+ token,
99
+ expiresAt: new Date(Date.now() + SESSION_MAX_AGE_SECONDS * 1000),
100
+ },
101
+ });
102
+ const safeUser = { id: user.id, email: user.email, name: user.name, createdAt: user.createdAt, updatedAt: user.updatedAt };
103
+ const response = NextResponse.json({ success: true, user: safeUser });
104
+ setSessionCookie(response, token);
105
+ return response;
106
+ }
107
+ catch (err) {
108
+ console.error('[auth] passkey verify-registration error:', err);
109
+ return NextResponse.json({ error: 'Failed to verify passkey registration' }, { status: 500 });
110
+ }
111
+ }
112
+ // ── POST /api/auth/passkey/authenticate ──────────────────────────────────────
113
+ export async function handlePasskeyAuthenticate(request, { webAuthnConfig, prisma }) {
114
+ var _a;
115
+ try {
116
+ const { email } = await request.json();
117
+ if (!email)
118
+ return NextResponse.json({ error: 'Email is required' }, { status: 400 });
119
+ const user = await prisma.user.findUnique({ where: { email }, include: { passkeys: true } });
120
+ if (!user || !((_a = user.passkeys) === null || _a === void 0 ? void 0 : _a.length)) {
121
+ return NextResponse.json({ error: 'No passkeys found for this email. Please register first.' }, { status: 404 });
122
+ }
123
+ const options = await generateAuthenticationOptions({
124
+ rpID: webAuthnConfig.rpID,
125
+ allowCredentials: user.passkeys.map((pk) => ({
126
+ id: pk.credentialId,
127
+ type: 'public-key',
128
+ transports: pk.transports,
129
+ })),
130
+ userVerification: 'preferred',
131
+ });
132
+ challengeStore.set(email, options.challenge);
133
+ return NextResponse.json({ authenticationOptions: options, userId: user.id });
134
+ }
135
+ catch (err) {
136
+ console.error('[auth] passkey authenticate error:', err);
137
+ return NextResponse.json({ error: 'Failed to initiate passkey authentication' }, { status: 500 });
138
+ }
139
+ }
140
+ // ── POST /api/auth/passkey/verify-authentication ─────────────────────────────
141
+ export async function handlePasskeyVerifyAuthentication(request, { webAuthnConfig, prisma, jwtSecret = process.env.JWT_SECRET }) {
142
+ var _a;
143
+ try {
144
+ const { email, credential } = await request.json();
145
+ if (!email || !credential) {
146
+ return NextResponse.json({ error: 'Email and credential are required' }, { status: 400 });
147
+ }
148
+ const expectedChallenge = challengeStore.get(email);
149
+ if (!expectedChallenge) {
150
+ return NextResponse.json({ error: 'Challenge expired — please try again' }, { status: 400 });
151
+ }
152
+ const user = await prisma.user.findUnique({ where: { email }, include: { passkeys: true } });
153
+ if (!user)
154
+ return NextResponse.json({ error: 'User not found' }, { status: 404 });
155
+ const credentialId = Buffer.from(credential.id, 'base64url').toString('base64url');
156
+ const passkey = (_a = user.passkeys) === null || _a === void 0 ? void 0 : _a.find((pk) => pk.credentialId === credentialId);
157
+ if (!passkey)
158
+ return NextResponse.json({ error: 'Passkey not found' }, { status: 404 });
159
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
160
+ const verification = await verifyAuthenticationResponse({
161
+ response: credential,
162
+ expectedChallenge,
163
+ expectedOrigin: webAuthnConfig.expectedOrigin,
164
+ expectedRPID: webAuthnConfig.rpID,
165
+ authenticator: {
166
+ credentialID: Buffer.from(passkey.credentialId, 'base64url'),
167
+ credentialPublicKey: passkey.publicKey,
168
+ counter: Number(passkey.counter),
169
+ transports: passkey.transports,
170
+ },
171
+ requireUserVerification: true,
172
+ });
173
+ if (!verification.verified) {
174
+ return NextResponse.json({ error: 'Passkey authentication failed' }, { status: 400 });
175
+ }
176
+ await prisma.passkey.update({
177
+ where: { id: passkey.id },
178
+ data: { counter: BigInt(verification.authenticationInfo.newCounter), lastUsed: new Date() },
179
+ });
180
+ challengeStore.delete(email);
181
+ const token = signToken({ userId: user.id, email: user.email }, jwtSecret);
182
+ await prisma.session.create({
183
+ data: {
184
+ userId: user.id,
185
+ token,
186
+ expiresAt: new Date(Date.now() + SESSION_MAX_AGE_SECONDS * 1000),
187
+ },
188
+ });
189
+ const safeUser = { id: user.id, email: user.email, name: user.name, createdAt: user.createdAt, updatedAt: user.updatedAt };
190
+ const response = NextResponse.json({ success: true, user: safeUser });
191
+ setSessionCookie(response, token);
192
+ return response;
193
+ }
194
+ catch (err) {
195
+ console.error('[auth] passkey verify-authentication error:', err);
196
+ return NextResponse.json({ error: 'Failed to verify passkey authentication' }, { status: 500 });
197
+ }
198
+ }
199
+ //# sourceMappingURL=webauthn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn.js","sourceRoot":"","sources":["../../src/server/webauthn.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,98 @@
1
+ export interface AuthUser {
2
+ id: string;
3
+ email: string;
4
+ name?: string | null;
5
+ createdAt: Date;
6
+ updatedAt: Date;
7
+ }
8
+ export interface JWTPayload {
9
+ userId: string;
10
+ email: string;
11
+ iat?: number;
12
+ exp?: number;
13
+ }
14
+ export interface WebAuthnConfig {
15
+ rpName: string;
16
+ rpID: string;
17
+ origin: string;
18
+ expectedOrigin: string;
19
+ }
20
+ export interface AuthPrismaClient {
21
+ user: {
22
+ findUnique: (args: {
23
+ where: {
24
+ email?: string;
25
+ id?: string;
26
+ };
27
+ include?: {
28
+ passkeys?: boolean;
29
+ };
30
+ }) => Promise<(AuthUser & {
31
+ passkeys?: PasskeyRecord[];
32
+ passwordHash?: string | null;
33
+ }) | null>;
34
+ create: (args: {
35
+ data: {
36
+ email: string;
37
+ name?: string;
38
+ };
39
+ include?: {
40
+ passkeys?: boolean;
41
+ };
42
+ }) => Promise<AuthUser & {
43
+ passkeys: PasskeyRecord[];
44
+ }>;
45
+ };
46
+ passkey: {
47
+ create: (args: {
48
+ data: Record<string, unknown>;
49
+ }) => Promise<PasskeyRecord>;
50
+ update: (args: {
51
+ where: {
52
+ id: string;
53
+ };
54
+ data: Record<string, unknown>;
55
+ }) => Promise<PasskeyRecord>;
56
+ };
57
+ session: {
58
+ create: (args: {
59
+ data: {
60
+ userId: string;
61
+ token: string;
62
+ expiresAt: Date;
63
+ };
64
+ }) => Promise<SessionRecord>;
65
+ delete: (args: {
66
+ where: {
67
+ token: string;
68
+ };
69
+ }) => Promise<SessionRecord>;
70
+ findUnique: (args: {
71
+ where: {
72
+ token: string;
73
+ };
74
+ }) => Promise<SessionRecord | null>;
75
+ };
76
+ }
77
+ export interface PasskeyRecord {
78
+ id: string;
79
+ userId: string;
80
+ credentialId: string;
81
+ publicKey: Buffer | Uint8Array;
82
+ counter: bigint | number;
83
+ deviceType: string;
84
+ backedUp: boolean;
85
+ transports: string[];
86
+ createdAt: Date;
87
+ lastUsed: Date;
88
+ }
89
+ export interface SessionRecord {
90
+ id: string;
91
+ userId: string;
92
+ token: string;
93
+ expiresAt: Date;
94
+ createdAt: Date;
95
+ }
96
+ export declare const SESSION_COOKIE = "session";
97
+ export declare const SESSION_MAX_AGE_SECONDS: number;
98
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"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"}
package/dist/types.js ADDED
@@ -0,0 +1,3 @@
1
+ export const SESSION_COOKIE = 'session';
2
+ export const SESSION_MAX_AGE_SECONDS = 60 * 60 * 24 * 7; // 7 days
3
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAgEA,MAAM,CAAC,MAAM,cAAc,GAAG,SAAS,CAAA;AACvC,MAAM,CAAC,MAAM,uBAAuB,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,GAAG,CAAC,CAAA,CAAC,SAAS"}
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@yannvr/auth",
3
+ "version": "1.0.0",
4
+ "description": "Shared passkey auth (WebAuthn + HttpOnly cookies) for Next.js apps",
5
+ "license": "MIT",
6
+ "exports": {
7
+ "./server": {
8
+ "types": "./dist/server/index.d.ts",
9
+ "default": "./dist/server/index.js"
10
+ },
11
+ "./client": {
12
+ "types": "./dist/client/index.d.ts",
13
+ "default": "./dist/client/index.js"
14
+ }
15
+ },
16
+ "files": ["dist"],
17
+ "scripts": {
18
+ "build": "tsc --project tsconfig.build.json"
19
+ },
20
+ "peerDependencies": {
21
+ "@simplewebauthn/browser": ">=13",
22
+ "@simplewebauthn/server": ">=13",
23
+ "jsonwebtoken": ">=9",
24
+ "next": ">=14",
25
+ "react": ">=18"
26
+ },
27
+ "devDependencies": {
28
+ "@simplewebauthn/browser": "^13",
29
+ "@simplewebauthn/server": "^13",
30
+ "@types/jsonwebtoken": "^9",
31
+ "@types/node": "^20",
32
+ "@types/react": "^19",
33
+ "jsonwebtoken": "^9",
34
+ "next": "^15",
35
+ "react": "^18",
36
+ "typescript": "^5"
37
+ }
38
+ }