From ba70435e63c1d841789aa6e889df39bce70b99e4 Mon Sep 17 00:00:00 2001 From: TYRONE AVNIT Date: Wed, 4 Dec 2024 12:47:29 -0600 Subject: [PATCH] TECH-1068: Fix Hono example --- packages/civic-auth/server/hono/README.md | 2 +- packages/civic-auth/server/hono/src/index.ts | 102 ++++++++++--------- 2 files changed, 55 insertions(+), 49 deletions(-) diff --git a/packages/civic-auth/server/hono/README.md b/packages/civic-auth/server/hono/README.md index aaa4d80..4fbd490 100644 --- a/packages/civic-auth/server/hono/README.md +++ b/packages/civic-auth/server/hono/README.md @@ -32,7 +32,7 @@ SESSION_SECRET=your_secure_session_secret Start the hono server using _Yarn_: ```bash -yarn dv +yarn dev ``` The server will start on `http://localhost:3000`. diff --git a/packages/civic-auth/server/hono/src/index.ts b/packages/civic-auth/server/hono/src/index.ts index 7abd112..71e1337 100644 --- a/packages/civic-auth/server/hono/src/index.ts +++ b/packages/civic-auth/server/hono/src/index.ts @@ -1,19 +1,36 @@ -import { Hono } from "hono"; -import { serve } from "@hono/node-server"; -import { buildLoginUrl, resolveOAuthAccessCode } from "@civic/auth/server"; -import { getCookie, setCookie } from "hono/cookie"; -import { CookieStorage } from "@civic/auth/server"; -import type { Context } from "hono"; +import type { AuthStorage } from '@civic/auth'; +import { + CookieStorage, + resolveOAuthAccessCode, + isLoggedIn, + getUser, + buildLoginUrl, + refreshTokens +} from '@civic/auth/server'; +import { serve } from '@hono/node-server'; +import { type Context, Hono } from 'hono'; +import { getCookie, setCookie } from 'hono/cookie'; import "dotenv/config"; +type Variables = { storage: AuthStorage }; + +export const app = new Hono<{ Variables: Variables }>(); +const PORT = process.env.PORT ? parseInt(process.env.PORT) : 3000; + +const config = { + clientId: process.env.CLIENT_ID!, + redirectUrl: `http://localhost:${PORT}/auth/callback`, + oauthServer: process.env.OAUTH_SERVER ?? "https://auth.civic.com/oauth", +} + +// Map hono cookies to the CookieStorage interface class HonoCookieStorage extends CookieStorage { constructor(private c: Context) { super(); } async get(key: string): Promise { - const value = getCookie(this.c, key); - return value !== undefined ? value : null; + return Promise.resolve(getCookie(this.c, key) ?? null); } async set(key: string, value: string): Promise { @@ -21,63 +38,52 @@ class HonoCookieStorage extends CookieStorage { } } -type Env = { - Variables: { - storage: HonoCookieStorage; - }; -}; - -const app = new Hono(); - -const config = { - clientId: process.env.CLIENT_ID!, - redirectUrl: `http://localhost:3000/auth/callback`, // change to your domain when deploying -}; - // Middleware to attach CookieStorage to each request -app.use("*", async (c, next) => { - const storage = new HonoCookieStorage(c); - c.set("storage", storage); +app.use('*', async (c, next) => { + const storage = new HonoCookieStorage(c) + c.set('storage', storage); return next(); }); -app.get("/", async (c) => { - const url = await buildLoginUrl(config, c.get("storage")); +// Endpoint to trigger redirect to Civic Auth OAuth server +app.get('/', async (c) => { + const url = await buildLoginUrl(config, c.get('storage')); return c.redirect(url.toString()); }); -app.get("/auth/callback", async (c) => { - const code = c.req.query("code"); - const state = c.req.query("state"); - - console.log({ code, state }); - - if (!code || !state) { - return c.redirect("/"); - } +// Endpoint to handle OAuth callback and resolve access code +app.get('/auth/callback', async (c) => { + const code = c.req.query('code') as string + const state = c.req.query('state') as string // Resolve OAuth access code and set session - await resolveOAuthAccessCode(code, state, c.get("storage"), config); - - c.redirect("/admin/hello"); + await resolveOAuthAccessCode(code, state, c.get('storage'), config); + return c.redirect('/admin/hello'); }); -app.get("/admin/hello", async (c) => { - const user = await c.get("storage").get("user"); - - if (!user) { - return c.redirect("/"); - } +// Authentication middleware to protect routes +// Apply to /admin routes +app.use('/admin/*', async (c, next) => { + if (!(await isLoggedIn(c.get('storage')))) return c.text('Unauthorized', 401); - return c.json({ user }); + return next(); }); -const port = 3000; +// Protected route to get logged-in user information +app.get('/admin/hello', async (c) => { + const user = await getUser(c.get('storage')); + return c.text(`Hello, ${user?.name}!`); +}); -console.log(`Server is running on http://localhost:${port}`); +app.get('/admin/refresh', async (c) => { + await refreshTokens(c.get('storage'), config); + c.text('Tokens refreshed'); +}); serve({ fetch: app.fetch, - port, + port: PORT, }); + +console.log(`Server is running on http://localhost:${PORT}`);