@panguard-ai/panguard-auth 0.1.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 (101) hide show
  1. package/dist/auth.d.ts +32 -0
  2. package/dist/auth.d.ts.map +1 -0
  3. package/dist/auth.js +68 -0
  4. package/dist/auth.js.map +1 -0
  5. package/dist/crypto.d.ts +21 -0
  6. package/dist/crypto.d.ts.map +1 -0
  7. package/dist/crypto.js +60 -0
  8. package/dist/crypto.js.map +1 -0
  9. package/dist/database.d.ts +266 -0
  10. package/dist/database.d.ts.map +1 -0
  11. package/dist/database.js +936 -0
  12. package/dist/database.js.map +1 -0
  13. package/dist/email-verify.d.ts +31 -0
  14. package/dist/email-verify.d.ts.map +1 -0
  15. package/dist/email-verify.js +506 -0
  16. package/dist/email-verify.js.map +1 -0
  17. package/dist/error-tracker.d.ts +24 -0
  18. package/dist/error-tracker.d.ts.map +1 -0
  19. package/dist/error-tracker.js +80 -0
  20. package/dist/error-tracker.js.map +1 -0
  21. package/dist/google-oauth.d.ts +40 -0
  22. package/dist/google-oauth.d.ts.map +1 -0
  23. package/dist/google-oauth.js +77 -0
  24. package/dist/google-oauth.js.map +1 -0
  25. package/dist/google-sheets.d.ts +35 -0
  26. package/dist/google-sheets.d.ts.map +1 -0
  27. package/dist/google-sheets.js +128 -0
  28. package/dist/google-sheets.js.map +1 -0
  29. package/dist/index.d.ts +27 -0
  30. package/dist/index.d.ts.map +1 -0
  31. package/dist/index.js +18 -0
  32. package/dist/index.js.map +1 -0
  33. package/dist/lemonsqueezy.d.ts +61 -0
  34. package/dist/lemonsqueezy.d.ts.map +1 -0
  35. package/dist/lemonsqueezy.js +254 -0
  36. package/dist/lemonsqueezy.js.map +1 -0
  37. package/dist/middleware.d.ts +22 -0
  38. package/dist/middleware.d.ts.map +1 -0
  39. package/dist/middleware.js +40 -0
  40. package/dist/middleware.js.map +1 -0
  41. package/dist/openapi.d.ts +17 -0
  42. package/dist/openapi.d.ts.map +1 -0
  43. package/dist/openapi.js +683 -0
  44. package/dist/openapi.js.map +1 -0
  45. package/dist/rate-limiter.d.ts +46 -0
  46. package/dist/rate-limiter.d.ts.map +1 -0
  47. package/dist/rate-limiter.js +64 -0
  48. package/dist/rate-limiter.js.map +1 -0
  49. package/dist/routes/admin.d.ts +30 -0
  50. package/dist/routes/admin.d.ts.map +1 -0
  51. package/dist/routes/admin.js +490 -0
  52. package/dist/routes/admin.js.map +1 -0
  53. package/dist/routes/auth.d.ts +18 -0
  54. package/dist/routes/auth.d.ts.map +1 -0
  55. package/dist/routes/auth.js +426 -0
  56. package/dist/routes/auth.js.map +1 -0
  57. package/dist/routes/billing.d.ts +14 -0
  58. package/dist/routes/billing.d.ts.map +1 -0
  59. package/dist/routes/billing.js +176 -0
  60. package/dist/routes/billing.js.map +1 -0
  61. package/dist/routes/index.d.ts +60 -0
  62. package/dist/routes/index.d.ts.map +1 -0
  63. package/dist/routes/index.js +133 -0
  64. package/dist/routes/index.js.map +1 -0
  65. package/dist/routes/oauth.d.ts +15 -0
  66. package/dist/routes/oauth.d.ts.map +1 -0
  67. package/dist/routes/oauth.js +215 -0
  68. package/dist/routes/oauth.js.map +1 -0
  69. package/dist/routes/shared.d.ts +71 -0
  70. package/dist/routes/shared.d.ts.map +1 -0
  71. package/dist/routes/shared.js +100 -0
  72. package/dist/routes/shared.js.map +1 -0
  73. package/dist/routes/totp.d.ts +14 -0
  74. package/dist/routes/totp.d.ts.map +1 -0
  75. package/dist/routes/totp.js +166 -0
  76. package/dist/routes/totp.js.map +1 -0
  77. package/dist/routes/usage.d.ts +14 -0
  78. package/dist/routes/usage.d.ts.map +1 -0
  79. package/dist/routes/usage.js +127 -0
  80. package/dist/routes/usage.js.map +1 -0
  81. package/dist/routes/waitlist.d.ts +16 -0
  82. package/dist/routes/waitlist.d.ts.map +1 -0
  83. package/dist/routes/waitlist.js +171 -0
  84. package/dist/routes/waitlist.js.map +1 -0
  85. package/dist/routes.d.ts +72 -0
  86. package/dist/routes.d.ts.map +1 -0
  87. package/dist/routes.js +1806 -0
  88. package/dist/routes.js.map +1 -0
  89. package/dist/totp.d.ts +41 -0
  90. package/dist/totp.d.ts.map +1 -0
  91. package/dist/totp.js +129 -0
  92. package/dist/totp.js.map +1 -0
  93. package/dist/types.d.ts +155 -0
  94. package/dist/types.d.ts.map +1 -0
  95. package/dist/types.js +6 -0
  96. package/dist/types.js.map +1 -0
  97. package/dist/usage-meter.d.ts +49 -0
  98. package/dist/usage-meter.d.ts.map +1 -0
  99. package/dist/usage-meter.js +123 -0
  100. package/dist/usage-meter.js.map +1 -0
  101. package/package.json +33 -0
@@ -0,0 +1,176 @@
1
+ /**
2
+ * Billing (Lemon Squeezy) route handlers:
3
+ * handleBillingWebhook, handleBillingCheckout, handleBillingPortal, handleBillingStatus.
4
+ * @module @panguard-ai/panguard-auth/routes/billing
5
+ */
6
+ import { authenticateRequest } from '../middleware.js';
7
+ import { verifyWebhookSignature, handleWebhookEvent, createCheckoutUrl, getCustomerPortalUrl, } from '../lemonsqueezy.js';
8
+ import { readBody, readRawBody, json } from './shared.js';
9
+ export function createBillingRoutes(ctx) {
10
+ const { db, config } = ctx;
11
+ /**
12
+ * POST /api/billing/webhook
13
+ * Receives Lemon Squeezy webhook events. Verifies HMAC signature.
14
+ */
15
+ async function handleBillingWebhook(req, res) {
16
+ if (req.method !== 'POST') {
17
+ json(res, 405, { ok: false, error: 'Method not allowed' });
18
+ return;
19
+ }
20
+ if (!config.lemonsqueezy) {
21
+ json(res, 501, { ok: false, error: 'Billing not configured' });
22
+ return;
23
+ }
24
+ const rawResult = await readRawBody(req);
25
+ if (!rawResult.ok) {
26
+ json(res, rawResult.status, { ok: false, error: 'Invalid request body' });
27
+ return;
28
+ }
29
+ // Verify HMAC signature
30
+ const signature = req.headers['x-signature'];
31
+ if (!signature ||
32
+ !verifyWebhookSignature(rawResult.raw, signature, config.lemonsqueezy.webhookSecret)) {
33
+ json(res, 401, { ok: false, error: 'Invalid webhook signature' });
34
+ return;
35
+ }
36
+ let payload;
37
+ try {
38
+ payload = JSON.parse(rawResult.raw);
39
+ }
40
+ catch {
41
+ json(res, 400, { ok: false, error: 'Invalid JSON' });
42
+ return;
43
+ }
44
+ const result = handleWebhookEvent(payload, config.lemonsqueezy, db);
45
+ // Always return 200 to prevent Lemon Squeezy from retrying
46
+ json(res, 200, { ok: true, data: result });
47
+ }
48
+ /**
49
+ * POST /api/billing/checkout
50
+ * Creates a checkout URL for the authenticated user.
51
+ * Body: { variantId: string } or { tier: string }
52
+ * When `tier` is provided, the server resolves it to a variant ID via variantTierMap.
53
+ */
54
+ async function handleBillingCheckout(req, res) {
55
+ if (req.method !== 'POST') {
56
+ json(res, 405, { ok: false, error: 'Method not allowed' });
57
+ return;
58
+ }
59
+ if (!config.lemonsqueezy) {
60
+ json(res, 501, { ok: false, error: 'Billing not configured' });
61
+ return;
62
+ }
63
+ const user = authenticateRequest(req, db);
64
+ if (!user) {
65
+ json(res, 401, { ok: false, error: 'Not authenticated' });
66
+ return;
67
+ }
68
+ const body = await readBody(req);
69
+ if (!body.ok) {
70
+ json(res, body.status, { ok: false, error: 'Invalid request body' });
71
+ return;
72
+ }
73
+ let resolvedVariantId;
74
+ // Accept variantId directly
75
+ if (typeof body.data['variantId'] === 'string' &&
76
+ body.data['variantId'].length > 0) {
77
+ resolvedVariantId = body.data['variantId'];
78
+ }
79
+ // Or resolve from tier name
80
+ else if (typeof body.data['tier'] === 'string' && body.data['tier'].length > 0) {
81
+ const tierMap = config.lemonsqueezy.variantTierMap;
82
+ // Reverse lookup: find variant ID for this tier
83
+ const entry = Object.entries(tierMap).find(([, t]) => t === body.data['tier']);
84
+ if (!entry) {
85
+ json(res, 400, {
86
+ ok: false,
87
+ error: `No variant configured for tier: ${body.data['tier']}`,
88
+ });
89
+ return;
90
+ }
91
+ resolvedVariantId = entry[0];
92
+ }
93
+ if (!resolvedVariantId) {
94
+ json(res, 400, { ok: false, error: 'variantId or tier is required' });
95
+ return;
96
+ }
97
+ const checkoutUrl = await createCheckoutUrl(config.lemonsqueezy, resolvedVariantId, {
98
+ id: user.id,
99
+ email: user.email,
100
+ name: user.name,
101
+ });
102
+ if (!checkoutUrl) {
103
+ json(res, 502, { ok: false, error: 'Failed to create checkout session' });
104
+ return;
105
+ }
106
+ json(res, 200, { ok: true, data: { url: checkoutUrl } });
107
+ }
108
+ /**
109
+ * GET /api/billing/portal
110
+ * Returns the customer portal URL for the authenticated user.
111
+ */
112
+ async function handleBillingPortal(req, res) {
113
+ if (req.method !== 'GET') {
114
+ json(res, 405, { ok: false, error: 'Method not allowed' });
115
+ return;
116
+ }
117
+ if (!config.lemonsqueezy) {
118
+ json(res, 501, { ok: false, error: 'Billing not configured' });
119
+ return;
120
+ }
121
+ const user = authenticateRequest(req, db);
122
+ if (!user) {
123
+ json(res, 401, { ok: false, error: 'Not authenticated' });
124
+ return;
125
+ }
126
+ const subscription = db.getActiveSubscription(user.id);
127
+ if (!subscription?.lsSubscriptionId) {
128
+ json(res, 404, { ok: false, error: 'No active subscription found' });
129
+ return;
130
+ }
131
+ const portalUrl = await getCustomerPortalUrl(config.lemonsqueezy, subscription.lsSubscriptionId);
132
+ if (!portalUrl) {
133
+ json(res, 502, { ok: false, error: 'Failed to get portal URL' });
134
+ return;
135
+ }
136
+ json(res, 200, { ok: true, data: { url: portalUrl } });
137
+ }
138
+ /**
139
+ * GET /api/billing/status
140
+ * Returns the current subscription status for the authenticated user.
141
+ */
142
+ function handleBillingStatus(req, res) {
143
+ if (req.method !== 'GET') {
144
+ json(res, 405, { ok: false, error: 'Method not allowed' });
145
+ return;
146
+ }
147
+ const user = authenticateRequest(req, db);
148
+ if (!user) {
149
+ json(res, 401, { ok: false, error: 'Not authenticated' });
150
+ return;
151
+ }
152
+ const subscription = db.getActiveSubscription(user.id);
153
+ json(res, 200, {
154
+ ok: true,
155
+ data: {
156
+ tier: user.tier,
157
+ planExpiresAt: user.planExpiresAt,
158
+ subscription: subscription
159
+ ? {
160
+ status: subscription.status,
161
+ tier: subscription.tier,
162
+ renewsAt: subscription.renewsAt,
163
+ endsAt: subscription.endsAt,
164
+ }
165
+ : null,
166
+ },
167
+ });
168
+ }
169
+ return {
170
+ handleBillingWebhook,
171
+ handleBillingCheckout,
172
+ handleBillingPortal,
173
+ handleBillingStatus,
174
+ };
175
+ }
176
+ //# sourceMappingURL=billing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"billing.js","sourceRoot":"","sources":["../../src/routes/billing.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,60 @@
1
+ /**
2
+ * Auth route handler factory.
3
+ * Creates all route handlers bound to a shared context (db, config, rate limiters, flow maps).
4
+ * @module @panguard-ai/panguard-auth/routes
5
+ */
6
+ import type { AuthRouteConfig } from './shared.js';
7
+ export type { AuthRouteConfig } from './shared.js';
8
+ /**
9
+ * Create all auth route handlers bound to a database instance.
10
+ */
11
+ export declare function createAuthHandlers(config: AuthRouteConfig): {
12
+ handleWaitlistJoin: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
13
+ handleWaitlistVerify: (req: import("http").IncomingMessage, res: import("http").ServerResponse, token: string) => void;
14
+ handleWaitlistStats: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
15
+ handleWaitlistList: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
16
+ handleRegister: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
17
+ handleLogin: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
18
+ handleLogout: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
19
+ handleMe: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
20
+ handleForgotPassword: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
21
+ handleResetPassword: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
22
+ handleGoogleAuth: (_req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
23
+ handleGoogleCallback: (req: import("http").IncomingMessage, res: import("http").ServerResponse, code: string, state: string | null) => Promise<void>;
24
+ handleOAuthExchange: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
25
+ handleCliAuth: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
26
+ handleCliExchange: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
27
+ handleAdminUsers: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
28
+ handleAdminUpdateTier: (req: import("http").IncomingMessage, res: import("http").ServerResponse, userId: string) => Promise<void>;
29
+ handleAdminUpdateRole: (req: import("http").IncomingMessage, res: import("http").ServerResponse, userId: string) => Promise<void>;
30
+ handleAdminStats: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
31
+ handleAdminWaitlistApprove: (req: import("http").IncomingMessage, res: import("http").ServerResponse, entryId: string) => Promise<void>;
32
+ handleAdminWaitlistReject: (req: import("http").IncomingMessage, res: import("http").ServerResponse, entryId: string) => Promise<void>;
33
+ handleAdminDashboard: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
34
+ handleAdminUsersSearch: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
35
+ handleAdminSessions: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
36
+ handleAdminSessionRevoke: (req: import("http").IncomingMessage, res: import("http").ServerResponse, sessionId: string) => void;
37
+ handleAdminActivity: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
38
+ handleAdminAuditLog: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
39
+ handleAdminAuditActions: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
40
+ handleAdminUsageOverview: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
41
+ handleAdminUsageUser: (req: import("http").IncomingMessage, res: import("http").ServerResponse, userId: string) => void;
42
+ handleAdminUserDetail: (req: import("http").IncomingMessage, res: import("http").ServerResponse, userId: string) => void;
43
+ handleAdminUserSuspend: (req: import("http").IncomingMessage, res: import("http").ServerResponse, userId: string) => Promise<void>;
44
+ handleAdminBulkAction: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
45
+ handleDeleteAccount: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
46
+ handleExportData: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
47
+ handleTotpSetup: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
48
+ handleTotpVerify: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
49
+ handleTotpDisable: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
50
+ handleTotpStatus: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
51
+ handleBillingWebhook: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
52
+ handleBillingCheckout: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
53
+ handleBillingPortal: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
54
+ handleBillingStatus: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
55
+ handleUsageSummary: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
56
+ handleUsageLimits: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => void;
57
+ handleUsageCheck: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
58
+ handleUsageRecord: (req: import("http").IncomingMessage, res: import("http").ServerResponse) => Promise<void>;
59
+ };
60
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,eAAe,EAAgB,MAAM,aAAa,CAAC;AASjE,YAAY,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEnD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2HzD"}
@@ -0,0 +1,133 @@
1
+ /**
2
+ * Auth route handler factory.
3
+ * Creates all route handlers bound to a shared context (db, config, rate limiters, flow maps).
4
+ * @module @panguard-ai/panguard-auth/routes
5
+ */
6
+ import { ensureSheetHeaders } from '../google-sheets.js';
7
+ import { RateLimiter } from '../rate-limiter.js';
8
+ import { createAuthRoutes } from './auth.js';
9
+ import { createOAuthRoutes } from './oauth.js';
10
+ import { createTotpRoutes } from './totp.js';
11
+ import { createBillingRoutes } from './billing.js';
12
+ import { createAdminRoutes } from './admin.js';
13
+ import { createWaitlistRoutes } from './waitlist.js';
14
+ import { createUsageRoutes } from './usage.js';
15
+ /**
16
+ * Create all auth route handlers bound to a database instance.
17
+ */
18
+ export function createAuthHandlers(config) {
19
+ const { db } = config;
20
+ // Rate limiters
21
+ const loginLimiter = new RateLimiter({ windowMs: 15 * 60 * 1000, maxRequests: 10 });
22
+ const registerLimiter = new RateLimiter({ windowMs: 60 * 60 * 1000, maxRequests: 5 });
23
+ const resetLimiter = new RateLimiter({ windowMs: 15 * 60 * 1000, maxRequests: 5 });
24
+ const waitlistLimiter = new RateLimiter({ windowMs: 60 * 60 * 1000, maxRequests: 5 });
25
+ // Pending OAuth flows: state -> { codeVerifier, createdAt }
26
+ const pendingOAuthFlows = new Map();
27
+ // Pending CLI auth flows: state -> { callbackUrl, createdAt }
28
+ const pendingCliFlows = new Map();
29
+ // One-time OAuth exchange codes: code -> { sessionToken, expiresAt, createdAt }
30
+ const oauthExchangeCodes = new Map();
31
+ // Cleanup stale flows every 5 minutes
32
+ const oauthCleanupTimer = setInterval(() => {
33
+ const cutoff = Date.now() - 10 * 60 * 1000; // 10 min TTL
34
+ for (const [state, flow] of pendingOAuthFlows) {
35
+ if (flow.createdAt < cutoff)
36
+ pendingOAuthFlows.delete(state);
37
+ }
38
+ for (const [state, flow] of pendingCliFlows) {
39
+ if (flow.createdAt < cutoff)
40
+ pendingCliFlows.delete(state);
41
+ }
42
+ const codeCutoff = Date.now() - 5 * 60 * 1000; // 5 min TTL for exchange codes
43
+ for (const [code, data] of oauthExchangeCodes) {
44
+ if (data.createdAt < codeCutoff)
45
+ oauthExchangeCodes.delete(code);
46
+ }
47
+ }, 5 * 60 * 1000);
48
+ if (oauthCleanupTimer.unref)
49
+ oauthCleanupTimer.unref();
50
+ const ctx = {
51
+ db,
52
+ config,
53
+ loginLimiter,
54
+ registerLimiter,
55
+ resetLimiter,
56
+ waitlistLimiter,
57
+ pendingOAuthFlows,
58
+ pendingCliFlows,
59
+ oauthExchangeCodes,
60
+ };
61
+ // Init Google Sheets headers (best-effort, non-blocking)
62
+ if (config.sheets) {
63
+ ensureSheetHeaders(config.sheets).catch(() => {
64
+ // Best-effort header initialization
65
+ });
66
+ }
67
+ // Compose all handlers from sub-modules
68
+ const authHandlers = createAuthRoutes(ctx);
69
+ const oauthHandlers = createOAuthRoutes(ctx);
70
+ const totpHandlers = createTotpRoutes(ctx);
71
+ const billingHandlers = createBillingRoutes(ctx);
72
+ const adminHandlers = createAdminRoutes(ctx);
73
+ const waitlistHandlers = createWaitlistRoutes(ctx);
74
+ const usageHandlers = createUsageRoutes(ctx);
75
+ return {
76
+ // Waitlist
77
+ handleWaitlistJoin: waitlistHandlers.handleWaitlistJoin,
78
+ handleWaitlistVerify: waitlistHandlers.handleWaitlistVerify,
79
+ handleWaitlistStats: waitlistHandlers.handleWaitlistStats,
80
+ handleWaitlistList: waitlistHandlers.handleWaitlistList,
81
+ // Auth
82
+ handleRegister: authHandlers.handleRegister,
83
+ handleLogin: authHandlers.handleLogin,
84
+ handleLogout: authHandlers.handleLogout,
85
+ handleMe: authHandlers.handleMe,
86
+ handleForgotPassword: authHandlers.handleForgotPassword,
87
+ handleResetPassword: authHandlers.handleResetPassword,
88
+ // OAuth / CLI
89
+ handleGoogleAuth: oauthHandlers.handleGoogleAuth,
90
+ handleGoogleCallback: oauthHandlers.handleGoogleCallback,
91
+ handleOAuthExchange: oauthHandlers.handleOAuthExchange,
92
+ handleCliAuth: oauthHandlers.handleCliAuth,
93
+ handleCliExchange: oauthHandlers.handleCliExchange,
94
+ // Admin
95
+ handleAdminUsers: adminHandlers.handleAdminUsers,
96
+ handleAdminUpdateTier: adminHandlers.handleAdminUpdateTier,
97
+ handleAdminUpdateRole: adminHandlers.handleAdminUpdateRole,
98
+ handleAdminStats: adminHandlers.handleAdminStats,
99
+ handleAdminWaitlistApprove: waitlistHandlers.handleAdminWaitlistApprove,
100
+ handleAdminWaitlistReject: waitlistHandlers.handleAdminWaitlistReject,
101
+ handleAdminDashboard: adminHandlers.handleAdminDashboard,
102
+ handleAdminUsersSearch: adminHandlers.handleAdminUsersSearch,
103
+ handleAdminSessions: adminHandlers.handleAdminSessions,
104
+ handleAdminSessionRevoke: adminHandlers.handleAdminSessionRevoke,
105
+ handleAdminActivity: adminHandlers.handleAdminActivity,
106
+ handleAdminAuditLog: adminHandlers.handleAdminAuditLog,
107
+ handleAdminAuditActions: adminHandlers.handleAdminAuditActions,
108
+ handleAdminUsageOverview: adminHandlers.handleAdminUsageOverview,
109
+ handleAdminUsageUser: adminHandlers.handleAdminUsageUser,
110
+ handleAdminUserDetail: adminHandlers.handleAdminUserDetail,
111
+ handleAdminUserSuspend: adminHandlers.handleAdminUserSuspend,
112
+ handleAdminBulkAction: adminHandlers.handleAdminBulkAction,
113
+ // GDPR
114
+ handleDeleteAccount: authHandlers.handleDeleteAccount,
115
+ handleExportData: authHandlers.handleExportData,
116
+ // TOTP (2FA)
117
+ handleTotpSetup: totpHandlers.handleTotpSetup,
118
+ handleTotpVerify: totpHandlers.handleTotpVerify,
119
+ handleTotpDisable: totpHandlers.handleTotpDisable,
120
+ handleTotpStatus: totpHandlers.handleTotpStatus,
121
+ // Billing (Lemon Squeezy)
122
+ handleBillingWebhook: billingHandlers.handleBillingWebhook,
123
+ handleBillingCheckout: billingHandlers.handleBillingCheckout,
124
+ handleBillingPortal: billingHandlers.handleBillingPortal,
125
+ handleBillingStatus: billingHandlers.handleBillingStatus,
126
+ // Usage / Quota
127
+ handleUsageSummary: usageHandlers.handleUsageSummary,
128
+ handleUsageLimits: usageHandlers.handleUsageLimits,
129
+ handleUsageCheck: usageHandlers.handleUsageCheck,
130
+ handleUsageRecord: usageHandlers.handleUsageRecord,
131
+ };
132
+ }
133
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/routes/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * OAuth and CLI authentication route handlers:
3
+ * handleGoogleAuth, handleGoogleCallback, handleOAuthExchange, handleCliAuth, handleCliExchange.
4
+ * @module @panguard-ai/panguard-auth/routes/oauth
5
+ */
6
+ import type { IncomingMessage, ServerResponse } from 'node:http';
7
+ import type { RouteContext } from './shared.js';
8
+ export declare function createOAuthRoutes(ctx: RouteContext): {
9
+ handleGoogleAuth: (_req: IncomingMessage, res: ServerResponse) => void;
10
+ handleGoogleCallback: (req: IncomingMessage, res: ServerResponse, code: string, state: string | null) => Promise<void>;
11
+ handleOAuthExchange: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
12
+ handleCliAuth: (req: IncomingMessage, res: ServerResponse) => void;
13
+ handleCliExchange: (req: IncomingMessage, res: ServerResponse) => Promise<void>;
14
+ };
15
+ //# sourceMappingURL=oauth.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.d.ts","sourceRoot":"","sources":["../../src/routes/oauth.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,eAAe,EAAE,cAAc,EAAE,MAAM,WAAW,CAAC;AAWjE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAGhD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,YAAY;6BAGjB,eAAe,OAAO,cAAc,KAAG,IAAI;gCAepE,eAAe,OACf,cAAc,QACb,MAAM,SACL,MAAM,GAAG,IAAI,KACnB,OAAO,CAAC,IAAI,CAAC;+BAsLwB,eAAe,OAAO,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;yBAlGhE,eAAe,OAAO,cAAc,KAAG,IAAI;6BAoCjC,eAAe,OAAO,cAAc,KAAG,OAAO,CAAC,IAAI,CAAC;EAsG3F"}
@@ -0,0 +1,215 @@
1
+ /**
2
+ * OAuth and CLI authentication route handlers:
3
+ * handleGoogleAuth, handleGoogleCallback, handleOAuthExchange, handleCliAuth, handleCliExchange.
4
+ * @module @panguard-ai/panguard-auth/routes/oauth
5
+ */
6
+ import { hashPassword, generateSessionToken, generateVerifyToken, sessionExpiry } from '../auth.js';
7
+ import { authenticateRequest } from '../middleware.js';
8
+ import { getGoogleAuthUrl, exchangeCodeForTokens, getGoogleUserInfo, generateCodeVerifier, generateCodeChallenge, generateOAuthState, } from '../google-oauth.js';
9
+ import { readBody, json } from './shared.js';
10
+ export function createOAuthRoutes(ctx) {
11
+ const { db, config, pendingOAuthFlows, pendingCliFlows, oauthExchangeCodes } = ctx;
12
+ function handleGoogleAuth(_req, res) {
13
+ if (!config.google) {
14
+ json(res, 501, { ok: false, error: 'Google OAuth not configured' });
15
+ return;
16
+ }
17
+ const state = generateOAuthState();
18
+ const codeVerifier = generateCodeVerifier();
19
+ const codeChallenge = generateCodeChallenge(codeVerifier);
20
+ pendingOAuthFlows.set(state, { codeVerifier, createdAt: Date.now() });
21
+ const url = getGoogleAuthUrl(config.google, state, codeChallenge);
22
+ res.writeHead(302, { Location: url });
23
+ res.end();
24
+ }
25
+ async function handleGoogleCallback(req, res, code, state) {
26
+ if (!config.google) {
27
+ json(res, 501, { ok: false, error: 'Google OAuth not configured' });
28
+ return;
29
+ }
30
+ // Validate state parameter (CSRF protection)
31
+ if (!state) {
32
+ json(res, 400, { ok: false, error: 'Missing state parameter' });
33
+ return;
34
+ }
35
+ const flow = pendingOAuthFlows.get(state);
36
+ if (!flow) {
37
+ json(res, 403, { ok: false, error: 'Invalid or expired OAuth state' });
38
+ return;
39
+ }
40
+ pendingOAuthFlows.delete(state);
41
+ try {
42
+ const tokens = await exchangeCodeForTokens(config.google, code, flow.codeVerifier);
43
+ const googleUser = await getGoogleUserInfo(tokens.access_token);
44
+ if (!googleUser.email) {
45
+ json(res, 400, { ok: false, error: 'No email from Google account' });
46
+ return;
47
+ }
48
+ if (!googleUser.email_verified) {
49
+ json(res, 403, { ok: false, error: 'Email not verified with Google' });
50
+ return;
51
+ }
52
+ // Find or create user
53
+ let user = db.getUserByEmail(googleUser.email);
54
+ if (!user) {
55
+ // Auto-create user from Google profile (random password since they use OAuth)
56
+ const randomPw = generateSessionToken(); // Not used for login, just to fill the field
57
+ const pwHash = await hashPassword(randomPw);
58
+ user = db.createUser({
59
+ email: googleUser.email,
60
+ name: googleUser.name || googleUser.email,
61
+ password: randomPw,
62
+ }, pwHash);
63
+ // Activate 14-day Solo trial for new OAuth users
64
+ const trialExpiry = new Date(Date.now() + 14 * 24 * 60 * 60 * 1000)
65
+ .toISOString()
66
+ .replace('T', ' ')
67
+ .split('.')[0];
68
+ db.updateUserTier(user.id, 'solo', trialExpiry);
69
+ user = db.getUserById(user.id);
70
+ }
71
+ db.updateLastLogin(user.id);
72
+ const sessionToken = generateSessionToken();
73
+ const session = db.createSession(user.id, sessionToken, sessionExpiry());
74
+ // Use one-time exchange code instead of putting token in URL
75
+ const exchangeCode = generateVerifyToken(); // UUID v4
76
+ oauthExchangeCodes.set(exchangeCode, {
77
+ sessionToken,
78
+ expiresAt: session.expiresAt,
79
+ createdAt: Date.now(),
80
+ });
81
+ const baseUrl = config.baseUrl ?? '';
82
+ const redirectUrl = `${baseUrl}/login?code=${exchangeCode}`;
83
+ res.writeHead(302, { Location: redirectUrl });
84
+ res.end();
85
+ }
86
+ catch {
87
+ json(res, 500, {
88
+ ok: false,
89
+ error: 'Google OAuth failed',
90
+ });
91
+ }
92
+ }
93
+ /**
94
+ * GET /api/auth/cli
95
+ * Initiates CLI auth flow: validates callback URL and redirects to web login.
96
+ */
97
+ function handleCliAuth(req, res) {
98
+ const urlObj = new URL(req.url ?? '/', `http://${req.headers.host ?? 'localhost'}`);
99
+ const callbackUrl = urlObj.searchParams.get('callback');
100
+ const state = urlObj.searchParams.get('state');
101
+ if (!callbackUrl || !state) {
102
+ json(res, 400, { ok: false, error: 'Missing callback or state parameter' });
103
+ return;
104
+ }
105
+ // Security: callback must be localhost
106
+ try {
107
+ const callbackParsed = new URL(callbackUrl);
108
+ if (!['localhost', '127.0.0.1', '[::1]'].includes(callbackParsed.hostname)) {
109
+ json(res, 400, { ok: false, error: 'Callback must be localhost' });
110
+ return;
111
+ }
112
+ }
113
+ catch {
114
+ json(res, 400, { ok: false, error: 'Invalid callback URL' });
115
+ return;
116
+ }
117
+ pendingCliFlows.set(state, { callbackUrl, createdAt: Date.now() });
118
+ // Redirect to web login page with cli_state
119
+ const baseUrl = config.baseUrl ?? '';
120
+ const loginUrl = `${baseUrl}/login?cli_state=${encodeURIComponent(state)}`;
121
+ res.writeHead(302, { Location: loginUrl });
122
+ res.end();
123
+ }
124
+ /**
125
+ * POST /api/auth/cli/exchange
126
+ * Exchanges a web session token for a long-lived CLI session.
127
+ * Returns the CLI callback redirect URL.
128
+ */
129
+ async function handleCliExchange(req, res) {
130
+ if (req.method !== 'POST') {
131
+ json(res, 405, { ok: false, error: 'Method not allowed' });
132
+ return;
133
+ }
134
+ const result = await readBody(req);
135
+ if (!result.ok) {
136
+ json(res, result.status, {
137
+ ok: false,
138
+ error: result.status === 413 ? 'Payload too large' : 'Invalid JSON body',
139
+ });
140
+ return;
141
+ }
142
+ const body = result.data;
143
+ const { token, state } = body;
144
+ if (typeof token !== 'string' || typeof state !== 'string') {
145
+ json(res, 400, { ok: false, error: 'Token and state are required' });
146
+ return;
147
+ }
148
+ // Validate web session
149
+ const user = authenticateRequest({ headers: { authorization: `Bearer ${token}` } }, db);
150
+ if (!user) {
151
+ json(res, 401, { ok: false, error: 'Invalid session token' });
152
+ return;
153
+ }
154
+ // Look up pending CLI flow
155
+ const flow = pendingCliFlows.get(state);
156
+ if (!flow) {
157
+ json(res, 403, { ok: false, error: 'Invalid or expired CLI state' });
158
+ return;
159
+ }
160
+ pendingCliFlows.delete(state);
161
+ // Create long-lived CLI session (30 days)
162
+ const cliToken = generateSessionToken();
163
+ const cliSession = db.createSession(user.id, cliToken, sessionExpiry(24 * 30));
164
+ // Build redirect URL back to CLI callback
165
+ const params = new URLSearchParams({
166
+ token: cliSession.token,
167
+ expires: cliSession.expiresAt,
168
+ email: user.email,
169
+ name: user.name,
170
+ tier: user.tier,
171
+ state,
172
+ });
173
+ const redirectUrl = `${flow.callbackUrl}?${params.toString()}`;
174
+ json(res, 200, { ok: true, data: { redirectUrl } });
175
+ }
176
+ /**
177
+ * POST /api/auth/oauth/exchange
178
+ * Exchange a one-time code for a session token (keeps tokens out of URLs).
179
+ */
180
+ async function handleOAuthExchange(req, res) {
181
+ if (req.method !== 'POST') {
182
+ json(res, 405, { ok: false, error: 'Method not allowed' });
183
+ return;
184
+ }
185
+ const body = await readBody(req);
186
+ if (!body.ok) {
187
+ json(res, body.status, { ok: false, error: 'Invalid request body' });
188
+ return;
189
+ }
190
+ const code = body.data['code'];
191
+ if (typeof code !== 'string' || !code) {
192
+ json(res, 400, { ok: false, error: 'code is required' });
193
+ return;
194
+ }
195
+ const data = oauthExchangeCodes.get(code);
196
+ if (!data) {
197
+ json(res, 400, { ok: false, error: 'Invalid or expired code' });
198
+ return;
199
+ }
200
+ // One-time use — delete immediately
201
+ oauthExchangeCodes.delete(code);
202
+ json(res, 200, {
203
+ ok: true,
204
+ data: { token: data.sessionToken, expiresAt: data.expiresAt },
205
+ });
206
+ }
207
+ return {
208
+ handleGoogleAuth,
209
+ handleGoogleCallback,
210
+ handleOAuthExchange,
211
+ handleCliAuth,
212
+ handleCliExchange,
213
+ };
214
+ }
215
+ //# sourceMappingURL=oauth.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"oauth.js","sourceRoot":"","sources":["../../src/routes/oauth.ts"],"names":[],"mappings":"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"}