This content originally appeared on DEV Community and was authored by Jones Charles
Introduction ๐
Hey there, fellow developers! Today, we're diving into authentication in Go applications using GToken, a powerful middleware for the GoFrame framework. If you're building web applications in Go and need a robust auth system, you're in the right place!
What We'll Cover ๐
- Setting up GToken in your GoFrame app
- Building a secure authentication flow
- Handling tokens like a pro
- Real-world examples you can use today
- Best practices and common pitfalls
Why GToken? ๐ค
Before we dive in, you might be wondering: "Why should I use GToken instead of writing my own auth system or using other solutions?"
Here's why:
- ๐ Quick to implement
- ๐ Built-in security features
- ๐ ๏ธ Highly configurable
- ๐ฏ Specifically designed for GoFrame
- ๐ช Production-ready
Getting Started ๐
First things first, let's get our dependencies set up:
go get github.com/goflyfox/gtoken
go get github.com/gogf/gf/v2
The Basic Setup ๐๏ธ
Let's start with a simple authentication setup. I'll show you the minimal configuration needed to get up and running:
package main
import (
"github.com/goflyfox/gtoken/gtoken"
"github.com/gogf/gf/v2/frame/g"
"github.com/gogf/gf/v2/net/ghttp"
)
func main() {
// Create a basic GToken instance
gfToken := >oken.GfToken{
ServerName: "my-awesome-app",
LoginPath: "/login",
// We'll add our login handler soon!
LoginBeforeFunc: LoginHandler,
// No authentication needed for public routes
AuthExcludePaths: g.SliceStr{"/public"},
}
// That's it! Let's start our server
s := g.Server()
s.Group("/", func(group *ghttp.RouterGroup) {
gfToken.Middleware(context.Background(), group)
// Your protected routes go here
group.ALL("/profile", getProfile)
})
s.Run()
}
Pretty clean, right? ๐
Building the Login Flow ๐
Here's where things get interesting. Let's create a login handler that actually does something useful:
func LoginHandler(r *ghttp.Request) (string, interface{}) {
username := r.Get("username").String()
password := r.Get("password").String()
// ๐จ Pro tip: Never store passwords in plain text!
// This is just an example
if isValidUser(username, password) {
// Return user ID and any custom data you want to store
return username, map[string]interface{}{
"role": "user",
"name": "John Doe",
// Add any user data you need in your app
}
}
r.Response.WriteJson(gtoken.Fail("Invalid credentials"))
r.ExitAll()
return "", nil
}
func isValidUser(username, password string) bool {
// In a real app, you'd check against your database
// and use proper password hashing
return username == "demo" && password == "demo123"
}
Making It Production-Ready ๐
Now, let's add some real-world features that you'll definitely need in production:
1. Token Configuration
gfToken := >oken.GfToken{
// Previous config...
Timeout: 24 * 60 * 60 * 1000, // 24 hours
MaxRefresh: 72 * 60 * 60 * 1000, // 3 days
MultiLogin: true, // Allow users to login from multiple devices
EncryptKey: []byte(os.Getenv("TOKEN_SECRET")), // Always use env vars for secrets!
}
2. Redis Storage for Scalability
# config.yaml
redis:
default:
address: ${REDIS_HOST}:${REDIS_PORT}
db: 1
pass: ${REDIS_PASSWORD}
gfToken.CacheMode = 2 // Use Redis mode
gfToken.CacheKey = "myapp:tokens:"
Cool Features You Should Know About ๐
Multi-device Login Control
// In your login handler
if deviceCount := getUserActiveDevices(username); deviceCount >= 3 {
r.Response.WriteJson(gtoken.Fail("Too many active devices"))
r.ExitAll()
return
}
Automatic Token Refresh
GToken handles token refresh automatically, but you can customize it:
gfToken.MaxRefresh = 30 * 24 * 60 * 60 * 1000 // 30 days
Error Handling Like a Pro ๐ช
Here's how to handle auth errors gracefully:
func ErrorHandler(r *ghttp.Request) {
defer func() {
if err := recover(); err != nil {
// Log the error (use your favorite logger)
logger.Error("Authentication error", err)
// Return a nice error message
r.Response.WriteJson(gtoken.Json(401, "Oops! Please login again", nil))
r.ExitAll()
}
}()
r.Middleware.Next()
}
Testing Your Auth System ๐งช
Here's a quick test you can run:
func TestLogin(t *testing.T) {
client := ghttp.NewClient()
resp, err := client.Post("http://localhost:8080/login",
g.Map{
"username": "demo",
"password": "demo123",
},
)
assert.Nil(t, err)
assert.Equal(t, 200, resp.StatusCode)
// Parse response and check token
var result g.Map
resp.Json(&result)
assert.NotEmpty(t, result["token"])
}
Common Pitfalls to Avoid โ ๏ธ
-
Don't store sensitive data in tokens
- Keep tokens lightweight
- Store only references to data
Always use HTTPS in production
s.SetHTTPSConfig(&ghttp.ServerHTTPSConfig{
Certificate: "server.crt",
Key: "server.key",
})
-
Set appropriate timeouts
- Token lifetime should match your security needs
- Consider your users' usage patterns
Advanced Use Cases ๐ฅ
Custom Token Format
Want to customize your token format? Here's how:
type CustomTokenData struct {
UserID string `json:"userId"`
Role string `json:"role"`
Tenant string `json:"tenant"`
LastLogin time.Time `json:"lastLogin"`
}
func LoginHandler(r *ghttp.Request) (string, interface{}) {
// ... authentication logic ...
tokenData := CustomTokenData{
UserID: "user123",
Role: "admin",
Tenant: "org_xyz",
LastLogin: time.Now(),
}
return tokenData.UserID, tokenData
}
Rate Limiting
Here's a simple rate limiter middleware you can add:
func RateLimiter(r *ghttp.Request) {
key := "rate:" + r.GetClientIp()
count, err := g.Redis().Get(r.Context(), key)
if err != nil {
panic("Rate limiter error")
}
if count.Int() > 100 { // 100 requests per minute
r.Response.WriteStatusExit(429, "Too many requests")
return
}
g.Redis().Incr(r.Context(), key)
g.Redis().Expire(r.Context(), key, 60) // Reset after 1 minute
r.Middleware.Next()
}
Debugging Tips ๐
1. Token Inspection
Here's a handy endpoint to debug token contents:
func inspectToken(r *ghttp.Request) {
tokenData := gfToken.GetTokenData(r)
debug := map[string]interface{}{
"token": r.Header.Get("Authorization"),
"tokenData": tokenData,
"expiresIn": gfToken.GetTokenExpire(r),
"permissions": getPermissions(tokenData),
}
r.Response.WriteJson(gtoken.Succ("Token inspection", debug))
}
2. Logging Middleware
Add comprehensive logging to track authentication issues:
func LoggingMiddleware(r *ghttp.Request) {
start := time.Now()
// Get the token before any potential errors
token := r.Header.Get("Authorization")
// Create a copy of the response writer to capture the status code
writer := responsewriter.New(r.Response.Writer)
r.Response.Writer = writer
defer func() {
duration := time.Since(start)
log := map[string]interface{}{
"path": r.URL.Path,
"method": r.Method,
"duration": duration.String(),
"status": writer.Status(),
"hasToken": token != "",
"ip": r.GetClientIp(),
"userAgent": r.UserAgent(),
}
if err := recover(); err != nil {
log["error"] = err
// Re-panic after logging
panic(err)
}
// Use your favorite logger here
logger.Info("Request log", log)
}()
r.Middleware.Next()
}
Real-World Scenarios ๐
Handling Microservices
When using GToken in a microservices architecture:
// Service-to-service authentication
func ValidateServiceToken(r *ghttp.Request) {
token := r.Header.Get("X-Service-Token")
if !isValidServiceToken(token) {
r.Response.WriteStatusExit(401, "Invalid service token")
return
}
r.Middleware.Next()
}
func isValidServiceToken(token string) bool {
// Verify against your service registry
// You might want to use mutual TLS instead for service-to-service auth
return token == os.Getenv("INTERNAL_SERVICE_TOKEN")
}
User Session Management
Track and manage user sessions:
type Session struct {
UserID string `json:"userId"`
DeviceID string `json:"deviceId"`
LastSeen time.Time `json:"lastSeen"`
IPAddress string `json:"ipAddress"`
UserAgent string `json:"userAgent"`
}
func trackSession(r *ghttp.Request) {
tokenData := gfToken.GetTokenData(r)
deviceId := r.Header.Get("X-Device-ID")
session := Session{
UserID: tokenData.Id,
DeviceID: deviceId,
LastSeen: time.Now(),
IPAddress: r.GetClientIp(),
UserAgent: r.UserAgent(),
}
// Store in Redis with expiration
key := fmt.Sprintf("session:%s:%s", tokenData.Id, deviceId)
g.Redis().SetEX(r.Context(), key, session, 24*time.Hour)
r.Middleware.Next()
}
// List active sessions
func getUserSessions(r *ghttp.Request) {
tokenData := gfToken.GetTokenData(r)
pattern := fmt.Sprintf("session:%s:*", tokenData.Id)
keys, _ := g.Redis().Keys(r.Context(), pattern)
sessions := make([]Session, 0)
for _, key := range keys {
var session Session
data, _ := g.Redis().Get(r.Context(), key.String())
data.Struct(&session)
sessions = append(sessions, session)
}
r.Response.WriteJson(gtoken.Succ("Active sessions", sessions))
}
Performance Optimization Tips ๐จ
- Token Size Optimization
// Bad: Including too much data in token
func badTokenData() interface{} {
return map[string]interface{}{
"user": map[string]interface{}{
"id": 123,
"name": "John",
"email": "john@example.com",
"preferences": map[string]interface{}{
// Lots of unnecessary data
},
},
}
}
// Good: Keep tokens lightweight
func goodTokenData() interface{} {
return map[string]interface{}{
"uid": "u123",
"role": "user",
}
}
- Caching Strategies
func getUserPermissions(r *ghttp.Request) []string {
tokenData := gfToken.GetTokenData(r)
cacheKey := fmt.Sprintf("perm:%s", tokenData.Id)
// Try cache first
if perms, err := g.Redis().Get(r.Context(), cacheKey); err == nil {
return perms.Strings()
}
// Cache miss - fetch from DB
permissions := fetchPermissionsFromDB(tokenData.Id)
// Cache for 15 minutes
g.Redis().SetEX(r.Context(), cacheKey, permissions, 15*time.Minute)
return permissions
}
Security Checklist โ
Before deploying to production, ensure you have:
- [ ] Implemented HTTPS with proper certificates
- [ ] Set secure cookie flags
- [ ] Configured appropriate token timeouts
- [ ] Implemented rate limiting
- [ ] Set up monitoring and alerting
- [ ] Added comprehensive logging
- [ ] Performed security testing
- [ ] Documented security procedures
- [ ] Set up automated security scanning
- [ ] Created incident response procedures
Wrapping Up ๐
That's it! You now have a solid foundation for building secure authentication in your Go applications. The complete code for this tutorial is available on GitHub.
What's Next? ๐
You could extend this setup by:
- Adding role-based access control
- Implementing OAuth2 integration
- Adding rate limiting
- Building session management
Let me know in the comments what you'd like to learn about next!
Found this helpful? Follow me for more Go tutorials and consider giving this article a โค๏ธ like!
This article was crafted based on real-world experience implementing GToken in production applications. Feel free to reach out with questions or share your own experiences in the comments below!
This content originally appeared on DEV Community and was authored by Jones Charles
Jones Charles | Sciencx (2025-03-30T13:23:30+00:00) Building Secure Authentication in Go with GoFrame GToken ๐. Retrieved from https://www.scien.cx/2025/03/30/building-secure-authentication-in-go-with-goframe-gtoken-%f0%9f%94%90/
Please log in to upload a file.
There are no updates yet.
Click the Upload button above to add an update.