🐹 Auth Hash Directives in TuskLang for Go
Auth Hash Directives in TuskLang for Go
Overview
Auth hash directives in TuskLang provide powerful authentication and authorization configuration capabilities directly in your configuration files. These directives enable you to define sophisticated authentication methods, authorization rules, and security policies with Go integration for secure applications.
Basic Auth Directive Syntax
// TuskLang auth directive
#auth {
providers: {
jwt: {
enabled: true
secret: "@env('JWT_SECRET')"
algorithm: "HS256"
expires_in: "24h"
refresh_expires_in: "7d"
}
oauth2: {
enabled: true
providers: {
google: {
client_id: "@env('GOOGLE_CLIENT_ID')"
client_secret: "@env('GOOGLE_CLIENT_SECRET')"
redirect_url: "https://myapp.com/auth/google/callback"
scopes: ["email", "profile"]
}
github: {
client_id: "@env('GITHUB_CLIENT_ID')"
client_secret: "@env('GITHUB_CLIENT_SECRET')"
redirect_url: "https://myapp.com/auth/github/callback"
scopes: ["user:email"]
}
}
}
session: {
enabled: true
store: "redis"
secret: "@env('SESSION_SECRET')"
max_age: 3600
secure: true
http_only: true
}
}
policies: {
admin_access: {
roles: ["admin"]
permissions: ["read", "write", "delete"]
}
user_access: {
roles: ["user"]
permissions: ["read", "write"]
}
guest_access: {
roles: ["guest"]
permissions: ["read"]
}
}
routes: {
"/api/admin": {
required: true
roles: ["admin"]
permissions: ["read", "write", "delete"]
}
"/api/users": {
required: true
roles: ["user", "admin"]
permissions: ["read", "write"]
}
"/api/public": {
required: false
roles: ["guest", "user", "admin"]
permissions: ["read"]
}
}
}
Go Integration
package mainimport (
"context"
"crypto/rand"
"encoding/base64"
"fmt"
"log"
"net/http"
"strings"
"time"
"github.com/golang-jwt/jwt/v4"
"github.com/gorilla/sessions"
"github.com/tusklang/go-sdk"
)
type AuthDirective struct {
Providers map[string]Provider tsk:"providers"
Policies map[string]Policy tsk:"policies"
Routes map[string]Route tsk:"routes"
}
type Provider struct {
Enabled bool tsk:"enabled"
Config map[string]interface{} tsk:",inline"
}
type Policy struct {
Roles []string tsk:"roles"
Permissions []string tsk:"permissions"
}
type Route struct {
Required bool tsk:"required"
Roles []string tsk:"roles"
Permissions []string tsk:"permissions"
}
type User struct {
ID string json:"id"
Email string json:"email"
Roles []string json:"roles"
Permissions []string json:"permissions"
}
type AuthManager struct {
directive AuthDirective
jwtSecret string
sessionStore *sessions.CookieStore
oauthProviders map[string]OAuthProvider
}
type OAuthProvider struct {
ClientID string json:"client_id"
ClientSecret string json:"client_secret"
RedirectURL string json:"redirect_url"
Scopes []string json:"scopes"
}
func main() {
// Load auth configuration
config, err := tusk.LoadFile("auth-config.tsk")
if err != nil {
log.Fatalf("Error loading auth config: %v", err)
}
var authDirective AuthDirective
if err := config.Get("#auth", &authDirective); err != nil {
log.Fatalf("Error parsing auth directive: %v", err)
}
// Initialize auth manager
authManager := NewAuthManager(authDirective)
// Setup HTTP server with auth middleware
mux := http.NewServeMux()
// Auth routes
mux.HandleFunc("/auth/login", authManager.handleLogin)
mux.HandleFunc("/auth/logout", authManager.handleLogout)
mux.HandleFunc("/auth/google", authManager.handleGoogleAuth)
mux.HandleFunc("/auth/github", authManager.handleGitHubAuth)
// Protected routes
mux.HandleFunc("/api/admin", authManager.withAuth(authManager.handleAdmin))
mux.HandleFunc("/api/users", authManager.withAuth(authManager.handleUsers))
mux.HandleFunc("/api/public", authManager.handlePublic)
log.Println("Server starting on :8080")
log.Fatal(http.ListenAndServe(":8080", mux))
}
func NewAuthManager(directive AuthDirective) *AuthManager {
manager := &AuthManager{
directive: directive,
oauthProviders: make(map[string]OAuthProvider),
}
// Initialize JWT secret
if jwt, exists := directive.Providers["jwt"]; exists && jwt.Enabled {
manager.jwtSecret = jwt.Config["secret"].(string)
}
// Initialize session store
if session, exists := directive.Providers["session"]; exists && session.Enabled {
secret := session.Config["secret"].(string)
manager.sessionStore = sessions.NewCookieStore([]byte(secret))
}
// Initialize OAuth providers
if oauth2, exists := directive.Providers["oauth2"]; exists && oauth2.Enabled {
providers := oauth2.Config["providers"].(map[string]interface{})
for name, config := range providers {
providerConfig := config.(map[string]interface{})
manager.oauthProviders[name] = OAuthProvider{
ClientID: providerConfig["client_id"].(string),
ClientSecret: providerConfig["client_secret"].(string),
RedirectURL: providerConfig["redirect_url"].(string),
Scopes: providerConfig["scopes"].([]string),
}
}
}
return manager
}
// Authentication middleware
func (a *AuthManager) withAuth(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user, err := a.authenticateRequest(r)
if err != nil {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}
// Check route requirements
route := a.getRouteConfig(r.URL.Path)
if route.Required && user == nil {
http.Error(w, "Authentication required", http.StatusUnauthorized)
return
}
if user != nil {
// Check roles and permissions
if !a.authorizeUser(user, route) {
http.Error(w, "Insufficient permissions", http.StatusForbidden)
return
}
// Add user to context
ctx := context.WithValue(r.Context(), "user", user)
r = r.WithContext(ctx)
}
handler(w, r)
}
}
func (a AuthManager) authenticateRequest(r http.Request) (*User, error) {
// Try JWT authentication
if user, err := a.authenticateJWT(r); err == nil {
return user, nil
}
// Try session authentication
if user, err := a.authenticateSession(r); err == nil {
return user, nil
}
// Try OAuth authentication
if user, err := a.authenticateOAuth(r); err == nil {
return user, nil
}
return nil, fmt.Errorf("no valid authentication found")
}
func (a AuthManager) authenticateJWT(r http.Request) (*User, error) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
return nil, fmt.Errorf("no authorization header")
}
if !strings.HasPrefix(authHeader, "Bearer ") {
return nil, fmt.Errorf("invalid authorization header format")
}
tokenString := strings.TrimPrefix(authHeader, "Bearer ")
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("unexpected signing method: %v", token.Header["alg"])
}
return []byte(a.jwtSecret), nil
})
if err != nil {
return nil, err
}
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
user := &User{
ID: claims["user_id"].(string),
Email: claims["email"].(string),
}
if roles, ok := claims["roles"].([]interface{}); ok {
for _, role := range roles {
user.Roles = append(user.Roles, role.(string))
}
}
if permissions, ok := claims["permissions"].([]interface{}); ok {
for _, perm := range permissions {
user.Permissions = append(user.Permissions, perm.(string))
}
}
return user, nil
}
return nil, fmt.Errorf("invalid token")
}
func (a AuthManager) authenticateSession(r http.Request) (*User, error) {
if a.sessionStore == nil {
return nil, fmt.Errorf("session store not configured")
}
session, err := a.sessionStore.Get(r, "user-session")
if err != nil {
return nil, err
}
userID, ok := session.Values["user_id"].(string)
if !ok {
return nil, fmt.Errorf("no user_id in session")
}
// In a real application, you would fetch user data from database
user := &User{
ID: userID,
Email: session.Values["email"].(string),
Roles: []string{"user"},
}
return user, nil
}
func (a AuthManager) authenticateOAuth(r http.Request) (*User, error) {
// OAuth authentication would be implemented here
// This is a simplified example
return nil, fmt.Errorf("oauth authentication not implemented")
}
func (a AuthManager) authorizeUser(user User, route Route) bool {
// Check if user has required roles
if len(route.Roles) > 0 {
hasRole := false
for _, requiredRole := range route.Roles {
for _, userRole := range user.Roles {
if requiredRole == userRole {
hasRole = true
break
}
}
if hasRole {
break
}
}
if !hasRole {
return false
}
}
// Check if user has required permissions
if len(route.Permissions) > 0 {
for _, requiredPerm := range route.Permissions {
hasPerm := false
for _, userPerm := range user.Permissions {
if requiredPerm == userPerm {
hasPerm = true
break
}
}
if !hasPerm {
return false
}
}
}
return true
}
func (a *AuthManager) getRouteConfig(path string) Route {
// Find matching route configuration
for routePath, config := range a.directive.Routes {
if strings.HasPrefix(path, routePath) {
return config
}
}
// Default configuration
return Route{
Required: false,
Roles: []string{"guest"},
Permissions: []string{"read"},
}
}
// Handler functions
func (a AuthManager) handleLogin(w http.ResponseWriter, r http.Request) {
if r.Method != "POST" {
http.Error(w, "Method not allowed", http.StatusMethodNotAllowed)
return
}
// Parse login credentials
email := r.FormValue("email")
password := r.FormValue("password")
// Validate credentials (simplified)
if email == "admin@example.com" && password == "password" {
user := &User{
ID: "1",
Email: email,
Roles: []string{"admin"},
Permissions: []string{"read", "write", "delete"},
}
// Generate JWT token
token := a.generateJWT(user)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(fmt.Sprintf({"token": "%s"}
, token)))
} else {
http.Error(w, "Invalid credentials", http.StatusUnauthorized)
}
}
func (a AuthManager) handleLogout(w http.ResponseWriter, r http.Request) {
// Clear session
if a.sessionStore != nil {
session, _ := a.sessionStore.Get(r, "user-session")
session.Options.MaxAge = -1
session.Save(r, w)
}
w.Write([]byte({"message": "Logged out successfully"}
))
}
func (a AuthManager) handleGoogleAuth(w http.ResponseWriter, r http.Request) {
provider, exists := a.oauthProviders["google"]
if !exists {
http.Error(w, "Google OAuth not configured", http.StatusInternalServerError)
return
}
// Redirect to Google OAuth
authURL := fmt.Sprintf(
"https://accounts.google.com/o/oauth2/auth?client_id=%s&redirect_uri=%s&scope=%s&response_type=code",
provider.ClientID,
provider.RedirectURL,
strings.Join(provider.Scopes, " "),
)
http.Redirect(w, r, authURL, http.StatusTemporaryRedirect)
}
func (a AuthManager) handleGitHubAuth(w http.ResponseWriter, r http.Request) {
provider, exists := a.oauthProviders["github"]
if !exists {
http.Error(w, "GitHub OAuth not configured", http.StatusInternalServerError)
return
}
// Redirect to GitHub OAuth
authURL := fmt.Sprintf(
"https://github.com/login/oauth/authorize?client_id=%s&redirect_uri=%s&scope=%s",
provider.ClientID,
provider.RedirectURL,
strings.Join(provider.Scopes, " "),
)
http.Redirect(w, r, authURL, http.StatusTemporaryRedirect)
}
func (a AuthManager) handleAdmin(w http.ResponseWriter, r http.Request) {
user := r.Context().Value("user").(*User)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(fmt.Sprintf({"message": "Welcome admin %s", "data": "admin-only-data"}
, user.Email)))
}
func (a AuthManager) handleUsers(w http.ResponseWriter, r http.Request) {
user := r.Context().Value("user").(*User)
w.Header().Set("Content-Type", "application/json")
w.Write([]byte(fmt.Sprintf({"message": "Welcome %s", "users": []}
, user.Email)))
}
func (a AuthManager) handlePublic(w http.ResponseWriter, r http.Request) {
w.Header().Set("Content-Type", "application/json")
w.Write([]byte({"message": "Public data", "data": "public-data"}
))
}
func (a AuthManager) generateJWT(user User) string {
claims := jwt.MapClaims{
"user_id": user.ID,
"email": user.Email,
"roles": user.Roles,
"permissions": user.Permissions,
"exp": time.Now().Add(time.Hour * 24).Unix(),
"iat": time.Now().Unix(),
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, _ := token.SignedString([]byte(a.jwtSecret))
return tokenString
}
func (a *AuthManager) generateRandomSecret() string {
b := make([]byte, 32)
rand.Read(b)
return base64.StdEncoding.EncodeToString(b)
}
Advanced Auth Features
Role-Based Access Control (RBAC)
// TuskLang configuration with RBAC
#auth {
rbac: {
roles: {
admin: {
permissions: ["read", "write", "delete", "manage_users"]
inherits: []
}
manager: {
permissions: ["read", "write", "manage_team"]
inherits: ["user"]
}
user: {
permissions: ["read", "write"]
inherits: ["guest"]
}
guest: {
permissions: ["read"]
inherits: []
}
}
resources: {
users: {
permissions: ["read", "write", "delete"]
roles: ["admin"]
}
posts: {
permissions: ["read", "write", "delete"]
roles: ["admin", "manager", "user"]
}
comments: {
permissions: ["read", "write"]
roles: ["admin", "manager", "user", "guest"]
}
}
}
}
Multi-Factor Authentication (MFA)
// TuskLang configuration with MFA
#auth {
mfa: {
enabled: true
methods: {
totp: {
enabled: true
issuer: "MyApp"
algorithm: "SHA1"
digits: 6
period: 30
}
sms: {
enabled: true
provider: "twilio"
template: "Your verification code is: {code}"
}
email: {
enabled: true
template: "verification-email.html"
subject: "Your verification code"
}
}
policies: {
admin_required: {
roles: ["admin"]
required: true
}
optional: {
roles: ["user"]
required: false
}
}
}
}
Performance Considerations
- Token Caching: Cache validated tokens to reduce validation overhead - Session Storage: Use efficient session storage (Redis, database) - Rate Limiting: Implement rate limiting for auth endpoints - Async Processing: Use goroutines for non-blocking auth operations - Connection Pooling: Use connection pooling for database auth
Security Notes
- Secure Secrets: Use secure secret management for sensitive data - Token Expiration: Implement proper token expiration and refresh - HTTPS Only: Use HTTPS for all authentication endpoints - Input Validation: Validate all authentication inputs - Audit Logging: Log all authentication events for security auditing
Best Practices
1. Principle of Least Privilege: Grant minimal required permissions 2. Secure Defaults: Use secure default configurations 3. Regular Rotation: Rotate secrets and tokens regularly 4. Monitoring: Monitor authentication events and failures 5. Testing: Test authentication flows thoroughly 6. Documentation: Document authentication policies and procedures
Integration Examples
With Gin Framework
import (
"github.com/gin-gonic/gin"
"github.com/tusklang/go-sdk"
)func setupGinAuth(config tusk.Config) *gin.Engine {
var authDirective AuthDirective
config.Get("#auth", &authDirective)
router := gin.Default()
// Apply auth middleware
router.Use(authMiddleware(authDirective))
return router
}
func authMiddleware(directive AuthDirective) gin.HandlerFunc {
return func(c *gin.Context) {
// Implement authentication logic
user, err := authenticateRequest(c.Request)
if err != nil {
c.AbortWithStatus(http.StatusUnauthorized)
return
}
c.Set("user", user)
c.Next()
}
}
With Echo Framework
import (
"github.com/labstack/echo/v4"
"github.com/tusklang/go-sdk"
)func setupEchoAuth(config tusk.Config) *echo.Echo {
var authDirective AuthDirective
config.Get("#auth", &authDirective)
e := echo.New()
// Apply auth middleware
e.Use(authMiddleware(authDirective))
return e
}
This comprehensive auth hash directives documentation provides Go developers with everything they need to build sophisticated authentication and authorization systems using TuskLang's powerful directive system.