-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #15 from civicteam/TECH-1068__fix-hono-example
TECH-1068: Fix Hono example
- Loading branch information
Showing
2 changed files
with
55 additions
and
49 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,83 +1,89 @@ | ||
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<string | null> { | ||
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<void> { | ||
setCookie(this.c, key, value); | ||
} | ||
} | ||
|
||
type Env = { | ||
Variables: { | ||
storage: HonoCookieStorage; | ||
}; | ||
}; | ||
|
||
const app = new Hono<Env>(); | ||
|
||
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}`); |