@sentropic/auth-hono 0.2.1

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 (78) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +80 -0
  3. package/dist/contracts.d.ts +64 -0
  4. package/dist/contracts.d.ts.map +1 -0
  5. package/dist/contracts.js +85 -0
  6. package/dist/contracts.js.map +1 -0
  7. package/dist/credential-route-handlers.d.ts +13 -0
  8. package/dist/credential-route-handlers.d.ts.map +1 -0
  9. package/dist/credential-route-handlers.js +102 -0
  10. package/dist/credential-route-handlers.js.map +1 -0
  11. package/dist/email-verification.d.ts +41 -0
  12. package/dist/email-verification.d.ts.map +1 -0
  13. package/dist/email-verification.js +65 -0
  14. package/dist/email-verification.js.map +1 -0
  15. package/dist/index.d.ts +15 -0
  16. package/dist/index.d.ts.map +1 -0
  17. package/dist/index.js +15 -0
  18. package/dist/index.js.map +1 -0
  19. package/dist/magic-link.d.ts +40 -0
  20. package/dist/magic-link.d.ts.map +1 -0
  21. package/dist/magic-link.js +94 -0
  22. package/dist/magic-link.js.map +1 -0
  23. package/dist/middleware.d.ts +25 -0
  24. package/dist/middleware.d.ts.map +1 -0
  25. package/dist/middleware.js +104 -0
  26. package/dist/middleware.js.map +1 -0
  27. package/dist/ports.d.ts +279 -0
  28. package/dist/ports.d.ts.map +1 -0
  29. package/dist/ports.js +2 -0
  30. package/dist/ports.js.map +1 -0
  31. package/dist/route-handlers.d.ts +19 -0
  32. package/dist/route-handlers.d.ts.map +1 -0
  33. package/dist/route-handlers.js +106 -0
  34. package/dist/route-handlers.js.map +1 -0
  35. package/dist/router.d.ts +59 -0
  36. package/dist/router.d.ts.map +1 -0
  37. package/dist/router.js +65 -0
  38. package/dist/router.js.map +1 -0
  39. package/dist/session-route-handlers.d.ts +9 -0
  40. package/dist/session-route-handlers.d.ts.map +1 -0
  41. package/dist/session-route-handlers.js +74 -0
  42. package/dist/session-route-handlers.js.map +1 -0
  43. package/dist/session.d.ts +33 -0
  44. package/dist/session.d.ts.map +1 -0
  45. package/dist/session.js +123 -0
  46. package/dist/session.js.map +1 -0
  47. package/dist/webauthn-authentication-route-handlers.d.ts +26 -0
  48. package/dist/webauthn-authentication-route-handlers.d.ts.map +1 -0
  49. package/dist/webauthn-authentication-route-handlers.js +97 -0
  50. package/dist/webauthn-authentication-route-handlers.js.map +1 -0
  51. package/dist/webauthn-authentication.d.ts +39 -0
  52. package/dist/webauthn-authentication.d.ts.map +1 -0
  53. package/dist/webauthn-authentication.js +110 -0
  54. package/dist/webauthn-authentication.js.map +1 -0
  55. package/dist/webauthn-registration-route-handlers.d.ts +47 -0
  56. package/dist/webauthn-registration-route-handlers.d.ts.map +1 -0
  57. package/dist/webauthn-registration-route-handlers.js +111 -0
  58. package/dist/webauthn-registration-route-handlers.js.map +1 -0
  59. package/dist/webauthn-registration.d.ts +47 -0
  60. package/dist/webauthn-registration.d.ts.map +1 -0
  61. package/dist/webauthn-registration.js +103 -0
  62. package/dist/webauthn-registration.js.map +1 -0
  63. package/package.json +98 -0
  64. package/src/contracts.ts +99 -0
  65. package/src/credential-route-handlers.ts +178 -0
  66. package/src/email-verification.ts +127 -0
  67. package/src/index.ts +14 -0
  68. package/src/magic-link.ts +167 -0
  69. package/src/middleware.ts +178 -0
  70. package/src/ports.ts +289 -0
  71. package/src/route-handlers.ts +182 -0
  72. package/src/router.ts +149 -0
  73. package/src/session-route-handlers.ts +128 -0
  74. package/src/session.ts +201 -0
  75. package/src/webauthn-authentication-route-handlers.ts +200 -0
  76. package/src/webauthn-authentication.ts +211 -0
  77. package/src/webauthn-registration-route-handlers.ts +248 -0
  78. package/src/webauthn-registration.ts +204 -0
@@ -0,0 +1,106 @@
1
+ import { z } from 'zod';
2
+ const requestEmailCodeSchema = z.object({
3
+ email: z.string().email(),
4
+ });
5
+ const verifyEmailCodeSchema = z.object({
6
+ code: z.string().regex(/^\d{6}$/),
7
+ email: z.string().email(),
8
+ });
9
+ const requestMagicLinkSchema = z.object({
10
+ email: z.string().email(),
11
+ });
12
+ const verifyMagicLinkSchema = z.object({
13
+ token: z.string().min(1),
14
+ });
15
+ export const createAuthEmailRouteHandlers = (options) => ({
16
+ async requestEmailCode(c) {
17
+ const input = await parseJson(c, requestEmailCodeSchema);
18
+ if (!input.ok) {
19
+ return invalidRequest(c, input.error);
20
+ }
21
+ const result = await options.service.requestEmailCode(input.value);
22
+ if (!result.success) {
23
+ return serviceError(c, result.error);
24
+ }
25
+ return c.json({
26
+ delivery: 'email',
27
+ expiresAt: result.expiresAt.toISOString(),
28
+ success: true,
29
+ });
30
+ },
31
+ async verifyEmailCode(c) {
32
+ const input = await parseJson(c, verifyEmailCodeSchema);
33
+ if (!input.ok) {
34
+ return invalidRequest(c, input.error);
35
+ }
36
+ const result = await options.service.verifyEmailCode(input.value);
37
+ if (!result.valid) {
38
+ return serviceError(c, result.error);
39
+ }
40
+ return c.json({
41
+ success: true,
42
+ verificationToken: result.verificationToken,
43
+ });
44
+ },
45
+ });
46
+ export const createAuthMagicLinkRouteHandlers = (options) => ({
47
+ async requestMagicLink(c) {
48
+ const input = await parseJson(c, requestMagicLinkSchema);
49
+ if (!input.ok) {
50
+ return invalidRequest(c, input.error);
51
+ }
52
+ const result = await options.service.requestMagicLink(input.value);
53
+ if (!result.success) {
54
+ return serviceError(c, result.error);
55
+ }
56
+ return c.json(formatRequestMagicLinkSuccess(options, result));
57
+ },
58
+ async verifyMagicLink(c) {
59
+ const input = await parseJson(c, verifyMagicLinkSchema);
60
+ if (!input.ok) {
61
+ return invalidRequest(c, input.error);
62
+ }
63
+ const result = await options.service.verifyMagicLink(input.value);
64
+ if (!result.valid) {
65
+ return serviceError(c, result.error);
66
+ }
67
+ return c.json({
68
+ success: true,
69
+ user: {
70
+ displayName: result.user.displayName,
71
+ email: result.email,
72
+ id: result.user.id,
73
+ role: result.user.role,
74
+ },
75
+ });
76
+ },
77
+ });
78
+ const formatRequestMagicLinkSuccess = (options, result) => options.formatRequestMagicLinkSuccess
79
+ ? options.formatRequestMagicLinkSuccess(result)
80
+ : {
81
+ delivery: 'magic_link',
82
+ expiresAt: result.expiresAt.toISOString(),
83
+ success: true,
84
+ };
85
+ const parseJson = async (c, schema) => {
86
+ const body = await c.req.json().catch(() => null);
87
+ const parsed = schema.safeParse(body);
88
+ if (!parsed.success) {
89
+ return { error: parsed.error, ok: false };
90
+ }
91
+ return { ok: true, value: parsed.data };
92
+ };
93
+ const invalidRequest = (c, error) => c.json({
94
+ error: {
95
+ code: 'invalid_input',
96
+ details: error.errors,
97
+ message: 'Invalid request data.',
98
+ },
99
+ }, 400);
100
+ const serviceError = (c, error) => c.json({
101
+ error: {
102
+ code: error.code,
103
+ message: error.message,
104
+ },
105
+ }, error.status);
106
+ //# sourceMappingURL=route-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"route-handlers.js","sourceRoot":"","sources":["../src/route-handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,59 @@
1
+ import { Hono, type Context } from 'hono';
2
+ import { type AuthHonoAuthUiMethod, type AuthHonoRouteContract } from './contracts.js';
3
+ import type { AuthHonoPorts } from './ports.js';
4
+ export interface CreateAuthRouterOptions {
5
+ cookieNames?: AuthHonoCookieNames;
6
+ emailCode?: AuthHonoEmailCodePolicy;
7
+ handlers?: AuthHonoRouteHandlers;
8
+ magicLink?: AuthHonoMagicLinkPolicy;
9
+ ports?: AuthHonoPorts;
10
+ responsePolicy?: AuthHonoResponsePolicy;
11
+ routeOverrides?: Partial<Record<AuthHonoAuthUiMethod, AuthHonoRouteContract>>;
12
+ routePrefix?: string;
13
+ rp?: AuthHonoRelyingPartyOptions;
14
+ session?: AuthHonoSessionPolicy;
15
+ serviceName?: string;
16
+ }
17
+ export interface AuthHonoCookieNames {
18
+ session: string;
19
+ refresh: string;
20
+ }
21
+ export interface AuthHonoRelyingPartyOptions {
22
+ id: string;
23
+ name: string;
24
+ expectedOrigins: string[];
25
+ }
26
+ export interface AuthHonoSessionPolicy {
27
+ sessionTtlSeconds: number;
28
+ refreshTtlSeconds: number;
29
+ }
30
+ export interface AuthHonoEmailCodePolicy {
31
+ length: number;
32
+ ttlSeconds: number;
33
+ maxRequestsPerWindow: number;
34
+ windowSeconds: number;
35
+ }
36
+ export interface AuthHonoMagicLinkPolicy {
37
+ ttlSeconds: number;
38
+ baseUrl?: string;
39
+ }
40
+ export interface AuthHonoResponsePolicy {
41
+ notImplementedCode: string;
42
+ notImplementedMessage: string;
43
+ }
44
+ export interface AuthHonoRouterConfig {
45
+ cookieNames: AuthHonoCookieNames;
46
+ emailCode: AuthHonoEmailCodePolicy;
47
+ magicLink: AuthHonoMagicLinkPolicy;
48
+ responsePolicy: AuthHonoResponsePolicy;
49
+ routePrefix: string;
50
+ routes: Record<AuthHonoAuthUiMethod, AuthHonoRouteContract>;
51
+ rp?: AuthHonoRelyingPartyOptions;
52
+ session: AuthHonoSessionPolicy;
53
+ serviceName: string;
54
+ }
55
+ export type AuthHonoRouteHandler = (c: Context) => Response | Promise<Response>;
56
+ export type AuthHonoRouteHandlers = Partial<Record<AuthHonoAuthUiMethod, AuthHonoRouteHandler>>;
57
+ export declare const createAuthHonoConfig: (options?: CreateAuthRouterOptions) => AuthHonoRouterConfig;
58
+ export declare const createAuthRouter: (options?: CreateAuthRouterOptions) => Hono;
59
+ //# sourceMappingURL=router.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.d.ts","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,KAAK,OAAO,EAAE,MAAM,MAAM,CAAC;AAE1C,OAAO,EAEL,KAAK,oBAAoB,EACzB,KAAK,qBAAqB,EAC3B,MAAM,gBAAgB,CAAC;AACxB,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD,MAAM,WAAW,uBAAuB;IACtC,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAClC,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC,QAAQ,CAAC,EAAE,qBAAqB,CAAC;IACjC,SAAS,CAAC,EAAE,uBAAuB,CAAC;IACpC,KAAK,CAAC,EAAE,aAAa,CAAC;IACtB,cAAc,CAAC,EAAE,sBAAsB,CAAC;IACxC,cAAc,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,qBAAqB,CAAC,CAAC,CAAC;IAC9E,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,EAAE,CAAC,EAAE,2BAA2B,CAAC;IACjC,OAAO,CAAC,EAAE,qBAAqB,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,mBAAmB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,MAAM,CAAC;CACjB;AAED,MAAM,WAAW,2BAA2B;IAC1C,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,MAAM,WAAW,qBAAqB;IACpC,iBAAiB,EAAE,MAAM,CAAC;IAC1B,iBAAiB,EAAE,MAAM,CAAC;CAC3B;AAED,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,aAAa,EAAE,MAAM,CAAC;CACvB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,MAAM,WAAW,sBAAsB;IACrC,kBAAkB,EAAE,MAAM,CAAC;IAC3B,qBAAqB,EAAE,MAAM,CAAC;CAC/B;AAED,MAAM,WAAW,oBAAoB;IACnC,WAAW,EAAE,mBAAmB,CAAC;IACjC,SAAS,EAAE,uBAAuB,CAAC;IACnC,SAAS,EAAE,uBAAuB,CAAC;IACnC,cAAc,EAAE,sBAAsB,CAAC;IACvC,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC,oBAAoB,EAAE,qBAAqB,CAAC,CAAC;IAC5D,EAAE,CAAC,EAAE,2BAA2B,CAAC;IACjC,OAAO,EAAE,qBAAqB,CAAC;IAC/B,WAAW,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,MAAM,oBAAoB,GAAG,CAAC,CAAC,EAAE,OAAO,KAAK,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAEhF,MAAM,MAAM,qBAAqB,GAAG,OAAO,CAAC,MAAM,CAAC,oBAAoB,EAAE,oBAAoB,CAAC,CAAC,CAAC;AAEhG,eAAO,MAAM,oBAAoB,aAAa,uBAAuB,KAAQ,oBA+B3E,CAAC;AAEH,eAAO,MAAM,gBAAgB,aAAa,uBAAuB,KAAQ,IA8BxE,CAAC"}
package/dist/router.js ADDED
@@ -0,0 +1,65 @@
1
+ import { Hono } from 'hono';
2
+ import { AUTH_HONO_ROUTE_MAP, } from './contracts.js';
3
+ export const createAuthHonoConfig = (options = {}) => ({
4
+ cookieNames: {
5
+ session: options.cookieNames?.session ?? 'session',
6
+ refresh: options.cookieNames?.refresh ?? 'refreshToken',
7
+ },
8
+ emailCode: {
9
+ length: options.emailCode?.length ?? 6,
10
+ ttlSeconds: options.emailCode?.ttlSeconds ?? 10 * 60,
11
+ maxRequestsPerWindow: options.emailCode?.maxRequestsPerWindow ?? 3,
12
+ windowSeconds: options.emailCode?.windowSeconds ?? 10 * 60,
13
+ },
14
+ magicLink: {
15
+ ttlSeconds: options.magicLink?.ttlSeconds ?? 10 * 60,
16
+ baseUrl: options.magicLink?.baseUrl,
17
+ },
18
+ responsePolicy: {
19
+ notImplementedCode: options.responsePolicy?.notImplementedCode ?? 'not_implemented',
20
+ notImplementedMessage: options.responsePolicy?.notImplementedMessage ?? 'Auth route handler is not implemented yet.',
21
+ },
22
+ routePrefix: normalizeRoutePrefix(options.routePrefix),
23
+ routes: {
24
+ ...AUTH_HONO_ROUTE_MAP,
25
+ ...options.routeOverrides,
26
+ },
27
+ rp: options.rp,
28
+ session: {
29
+ sessionTtlSeconds: options.session?.sessionTtlSeconds ?? 7 * 24 * 60 * 60,
30
+ refreshTtlSeconds: options.session?.refreshTtlSeconds ?? 30 * 24 * 60 * 60,
31
+ },
32
+ serviceName: options.serviceName ?? 'auth',
33
+ });
34
+ export const createAuthRouter = (options = {}) => {
35
+ const router = new Hono();
36
+ const config = createAuthHonoConfig(options);
37
+ router.get(joinRoutePath(config.routePrefix, '/health'), (c) => c.json({ status: 'ok', service: config.serviceName }));
38
+ for (const routeName of Object.keys(config.routes)) {
39
+ const contract = config.routes[routeName];
40
+ const handler = options.handlers?.[routeName];
41
+ router.on(contract.method, joinRoutePath(config.routePrefix, contract.path), (c) => {
42
+ if (handler) {
43
+ return handler(c);
44
+ }
45
+ return c.json({
46
+ error: {
47
+ code: config.responsePolicy.notImplementedCode,
48
+ message: config.responsePolicy.notImplementedMessage,
49
+ },
50
+ }, 501);
51
+ });
52
+ }
53
+ return router;
54
+ };
55
+ const normalizeRoutePrefix = (prefix) => {
56
+ if (!prefix || prefix === '/') {
57
+ return '';
58
+ }
59
+ return `/${prefix.replace(/^\/+|\/+$/g, '')}`;
60
+ };
61
+ const joinRoutePath = (prefix, path) => {
62
+ const normalizedPath = path.startsWith('/') ? path : `/${path}`;
63
+ return `${prefix}${normalizedPath}`;
64
+ };
65
+ //# sourceMappingURL=router.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"router.js","sourceRoot":"","sources":["../src/router.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ import type { AuthHonoCookiePort } from './ports.js';
2
+ import type { AuthHonoSessionService } from './session.js';
3
+ import type { AuthHonoRouteHandlers } from './router.js';
4
+ export interface CreateAuthSessionRouteHandlersOptions {
5
+ cookies: AuthHonoCookiePort;
6
+ service: AuthHonoSessionService;
7
+ }
8
+ export declare const createAuthSessionRouteHandlers: (options: CreateAuthSessionRouteHandlersOptions) => AuthHonoRouteHandlers;
9
+ //# sourceMappingURL=session-route-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-route-handlers.d.ts","sourceRoot":"","sources":["../src/session-route-handlers.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACrD,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,cAAc,CAAC;AAC3D,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,qCAAqC;IACpD,OAAO,EAAE,kBAAkB,CAAC;IAC5B,OAAO,EAAE,sBAAsB,CAAC;CACjC;AAMD,eAAO,MAAM,8BAA8B,YAChC,qCAAqC,KAC7C,qBAyDD,CAAC"}
@@ -0,0 +1,74 @@
1
+ import { z } from 'zod';
2
+ const refreshSessionSchema = z.object({
3
+ refreshToken: z.string().min(1),
4
+ });
5
+ export const createAuthSessionRouteHandlers = (options) => ({
6
+ async refreshSession(c) {
7
+ const parsed = await parseJson(c, refreshSessionSchema);
8
+ if (!parsed.ok) {
9
+ return errorResponse(c, 400, 'invalid_input', 'Invalid request data.', parsed.error.errors);
10
+ }
11
+ const tokens = await options.service.refreshSession(parsed.value.refreshToken);
12
+ if (!tokens) {
13
+ return errorResponse(c, 401, 'invalid_session', 'Invalid or expired refresh token.');
14
+ }
15
+ appendSetCookie(c, options.cookies.serializeSessionCookie({
16
+ expiresAt: tokens.expiresAt,
17
+ token: tokens.sessionToken,
18
+ }));
19
+ appendSetCookie(c, options.cookies.serializeRefreshCookie({
20
+ expiresAt: tokens.expiresAt,
21
+ token: tokens.refreshToken,
22
+ }));
23
+ return c.json({
24
+ expiresAt: tokens.expiresAt.toISOString(),
25
+ refreshToken: tokens.refreshToken,
26
+ sessionToken: tokens.sessionToken,
27
+ success: true,
28
+ });
29
+ },
30
+ async logout(c) {
31
+ const sessionToken = readSessionToken(c, options.cookies);
32
+ if (!sessionToken) {
33
+ return errorResponse(c, 401, 'authentication_required', 'No session token provided.');
34
+ }
35
+ const session = await options.service.validateSessionToken(sessionToken);
36
+ if (!session) {
37
+ return errorResponse(c, 401, 'invalid_session', 'Invalid session.');
38
+ }
39
+ await options.service.revokeSession(session.sessionRecord.id);
40
+ appendSetCookie(c, options.cookies.serializeClearedSessionCookie());
41
+ appendSetCookie(c, options.cookies.serializeClearedRefreshCookie());
42
+ return c.json({ message: 'Logged out successfully', success: true });
43
+ },
44
+ });
45
+ const parseJson = async (c, schema) => {
46
+ const body = await c.req.json().catch(() => null);
47
+ const parsed = schema.safeParse(body);
48
+ if (!parsed.success) {
49
+ return { error: parsed.error, ok: false };
50
+ }
51
+ return { ok: true, value: parsed.data };
52
+ };
53
+ const readSessionToken = (c, cookies) => {
54
+ const cookieToken = cookies.readSessionToken(c.req.raw);
55
+ if (cookieToken) {
56
+ return cookieToken;
57
+ }
58
+ const authorization = c.req.header('authorization');
59
+ if (!authorization?.startsWith('Bearer ')) {
60
+ return null;
61
+ }
62
+ return authorization.slice('Bearer '.length).trim() || null;
63
+ };
64
+ const appendSetCookie = (c, value) => {
65
+ c.header('Set-Cookie', value, { append: true });
66
+ };
67
+ const errorResponse = (c, status, code, message, details) => c.json({
68
+ error: {
69
+ code,
70
+ ...(details ? { details } : {}),
71
+ message,
72
+ },
73
+ }, status);
74
+ //# sourceMappingURL=session-route-handlers.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session-route-handlers.js","sourceRoot":"","sources":["../src/session-route-handlers.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,33 @@
1
+ import type { AuthHonoDeviceInfo, AuthHonoPorts, AuthHonoSessionClaims, AuthHonoSessionRecord, AuthHonoUserRecord } from './ports.js';
2
+ export interface AuthHonoSessionTokens {
3
+ expiresAt: Date;
4
+ refreshToken: string;
5
+ sessionId: string;
6
+ sessionToken: string;
7
+ }
8
+ export interface AuthHonoValidatedSession {
9
+ role: string;
10
+ session: AuthHonoSessionClaims;
11
+ sessionRecord: AuthHonoSessionRecord;
12
+ user: AuthHonoUserRecord;
13
+ }
14
+ export interface CreateAuthSessionServiceOptions {
15
+ ports: AuthHonoPorts;
16
+ refreshTokenBytes?: number;
17
+ sessionTtlSeconds?: number;
18
+ }
19
+ export interface CreateAuthSessionInput {
20
+ deviceInfo?: AuthHonoDeviceInfo;
21
+ mfaVerified?: boolean;
22
+ user: AuthHonoUserRecord;
23
+ }
24
+ export interface AuthHonoSessionService {
25
+ createSession(input: CreateAuthSessionInput): Promise<AuthHonoSessionTokens>;
26
+ listUserSessions(userId: string): Promise<AuthHonoSessionRecord[]>;
27
+ refreshSession(refreshToken: string): Promise<AuthHonoSessionTokens | null>;
28
+ revokeAllSessions(userId: string): Promise<number>;
29
+ revokeSession(sessionId: string): Promise<boolean>;
30
+ validateSessionToken(sessionToken: string): Promise<AuthHonoValidatedSession | null>;
31
+ }
32
+ export declare const createAuthSessionService: (options: CreateAuthSessionServiceOptions) => AuthHonoSessionService;
33
+ //# sourceMappingURL=session.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.d.ts","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,aAAa,EACb,qBAAqB,EACrB,qBAAqB,EACrB,kBAAkB,EACnB,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,qBAAqB;IACpC,SAAS,EAAE,IAAI,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,qBAAqB,CAAC;IAC/B,aAAa,EAAE,qBAAqB,CAAC;IACrC,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED,MAAM,WAAW,+BAA+B;IAC9C,KAAK,EAAE,aAAa,CAAC;IACrB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B;AAED,MAAM,WAAW,sBAAsB;IACrC,UAAU,CAAC,EAAE,kBAAkB,CAAC;IAChC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,IAAI,EAAE,kBAAkB,CAAC;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,aAAa,CAAC,KAAK,EAAE,sBAAsB,GAAG,OAAO,CAAC,qBAAqB,CAAC,CAAC;IAC7E,gBAAgB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,EAAE,CAAC,CAAC;IACnE,cAAc,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,qBAAqB,GAAG,IAAI,CAAC,CAAC;IAC5E,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC;IACnD,aAAa,CAAC,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACnD,oBAAoB,CAAC,YAAY,EAAE,MAAM,GAAG,OAAO,CAAC,wBAAwB,GAAG,IAAI,CAAC,CAAC;CACtF;AAED,eAAO,MAAM,wBAAwB,YAC1B,+BAA+B,KACvC,sBA2JF,CAAC"}
@@ -0,0 +1,123 @@
1
+ export const createAuthSessionService = (options) => {
2
+ const sessionTtlSeconds = options.sessionTtlSeconds ?? 7 * 24 * 60 * 60;
3
+ const refreshTokenBytes = options.refreshTokenBytes ?? 32;
4
+ return {
5
+ async createSession(input) {
6
+ const now = options.ports.clock.now();
7
+ const sessionId = options.ports.random.uuid();
8
+ const expiresAt = options.ports.clock.addSeconds(now, sessionTtlSeconds);
9
+ const refreshToken = options.ports.random.token(refreshTokenBytes);
10
+ const sessionToken = await options.ports.tokens.signSessionToken({
11
+ userId: input.user.id,
12
+ sessionId,
13
+ role: input.user.role,
14
+ email: input.user.email,
15
+ displayName: input.user.displayName,
16
+ }, expiresAt);
17
+ const sessionTokenHash = await options.ports.tokens.hashSecret(sessionToken);
18
+ const refreshTokenHash = await options.ports.tokens.hashSecret(refreshToken);
19
+ const sessionRecord = await options.ports.sessions.create({
20
+ id: sessionId,
21
+ userId: input.user.id,
22
+ sessionTokenHash,
23
+ refreshTokenHash,
24
+ deviceInfo: input.deviceInfo,
25
+ mfaVerified: input.mfaVerified ?? false,
26
+ expiresAt,
27
+ now,
28
+ });
29
+ return {
30
+ expiresAt: sessionRecord.expiresAt,
31
+ refreshToken,
32
+ sessionId: sessionRecord.id,
33
+ sessionToken,
34
+ };
35
+ },
36
+ listUserSessions(userId) {
37
+ return options.ports.sessions.listForUser(userId);
38
+ },
39
+ async refreshSession(refreshToken) {
40
+ const now = options.ports.clock.now();
41
+ const refreshTokenHash = await options.ports.tokens.hashSecret(refreshToken);
42
+ const sessionRecord = await options.ports.sessions.findByRefreshTokenHash(refreshTokenHash);
43
+ if (!sessionRecord || sessionRecord.revokedAt || sessionRecord.expiresAt <= now) {
44
+ return null;
45
+ }
46
+ const user = await options.ports.users.findById(sessionRecord.userId);
47
+ if (!user) {
48
+ return null;
49
+ }
50
+ const decision = await options.ports.accountPolicy.canAuthenticate(user, now);
51
+ if (!decision.allowed) {
52
+ return null;
53
+ }
54
+ const role = await options.ports.accountPolicy.resolveSessionRole(user, now);
55
+ const expiresAt = options.ports.clock.addSeconds(now, sessionTtlSeconds);
56
+ const nextRefreshToken = options.ports.random.token(refreshTokenBytes);
57
+ const nextSessionToken = await options.ports.tokens.signSessionToken({
58
+ userId: user.id,
59
+ sessionId: sessionRecord.id,
60
+ role,
61
+ email: user.email,
62
+ displayName: user.displayName,
63
+ }, expiresAt);
64
+ const nextSessionTokenHash = await options.ports.tokens.hashSecret(nextSessionToken);
65
+ const nextRefreshTokenHash = await options.ports.tokens.hashSecret(nextRefreshToken);
66
+ const updatedSession = await options.ports.sessions.updateTokens({
67
+ expiresAt,
68
+ refreshTokenHash: nextRefreshTokenHash,
69
+ sessionId: sessionRecord.id,
70
+ sessionTokenHash: nextSessionTokenHash,
71
+ });
72
+ if (!updatedSession) {
73
+ return null;
74
+ }
75
+ return {
76
+ expiresAt: updatedSession.expiresAt,
77
+ refreshToken: nextRefreshToken,
78
+ sessionId: updatedSession.id,
79
+ sessionToken: nextSessionToken,
80
+ };
81
+ },
82
+ revokeAllSessions(userId) {
83
+ return options.ports.sessions.revokeAllForUser(userId);
84
+ },
85
+ revokeSession(sessionId) {
86
+ return options.ports.sessions.revoke(sessionId);
87
+ },
88
+ async validateSessionToken(sessionToken) {
89
+ const claims = await options.ports.tokens.verifySessionToken(sessionToken);
90
+ if (!claims) {
91
+ return null;
92
+ }
93
+ const tokenHash = await options.ports.tokens.hashSecret(sessionToken);
94
+ const sessionRecord = await options.ports.sessions.findByTokenHash(tokenHash);
95
+ const now = options.ports.clock.now();
96
+ if (!sessionRecord ||
97
+ sessionRecord.id !== claims.sessionId ||
98
+ sessionRecord.userId !== claims.userId ||
99
+ sessionRecord.revokedAt ||
100
+ sessionRecord.expiresAt <= now) {
101
+ return null;
102
+ }
103
+ const user = await options.ports.users.findById(claims.userId);
104
+ if (!user) {
105
+ return null;
106
+ }
107
+ const decision = await options.ports.accountPolicy.canAuthenticate(user, now);
108
+ if (!decision.allowed) {
109
+ return null;
110
+ }
111
+ const role = await options.ports.accountPolicy.resolveSessionRole(user, now);
112
+ const session = { ...claims, role };
113
+ await options.ports.sessions.touch(sessionRecord.id, now);
114
+ return {
115
+ role,
116
+ session,
117
+ sessionRecord,
118
+ user,
119
+ };
120
+ },
121
+ };
122
+ };
123
+ //# sourceMappingURL=session.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"session.js","sourceRoot":"","sources":["../src/session.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ import type { AuthenticationResponseJSON } from '@simplewebauthn/server';
2
+ import type { Context } from 'hono';
3
+ import type { AuthHonoGenerateAuthenticationOptionsInput, AuthHonoWebAuthnAuthenticationService } from './webauthn-authentication.js';
4
+ import type { AuthHonoRouteHandlers } from './router.js';
5
+ import type { AuthHonoRouteHandlerError } from './webauthn-registration-route-handlers.js';
6
+ export interface CreateAuthWebAuthnAuthenticationRouteHandlersOptions {
7
+ finalizeAuthentication?: AuthHonoFinalizeAuthentication;
8
+ resolveAuthenticationOptions: AuthHonoResolveAuthenticationOptions;
9
+ service: AuthHonoWebAuthnAuthenticationService;
10
+ }
11
+ export type AuthHonoResolveAuthenticationOptions = (input: AuthHonoAuthenticationOptionsRequest, c: Context) => Promise<AuthHonoGenerateAuthenticationOptionsInput | AuthHonoRouteHandlerError> | AuthHonoGenerateAuthenticationOptionsInput | AuthHonoRouteHandlerError;
12
+ export type AuthHonoFinalizeAuthentication = (result: AuthHonoFinalizeAuthenticationInput, c: Context) => Response | Promise<Response>;
13
+ export interface AuthHonoFinalizeAuthenticationInput {
14
+ credentialId: string;
15
+ request: AuthHonoAuthenticationVerifyRequest;
16
+ userId: string;
17
+ }
18
+ export interface AuthHonoAuthenticationOptionsRequest {
19
+ email?: string;
20
+ }
21
+ export interface AuthHonoAuthenticationVerifyRequest {
22
+ credential: AuthenticationResponseJSON;
23
+ deviceName?: string;
24
+ }
25
+ export declare const createAuthWebAuthnAuthenticationRouteHandlers: (options: CreateAuthWebAuthnAuthenticationRouteHandlersOptions) => AuthHonoRouteHandlers;
26
+ //# sourceMappingURL=webauthn-authentication-route-handlers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"webauthn-authentication-route-handlers.d.ts","sourceRoot":"","sources":["../src/webauthn-authentication-route-handlers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,wBAAwB,CAAC;AACzE,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,MAAM,CAAC;AAIpC,OAAO,KAAK,EACV,0CAA0C,EAC1C,qCAAqC,EACtC,MAAM,8BAA8B,CAAC;AACtC,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AACzD,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,2CAA2C,CAAC;AAE3F,MAAM,WAAW,oDAAoD;IACnE,sBAAsB,CAAC,EAAE,8BAA8B,CAAC;IACxD,4BAA4B,EAAE,oCAAoC,CAAC;IACnE,OAAO,EAAE,qCAAqC,CAAC;CAChD;AAED,MAAM,MAAM,oCAAoC,GAAG,CACjD,KAAK,EAAE,oCAAoC,EAC3C,CAAC,EAAE,OAAO,KAER,OAAO,CAAC,0CAA0C,GAAG,yBAAyB,CAAC,GAC/E,0CAA0C,GAC1C,yBAAyB,CAAC;AAE9B,MAAM,MAAM,8BAA8B,GAAG,CAC3C,MAAM,EAAE,mCAAmC,EAC3C,CAAC,EAAE,OAAO,KACP,QAAQ,GAAG,OAAO,CAAC,QAAQ,CAAC,CAAC;AAElC,MAAM,WAAW,mCAAmC;IAClD,YAAY,EAAE,MAAM,CAAC;IACrB,OAAO,EAAE,mCAAmC,CAAC;IAC7C,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,oCAAoC;IACnD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,mCAAmC;IAClD,UAAU,EAAE,0BAA0B,CAAC;IACvC,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAsBD,eAAO,MAAM,6CAA6C,YAC/C,oDAAoD,KAC5D,qBA+DD,CAAC"}
@@ -0,0 +1,97 @@
1
+ import { z } from 'zod';
2
+ const isRouteHandlerError = (value) => Boolean(value &&
3
+ typeof value === 'object' &&
4
+ 'error' in value &&
5
+ typeof value.error === 'object' &&
6
+ value.error?.code !== undefined);
7
+ const authenticationOptionsSchema = z.object({
8
+ email: z.string().email().optional(),
9
+ });
10
+ const authenticationVerifySchema = z.object({
11
+ credential: z.custom((value) => Boolean(value && typeof value === 'object' && 'response' in value)),
12
+ deviceName: z.string().max(100).optional(),
13
+ });
14
+ export const createAuthWebAuthnAuthenticationRouteHandlers = (options) => ({
15
+ async createPasskeyAuthenticationOptions(c) {
16
+ const input = await parseJson(c, authenticationOptionsSchema);
17
+ if (!input.ok) {
18
+ return invalidRequest(c, input.error);
19
+ }
20
+ const serviceInput = await options.resolveAuthenticationOptions(input.value, c);
21
+ if (isRouteHandlerError(serviceInput)) {
22
+ return simpleError(c, serviceInput.error.status, serviceInput.error.code, serviceInput.error.message);
23
+ }
24
+ const authenticationOptions = await options.service.generateAuthenticationOptions(serviceInput);
25
+ return c.json({ options: authenticationOptions });
26
+ },
27
+ async verifyPasskeyAuthentication(c) {
28
+ const input = await parseJson(c, authenticationVerifySchema);
29
+ if (!input.ok) {
30
+ return invalidRequest(c, input.error);
31
+ }
32
+ const challenge = extractChallenge(input.value.credential);
33
+ if (!challenge) {
34
+ return simpleError(c, 400, 'invalid_credential', 'Credential challenge is missing or invalid.');
35
+ }
36
+ const result = await options.service.verifyAuthentication({
37
+ credential: input.value.credential,
38
+ expectedChallenge: challenge,
39
+ });
40
+ if (!result.verified) {
41
+ return serviceError(c, result.error);
42
+ }
43
+ if (options.finalizeAuthentication) {
44
+ return options.finalizeAuthentication({
45
+ credentialId: result.credentialId,
46
+ request: input.value,
47
+ userId: result.userId,
48
+ }, c);
49
+ }
50
+ return c.json({
51
+ credentialId: result.credentialId,
52
+ success: true,
53
+ userId: result.userId,
54
+ });
55
+ },
56
+ });
57
+ const parseJson = async (c, schema) => {
58
+ const body = await c.req.json().catch(() => null);
59
+ const parsed = schema.safeParse(body);
60
+ if (!parsed.success) {
61
+ return { error: parsed.error, ok: false };
62
+ }
63
+ return { ok: true, value: parsed.data };
64
+ };
65
+ const extractChallenge = (credential) => {
66
+ const clientDataJson = credential.response?.clientDataJSON;
67
+ if (!clientDataJson) {
68
+ return null;
69
+ }
70
+ try {
71
+ const json = JSON.parse(decodeBase64Url(clientDataJson));
72
+ return typeof json.challenge === 'string' && json.challenge.length > 0 ? json.challenge : null;
73
+ }
74
+ catch {
75
+ return null;
76
+ }
77
+ };
78
+ const decodeBase64Url = (value) => {
79
+ const base64 = value.replace(/-/g, '+').replace(/_/g, '/');
80
+ const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
81
+ return atob(padded);
82
+ };
83
+ const invalidRequest = (c, error) => c.json({
84
+ error: {
85
+ code: 'invalid_input',
86
+ details: error.errors,
87
+ message: 'Invalid request data.',
88
+ },
89
+ }, 400);
90
+ const serviceError = (c, error) => simpleError(c, error.status, error.code, error.message);
91
+ const simpleError = (c, status, code, message) => c.json({
92
+ error: {
93
+ code,
94
+ message,
95
+ },
96
+ }, status);
97
+ //# sourceMappingURL=webauthn-authentication-route-handlers.js.map