repositories
loading repo index
repositories
loading repo index
repository
loading code, commits, and activity
public Clawd ADK gateway launch mirror
stars
latest
clone command
git clone gitlawb://did:key:z6Mkq5mY...iFZ5/my-project-publ...git clone gitlawb://did:key:z6Mkq5mY.../my-project-publ...2fa351d6docs: add automaton and perps launch sources16d ago| #1 | import type { Context, Next } from 'hono'; |
| #2 | import type { AppEnv, MoltbotEnv } from '../types'; |
| #3 | import { verifyAccessJWT } from './jwt'; |
| #4 | |
| #5 | /** |
| #6 | * Options for creating an access middleware |
| #7 | */ |
| #8 | export interface AccessMiddlewareOptions { |
| #9 | /** Response type: 'json' for API routes, 'html' for UI routes */ |
| #10 | type: 'json' | 'html'; |
| #11 | /** Whether to redirect to login when JWT is missing (only for 'html' type) */ |
| #12 | redirectOnMissing?: boolean; |
| #13 | } |
| #14 | |
| #15 | /** |
| #16 | * Check if running in development mode (skips CF Access auth) |
| #17 | */ |
| #18 | export function isDevMode(env: MoltbotEnv): boolean { |
| #19 | return env.DEV_MODE === 'true'; |
| #20 | } |
| #21 | |
| #22 | /** |
| #23 | * Extract JWT from request headers or cookies |
| #24 | */ |
| #25 | export function extractJWT(c: Context<AppEnv>): string | null { |
| #26 | const jwtHeader = c.req.header('CF-Access-JWT-Assertion'); |
| #27 | const jwtCookie = c.req.raw.headers.get('Cookie') |
| #28 | ?.split(';') |
| #29 | .find(cookie => cookie.trim().startsWith('CF_Authorization=')) |
| #30 | ?.split('=')[1]; |
| #31 | |
| #32 | return jwtHeader || jwtCookie || null; |
| #33 | } |
| #34 | |
| #35 | /** |
| #36 | * Create a Cloudflare Access authentication middleware |
| #37 | * |
| #38 | * @param options - Middleware options |
| #39 | * @returns Hono middleware function |
| #40 | */ |
| #41 | export function createAccessMiddleware(options: AccessMiddlewareOptions) { |
| #42 | const { type, redirectOnMissing = false } = options; |
| #43 | |
| #44 | return async (c: Context<AppEnv>, next: Next) => { |
| #45 | // Skip auth in dev mode |
| #46 | if (isDevMode(c.env)) { |
| #47 | c.set('accessUser', { email: 'dev@localhost', name: 'Dev User' }); |
| #48 | return next(); |
| #49 | } |
| #50 | |
| #51 | const teamDomain = c.env.CF_ACCESS_TEAM_DOMAIN; |
| #52 | const expectedAud = c.env.CF_ACCESS_AUD; |
| #53 | |
| #54 | // Check if CF Access is configured |
| #55 | if (!teamDomain || !expectedAud) { |
| #56 | if (type === 'json') { |
| #57 | return c.json({ |
| #58 | error: 'Cloudflare Access not configured', |
| #59 | hint: 'Set CF_ACCESS_TEAM_DOMAIN and CF_ACCESS_AUD environment variables', |
| #60 | }, 500); |
| #61 | } else { |
| #62 | return c.html(` |
| #63 | <html> |
| #64 | <body> |
| #65 | <h1>Admin UI Not Configured</h1> |
| #66 | <p>Set CF_ACCESS_TEAM_DOMAIN and CF_ACCESS_AUD environment variables.</p> |
| #67 | </body> |
| #68 | </html> |
| #69 | `, 500); |
| #70 | } |
| #71 | } |
| #72 | |
| #73 | // Get JWT |
| #74 | const jwt = extractJWT(c); |
| #75 | |
| #76 | if (!jwt) { |
| #77 | if (type === 'html' && redirectOnMissing) { |
| #78 | return c.redirect(`https://${teamDomain}`, 302); |
| #79 | } |
| #80 | |
| #81 | if (type === 'json') { |
| #82 | return c.json({ |
| #83 | error: 'Unauthorized', |
| #84 | hint: 'Missing Cloudflare Access JWT. Ensure this route is protected by Cloudflare Access.', |
| #85 | }, 401); |
| #86 | } else { |
| #87 | return c.html(` |
| #88 | <html> |
| #89 | <body> |
| #90 | <h1>Unauthorized</h1> |
| #91 | <p>Missing Cloudflare Access token.</p> |
| #92 | <a href="https://${teamDomain}">Login</a> |
| #93 | </body> |
| #94 | </html> |
| #95 | `, 401); |
| #96 | } |
| #97 | } |
| #98 | |
| #99 | // Verify JWT |
| #100 | try { |
| #101 | const payload = await verifyAccessJWT(jwt, teamDomain, expectedAud); |
| #102 | c.set('accessUser', { email: payload.email, name: payload.name }); |
| #103 | await next(); |
| #104 | } catch (err) { |
| #105 | console.error('Access JWT verification failed:', err); |
| #106 | |
| #107 | if (type === 'json') { |
| #108 | return c.json({ |
| #109 | error: 'Unauthorized', |
| #110 | details: err instanceof Error ? err.message : 'JWT verification failed', |
| #111 | }, 401); |
| #112 | } else { |
| #113 | return c.html(` |
| #114 | <html> |
| #115 | <body> |
| #116 | <h1>Unauthorized</h1> |
| #117 | <p>Your Cloudflare Access session is invalid or expired.</p> |
| #118 | <a href="https://${teamDomain}">Login again</a> |
| #119 | </body> |
| #120 | </html> |
| #121 | `, 401); |
| #122 | } |
| #123 | } |
| #124 | }; |
| #125 | } |
| #126 |