auth

package module
v0.1.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 18, 2026 License: AGPL-3.0 Imports: 8 Imported by: 0

README

project logo

httpauthshim

The glue code for HTTP authentication in Go.

Maturity Shield Discord

HTTP Auth against OAuth2, mTLS, external JWT (key/hmac), local users, trusted headers and even HTTP Basic.

Features

  • Multiple Authentication Methods: Supports JWT (header/cookie), OAuth2, local password-based sessions, and trusted HTTP headers
  • Extensible Auth Chain: Easily add custom authentication handlers
  • Session Management: Persistent session storage with YAML-based backend
  • Access Control Lists: Configurable ACLs based on username and usergroup matching
  • OAuth2 Providers: Built-in support for GitHub and Google, with extensible provider system
  • Security: Argon2id password hashing, timing attack prevention, secure session handling

Installation

go get github.com/jamesread/httpauthshim

Quick Start

package main

import (
    "log"
    "net/http"

    "github.com/jamesread/httpauthshim"
    "github.com/jamesread/httpauthshim/authpublic"
    "github.com/jamesread/httpauthshim/providers/hasoauth2"
    "github.com/jamesread/httpauthshim/sessions"
)

var authCtx *auth.AuthShimContext
var oauth2Handler *hasoauth2.OAuth2Handler

func setupAuth() error {
    cfg := &authpublic.Config{
        // Configure OAuth2 with GitHub
        OAuth2Providers: map[string]*authpublic.OAuth2Provider{
            "github": {
                ClientID:     "your-github-client-id",
                ClientSecret: "your-github-client-secret",
            },
        },
        OAuth2RedirectURL: "http://localhost:8080/oauth/callback",
    }

    // Create session storage with YAML persistence
    sessionStorage := sessions.NewSessionStorage(sessions.NewYAMLPersistence())

    // Create an AuthShimContext - this is the main entry point
    var err error
    authCtx, err = auth.NewAuthShimContext(cfg, sessionStorage)
    if err != nil {
        return err
    }

    // Set up OAuth2 handler
    oauth2Handler = hasoauth2.NewOAuth2Handler(cfg, authCtx.Sessions)

    // Add OAuth2 to auth chain
    authCtx.AddProvider(oauth2Handler.CheckUserFromOAuth2Cookie)

    return nil
}

func handler(w http.ResponseWriter, r *http.Request) {
    // Authenticate the user
    user := authCtx.AuthFromHttpReq(r)

    if user.IsGuest() {
        http.Error(w, "Unauthorized", http.StatusUnauthorized)
        return
    }

    // User is authenticated, proceed with request
    w.Write([]byte("Hello, " + user.Username))
}

func main() {
    if err := setupAuth(); err != nil {
        log.Fatal("Failed to initialize auth:", err)
    }

    // Set up OAuth2 routes
    http.HandleFunc("/oauth/login", oauth2Handler.HandleOAuthLogin)
    http.HandleFunc("/oauth/callback", oauth2Handler.HandleOAuthCallback)

    http.HandleFunc("/", handler)
    log.Fatal(http.ListenAndServe(":8080", nil))
}

Authentication Methods

JWT Authentication

Supports JWT tokens from:

  • HTTP headers (configurable header name, defaults to Authorization with Bearer prefix)
  • HTTP cookies (configurable cookie name)

Supports multiple verification methods:

  • Remote JWKS (JSON Web Key Set) URL
  • Local RSA public key file
  • HMAC secret

OAuth2 Authentication

Built-in support for GitHub and Google OAuth2 providers. Easily extensible for other providers.

import (
    "github.com/jamesread/httpauthshim/providers/hasoauth2"
    "github.com/jamesread/httpauthshim/sessions"
)

// Create session storage with YAML persistence
sessionStorage := sessions.NewSessionStorage(sessions.NewYAMLPersistence())

// Create AuthShimContext first
ctx, err := auth.NewAuthShimContext(cfg, sessionStorage)
if err != nil {
    // Handle error
}

// Pass session storage from AuthShimContext for instance-based storage
handler := hasoauth2.NewOAuth2Handler(cfg, ctx.Sessions)
http.HandleFunc("/oauth/login", handler.HandleOAuthLogin)
http.HandleFunc("/oauth/callback", handler.HandleOAuthCallback)

// Add OAuth2 to auth chain using the context instance
ctx.AddProvider(handler.CheckUserFromOAuth2Cookie)

Local Password Authentication

Password-based authentication with Argon2id hashing:

import "github.com/jamesread/httpauthshim/providers/haslocal"

// Create password hash
hash, err := haslocal.CreateHash("userpassword")

// Verify password
isValid := haslocal.CheckUserPassword(cfg, "username", "password")

// Optional: authenticate local users via Authorization: Bearer <apiKey>
ctx.AddProvider(haslocal.CheckUserFromApiKey)

// Issue a secure local session cookie after password login
sessionID, err := haslocal.NewSessionID()
haslocal.SetSessionCookie(w, r, cfg, sessionID)
ctx.RegisterUserSession("local", sessionID, username)

Cookie-based authentication (local sessions and OAuth2 session cookies) is vulnerable to CSRF on state-changing requests. This library does not implement CSRF tokens. Applications that use cookie sessions must add CSRF protection (e.g. double-submit cookie, synchronizer token, or framework middleware) for mutating endpoints. Prefer SameSite=Strict where OAuth redirects are not required; OAuth cookies use SameSite=Lax so provider redirects work.

Trusted HTTP Headers

Authenticate users based on trusted HTTP headers (useful for reverse proxy setups). Disabled by default — only enable behind a proxy that strips/sets these headers. When enabled, trustedProxyCIDRs is required and the request RemoteAddr must match:

httpHeader:
  enabled: true
  trustedProxyCIDRs:
    - "10.0.0.0/8"
    - "192.168.0.0/16"
  username: "X-Username"
  userGroup: "X-User-Group"
  userGroupSep: ","

mTLS (Mutual TLS) Authentication

Authenticate users based on client certificates in TLS connections:

import "github.com/jamesread/httpauthshim/providers/hasmtls"

cfg := &authpublic.Config{
    Mtls: authpublic.MtlsConfig{
        Enabled: true,
        UsernameFromCN: true,        // Extract username from Common Name
        GroupFromOU: true,           // Extract groups from Organizational Unit
        // Or use SAN fields:
        // UsernameFromSANEmail: true,
        // GroupFromSANDNS: true,
    },
}

// Create session storage with YAML persistence
sessionStorage := sessions.NewSessionStorage(sessions.NewYAMLPersistence())

ctx, _ := auth.NewAuthShimContext(cfg, sessionStorage)
ctx.AddProvider(hasmtls.CheckUserFromMtls)

Configuration options (under mtls key):

  • enabled: Enable mTLS authentication
  • requireClientCert: Require client certificate (default: false)
  • usernameFromCN: Extract username from Common Name
  • usernameFromSANEmail: Extract username from SAN email addresses
  • usernameStripEmailDomain: Strip domain from email addresses
  • usernameOID: Extract username from custom OID (e.g., "1.2.840.113549.1.9.1")
  • groupFromOU: Extract groups from Organizational Unit fields
  • groupFromSANEmail: Extract groups from SAN email addresses
  • groupFromSANDNS: Extract groups from SAN DNS names
  • groupSANPrefix: Filter SAN DNS names by prefix
  • groupOID: Extract groups from custom OID
  • groupSeparator: Separator for multiple groups in OID value

Configuration

The library uses a YAML-based configuration structure. See authpublic.Config for all available options.

Example configuration:

jwt:
  header: "Authorization"
  claimUsername: "sub"
  claimUserGroup: "groups"
  aud: "your-api"      # required when JWT verification is configured
  issuer: "your-idp"   # required when JWT verification is configured

# Separate cookies for local vs OAuth2 sessions (optional)
localSessionCookieName: "auth-sid-local"   # default
oauth2SessionCookieName: "auth-sid-oauth"  # default

# Session lifetimes (OWASP Session Management Cheat Sheet)
sessionIdleTimeoutSeconds: 1800      # 30 minutes idle (default); set -1 to disable
sessionAbsoluteTimeoutSeconds: 28800 # 8 hours absolute (default)

# Cookie Secure defaults to true. For local HTTP only:
# oauth2AllowInsecureCookies: true
# trustForwardedHeaders: true  # only behind a stripping reverse proxy

localUsers:
  enabled: true
  users:
    - username: "admin"
      usergroup: "admin"
      password: "$argon2id$v=19$m=65536,t=4,p=1$..."
      apiKey: "your-api-key"  # optional; Authorization: Bearer <apiKey>

oauth2Providers:
  github:
    clientId: "your-client-id"
    clientSecret: "your-client-secret"
    redirectUrl: "https://yourapp.com/oauth/callback"

accessControlLists:
  - name: "admin"
    matchUsernames: ["admin"]
    matchUsergroups: ["admin"]

Package Structure

  • auth - Main authentication package with auth chain and core functions
  • authpublic - Public types and configuration structures
  • sessions - Session management and storage
  • providers/hasjwt - JWT token parsing and validation
  • providers/hasoauth2 - OAuth2 authentication handlers
  • providers/haslocal - Local password-based authentication and sessions
  • providers/hasmtls - Mutual TLS (mTLS) client certificate authentication
  • providers/hastrustedheaders - Trusted HTTP headers authentication

Extending the Auth Chain

Add custom authentication handlers to your AuthShimContext:

import "github.com/jamesread/httpauthshim/sessions"

// Create session storage with YAML persistence
sessionStorage := sessions.NewSessionStorage(sessions.NewYAMLPersistence())

// Create AuthShimContext first
ctx, err := auth.NewAuthShimContext(cfg, sessionStorage)
if err != nil {
    // Handle error
}

// Add custom provider to the context's auth chain
ctx.AddProvider(func(authCtx *authpublic.AuthCheckingContext) *authpublic.AuthenticatedUser {
    // Your custom authentication logic
    // Return nil or empty user if authentication fails
    // Return AuthenticatedUser if successful
})

Note: The deprecated global auth.AddProvider() function modifies a shared global chain and should be avoided. Always use ctx.AddProvider() for per-context provider chains.

Session Management

Sessions are persisted to disk in YAML format. By default, sessions are stored in ~/.config/auth/sessions.yaml, but this can be configured via the BaseDir field in the config or the AUTH_HOME environment variable.

Default lifetimes follow the OWASP Session Management Cheat Sheet for low-risk apps: 30 minutes idle and 8 hours absolute. Both are configurable via sessionIdleTimeoutSeconds and sessionAbsoluteTimeoutSeconds.

When using AuthShimContext, sessions are automatically loaded on creation and managed through the context:

import "github.com/jamesread/httpauthshim/sessions"

// Create session storage with YAML persistence
sessionStorage := sessions.NewSessionStorage(sessions.NewYAMLPersistence())

// Create context (sessions are automatically loaded)
ctx, err := auth.NewAuthShimContext(cfg, sessionStorage)
if err != nil {
    // Handle error
}

// Register a session
ctx.RegisterUserSession("provider", "session-id", "username", "usergroup")

// Retrieve a session
session := ctx.GetUserSession("provider", "session-id")

// Delete a session
ctx.DeleteUserSession("provider", "session-id")

Backward Compatibility (Deprecated)

⚠️ DEPRECATED: The following functions use global session storage and are deprecated. Use AuthShimContext methods instead for instance-based storage.

import "github.com/jamesread/httpauthshim/sessions"

// Register a session (uses global storage - DEPRECATED)
sessions.RegisterUserSession(cfg, "provider", "session-id", "username", "usergroup")

// Retrieve a session (uses global storage - DEPRECATED)
session := sessions.GetUserSession("provider", "session-id")

// Delete a session (uses global storage - DEPRECATED)
sessions.DeleteUserSession(cfg, "provider", "session-id")

// Load sessions from disk (call on startup - DEPRECATED)
sessions.LoadUserSessions(cfg)

Migration: Replace these with ctx.RegisterUserSession(), ctx.GetUserSession(), ctx.DeleteUserSession() methods on your AuthShimContext instance.

Contributing

Please checkout CONTRIBUTING.md.

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func AddProvider

func AddProvider(check func(*types.AuthCheckingContext) *types.AuthenticatedUser)

AddProvider adds a provider to the global auth chain. DEPRECATED: Use AuthShimContext.AddProvider instead for per-context chains. This function is kept for backward compatibility but modifies a global chain that is shared across all contexts, which may cause unexpected behavior.

func AuthFromHttpReq

func AuthFromHttpReq(req *http.Request, cfg *types.Config) *types.AuthenticatedUser

func UserFromSystem

func UserFromSystem(cfg *authpublic.Config, username string) *authpublic.AuthenticatedUser

Types

type AuthShimContext

type AuthShimContext struct {
	Config   *authpublic.Config
	Sessions *sessions.SessionStorage
	// contains filtered or unexported fields
}

AuthShimContext contains the configuration and session storage for authentication. This is the main entry point for users of the library.

IMPORTANT: The Config should not be mutated after AuthShimContext creation. Mutating the config after context creation can lead to inconsistent behavior and is not thread-safe. If you need to change configuration, create a new AuthShimContext with the updated config.

CLEANUP: You MUST call Shutdown() when the AuthShimContext is no longer needed to prevent resource leaks (goroutines, file handles, etc.). This is especially important in long-running applications. Consider using defer or ensuring Shutdown is called in your application's cleanup logic (e.g., signal handlers).

func NewAuthShimContext

func NewAuthShimContext(cfg *authpublic.Config, sessionStorage *sessions.SessionStorage) (*AuthShimContext, error)

NewAuthShimContext creates a new AuthShimContext with the provided config and session storage. It loads existing sessions from disk. It validates the configuration and returns an error if validation fails. Callers must explicitly provide a SessionStorage implementation.

func (*AuthShimContext) AddProvider

AddProvider adds a custom authentication provider to this context's auth chain. This allows users to extend the authentication chain with custom providers. The provider is added to this specific context instance, not a global chain.

func (*AuthShimContext) AuthFromHeaders

func (ctx *AuthShimContext) AuthFromHeaders(headers http.Header) *authpublic.AuthenticatedUser

AuthFromHeaders authenticates a user from HTTP headers only. Useful when you have headers without a full request (e.g. gRPC metadata, proxies). Cookie-based providers work if a Cookie header is present. If no user is authenticated, it returns a guest user.

func (*AuthShimContext) AuthFromHeadersWithError

func (ctx *AuthShimContext) AuthFromHeadersWithError(headers http.Header) (*authpublic.AuthenticatedUser, error)

AuthFromHeadersWithError authenticates a user from HTTP headers only. If no user is authenticated, it returns a guest user and nil error.

func (*AuthShimContext) AuthFromHttpReq

func (ctx *AuthShimContext) AuthFromHttpReq(req *http.Request) *authpublic.AuthenticatedUser

AuthFromHttpReq authenticates a user from an HTTP request. It runs through the authentication chain and returns an AuthenticatedUser. If no user is authenticated, it returns a guest user. For error handling, use AuthFromHttpReqWithError instead.

func (*AuthShimContext) AuthFromHttpReqWithError

func (ctx *AuthShimContext) AuthFromHttpReqWithError(req *http.Request) (*authpublic.AuthenticatedUser, error)

AuthFromHttpReqWithError authenticates a user from an HTTP request. It runs through the authentication chain and returns an AuthenticatedUser and any error encountered. If no user is authenticated, it returns a guest user and nil error.

func (*AuthShimContext) ClearProviders

func (ctx *AuthShimContext) ClearProviders()

ClearProviders removes all providers from the auth chain.

func (*AuthShimContext) DeleteUserSession

func (ctx *AuthShimContext) DeleteUserSession(provider string, sid string)

DeleteUserSession deletes a user session

func (*AuthShimContext) GetProviders

GetProviders returns a copy of the current provider chain. Useful for debugging or inspection.

func (*AuthShimContext) GetUserSession

func (ctx *AuthShimContext) GetUserSession(provider string, sid string) *sessions.UserSession

GetUserSession retrieves a user session

func (*AuthShimContext) InsertProvider

func (ctx *AuthShimContext) InsertProvider(index int, check func(*authpublic.AuthCheckingContext) *authpublic.AuthenticatedUser)

InsertProvider inserts a provider at a specific index in the auth chain. If index is out of bounds, the provider is appended to the end.

func (*AuthShimContext) OnAuthenticated

func (ctx *AuthShimContext) OnAuthenticated(fn EnrichUserFunc)

OnAuthenticated registers a post-auth hook called after BuildUserAcls, including for guest users. Multiple hooks may be registered; they run in registration order.

func (*AuthShimContext) RegisterUserSession

func (ctx *AuthShimContext) RegisterUserSession(provider string, sid string, username string, usergroup ...string)

RegisterUserSession registers a new user session

func (*AuthShimContext) RemoveProviderByIndex

func (ctx *AuthShimContext) RemoveProviderByIndex(index int) error

RemoveProviderByIndex removes a provider from the auth chain by index. Returns an error if the index is out of bounds.

func (*AuthShimContext) Shutdown

func (ctx *AuthShimContext) Shutdown() error

Shutdown performs cleanup operations, including: - Final session storage write - Stopping background goroutines This MUST be called when the context is no longer needed to prevent resource leaks. It is safe to call Shutdown multiple times; subsequent calls are no-ops.

func (*AuthShimContext) UserFromSystem

func (ctx *AuthShimContext) UserFromSystem(username string) *authpublic.AuthenticatedUser

UserFromSystem returns a system user with the given username

func (*AuthShimContext) UserGuest

func (ctx *AuthShimContext) UserGuest() *authpublic.AuthenticatedUser

UserGuest returns a guest user with appropriate ACLs

type EnrichUserFunc

type EnrichUserFunc func(user *authpublic.AuthenticatedUser, cfg *authpublic.Config)

EnrichUserFunc is called after authentication and BuildUserAcls (including for guest users).

Directories

Path Synopsis
providers

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL