{"id":28635822,"url":"https://github.com/simp-lee/guardian","last_synced_at":"2025-09-22T03:44:11.254Z","repository":{"id":288766984,"uuid":"957817429","full_name":"simp-lee/guardian","owner":"simp-lee","description":"A comprehensive auth library for Gin with JWT support, RBAC, user-specific permissions, hierarchical resources, multi-DB storage (MySQL/PostgreSQL/SQLite), and high-performance caching.","archived":false,"fork":false,"pushed_at":"2025-05-12T13:21:55.000Z","size":115,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2025-06-12T17:11:48.065Z","etag":null,"topics":["jwt","rbac","rbac-authorization"],"latest_commit_sha":null,"homepage":"https://pkg.go.dev/github.com/simp-lee/guardian","language":"Go","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/simp-lee.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2025-03-31T07:22:05.000Z","updated_at":"2025-05-12T13:33:35.000Z","dependencies_parsed_at":"2025-05-12T13:23:10.882Z","dependency_job_id":"820d1b54-dc0a-43cc-8ca9-8efd5bd3cb7e","html_url":"https://github.com/simp-lee/guardian","commit_stats":null,"previous_names":["simp-lee/guardian"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/simp-lee/guardian","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simp-lee%2Fguardian","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simp-lee%2Fguardian/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simp-lee%2Fguardian/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simp-lee%2Fguardian/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/simp-lee","download_url":"https://codeload.github.com/simp-lee/guardian/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/simp-lee%2Fguardian/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":276342516,"owners_count":25625581,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-09-22T02:00:08.972Z","response_time":79,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["jwt","rbac","rbac-authorization"],"created_at":"2025-06-12T17:11:37.149Z","updated_at":"2025-09-22T03:44:11.230Z","avatar_url":"https://github.com/simp-lee.png","language":"Go","funding_links":[],"categories":[],"sub_categories":[],"readme":"# Guardian: Go Authentication \u0026 Authorization Library\n\n**English** | [简体中文](README_ZH.md)\n\n## Introduction\n\n`Guardian` is a comprehensive security library specifically designed for `Gin` framework applications, providing role-based access control (RBAC) with JWT authentication to secure your Go APIs.\n\n## Features\n\n- **Role-Based Access Control (RBAC)**: Define granular permissions through roles\n- **JWT Authentication**: Secure token generation, validation, and refreshing\n- **OAuth 2.0 Support**: Third-party authentication with WeChat, GitHub, and extensible provider support\n- **Gin Middleware Integration**: Drop-in security middleware for Gin web applications\n- **Permission Management**: Resource and action-based permission checks with hierarchical support\n- **Token Lifecycle Management**: Generate, validate, refresh, and revoke tokens\n- **Rate Limiting**: Protection against brute force attacks with configurable limits\n- **Flexible Storage**: In-memory storage by default with SQL storage support (MySQL, PostgreSQL, SQLite) and customizable backends\n- **Hierarchical Resource Paths**: Support for nested resource permissions with wildcards\n- **High-Performance Caching**: Built-in multi-level caching system for improved access speed\n- **Auto Refresh**: Automatically refresh tokens before expiration\n- **Simple Error Handling**: Clean, idiomatic Go error handling using standard error types\n- **Configuration Validation**: Comprehensive validation system to prevent configuration errors\n- **Event Callbacks**: Hook into authentication and authorization events\n- **Session Management**: Redis-based session storage for OAuth state management in production\n\n## Installation\n\n```bash\ngo get github.com/simp-lee/guardian\n```\n\n## Quick Start\n\n```go\npackage main\n\nimport (\n    \"time\"\n    \"github.com/gin-gonic/gin\"\n    \"github.com/simp-lee/guardian\"\n    \"github.com/simp-lee/guardian/middleware\"\n)\n\nfunc main() {\n    // Initialize Guardian with a secret key\n    g, err := guardian.New(\n        guardian.WithSecretKey(\"your-secure-secret-key\"),\n    )\n    if err != nil {\n        panic(err)\n    }\n    defer g.Close()\n    \n    // Create roles\n    g.CreateRole(\"admin\", \"Administrator\", \"Full system access\")\n    g.CreateRole(\"user\", \"Regular User\", \"Basic access\")\n    \n    // Define permissions\n    g.AddRolePermission(\"admin\", \"users\", \"*\")      // Admin can do anything with users\n    g.AddRolePermission(\"user\", \"profile\", \"read\")  // Users can read profiles\n    g.AddRolePermission(\"user\", \"profile\", \"update\") // Users can update profiles\n\n    // Add user-specific permission (directly to a user, without using roles)\n    g.AddUserPermission(\"user456\", \"articles\", \"delete\") // This user can delete articles\n    \n    // Assign role to user\n    g.AddUserRole(\"user123\", \"user\")\n    \n    // Set up Gin router\n    r := gin.Default()\n    \n    // Add Guardian's error handling middleware for structured error responses\n    r.Use(guardian.ErrorMiddleware(guardian.DefaultHTTPErrorHandler()))\n    \n    // Public routes\n    r.POST(\"/login\", g.RateLimit(), func(c *gin.Context) {\n        // Validate credentials (implementation not shown)\n        userID := \"user123\" // Retrieved from authentication\n        \n        // Generate JWT token with 24-hour validity\n        token, err := g.GenerateToken(userID, 24*time.Hour)\n        if err != nil {\n            c.JSON(500, gin.H{\"error\": \"Failed to generate token\"})\n            return\n        }\n        \n        c.JSON(200, gin.H{\"token\": token})\n    })\n    \n    // Protected routes\n    protected := r.Group(\"/api\")\n    protected.Use(g.Auth()) // Apply authentication middleware\n    \n    protected.GET(\"/profile\", func(c *gin.Context) {\n        // After authentication, user ID is available in context\n        userID := c.GetString(middleware.CtxKeyUserID)\n        c.JSON(200, gin.H{\"message\": \"Profile for user: \" + userID})\n    })\n    \n    // Route with permission check\n    protected.POST(\"/users\", g.RequirePermission(\"users\", \"create\"), func(c *gin.Context) {\n        c.JSON(200, gin.H{\"message\": \"User created\"})\n    })\n    \n    // Route with role check\n    protected.GET(\"/admin\", g.RequireRole([]string{\"admin\"}), func(c *gin.Context) {\n        c.JSON(200, gin.H{\"message\": \"Admin area\"})\n    })\n    \n    r.Run(\":8080\")\n}\n```\n\n## Core Concepts\n\n### Authentication\n\n`Guardian` supports two primary authentication methods:\n\n1. **JWT Authentication**: Traditional credential-based authentication with JWT tokens\n2. **OAuth 2.0 Authentication**: Third-party authentication via providers like GitHub and WeChat\n\n#### JWT Authentication Workflow\n\n1. **Generate Token**: After verifying user credentials, generate a token containing the user's ID and roles\n2. **Validate Token**: Guardian validates tokens during requests using the `Auth()` middleware\n3. **Token Refresh**: Optionally refresh tokens before expiration to maintain session\n\n#### OAuth 2.0 Authentication Workflow\n\n1. **OAuth Flow**: Users authenticate via trusted third-party providers\n2. **User Mapping**: OAuth users are automatically mapped to Guardian users with default or custom logic\n3. **JWT Token**: After successful OAuth authentication, Guardian generates a JWT token for subsequent requests\n\n```go\n// Generate token with 1 hour validity\ntoken, err := g.GenerateToken(\"user123\", time.Hour)\n\n// Refresh token before expiration\nnewToken, err := g.RefreshToken(oldToken)\n\n// Revoke token when needed (logout, etc.)\ng.RevokeToken(token)\n\n// Revoke all tokens for a user (password change, security issue)\ng.RevokeAllUserTokens(\"user123\")\n```\n\n### Token Structure\n\nFrom the JWT service, parsed tokens provide the following information:\n\n```go\ntype Token struct {\n    UserID    string       // User ID\n    Roles     []string     // User's role list\n    ExpiresAt time.Time    // Token expiration time\n    IssuedAt  time.Time    // Token issue time\n    TokenID   string       // Unique token identifier\n}\n```\n\n### Roles and Permissions\n\nGuardian's RBAC system consists of:\n\n- **Roles**: Named collections of permissions (e.g., \"admin\", \"editor\")\n- **Permissions**: Defined as resource + action pairs (e.g., \"articles:read\")\n- **Users**: Assigned roles that grant them permissions\n\n```go\n// Create a role\ng.CreateRole(\"editor\", \"Content Editor\", \"Can manage content\")\n\n// Add permissions to role\ng.AddRolePermission(\"editor\", \"articles\", \"create\")\ng.AddRolePermission(\"editor\", \"articles\", \"update\")\ng.AddRolePermission(\"editor\", \"articles\", \"delete\")\ng.AddRolePermission(\"editor\", \"articles\", \"read\")\n\n// Add multiple permissions at once\ng.AddRolePermissions(\"editor\", \"comments\", []string{\"create\", \"read\", \"update\"})\n\n// Assign role to user\ng.AddUserRole(\"user123\", \"editor\")\n\n// Check if user has specific permission\nhasPermission, _ := g.HasPermission(\"user123\", \"articles\", \"update\")\n```\n\n### User-Specific Permissions\n\nIn addition to role-based permissions, `Guardian` allows assigning permissions directly to specific users without using roles. This is useful for granting temporary access, handling exceptions, or fine-tuning permissions for individual users.\n\n```go\n// Add direct permission to a user\ng.AddUserPermission(\"user123\", \"articles\", \"delete\")\n\n// Add multiple permissions at once\ng.AddUserPermissions(\"user123\", \"comments\", []string{\"edit\", \"delete\", \"moderate\"})\n\n// Check if user has direct permission (excluding role-inherited permissions)\nhasDirectPermission, _ := g.HasUserDirectPermission(\"user123\", \"articles\", \"delete\")\n\n// Remove specific direct permission\ng.RemoveUserPermission(\"user123\", \"articles\", \"delete\")\n\n// Remove all direct permissions\ng.RemoveAllUserPermissions(\"user123\")\n```\n\nWhen checking permissions with `HasPermission`, Guardian first checks user-specific permissions, then checks role-based permissions. This means a user can get permission from either source:\n\n```go\n// Assign a role to user\ng.AddUserRole(\"user123\", \"editor\")  // editor role has articles:edit permission\n\n// Add direct permission to same user\ng.AddUserPermission(\"user123\", \"articles\", \"delete\")\n\n// Check permissions - both return true\nhasEditPermission, _ := g.HasPermission(\"user123\", \"articles\", \"edit\")   // From role\nhasDeletePermission, _ := g.HasPermission(\"user123\", \"articles\", \"delete\") // Direct permission\n```\n\nUser-specific permissions are ideal for:\n\n1. **Temporary access**: Grant time-limited access without creating new roles\n2. **Exceptions**: Allow specific users to perform actions their roles don't permit\n3. **Fine-grained control**: Adjust individual permissions without modifying role definitions\n4. **Permission overrides**: Create exceptions for specific users without affecting entire role groups\n\nUser-specific permissions support all the same features as role permissions, including hierarchical resources and wildcards:\n\n```go\n// Using wildcards and hierarchies\ng.AddUserPermission(\"user123\", \"reports/*\", \"read\")     // User can read any report\ng.AddUserPermission(\"user123\", \"admin/settings\", \"*\")   // User can perform any action on admin settings\n```\n\n**Note**: When a user's role is removed, their directly assigned permissions remain effective. Only explicit calls to `RemoveUserPermission` or `RemoveAllUserPermissions` will remove direct permissions.\n\n### Caching System\n\n`Guardian` includes a high-performance multi-level caching system that significantly improves performance for frequently accessed data. The caching system automatically manages role and user permission data, reducing access to the underlying storage.\n\n```go\n// Enable caching with custom settings\ng, _ := guardian.New(\n    guardian.WithSecretKey(\"your-secure-secret-key\"),\n    guardian.WithCache(true), // Enable caching (enabled by default)\n    guardian.WithRoleCacheTTL(1 * time.Hour), // Time-to-live for role cache\n    guardian.WithUserRoleCacheTTL(30 * time.Minute), // Time-to-live for user-role relations\n    guardian.WithCacheCleanupInterval(10 * time.Minute), // Cache cleanup interval\n)\n\n// Or use a completely custom cache configuration\ncacheConfig := storage.CacheConfig{\n    EnableRoleCache:      true,\n    RoleCacheTTL:         2 * time.Hour,\n    EnableUserRoleCache:  true,\n    UserRoleCacheTTL:     1 * time.Hour,\n    CleanupInterval:      15 * time.Minute,\n    MaxRoleCacheSize:     2000,  // Maximum of 2000 roles in cache\n    MaxUserRoleCacheSize: 10000, // Maximum of 10000 user-role relations in cache\n}\n\ng, _ := guardian.New(\n    guardian.WithSecretKey(\"your-secure-secret-key\"),\n    guardian.WithCacheConfig(cacheConfig),\n)\n```\n\nThe caching system provides the following capabilities:\n\n1. **Role Caching**: Caches role definitions and permissions, reducing the overhead of repeatedly fetching the same roles from storage.\n2. **User-Role Caching**: Caches user-role relationships to speed up permission checks.\n3. **Automatic Invalidation**: Automatically invalidates relevant caches when roles or permissions change.\n4. **Periodic Cleanup**: Periodically cleans up expired entries to prevent memory leaks.\n5. **Size Limiting**: Configurable maximum cache entries to control memory usage.\n6. **Performance Monitoring**: Built-in hit and miss rate statistics.\n\n### Hierarchy Notation\n\nResources can be organized in hierarchies using the forward slash (`/`) as a separator:\n    \n```\narticles/draft\narticles/published\nusers/profiles\nusers/settings\n```\n\n### Wildcard Permissions\n\nThe wildcard character (`*`) can be used in several ways:\n\n1. **Action wildcard**: `\"*\"` matches any action on a specific resource\n```go\ng.AddRolePermission(\"admin\", \"articles\", \"*\")  // Admin can perform any action on articles\n```\n\n2. **Resource wildcard**: `\"*\"` matches any resource for a specific action\n```go\ng.AddRolePermission(\"reviewer\", \"*\", \"read\")  // Reviewer can read any resource\n```\n\n3. **Global wildcard**: `\"*\"` for both resource and action grants full access\n```go\ng.AddRolePermission(\"superadmin\", \"*\", \"*\")  // Superadmin has full access\n```\n\n4. **Hierarchical wildcard**: `\"resource/*\"` matches all sub-resources\n```go\ng.AddRolePermission(\"editor\", \"articles/*\", \"update\")  // Editor can update any article sub-resource\n```\n\n### Hierarchical Permission Resolution\n\nWhen checking permissions for a hierarchical resource path, `Guardian` uses the following process:\n\n1. First checks for an exact match on the specific resource and action\n2. Then checks for wildcard actions (`*`) on the specific resource\n3. Then checks for the specific action on wildcard resources (`*`)\n4. Then checks for wildcard permissions on wildcard resources (`*`, `*`)\n5. Finally traverses up the resource hierarchy, checking parent resources with wildcards\n\nFor example, when checking permission for `\"articles/draft/section\"` with `\"edit\"` action:\n\n1. Checks `\"articles/draft/section\"` with `\"edit\"` permission\n2. Checks `\"articles/draft/section\"` with `\"*\"` permission\n3. Checks `\"*\"` with `\"edit\"` permission\n4. Checks `\"*\"` with `\"*\"` permission\n5. Checks hierarchical wildcards:\n  - `\"articles/draft/*\"` with `\"edit\"` permission\n  - `\"articles/draft/*\"` with `\"*\"` permission\n  - `\"articles/*\"` with `\"edit\"` permission\n  - `\"articles/*\"` with `\"*\"` permission\n\nThis hierarchical resolution enables efficient permission structures where higher-level permissions automatically apply to sub-resources without requiring explicit definitions.\n\n**Important**: Permissions are inherited ONLY through explicit wildcard patterns. A permission on `\"articles/drafts\"` (without wildcard) will NOT automatically apply to `\"articles/drafts/special\"`.\n\n**Examples**:\n\n```go\n// Set up resource hierarchy permissions\ng.AddRolePermission(\"contentManager\", \"content/*\", \"read\")   // Can read all content\ng.AddRolePermission(\"articleEditor\", \"content/articles/*\", \"edit\")  // Can edit all articles\ng.AddRolePermission(\"draftEditor\", \"content/articles/drafts/*\", \"publish\")  // Can publish drafts\n\n// These permissions apply automatically:\n// - contentManager can read content/articles, content/videos, etc.\n// - articleEditor can edit content/articles/published, content/articles/drafts, etc.\n// - draftEditor can publish any draft article\n\n// Check permissions\nhasPermission, _ := g.HasPermission(\"user123\", \"content/articles/drafts/article-1\", \"read\")\n// Returns true if user123 has contentManager role (due to content/* permission)\n```\n\nThis approach significantly reduces the number of permission entries required while maintaining granular control over your resources.\n\n### Middleware\n\n`Guardian` provides middleware functions specifically for the `Gin` framework. When using `Guardian` middleware, the order is important:\n\n1. **g.Auth()** should be applied first to authenticate users\n2. **g.RequireRole()** or **g.RequirePermission()** should be applied after Auth\n3. **g.RateLimit()** can be applied independently, typically for public endpoints like login\n\n```go\n// Correct order\nprotected := r.Group(\"/api\")\nprotected.Use(g.Auth())  // Authenticate first\nprotected.Use(g.RequireRole([]string{\"admin\"}))  // Then check roles\n\n// Apply rate limiting to login endpoint\nr.POST(\"/login\", g.RateLimit(), loginHandler)\n```\n\n### Context Values\n\nAfter successful authentication, Guardian's Auth middleware sets the following keys in the `Gin` context:\n\n```go\n// Access authenticated user ID\nuserID := c.GetString(middleware.CtxKeyUserID)\n\n// Access user roles (if any)\nroles := c.GetStringSlice(middleware.CtxKeyRoles)\n\n// Access the full parsed token object\ntoken := c.MustGet(middleware.CtxKeyToken).(*jwt.Token)\n```\n\nThese context values are available in all route handlers that execute after the Auth middleware. This allows your API handlers to access user information without having to parse the token again.\n\nExample protected route handler:\n\n```go\nprotected.GET(\"/profile\", func(c *gin.Context) {\n    // User ID is automatically available from context\n    userID := c.GetString(middleware.CtxKeyUserID)\n    \n    // You can use the user ID to fetch user data\n    userData, err := userService.GetUserProfile(userID)\n    if err != nil {\n        c.JSON(500, gin.H{\"error\": \"Failed to get user data\"})\n        return\n    }\n    \n    c.JSON(200, userData)\n})\n```\n\nNote that these context keys are only set after the Auth middleware has processed a request with a valid token. They will not be available in routes that don't use the Auth middleware (like your login endpoint).\n\n## OAuth 2.0 Authentication\n\nGuardian provides built-in support for OAuth 2.0 authentication with popular providers like WeChat and GitHub. OAuth allows users to authenticate using their existing accounts from third-party services, eliminating the need for users to create separate accounts.\n\n### Supported Providers\n\n- **WeChat**: WeChat Open Platform OAuth 2.0 for web applications\n- **GitHub**: GitHub OAuth Apps with configurable scopes\n- **Extensible**: Add custom providers by implementing the `Provider` interface\n\n### Quick OAuth Setup\n\n```go\npackage main\n\nimport (\n    \"github.com/gin-gonic/gin\"\n    \"github.com/simp-lee/guardian\"\n    \"github.com/simp-lee/guardian/middleware\"\n    \"github.com/simp-lee/guardian/oauth\"\n)\n\nfunc main() {\n    // Initialize Guardian with OAuth configuration\n    g, err := guardian.New(\n        guardian.WithSecretKey(\"your-secure-secret-key\"),\n        guardian.WithOAuth(\u0026oauth.Config{\n            BaseURL:      \"http://localhost:8080\",\n            CallbackPath: \"/auth/callback\",\n            StateTimeout: 10 * time.Minute, // OAuth state parameter timeout\n            Providers: map[string]oauth.ProviderConfig{\n                \"github\": {\n                    ClientID:     \"your-github-client-id\",\n                    ClientSecret: \"your-github-client-secret\",\n                    Scopes:       []string{\"user:email\"},\n                },\n                \"wechat\": {\n                    ClientID:     \"your-wechat-appid\",\n                    ClientSecret: \"your-wechat-secret\",\n                    Scopes:       []string{\"snsapi_login\"},\n                    Extra: map[string]string{\n                        \"language\": \"zh_CN\", // WeChat interface language\n                    },\n                },\n            },\n        }),\n    )\n    if err != nil {\n        panic(err)\n    }\n    defer g.Close()\n\n    r := gin.Default()\n\n    // OAuth routes\n    auth := r.Group(\"/auth\")\n    {\n        auth.GET(\"/:provider\", g.OAuth())                    // Start OAuth flow\n        auth.GET(\"/callback/:provider\", g.OAuthCallback())   // Handle OAuth callback\n    }\n\n    // Protected routes\n    api := r.Group(\"/api\")\n    api.Use(g.Auth())\n    {\n        api.GET(\"/profile\", func(c *gin.Context) {\n            userID := c.GetString(middleware.CtxKeyUserID)\n            roles := c.GetStringSlice(middleware.CtxKeyRoles)\n            c.JSON(200, gin.H{\n                \"user_id\": userID,\n                \"roles\":   roles,\n                \"message\": \"Welcome!\",\n            })\n        })\n    }\n\n    r.Run(\":8080\")\n}\n```\n\n### OAuth Flow\n\nThe OAuth authentication process follows these steps:\n\n1. **Initiate OAuth**: User visits `/auth/github` or `/auth/wechat` to start OAuth flow\n2. **Authorization**: User is redirected to the provider (GitHub/WeChat) for authorization  \n3. **Callback**: Provider redirects back to `/auth/callback/{provider}` with authorization code\n4. **Token Generation**: Guardian processes the callback and generates a JWT token\n5. **Authentication**: User can now access protected routes using the JWT token\n\n### Provider Configuration\n\n#### GitHub OAuth Setup\n\n1. Create a GitHub OAuth App in your GitHub Developer Settings\n2. Set Authorization callback URL to: `http://your-domain.com/auth/callback/github`\n3. Configure in Guardian:\n\n```go\n\"github\": {\n    ClientID:     \"your-github-client-id\",\n    ClientSecret: \"your-github-client-secret\", \n    Scopes:       []string{\"user:email\"}, // Request email access\n}\n```\n\n#### WeChat OAuth Setup\n\n1. Register an application on WeChat Open Platform\n2. Set redirect URI to: `http://your-domain.com/auth/callback/wechat`\n3. Configure in Guardian:\n\n```go\n\"wechat\": {\n    ClientID:     \"your-wechat-appid\",\n    ClientSecret: \"your-wechat-secret\",\n    Scopes:       []string{\"snsapi_login\"}, // For web login\n    Extra: map[string]string{\n        \"language\": \"zh_CN\", // Interface language: zh_CN or en\n    },\n}\n```\n\n### Production Configuration with Redis\n\nFor production environments, use Redis for OAuth session storage to support multiple server instances and ensure session persistence:\n\n```go\nimport (\n    \"github.com/redis/go-redis/v9\"\n    \"github.com/simp-lee/guardian/oauth/sessionstore\"\n)\n\nfunc main() {\n    // Redis client for OAuth session storage\n    redisClient := redis.NewClient(\u0026redis.Options{\n        Addr:     \"localhost:6379\",\n        Password: \"\", // Set password if required\n        DB:       0,  // Default DB\n    })\n    redisStore := sessionstore.NewRedis(redisClient)\n\n    g, _ := guardian.New(\n        guardian.WithSecretKey(\"your-secure-secret-key\"),\n        guardian.WithOAuth(\u0026oauth.Config{\n            BaseURL:      \"https://your-app.com\",\n            CallbackPath: \"/auth/callback\", \n            StateTimeout: 10 * time.Minute, // OAuth state timeout\n            Providers: map[string]oauth.ProviderConfig{\n                \"github\": {\n                    ClientID:     \"your-github-client-id\",\n                    ClientSecret: \"your-github-client-secret\",\n                    Scopes:       []string{\"user:email\"},\n                },\n                \"wechat\": {\n                    ClientID:     \"your-wechat-appid\",\n                    ClientSecret: \"your-wechat-secret\",\n                    Scopes:       []string{\"snsapi_login\"},\n                    Extra: map[string]string{\n                        \"language\": \"zh_CN\",\n                    },\n                },\n            },\n        }, redisStore), // Pass Redis store as second parameter\n    )\n\n    // Setup routes...\n}\n```\n\n### User Mapping and Customization\n\nOAuth users are automatically mapped to Guardian users with the following pattern:\n\n```go\n// Default user ID format: \"{provider}_{oauth_user_id}\"\nuserID := \"github_12345\"  // For GitHub user with ID 12345\nuserID := \"wechat_abc123\" // For WeChat user with unionid abc123\n\n// Default role assignment\nroles := []string{\"user\"} // OAuth users get \"user\" role by default\n```\n\nYou can customize user mapping by providing a custom OAuth user processor:\n\n```go\nimport \"github.com/simp-lee/guardian/oauth\"\n\n// Custom user processor\ng.SetOAuthUserProcessor(func(oauthUser *oauth.User) (userID string, roles []string, userData map[string]any, err error) {\n    // Custom logic to map OAuth user to your system\n    userID = fmt.Sprintf(\"oauth_%s_%s\", oauthUser.Provider, oauthUser.UserID)\n    \n    // Assign roles based on email domain or other criteria\n    if strings.HasSuffix(oauthUser.Email, \"@yourcompany.com\") {\n        roles = []string{\"admin\", \"user\"}\n    } else {\n        roles = []string{\"user\"}\n    }\n    \n    // Include comprehensive user data from OAuth profile\n    userData = map[string]any{\n        \"email\":        oauthUser.Email,\n        \"name\":         oauthUser.Name,\n        \"first_name\":   oauthUser.FirstName,\n        \"last_name\":    oauthUser.LastName,\n        \"nickname\":     oauthUser.NickName,\n        \"avatar\":       oauthUser.AvatarURL,\n        \"location\":     oauthUser.Location,\n        \"provider\":     oauthUser.Provider,\n        \"access_token\": oauthUser.AccessToken, // Store if needed for API calls\n        \"expires_at\":   oauthUser.ExpiresAt,\n        \"raw_data\":     oauthUser.RawData,     // Provider-specific additional data\n    }\n    \n    return userID, roles, userData, nil\n})\n```\n\nAvailable OAuth user information:\n\n```go\nimport \"time\"\n\ntype oauth.User struct {\n    // Provider information\n    Provider string `json:\"provider\"`\n    UserID   string `json:\"user_id\"` // Provider's user ID\n    Email    string `json:\"email\"`\n\n    // User profile\n    Name      string `json:\"name\"`\n    FirstName string `json:\"first_name,omitzero\"`\n    LastName  string `json:\"last_name,omitzero\"`\n    NickName  string `json:\"nickname,omitzero\"`\n    AvatarURL string `json:\"avatar_url,omitzero\"`\n    Location  string `json:\"location,omitzero\"`\n\n    // OAuth tokens\n    AccessToken  string    `json:\"access_token\"`\n    RefreshToken string    `json:\"refresh_token,omitzero\"`\n    ExpiresAt    time.Time `json:\"expires_at,omitzero\"`\n\n    // Additional data\n    RawData map[string]any `json:\"raw_data,omitzero\"`\n}\n\ntype oauth.Token struct {\n    AccessToken  string `json:\"access_token\"`\n    RefreshToken string `json:\"refresh_token\"`\n    TokenType    string `json:\"token_type\"`\n    ExpiresIn    int    `json:\"expires_in\"`\n    Scope        string `json:\"scope\"`\n}\n```\n\n### Custom OAuth Providers\n\nGuardian supports adding custom OAuth providers by implementing the `Provider` interface. Built-in providers (GitHub, WeChat) are automatically available, but you can add additional providers.\n\n#### Provider Interface\n\n```go\ntype Provider interface {\n    Name() string                                     // Provider name (e.g., \"google\", \"facebook\")\n    BeginAuth(state string) (Session, error)          // Start OAuth flow, return authorization URL\n    FetchUser(session Session) (*User, error)         // Exchange code for user info\n    RefreshToken(refreshToken string) (*Token, error) // Refresh access token using refresh token\n    SetCallbackURL(url string)                        // Set OAuth callback URL\n    UnmarshalSession(data string) (Session, error)    // Create session from marshaled data\n}\n\ntype Session interface {\n    GetAuthURL() (string, error)                                    // Get authorization URL for redirect\n    Authorize(provider Provider, params url.Values) (string, error) // Exchange authorization code for access token\n    Marshal() string                                                // Serialize session to string\n}\n```\n\n#### Provider Factory Registration\n\nFor fully custom providers, you may need to register them in the provider factory. See `oauth/provider.go` for the provider factory implementation.\n\n## Advanced Configuration\n\n```go\nimport \"github.com/simp-lee/guardian/oauth\"\n\ng, _ := guardian.New(\n    // Required: Secret key for JWT signing\n    guardian.WithSecretKey(\"your-secure-secret-key\"),\n    \n    // Optional: Custom storage implementation\n    guardian.WithStorage(myCustomStorage),\n    \n    // Optional: Enable auto-refresh when tokens are near expiration\n    guardian.WithAutoRefresh(30 * time.Minute),\n    \n    // Optional: Custom token header name\n    guardian.WithHeaderName(\"X-Auth-Token\"),\n    \n    // Optional: Set the cleanup interval for expired tokens\n    guardian.WithCleanupInterval(2 * time.Hour),\n\n    // Optional: Enable role and permission caching\n    guardian.WithCache(true),\n    \n    // Optional: Set role cache expiration time\n    guardian.WithRoleCacheTTL(1 * time.Hour),\n    \n    // Optional: Set user-role cache expiration time\n    guardian.WithUserRoleCacheTTL(30 * time.Minute),\n    \n    // Optional: Set cache cleanup interval\n    guardian.WithCacheCleanupInterval(10 * time.Minute),\n    \n    // Optional: Configure OAuth 2.0 authentication\n    guardian.WithOAuth(\u0026oauth.Config{\n        BaseURL:      \"http://localhost:8080\",\n        CallbackPath: \"/auth/callback\",\n        StateTimeout: 10 * time.Minute,\n        Providers: map[string]oauth.ProviderConfig{\n            \"github\": {\n                ClientID:     \"your-github-client-id\",\n                ClientSecret: \"your-github-client-secret\",\n                Scopes:       []string{\"user:email\"},\n            },\n        },\n    }),\n)\n```\n\n### Authentication Middleware Options\n\n```go\ng.Auth(\n    // Custom authentication header name\n    guardian.WithAuthHeaderName(\"X-API-Token\"),\n    \n    // Custom token type (default is \"Bearer\")\n    guardian.WithAuthTokenType(\"Custom\"),\n    \n    // Callback for successful authentication\n    guardian.OnAuthSuccess(func(c *gin.Context, token *jwt.Token) {\n        // Custom handling logic\n    }),\n    \n    // Callback for authentication failure\n    guardian.OnAuthFailure(func(c *gin.Context, err error) {\n        // Custom handling logic\n    }),\n    \n    // Custom error handler\n    guardian.WithAuthErrorHandler(func(c *gin.Context, err error) {\n        c.JSON(401, gin.H{\"custom_error\": err.Error()})\n        c.Abort()\n    }),\n)\n```\n\n### Auto Refresh Options\n\nIf you've enabled auto-refresh in the main configuration:\n\n```go\n// Enable auto-refresh in the Guardian constructor\ng, _ := guardian.New(\n    guardian.WithSecretKey(\"your-secret\"),\n    // Enable global auto-refresh and set the default threshold\n    // The token will be refreshed if its remaining validity is less than 15 minutes\n    guardian.WithAutoRefresh(15 * time.Minute),\n)\n```\n\nWhen you apply the Auth middleware and auto-refresh is enabled, the refresh functionality will be automatically included:\n\n```go\n// Apply standard authentication middleware \nrouter.Use(g.Auth())\n```\n\nFor more control over the refresh behavior, you can manually configure the auto-refresh middleware:\n\n```go\nimport (\n    \"github.com/simp-lee/guardian\"\n    \"github.com/simp-lee/guardian/middleware\"\n)\n\n// Apply the auto-refresh middleware with custom options\nrouter.Use(middleware.AutoRefresh(\n    // Callback when a token is refreshed\n    guardian.OnTokenRefresh(func(c *gin.Context, oldToken, newToken string) {\n        // Log refresh event or perform other actions\n        log.Printf(\"Token refreshed: %s -\u003e %s\", oldToken, newToken)\n    }),\n    \n    // Custom refresh threshold for this specific middleware instance\n    // Overrides the global setting from Guardian constructor\n    guardian.WithRefreshThreshold(10 * time.Minute),\n))\n```\n\nWhen a token is automatically refreshed, the new token is returned in the HTTP response `X-New-Token` header. Clients should check for this header and use the new token in subsequent requests.\n\n### Permission Middleware Options\n\n```go\ng.RequirePermission(\"articles\", \"edit\",\n    // Callback when permission is granted\n    guardian.OnPermissionGranted(func(c *gin.Context, userID, resource, action string) {\n        // Log access, etc.\n    }),\n    \n    // Callback when permission is denied\n    guardian.OnPermissionDenied(func(c *gin.Context, userID, resource, action string) {\n        // Log denial, etc.\n    }),\n    \n    // Custom error handler\n    guardian.WithPermissionErrorHandler(func(c *gin.Context, err error) {\n        c.JSON(403, gin.H{\"error\": \"No permission to access this resource\"})\n        c.Abort()\n    }),\n)\n```\n\n### Role Middleware Options\n\n```go\ng.RequireRole([]string{\"admin\", \"editor\"},\n    // Callback when role check passes\n    guardian.OnRoleGranted(func(c *gin.Context, userID string, roles []string) {\n        // Log access, etc.\n    }),\n    \n    // Callback when role check fails\n    guardian.OnRoleDenied(func(c *gin.Context, userID string, roles []string) {\n        // Log denial, etc.\n    }),\n    \n    // Custom error handler\n    guardian.WithRoleErrorHandler(func(c *gin.Context, err error) {\n        c.JSON(403, gin.H{\"error\": \"Higher privileges required\"})\n        c.Abort()\n    }),\n)\n```\n\n### Rate Limit Middleware Options\n\n```go\ng.RateLimit(\n    // Set the requests per minute limit\n    guardian.WithRateLimitRequestsPerMinute(60),\n    \n    // Set the burst limit for concurrent requests\n    guardian.WithRateLimitBurst(10),\n    \n    // Set the cleanup interval for rate limit entries\n    guardian.WithRateLimitCleanupInterval(5 * time.Minute),\n    \n    // Set the expiration time for rate limit entries\n    guardian.WithRateLimitExpirationTime(10 * time.Minute),\n    \n    // Custom error handler\n    guardian.WithRateLimitErrorHandler(func(c *gin.Context, err error) {\n        c.JSON(429, gin.H{\"error\": \"Too many requests, please try again later\"})\n        c.Abort()\n    }),\n    \n    // Custom key extractor (for identifying request source)\n    guardian.WithRateLimitKeyExtractor(func(c *gin.Context) string {\n        // For example, extract user ID from custom header or JWT token\n        return c.GetHeader(\"X-User-ID\")\n    }),\n    \n    // Callback when rate limit is triggered\n    guardian.OnRateLimited(func(key string, c *gin.Context) {\n        // Log rate-limited request\n    }),\n    \n    // Callback for normal request (includes tokens remaining)\n    guardian.OnRateLimitRequest(func(key string, remaining int, c *gin.Context) {\n        // Can be used for monitoring or adding custom response headers\n    }),\n)\n```\n\nThe rate limit middleware adds the following headers to responses:\n\n- `X-RateLimit-Limit`: Indicates the rate limit ceiling\n- `X-RateLimit-Remaining`: Indicates the number of tokens remaining\n\n## Storage Options\n\n`Guardian` supports multiple storage backends for roles, permissions and user-role mappings:\n\n### Memory Storage (Default)\n\nBy default, `Guardian` uses an in-memory storage implementation that stores all data in Go maps:\n\n```go\n// Using the default memory storage\ng, _ := guardian.New(\n    guardian.WithSecretKey(\"your-secret-key\"),\n    // No storage option specified - memory storage will be used\n)\n```\n\nMemory storage is suitable for testing or applications where persistence is not required. However, all data will be lost when the application restarts.\n\n### SQL Storage\n\nFor production systems, `Guardian` provides SQL-based storage implementations that persist role and permission data to a database:\n\n```go\nimport (\n    \"github.com/simp-lee/guardian\"\n    \"github.com/simp-lee/guardian/storage\"\n    _ \"github.com/go-sql-driver/mysql\" // Import MySQL driver\n)\n\n// Create MySQL storage\nsqlStorage, err := storage.CreateMySQLStorage(\n    \"user:password@tcp(127.0.0.1:3306)/guardian_db?parseTime=true\"\n)\nif err != nil {\n    log.Fatalf(\"Failed to create SQL storage: %v\", err)\n}\n\n// Create Guardian instance with SQL storage\ng, err := guardian.New(\n    guardian.WithSecretKey(\"your-secure-secret-key\"),\n    guardian.WithStorage(sqlStorage),\n)\nif err != nil {\n    log.Fatalf(\"Failed to create Guardian: %v\", err)\n}\ndefer g.Close()\n```\n\n#### Supported Database Systems\n\nGuardian currently supports the following database systems:\n\n1. **MySQL/MariaDB**:\n   ```go\n   storage, err := storage.CreateMySQLStorage(\n       \"user:password@tcp(127.0.0.1:3306)/dbname?parseTime=true\"\n   )\n   ```\n\n2. **PostgreSQL**:\n   ```go\n   storage, err := storage.CreatePostgresStorage(\n       \"host=localhost port=5432 user=postgres password=secret dbname=guardian_db sslmode=disable\"\n   )\n   ```\n\n3. **SQLite**:\n   ```go\n   storage, err := storage.CreateSQLiteStorage(\"path/to/database.db\")\n   // Or use in-memory SQLite database\n   storage, err := storage.CreateSQLiteStorage(\":memory:\")\n   ```\n\n#### Using with GORM\n\nIf your application already uses `GORM` for database access, you can integrate `Guardian` with your existing database connection:\n\n```go\nimport (\n    \"github.com/simp-lee/guardian\"\n    \"github.com/simp-lee/guardian/storage\"\n    \"gorm.io/driver/mysql\"\n    \"gorm.io/gorm\"\n)\n\n// Create GORM connection\ndsn := \"user:password@tcp(127.0.0.1:3306)/database?charset=utf8mb4\u0026parseTime=True\u0026loc=Local\"\ngormDB, err := gorm.Open(mysql.Open(dsn), \u0026gorm.Config{})\nif err != nil {\n    log.Fatalf(\"Failed to connect to database: %v\", err)\n}\n\n// Get the underlying *sql.DB instance\nsqlDB, err := gormDB.DB()\nif err != nil {\n    log.Fatalf(\"Failed to get underlying sql.DB: %v\", err)\n}\n\n// Create Guardian storage using the existing connection\nguardianStorage, err := storage.NewSQLStorage(sqlDB)\nif err != nil {\n    log.Fatalf(\"Failed to create Guardian storage: %v\", err)\n}\n\n// Create Guardian instance\ng, err := guardian.New(\n    guardian.WithSecretKey(\"your-secure-key\"),\n    guardian.WithStorage(guardianStorage),\n)\n```\n\n#### Database Schema\n\nWhen using SQL storage, `Guardian` automatically creates the following tables:\n\n- `guardian_roles`: Stores role definitions and permissions\n- `guardian_user_roles`: Stores user-role associations\n\nThe tables are created with `IF NOT EXISTS` clauses, so they won't conflict with existing tables.\n\n### Database Driver Installation\n\nDepending on your chosen database system, you'll need to install the appropriate driver:\n\n```bash\n# MySQL driver\ngo get github.com/go-sql-driver/mysql\n\n# PostgreSQL driver\ngo get github.com/lib/pq\n\n# SQLite driver (CGO required)\ngo get github.com/mattn/go-sqlite3\n\n# SQLite driver (Pure Go, no CGO)\ngo get modernc.org/sqlite\n```\n\n### Custom Storage Implementation\n\nYou can implement your own storage backend by implementing the Storage interface:\n\n```go\ntype Storage interface {\n    // Role management\n    CreateRole(role *Role) error\n    GetRole(roleID string) (*Role, error)\n    UpdateRole(role *Role) error\n    DeleteRole(roleID string) error\n    ListRoles() ([]*Role, error)\n\n    // User-Role management\n    AddUserRole(userID, roleID string) error\n    RemoveUserRole(userID, roleID string) error\n    GetUserRoles(userID string) ([]string, error)\n\n    // Permission management\n    AddRolePermission(roleID, resource, action string) error\n    RemoveRolePermission(roleID, resource string) error\n    HasPermission(roleID, resource, action string) (bool, error)\n\n    // Resource management\n    Close() error\n}\n```\n\nThen use your custom implementation with Guardian:\n\n```go\ncustomStorage := NewMyCustomStorage(...)\ng, _ := guardian.New(\n    guardian.WithSecretKey(\"your-secret-key\"),\n    guardian.WithStorage(customStorage),\n)\n```\n\n## Cache Management\n\n`Guardian` provides complete control over the caching system, allowing you to fine-tune it according to application needs:\n\n```go\n// Get cache statistics\nroleHits, roleMisses, userRoleHits, userRoleMisses := g.GetCacheStats()\nfmt.Printf(\"Role Cache: Hit Rate %.2f%%\\n\", \n    float64(roleHits)/float64(roleHits+roleMisses)*100)\nfmt.Printf(\"User Role Cache: Hit Rate %.2f%%\\n\", \n    float64(userRoleHits)/float64(userRoleHits+userRoleMisses)*100)\n\n// Get current cache size\nroleCacheSize, userRoleCacheSize := g.GetCacheSize()\nfmt.Printf(\"Cache Size: %d roles, %d user role entries\\n\", \n    roleCacheSize, userRoleCacheSize)\n\n// Clear all caches (e.g., after a bulk permission change)\ng.ClearCache()\n```\n\nAdjust cache configuration to balance memory usage and performance:\n\n```go\n// Custom cache configuration suitable for large applications with many users but fewer roles\nconfig := storage.CacheConfig{\n    EnableRoleCache:      true,\n    RoleCacheTTL:         4 * time.Hour,  // Roles do not change often, can be cached longer\n    EnableUserRoleCache:  true,\n    UserRoleCacheTTL:     30 * time.Minute,\n    CleanupInterval:      10 * time.Minute,\n    MaxRoleCacheSize:     500,    // System has fewer roles, appropriate limit\n    MaxUserRoleCacheSize: 100000, // Large number of users, requires larger cache\n}\n\ng, _ := guardian.New(\n    guardian.WithSecretKey(\"your-secret-key\"),\n    guardian.WithCacheConfig(config),\n)\n```\n\n## Configuration Validation\n\nGuardian includes a comprehensive configuration validation system that helps prevent common configuration errors and security issues.\n\n### Configuration Validation Features\n\nThe validation system checks for:\n\n**Security Configuration:**\n- **Secret Key Strength**: Ensures JWT secret keys meet minimum length and entropy requirements\n- **Token Timing**: Validates RefreshThreshold and cleanup interval settings\n- **OAuth Security**: Validates OAuth provider configurations and callback URLs\n\n**Performance Configuration:**\n- **Cache Settings**: Validates cache TTL values and cleanup intervals\n- **Rate Limiting**: Ensures rate limit values are within reasonable bounds\n- **Storage Configuration**: Validates storage connection parameters\n\n**Correctness Validation:**\n- **Required Fields**: Ensures all mandatory configuration fields are provided\n- **Value Ranges**: Validates that numeric values are within acceptable ranges\n- **Dependencies**: Checks that dependent features are properly configured\n\n### Example: Production vs Development Configuration\n\n```go\n// Production configuration\nprodGuardian, err := guardian.New(\n    guardian.WithSecretKey(\"very-long-secure-production-secret-key-with-high-entropy-2024\"),\n    guardian.WithRefreshThreshold(15 * time.Minute),    // Refresh before expiration\n    guardian.WithCache(true),                           // Enable caching\n)\n\n// Development configuration\ndevGuardian, err := guardian.New(\n    guardian.WithSecretKey(\"dev-secret-key\"),\n)\n```\n\n### Validation Error Handling\n\nConfiguration validation errors provide detailed information about what needs to be fixed:\n\n```go\ng, err := guardian.New(\n    guardian.WithSecretKey(\"short\"), // Too short\n)\nif err != nil {\n    // err will contain detailed validation failure information\n    fmt.Printf(\"Configuration validation failed: %v\", err)\n}\n```\n\n### Custom Validation Rules\n\nYou can also manually validate configuration (using exported helpers) before creating Guardian, but in most cases simply calling `guardian.New(...)` is sufficient—invalid settings will be reported with actionable error messages. Advanced per-mode validation helpers are not exposed.\n\n## Error Handling\n\n`Guardian` follows Go's idiomatic error handling principles using standard error types and the `errors` package. All errors are simple, clear, and consistent.\n\n### Simple Error Handling\n\nGuardian uses standard Go error handling with these features:\n\n### Standard Error Definitions\n\nCanonical error variables are defined in their respective packages to avoid duplication:\n\n```go\nimport (\n    \"errors\"\n    \"github.com/simp-lee/guardian\"\n    \"github.com/simp-lee/guardian/jwt\"\n    \"github.com/simp-lee/guardian/rbac\"\n    \"github.com/simp-lee/guardian/storage\"\n)\n\n// Authentication errors come from the jwt package\nif errors.Is(err, jwt.ErrInvalidToken) {\n    // handle invalid token\n}\nif errors.Is(err, jwt.ErrExpiredToken) {\n    // handle expired token\n}\n\n// Authorization and input validation errors come from rbac\nif errors.Is(err, rbac.ErrInvalidResource) || errors.Is(err, rbac.ErrInvalidAction) {\n    // handle bad input\n}\n\n// Storage-level conflicts/not-found come from storage\nif errors.Is(err, storage.ErrRoleNotFound) {\n    // handle missing role\n}\nif errors.Is(err, storage.ErrRoleAlreadyExists) {\n    // handle duplicate role\n}\n\n// Guardian package keeps a few generic errors for HTTP layer convenience\nif errors.Is(err, guardian.ErrPermissionDenied) {\n    // handle permission denied\n}\n```\n\n### HTTP Error Responses\n\nAll errors are automatically converted to JSON responses:\n\n```json\n{\n  \"error\": \"invalid token\", \n  \"success\": false\n}\n```\n\nStatus codes are automatically mapped:\n- `401` - Authentication errors (invalid/expired tokens)\n- `403` - Authorization errors (insufficient permissions)\n- `404` - Resource not found errors\n- `409` - Conflict errors (duplicate resources)\n- `429` - Rate limit exceeded\n- `500` - Internal server errors\n\n### Error Middleware\n\n```go\nimport \"github.com/simp-lee/guardian\"\n\nr := gin.Default()\n\n// Add the error handling middleware\nr.Use(guardian.ErrorMiddleware(guardian.DefaultHTTPErrorHandler()))\n\n// Your routes...\n```\n\n### Custom Error Handling\n\nYou can customize error handling using the provided utilities:\n\n```go\n// In your middleware or handlers\nif err != nil {\n    guardian.AbortWithError(c, err)\n    return\n}\n\n// Or create contextual errors\nerr := guardian.NewAuthenticationError(\"token validation failed\", originalErr)\nguardian.AbortWithError(c, err)\n```\n\n## Security Best Practices\n\n1. **Use strong secret keys** and manage them securely (environment variables, secret management)\n2. **Keep token lifetimes short** - hours rather than days when possible\n3. **Revoke tokens** when users change passwords or during security incidents\n4. **Apply rate limiting** to authentication endpoints\n5. **Use HTTPS** for all production deployments\n6. **Follow least privilege principle** when assigning permissions\n7. **Regularly audit** role assignments and permissions\n8. **Handle token refreshes** - Clients should monitor the X-New-Token header and update their stored token\n9. **OAuth Security**: \n   - Store OAuth client secrets securely\n   - Use HTTPS for OAuth callback URLs\n   - Validate OAuth state parameters to prevent CSRF attacks\n   - Use Redis or persistent storage for OAuth sessions in production\n   - Regularly rotate OAuth client credentials\n\n## Complete API Reference\n\nFor the complete API reference, see the [godoc documentation](https://pkg.go.dev/github.com/simp-lee/guardian).\n\n## License\n\nMIT License\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimp-lee%2Fguardian","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsimp-lee%2Fguardian","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsimp-lee%2Fguardian/lists"}