-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcustom-auth.ts
More file actions
210 lines (190 loc) · 5.7 KB
/
custom-auth.ts
File metadata and controls
210 lines (190 loc) · 5.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
/**
* Custom Authentication Example
*
* This example demonstrates how to use custom token validation
* with typed authentication context that gets passed to your handlers.
*
* Features:
* - Custom token validation function
* - Typed authentication context
* - Role-based access control
* - Permission checking
*
* Run with: npx tsx examples/custom-auth.ts
*/
import { APIServer, z } from '../src/index'
// Define your authentication context type
interface AuthContext {
userId: string
username: string
role: 'admin' | 'user'
permissions: string[]
}
// Simulated database of tokens (in production, use JWT, database, etc.)
const tokenDatabase: Record<string, AuthContext> = {
'admin-token-123': {
userId: '1',
username: 'admin',
role: 'admin',
permissions: ['read', 'write', 'delete'],
},
'user-token-456': {
userId: '2',
username: 'john',
role: 'user',
permissions: ['read'],
},
}
// Create API server with custom auth validator
const app = new APIServer<AuthContext>({
port: 3000,
apiTitle: 'Custom Auth Example',
apiDescription: 'API with custom token validation and typed context',
// Custom token validator function
apiToken: async (token, request) => {
console.log(`Validating token for ${request.method} ${request.url}`)
// Look up token in database
const authContext = tokenDatabase[token]
if (!authContext) {
return {
valid: false,
error: 'Invalid or expired token',
}
}
// Return the validated context
return {
valid: true,
context: authContext,
}
},
})
// Public endpoint (no auth required)
app.createEndpoint({
method: 'GET',
url: '/public',
response: z.object({
message: z.string().describe('Welcome message for public access'),
}),
config: {
description: 'Public endpoint accessible without authentication',
tags: ['Public'],
},
handler: async () => {
return { message: 'This is a public endpoint!' }
},
})
// Protected endpoint with automatic authentication
// Using authenticated: true adds Bearer auth requirement and 401/403 responses
app.createEndpoint({
method: 'GET',
url: '/profile',
authenticated: true,
response: z.object({
userId: z.string().describe('Unique user identifier'),
username: z.string().describe('Username of the authenticated user'),
role: z.string().describe('User role (admin or user)'),
permissions: z.array(z.string()).describe('List of permissions granted to this user'),
}),
config: {
description: 'Get current user profile from auth context',
tags: ['Protected'],
},
handler: async (request) => {
// request.auth is fully typed as AuthContext and guaranteed to exist
// when authenticated: true is set
const { userId, username, role, permissions } = request.auth as AuthContext
return {
userId,
username,
role,
permissions,
}
},
})
// Admin-only endpoint with role checking
app.createEndpoint({
method: 'DELETE',
url: '/admin/users/:userId',
authenticated: true,
params: z.object({
userId: z.string().describe('The ID of the user to delete'),
}),
response: z.object({
message: z.string().describe('Confirmation message of the deletion'),
}),
config: {
description: 'Admin-only endpoint for deleting users',
tags: ['Admin'],
},
handler: async (request, reply) => {
const auth = request.auth as AuthContext
// Check if user has admin role
if (auth.role !== 'admin') {
return reply.code(403).send({
statusCode: 403,
error: 'Forbidden',
message: 'Admin access required',
})
}
const { userId } = request.params
return {
message: `User ${userId} deleted by ${auth.username}`,
}
},
})
// Permission-based endpoint
app.createEndpoint({
method: 'POST',
url: '/data',
authenticated: true,
body: z.object({
content: z.string().describe('The data content to be created'),
}),
response: z.object({
message: z.string().describe('Confirmation message with creator info'),
}),
config: {
description: 'Create data - requires write permission',
tags: ['Protected'],
},
handler: async (request, reply) => {
const auth = request.auth as AuthContext
// Check if user has write permission
if (!auth.permissions.includes('write')) {
return reply.code(403).send({
statusCode: 403,
error: 'Forbidden',
message: 'Write permission required',
})
}
return {
message: `Data created by ${auth.username}: ${request.body.content}`,
}
},
})
// Setup graceful shutdown
app.setupGracefulShutdown()
// Start the server
;(async () => {
await app.start()
console.log('\n✅ Server started with custom authentication!')
console.log('\n📝 Try these commands:')
console.log('\n1. Public endpoint (no auth):')
console.log(' curl http://localhost:3000/public')
console.log('\n2. Protected endpoint with user token:')
console.log(' curl -H "Authorization: Bearer user-token-456" http://localhost:3000/profile')
console.log('\n3. Protected endpoint with admin token:')
console.log(' curl -H "Authorization: Bearer admin-token-123" http://localhost:3000/profile')
console.log('\n4. Admin-only endpoint (requires admin token):')
console.log(' curl -X DELETE -H "Authorization: Bearer admin-token-123" http://localhost:3000/admin/users/123')
console.log('\n5. Permission check (user token - should fail):')
console.log(
' curl -X POST -H "Authorization: Bearer user-token-456" -H "Content-Type: application/json" -d \'{"content":"test"}\' http://localhost:3000/data',
)
console.log('\n6. Permission check (admin token - should succeed):')
console.log(
' curl -X POST -H "Authorization: Bearer admin-token-123" -H "Content-Type: application/json" -d \'{"content":"test"}\' http://localhost:3000/data',
)
console.log('\n7. Invalid token:')
console.log(' curl -H "Authorization: Bearer invalid-token" http://localhost:3000/profile')
})()