From 04cfe0f726789c24b3be17a4516ee6457dea1c42 Mon Sep 17 00:00:00 2001 From: Bas van Rossem Date: Wed, 17 Jun 2026 13:50:31 +0200 Subject: [PATCH] feat(api): add protected GET /api/me and full auth round-trip test --- apps/api/src/app.ts | 2 ++ apps/api/src/routes/me.ts | 20 ++++++++++++++++++++ apps/api/test/me.test.ts | 36 ++++++++++++++++++++++++++++++++++++ 3 files changed, 58 insertions(+) create mode 100644 apps/api/src/routes/me.ts create mode 100644 apps/api/test/me.test.ts diff --git a/apps/api/src/app.ts b/apps/api/src/app.ts index e64e029..bd8b6f4 100644 --- a/apps/api/src/app.ts +++ b/apps/api/src/app.ts @@ -1,10 +1,12 @@ import { Hono } from 'hono'; import { health } from './routes/health'; +import { me } from './routes/me'; import { auth } from './auth'; export function createApp(): Hono { const app = new Hono(); app.route('/', health); app.on(['POST', 'GET'], '/api/auth/*', (c) => auth.handler(c.req.raw)); + app.route('/', me); return app; } diff --git a/apps/api/src/routes/me.ts b/apps/api/src/routes/me.ts new file mode 100644 index 0000000..5c962ef --- /dev/null +++ b/apps/api/src/routes/me.ts @@ -0,0 +1,20 @@ +import { Hono } from 'hono'; +import type { MeResponse } from '@solelog/shared'; +import { auth } from '../auth'; + +export const me = new Hono(); + +me.get('/api/me', async (c) => { + const session = await auth.api.getSession({ headers: c.req.raw.headers }); + if (!session) { + return c.json({ error: 'Unauthorized' }, 401); + } + const body: MeResponse = { + user: { + id: session.user.id, + email: session.user.email, + name: session.user.name, + }, + }; + return c.json(body); +}); diff --git a/apps/api/test/me.test.ts b/apps/api/test/me.test.ts new file mode 100644 index 0000000..248be8b --- /dev/null +++ b/apps/api/test/me.test.ts @@ -0,0 +1,36 @@ +import { describe, it, expect } from 'vitest'; +import { createApp } from '../src/app'; + +const json = { 'content-type': 'application/json' }; + +describe('GET /api/me', () => { + it('rejects an unauthenticated request', async () => { + const app = createApp(); + const res = await app.request('/api/me'); + expect(res.status).toBe(401); + }); + + it('returns the user for a valid bearer token (sign-up -> sign-in -> me)', async () => { + const app = createApp(); + const creds = { email: 'me@example.com', password: 'sterk-wachtwoord-123', name: 'Me' }; + + await app.request('/api/auth/sign-up/email', { + method: 'POST', + headers: json, + body: JSON.stringify(creds), + }); + const signin = await app.request('/api/auth/sign-in/email', { + method: 'POST', + headers: json, + body: JSON.stringify({ email: creds.email, password: creds.password }), + }); + const token = signin.headers.get('set-auth-token'); + + const res = await app.request('/api/me', { + headers: { authorization: `Bearer ${token}` }, + }); + expect(res.status).toBe(200); + const body = await res.json(); + expect(body.user.email).toBe(creds.email); + }); +});