Authentication & Authorization
Secure your APIs with JWT authentication and RBAC authorization.
Authentication (Who are you?)
JWT Token Flow
1. User submits credentials → 2. Server validates → 3. Server returns JWT token
↓
4. Client stores token → 5. Client sends token in requests → 6. Server validates token
Protecting Routes
// Require authentication
router.GET("/profile",
middleware.RequireAuth(),
controller.GetProfile,
)
// Public route (no auth)
router.GET("/public", controller.PublicEndpoint)
Getting Current User
func (c *UserController) GetProfile(ctx *gin.Context) {
// Get user ID from context (set by RequireAuth middleware)
userID, exists := ctx.Get("user_id")
if !exists {
util.RespondWithError(ctx, util.NewErrorMessage("E4012", "Unauthorized"))
return
}
// Get user from database
user, err := c.svc.User().GetByID(ctx.Request.Context(), userID.(string))
// ...
}
Authorization (What can you do?)
RBAC Structure
User → Roles → Permission Groups → Permissions
Permission Naming
Use format: resource:action
Examples:
users:read- View usersusers:write- Create/update usersusers:delete- Delete usersadmin:all- All permissions
Protecting with Permissions
// Single permission
router.POST("/users",
middleware.RequireAuth(),
middleware.RequirePermission("users:create"),
controller.CreateUser,
)
// Multiple permissions (OR logic)
router.DELETE("/users/:id",
middleware.RequireAuth(),
middleware.RequireAnyPermission("users:delete", "admin:all"),
controller.DeleteUser,
)
// Multiple permissions (AND logic)
router.PUT("/users/:id/role",
middleware.RequireAuth(),
middleware.RequireAllPermissions("users:write", "roles:assign"),
controller.AssignRole,
)
Check Permission in Code
func (c *UserController) UpdateUser(ctx *gin.Context) {
userID := ctx.Param("id")
currentUserID := ctx.GetString("user_id")
// Check if user is updating their own profile or has admin permission
if userID != currentUserID {
hasPermission := c.svc.User().HasPermission(currentUserID, "users:write")
if !hasPermission {
util.RespondWithError(ctx, util.NewErrorMessage("E4031", "Permission denied"))
return
}
}
// Continue with update...
}
OAuth2/OIDC Integration
EZ-Console supports OAuth2 providers (Google, Okta, Azure AD, etc.):
Configuration
oauth:
enabled: true
providers:
- name: google
display_name: Google
client_id: your-client-id
client_secret: your-client-secret
auth_url: "https://accounts.google.com/o/oauth2/v2/auth"
token_url: "https://oauth2.googleapis.com/token"
user_info_url: "https://www.googleapis.com/oauth2/v2/userinfo"
redirect_url: "http://localhost:8080/api/auth/callback/google"
OAuth Flow
1. User clicks "Login with Google"
↓
2. Redirect to Google login
↓
3. User authorizes
↓
4. Google redirects back with code
↓
5. Exchange code for token
↓
6. Fetch user info
↓
7. Create/update user in database
↓
8. Return JWT token
LDAP/Active Directory
Configuration
ldap:
enabled: true
host: "ldap.example.com"
port: 389
base_dn: "dc=example,dc=com"
user_dn: "ou=users,dc=example,dc=com"
bind_dn: "cn=admin,dc=example,dc=com"
bind_password: "admin-password"
user_filter: "(uid=%s)"
Multi-Factor Authentication (MFA)
TOTP (Time-based One-Time Password)
// Enable MFA for user
func (c *UserController) EnableMFA(ctx *gin.Context) {
userID := ctx.GetString("user_id")
// Generate secret
secret, qrCode, err := c.svc.User().GenerateMFASecret(ctx.Request.Context(), userID)
if err != nil {
util.RespondWithError(ctx, util.NewErrorMessage("E5001", "Failed to generate MFA secret", err))
return
}
util.RespondWithSuccess(ctx, http.StatusOK, map[string]interface{}{
"secret": secret,
"qr_code": qrCode,
})
}
// Verify MFA code
func (c *UserController) VerifyMFA(ctx *gin.Context) {
var req struct {
Code string `json:"code" binding:"required"`
}
if err := ctx.ShouldBindJSON(&req); err != nil {
util.RespondWithError(ctx, util.NewErrorMessage("E4001", "Invalid request", err))
return
}
userID := ctx.GetString("user_id")
valid, err := c.svc.User().VerifyMFACode(ctx.Request.Context(), userID, req.Code)
if err != nil || !valid {
util.RespondWithError(ctx, util.NewErrorMessage("E4001", "Invalid MFA code"))
return
}
util.RespondWithSuccess(ctx, http.StatusOK, map[string]interface{}{
"verified": true,
})
}
Service Accounts
For API-to-API authentication:
// Create service account
serviceAccount, apiKey, err := c.svc.ServiceAccount().Create(ctx.Request.Context(), CreateServiceAccountRequest{
Name: "External API",
Description: "For external integrations",
Permissions: []string{"products:read", "orders:read"},
})
// Use API key in requests
// Authorization: Bearer <api-key>
Best Practices
DO ✅
- Always use HTTPS in production
- Store passwords with strong hashing (bcrypt)
- Implement rate limiting on auth endpoints
- Use refresh tokens for long-lived sessions
- Implement account lockout after failed attempts
- Log all authentication events
- Enforce strong password policies
DON'T ❌
- Don't store passwords in plain text
- Don't put sensitive data in JWT claims
- Don't use predictable session IDs
- Don't ignore failed login attempts
- Don't skip permission checks
- Don't trust client-side authorization
Next Steps
- Learn about middleware
- Implement audit logging
- Review API best practices