Skip to Content

Auth

The auth package provides JWT-based authentication with token generation and validation, custom claims, authentication middleware, and role-based access control (RBAC) powered by Casbin.

Import

import "github.com/gofastadev/gofasta/pkg/auth"

Key Types

Claims

type Claims struct { jwt.RegisteredClaims Role string `json:"role"` Roles []string `json:"roles,omitempty"` }

The authenticated principal is the registered sub claim (RFC 7519 §4.1.2), carried by the embedded jwt.RegisteredClaims. Read it with SubjectID():

subject := claims.SubjectID() // the `sub` claim, or "" when the token names none

Two things about sub worth knowing before you build on it:

  • It is a subject, not necessarily a user. Under the client-credentials grant sub conventionally holds a client id, so code assuming it names a row in your users table is wrong for that grant.
  • It is unique within the issuer, not globally — the globally unique identity is the pair (iss, sub). If you accept tokens from more than one issuer, key on both, or two issuers’ subjects can collide into one identity.

Roles come as a single role or a roles array; HasRole, HasAnyRole and RoleLadder read both.

AuthConfig

The auth configuration uses koanf struct tags and is part of the root AppConfig. Environment variables use the GOFASTA_ prefix (e.g., GOFASTA_AUTH_JWT_SECRET).

type AuthConfig struct { JWTSecret string `koanf:"jwt_secret"` AccessTokenExpiry time.Duration `koanf:"access_token_expiry"` RefreshTokenExpiry time.Duration `koanf:"refresh_token_expiry"` RBACModelPath string `koanf:"rbac_model"` RBACPolicyPath string `koanf:"rbac_policy"` }

Casbin RBAC configuration files (rbac_model.conf and rbac_policy.csv) are scaffolded in the configs/ directory by default.

Key Functions

FunctionSignatureDescription
GenerateTokenfunc GenerateToken(cfg AuthConfig, claims Claims) (string, error)Creates a signed JWT token from the given claims
ValidateTokenfunc ValidateToken(cfg AuthConfig, tokenStr string) (*Claims, error)Parses and validates a JWT token, returning the claims
GenerateRefreshTokenfunc GenerateRefreshToken(cfg AuthConfig, userID string) (string, error)Creates a long-lived refresh token
HashPasswordfunc HashPassword(password string) (string, error)Hashes a password using bcrypt
CheckPasswordfunc CheckPassword(hashed, password string) boolCompares a bcrypt hash with a plaintext password
NewEnforcerfunc NewEnforcer(cfg RBACConfig) (*casbin.Enforcer, error)Creates a Casbin enforcer for RBAC policy evaluation

Usage

Generating and Validating Tokens

cfg := auth.AuthConfig{ JWTSecret: "my-secret-key", AccessTokenExpiry: 15 * time.Minute, RefreshTokenExpiry: 7 * 24 * time.Hour, RBACModelPath: "configs/rbac_model.conf", RBACPolicyPath: "configs/rbac_policy.csv", } claims := auth.Claims{ UserID: "user-123", Email: "user@example.com", Roles: []string{"admin", "editor"}, } // Generate an access token token, err := auth.GenerateToken(cfg, claims) if err != nil { log.Fatalf("failed to generate token: %v", err) } // Validate the token parsed, err := auth.ValidateToken(cfg, token) if err != nil { log.Fatalf("invalid token: %v", err) } fmt.Println(parsed.UserID) // "user-123" fmt.Println(parsed.Roles) // ["admin", "editor"]

Password Hashing

hashed, err := auth.HashPassword("my-secure-password") if err != nil { log.Fatalf("failed to hash password: %v", err) } ok := auth.CheckPassword(hashed, "my-secure-password") fmt.Println(ok) // true

Casbin RBAC Setup

Define a Casbin model file (rbac_model.conf):

[request_definition] r = sub, obj, act [policy_definition] p = sub, obj, act [role_definition] g = _, _ [policy_effect] e = some(where (p.eft == allow)) [matchers] m = g(r.sub, p.sub) && r.obj == p.obj && r.act == p.act

Define a policy file (rbac_policy.csv):

p, admin, /api/users, GET p, admin, /api/users, POST p, editor, /api/posts, GET p, editor, /api/posts, PUT g, alice, admin g, bob, editor

Initialize the enforcer (paths default to configs/rbac_model.conf and configs/rbac_policy.csv):

enforcer, err := auth.NewEnforcer(auth.AuthConfig{ RBACModelPath: "configs/rbac_model.conf", RBACPolicyPath: "configs/rbac_policy.csv", }) if err != nil { log.Fatalf("failed to create enforcer: %v", err) } allowed, _ := enforcer.Enforce("alice", "/api/users", "POST") fmt.Println(allowed) // true

Wire Integration

var AuthSet = wire.NewSet( auth.NewEnforcer, wire.Struct(new(auth.AuthConfig), "*"), )
Last updated on