-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathauth_example.go
More file actions
310 lines (258 loc) · 8.59 KB
/
auth_example.go
File metadata and controls
310 lines (258 loc) · 8.59 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
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
package main
import (
"crypto/sha256"
"encoding/base64"
"encoding/hex"
"fmt"
"strings"
"time"
"github.com/hypnguyen1209/ming/v2"
"github.com/valyala/fasthttp"
)
// Simple in-memory user store
// In a real application, you would use a database
var users = map[string]string{
"admin": hashPassword("admin123"),
"john": hashPassword("password123"),
"jane": hashPassword("securepwd456"),
}
// Store for refresh tokens
var refreshTokens = map[string]string{}
// Simple helper to hash passwords
func hashPassword(password string) string {
hash := sha256.Sum256([]byte(password))
return hex.EncodeToString(hash[:])
}
// Basic auth middleware
func authMiddleware(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
// Get token from header
token := string(ctx.Request.Header.Peek("Authorization"))
// Check if token starts with "Bearer "
if !strings.HasPrefix(token, "Bearer ") {
ctx.SetStatusCode(fasthttp.StatusUnauthorized)
fmt.Fprintf(ctx, `{"error":"Unauthorized: Missing or invalid token"}`)
return
}
// Validate token (this is a simple example, use JWT for production)
token = token[7:] // Remove "Bearer " prefix
username, valid := validateToken(token)
if !valid {
ctx.SetStatusCode(fasthttp.StatusUnauthorized)
fmt.Fprintf(ctx, `{"error":"Unauthorized: Invalid token"}`)
return
}
// Set username in context for handlers to use
ctx.SetUserValue("username", username)
// Call the next handler
next(ctx)
}
}
// Admin access middleware - adds additional access control layer
func adminOnlyMiddleware(next fasthttp.RequestHandler) fasthttp.RequestHandler {
return func(ctx *fasthttp.RequestCtx) {
username := ctx.UserValue("username")
if username != "admin" {
ctx.SetStatusCode(fasthttp.StatusForbidden)
fmt.Fprintf(ctx, `{"error":"Forbidden: Admin access required"}`)
return
}
next(ctx)
}
}
// Simple token generation (for demo purposes)
// In a real application, use JWT with proper signing
func generateToken(username string) string {
// This is just a demo token format: username + timestamp + simple hash
// In production, use JWT with proper signing
timestamp := time.Now().Unix()
tokenData := fmt.Sprintf("%s:%d", username, timestamp)
hash := sha256.Sum256([]byte(tokenData + "secret-key"))
token := base64.StdEncoding.EncodeToString([]byte(tokenData)) + "." +
hex.EncodeToString(hash[:8])
return token
}
// Simple token validation
func validateToken(token string) (string, bool) {
parts := strings.Split(token, ".")
if len(parts) != 2 {
return "", false
}
// Decode the token data
decoded, err := base64.StdEncoding.DecodeString(parts[0])
if err != nil {
return "", false
}
// Parse data
tokenParts := strings.Split(string(decoded), ":")
if len(tokenParts) != 2 {
return "", false
}
username := tokenParts[0]
// Re-compute the hash to verify token integrity
hash := sha256.Sum256([]byte(string(decoded) + "secret-key"))
expectedHash := hex.EncodeToString(hash[:8])
// Verify hash
if parts[1] != expectedHash {
return "", false
}
return username, true
}
// Generate refresh token
func generateRefreshToken(username string) string {
tokenBytes := make([]byte, 32)
for i := 0; i < len(tokenBytes); i++ {
tokenBytes[i] = byte(i + 42) // Simple deterministic value for demo
}
token := hex.EncodeToString(tokenBytes)
refreshTokens[token] = username
return token
}
func main() {
r := ming.New()
// Set up custom 404 and panic handlers
r.NotFound = func(ctx *fasthttp.RequestCtx) {
ctx.SetStatusCode(fasthttp.StatusNotFound)
ctx.SetContentType("application/json")
fmt.Fprintf(ctx, `{"error":"Not Found"}`)
}
r.PanicHandler = func(ctx *fasthttp.RequestCtx, err interface{}) {
ctx.SetStatusCode(fasthttp.StatusInternalServerError)
ctx.SetContentType("application/json")
fmt.Fprintf(ctx, `{"error":"Internal Server Error"}`)
fmt.Printf("Panic recovered: %v\n", err)
}
// Public routes
r.Get("/", func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("application/json")
fmt.Fprintf(ctx, `{"message":"Welcome to Ming Auth Example API"}`)
})
// Authentication endpoint
r.Post("/auth/login", func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("application/json")
// Parse credentials from request body
username := string(ctx.FormValue("username"))
password := string(ctx.FormValue("password"))
// Validate credentials
storedHash, exists := users[username]
if !exists || storedHash != hashPassword(password) {
ctx.SetStatusCode(fasthttp.StatusUnauthorized)
fmt.Fprintf(ctx, `{"error":"Invalid username or password"}`)
return
}
// Generate access and refresh tokens
accessToken := generateToken(username)
refreshToken := generateRefreshToken(username)
// Return tokens
fmt.Fprintf(ctx, `{
"access_token": "%s",
"refresh_token": "%s",
"token_type": "Bearer",
"expires_in": 3600
}`, accessToken, refreshToken)
})
// Token refresh endpoint
r.Post("/auth/refresh", func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("application/json")
refreshToken := string(ctx.FormValue("refresh_token"))
username, exists := refreshTokens[refreshToken]
if !exists {
ctx.SetStatusCode(fasthttp.StatusUnauthorized)
fmt.Fprintf(ctx, `{"error":"Invalid refresh token"}`)
return
}
// Generate new access token
newAccessToken := generateToken(username)
// Return new access token
fmt.Fprintf(ctx, `{
"access_token": "%s",
"token_type": "Bearer",
"expires_in": 3600
}`, newAccessToken)
})
// User registration endpoint
r.Post("/auth/register", func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("application/json")
username := string(ctx.FormValue("username"))
password := string(ctx.FormValue("password"))
// Simple validation
if username == "" || password == "" {
ctx.SetStatusCode(fasthttp.StatusBadRequest)
fmt.Fprintf(ctx, `{"error":"Username and password are required"}`)
return
}
// Check if user already exists
if _, exists := users[username]; exists {
ctx.SetStatusCode(fasthttp.StatusConflict)
fmt.Fprintf(ctx, `{"error":"Username already exists"}`)
return
}
// Add user to the store
users[username] = hashPassword(password)
fmt.Fprintf(ctx, `{"message":"User registered successfully"}`)
})
// Protected API endpoints
// User profile - requires authentication
r.Get("/api/profile", authMiddleware(func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("application/json")
username := ctx.UserValue("username").(string)
// Return profile data
fmt.Fprintf(ctx, `{
"username": "%s",
"profile": {
"name": "User %s",
"email": "%s@example.com",
"role": "user"
}
}`, username, username, username)
}))
// User data - requires authentication
r.Get("/api/data", authMiddleware(func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("application/json")
username := ctx.UserValue("username").(string)
// Return some user data
fmt.Fprintf(ctx, `{
"username": "%s",
"data": [
{"id": 1, "value": "Item 1"},
{"id": 2, "value": "Item 2"},
{"id": 3, "value": "Item 3"}
]
}`, username)
}))
// Admin endpoint - requires admin role
r.Get("/api/admin", authMiddleware(adminOnlyMiddleware(func(ctx *fasthttp.RequestCtx) {
ctx.SetContentType("application/json")
// List all users (admin only function)
userList := []string{}
for username := range users {
userList = append(userList, username)
}
// Return admin data
fmt.Fprintf(ctx, `{
"message": "Admin access granted",
"users": %q
}`, userList)
})))
// Start server
fmt.Println("Ming Auth Example Server")
fmt.Println("Server running on http://localhost:8080")
fmt.Println("\nAvailable routes:")
fmt.Println("- GET / (Public welcome endpoint)")
fmt.Println("- POST /auth/register (Register new user)")
fmt.Println("- POST /auth/login (Login and get tokens)")
fmt.Println("- POST /auth/refresh (Refresh access token)")
fmt.Println("- GET /api/profile (Protected - requires auth)")
fmt.Println("- GET /api/data (Protected - requires auth)")
fmt.Println("- GET /api/admin (Protected - requires admin role)")
fmt.Println("\nTest users:")
fmt.Println("- admin / admin123 (has admin access)")
fmt.Println("- john / password123 (regular user)")
fmt.Println("- jane / securepwd456 (regular user)")
fmt.Println("\nExample curl commands:")
fmt.Println("1. Login:")
fmt.Println(" curl -X POST http://localhost:8080/auth/login -d \"username=admin&password=admin123\"")
fmt.Println("\n2. Access protected endpoint:")
fmt.Println(" curl -H \"Authorization: Bearer YOUR_ACCESS_TOKEN\" http://localhost:8080/api/profile")
r.Run(":8080")
}