Skip to content

Commit

Permalink
Merge pull request #15 from civicteam/TECH-1068__fix-hono-example
Browse files Browse the repository at this point in the history
TECH-1068: Fix Hono example
  • Loading branch information
kevinhcolgan authored Dec 5, 2024
2 parents d74f121 + ba70435 commit 4aa22d9
Show file tree
Hide file tree
Showing 2 changed files with 55 additions and 49 deletions.
2 changes: 1 addition & 1 deletion packages/civic-auth/server/hono/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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`.
Expand Down
102 changes: 54 additions & 48 deletions packages/civic-auth/server/hono/src/index.ts
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}`);

0 comments on commit 4aa22d9

Please sign in to comment.