feat(api): add protected GET /api/me and full auth round-trip test
This commit is contained in:
@@ -1,10 +1,12 @@
|
|||||||
import { Hono } from 'hono';
|
import { Hono } from 'hono';
|
||||||
import { health } from './routes/health';
|
import { health } from './routes/health';
|
||||||
|
import { me } from './routes/me';
|
||||||
import { auth } from './auth';
|
import { auth } from './auth';
|
||||||
|
|
||||||
export function createApp(): Hono {
|
export function createApp(): Hono {
|
||||||
const app = new Hono();
|
const app = new Hono();
|
||||||
app.route('/', health);
|
app.route('/', health);
|
||||||
app.on(['POST', 'GET'], '/api/auth/*', (c) => auth.handler(c.req.raw));
|
app.on(['POST', 'GET'], '/api/auth/*', (c) => auth.handler(c.req.raw));
|
||||||
|
app.route('/', me);
|
||||||
return app;
|
return app;
|
||||||
}
|
}
|
||||||
|
|||||||
20
apps/api/src/routes/me.ts
Normal file
20
apps/api/src/routes/me.ts
Normal file
@@ -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);
|
||||||
|
});
|
||||||
36
apps/api/test/me.test.ts
Normal file
36
apps/api/test/me.test.ts
Normal file
@@ -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);
|
||||||
|
});
|
||||||
|
});
|
||||||
Reference in New Issue
Block a user