Skip to main content

Overview

The Go React Scaffold uses JWT (JSON Web Tokens) for authentication. Tokens are signed using HMAC-SHA256 (HS256) algorithm and stored in HTTP-only cookies.

Token Generation

JWT tokens are generated during login and contain user claims with an expiration time.

Generation Function

func GenerateTokensAndSetCookies(user *users.User, c echo.Context) (string, error)
Source: backend/auth/auth.go:29 This function:
  1. Generates an access token with 24-hour expiration
  2. Sets the token in an HTTP-only cookie named Bearer
  3. Sets a user ID cookie for client-side access
  4. Returns the token string

Token Structure

Claims

JWT tokens contain the following claims:
user_id
string
The unique user identifier (UUID v4 format)
name
string
User’s name (currently not populated in login flow)
exp
number
Expiration timestamp in Unix epoch format

Claims Structure (Go)

type Claims struct {
    Name   string `json:"name"`
    UserId string `json:"user_id"`
    jwt.Claims
}
Source: backend/auth/auth.go:22-26

Token Expiration

Access Token

  • Duration: 24 hours
  • Set at: Login time
  • Calculation: time.Now().Add(24 * time.Hour)
expirationTime := time.Now().Add(24 * time.Hour)
Source: backend/auth/auth.go:43

Token Signing

Algorithm

  • Method: HMAC-SHA256 (HS256)
  • Secret: Retrieved from SESSION_KEY environment variable

Secret Key

func GetJWTSecret() string {
    return configs.GetSessionKey()
}
Source: backend/auth/auth.go:18-20
The SESSION_KEY environment variable must be kept secure and should be a strong, random string in production.

Token Generation Process

Step-by-Step

  1. Create Claims
claims := &Claims{
    UserId: user.ID,
    Claims: jwt.MapClaims{
        "exp": expirationTime.Unix(),
    },
}
  1. Create Token with Algorithm
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
  1. Sign Token
tokenString, err := token.SignedString(secret)
  1. Set Cookies
setTokenCookie(accessTokenCookieName, accessToken, exp, c)
setUserCookie(user, exp, c)
Source: backend/auth/auth.go:49-68
Name
string
default:"Bearer"
Cookie name for JWT token
Value
string
Signed JWT token string
Expires
time.Time
24 hours from generation
Path
string
default:"/"
Cookie path (root)
HttpOnly
boolean
default:true
HTTP-only flag to prevent XSS access
Name
string
default:"user"
Cookie name for user ID
Value
string
User ID (UUID)
Expires
time.Time
24 hours from generation
Path
string
default:"/"
Cookie path (root)
HttpOnly
boolean
default:false
Not HTTP-only, accessible to JavaScript
Source: backend/auth/auth.go:71-91

Token Validation

JWT Error Handler

func JWTErrorChecker(err error, c echo.Context) error {
    return c.Redirect(http.StatusMovedPermanently, c.Echo().Reverse("userSignInForm"))
}
Source: backend/auth/auth.go:94-97 This function is executed when:
  • Token is expired
  • Token signature is invalid
  • Token is missing
  • Token claims are malformed
On validation failure, users are redirected to the sign-in form.

Usage in Protected Routes

To protect routes with JWT authentication in Echo framework:
import (
    "github.com/labstack/echo/v4"
    echojwt "github.com/labstack/echo-jwt/v4"
)

// Configure JWT middleware
config := echojwt.Config{
    SigningKey:  []byte(auth.GetJWTSecret()),
    TokenLookup: "cookie:Bearer",
    ErrorHandler: auth.JWTErrorChecker,
}

// Apply to route group
protected := e.Group("/api")
protected.Use(echojwt.WithConfig(config))

Security Considerations

HTTP-Only Cookies

  • Tokens stored in HTTP-only cookies are not accessible via JavaScript
  • Prevents XSS attacks from stealing tokens
  • Automatically sent with requests to the same domain

Token Expiration

  • 24-hour expiration limits exposure window
  • Expired tokens must re-authenticate
  • No refresh token mechanism (yet)

Signing Secret

  • Use strong, random SESSION_KEY (minimum 32 characters)
  • Rotate secrets periodically in production
  • Never commit secrets to version control

Algorithm Choice

  • HS256 (HMAC-SHA256) provides:
    • Symmetric signing (same key for sign/verify)
    • Fast performance
    • Sufficient security for internal APIs
For public APIs or microservices, consider asymmetric algorithms (RS256) for better key distribution.

Token Lifecycle

  1. Generation: User logs in successfully
  2. Storage: Token stored in HTTP-only Bearer cookie
  3. Usage: Automatically sent with each request
  4. Validation: Echo JWT middleware validates on protected routes
  5. Expiration: After 24 hours, token is no longer valid
  6. Re-authentication: User must log in again

Example Token Payload

Decoded JWT payload (claims):
{
  "user_id": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
  "name": "",
  "exp": 1709654400
}

Source Code References

  • Token generation: backend/auth/auth.go:29-46
  • Claims structure: backend/auth/auth.go:22-26
  • Cookie setting: backend/auth/auth.go:71-91
  • Error handler: backend/auth/auth.go:94-97
  • Secret retrieval: backend/auth/auth.go:18-20

Environment Configuration

Required environment variable in .env:
SESSION_KEY=your-strong-secret-key-min-32-chars
Generate a strong secret key:
openssl rand -base64 32

Build docs developers (and LLMs) love