Skip to content

Commit eb45cb4

Browse files
committed
feat: auth flow 👀
1 parent 109ccc5 commit eb45cb4

16 files changed

Lines changed: 672 additions & 260 deletions

File tree

‎.vscode/settings.json‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
{
2+
"editor.formatOnSave": true,
3+
"editor.codeActionsOnSave": {
4+
"source.fixAll.biome": "explicit",
5+
"source.organizeImports.biome": "explicit"
6+
}
7+
}

‎bun.lock‎

Lines changed: 15 additions & 12 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎infra/vps.ts‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { domain } from "./dns";
44
import { email } from "./email";
55
import { allSecrets } from "./secret";
6+
import { www } from "./www";
67

78
export const vpc = new sst.aws.Vpc("gbfm_network");
89

@@ -52,7 +53,7 @@ export const service = new sst.aws.Service("gbfm_vps", {
5253
target: "release",
5354
dockerfile: "vps/Dockerfile",
5455
},
55-
link: [database, email, ...allSecrets],
56+
link: [database, email, www, ...allSecrets],
5657
});
5758

5859
export const vps_gateway = new sst.aws.ApiGatewayV2("gbfm_vps_gateway", {

‎vps/.gitignore‎

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,4 +5,6 @@ node_modules/
55
.bun/
66

77
# env
8-
.env
8+
*.env*
9+
10+
**.DS_Store
Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
import type { Context, Next } from 'hono'
2+
import { verify } from 'hono/jwt'
3+
import { env } from '@/env'
4+
import { getAuthorByEmailOrId } from '@/db/author.repo'
5+
6+
/**
7+
* Authenticates a user by verifying the JWT token in the Authorization header.
8+
*
9+
* @example
10+
* ```ts
11+
import { authenticate } from './middleware/auth'
12+
13+
// Apply to specific routes
14+
auth.get('/protected-route', authenticate, async (c) => {
15+
const user = c.get('user')
16+
return c.json({ message: 'Protected data', user })
17+
})
18+
19+
// Or apply to all routes in a group
20+
const protectedRoutes = new Hono()
21+
protectedRoutes.use('*', authenticate)
22+
23+
protectedRoutes.get('/profile', async (c) => {
24+
const user = c.get('user')
25+
return c.json({ user })
26+
})
27+
*/
28+
export const authenticate = async (c: Context, next: Next) => {
29+
const authHeader = c.req.header('Authorization')
30+
31+
if (!authHeader || !authHeader.startsWith('Bearer ')) {
32+
return c.json({ error: 'Authorization header required' }, 401)
33+
}
34+
35+
const token = authHeader.substring(7)
36+
37+
try {
38+
const payload = await verify(token, env.ACCESS_TOKEN_SECRET)
39+
40+
if (payload.type !== 'access') {
41+
return c.json({ error: 'Invalid token type' }, 401)
42+
}
43+
44+
const authorId = payload.sub
45+
if (!authorId || typeof authorId !== 'string') {
46+
return c.json({ error: 'Invalid token payload' }, 401)
47+
}
48+
49+
const author = await getAuthorByEmailOrId({ authorId })
50+
if (author.length === 0) {
51+
return c.json({ error: 'User not found' }, 404)
52+
}
53+
54+
c.set('user', author[0])
55+
await next()
56+
} catch (error) {
57+
return c.json({ error: 'Invalid or expired token' }, 401)
58+
}
59+
}

0 commit comments

Comments
 (0)