auth

package module
v0.3.1 Latest Latest
Warning

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

Go to latest
Published: Jul 2, 2026 License: MIT Imports: 4 Imported by: 0

README

gas-auth

Test Go Reference Go Version License

Authentication, authorization, and credential management for the Gas ecosystem. Provides JWT, server-side session, API key, and single-use token services with multi-dialect database support (PostgreSQL, MySQL, SQLite).

Install

go get github.com/gasmod/gas-auth

Packages

Package Description Implements Provider Interface
auth (root) BasePrincipal, Chain, middleware, sentinel errors, scheme constants gas.Authenticator (Chain) --
auth/jwt Stateless JWT authentication (HS256, RS256) gas.Authenticator, gas.Service jwt.Provider
auth/session Server-side session authentication gas.Authenticator, gas.PrincipalRevoker, gas.Service session.Provider
auth/apikey API key authentication with scopes gas.Authenticator, gas.PrincipalRevoker, gas.Service apikey.Provider
auth/token Single-use tokens (magic links, email verification, password reset) gas.Service token.Provider
auth/authtest Test mocks for Authenticator, Authorizer, PrincipalRevoker -- --

Provider Interfaces

Each service package exports a Provider interface that captures its public contract. Use these for dependency injection and mocking in consumer code:

var _ jwt.Provider     = (*jwt.Service)(nil)
var _ session.Provider = (*session.Service)(nil)
var _ apikey.Provider  = (*apikey.Service)(nil)
var _ token.Provider   = (*token.Service)(nil)

Quick Start

package main

import (
    "github.com/gasmod/gas"
    "github.com/gasmod/gas-auth/jwt"
    "github.com/gasmod/gas-auth/session"
)

func main() {
    app := gas.NewApp(
        gas.WithSingletonService[*jwt.Service](jwt.New(
            jwt.WithConfig(&jwt.Config{
                JWT: jwt.Settings{
                    SigningKey:    "your-secret-key",
                    SigningMethod: "HS256",
                },
            }),
        )),
        gas.WithSingletonService[*session.Service](session.New()),
    )

    app.Run()
}

Scheme Constants

The root auth package defines constants for authentication scheme names:

auth.SchemeJWT     // "jwt"
auth.SchemeSession // "session"
auth.SchemeAPIKey  // "apikey"

Middleware

// Require authentication on all routes in this group
router.Group(func(sub *gas.Router) {
    sub.UseMiddlewareFunc(auth.Middleware(jwtService))
    sub.Handle("myservice", "GET", "/protected", handleProtected)
})

// Require a specific auth scheme
router.Group(func(sub *gas.Router) {
    sub.UseMiddlewareFunc(auth.Middleware(chain))
    sub.UseMiddlewareFunc(auth.RequireScheme(auth.SchemeSession))
    sub.Handle("myservice", "GET", "/dashboard", handleDashboard)
})
Middleware Options
Option Description
WithOnError(fn func(http.ResponseWriter, *http.Request, error)) Custom error handler called on authentication failure

By default, the middleware writes a plain 401 Unauthorized response. Use WithOnError to customize:

router.Group(func(sub *gas.Router) {
    sub.UseMiddlewareFunc(auth.Middleware(jwtService, auth.WithOnError(
        func(w http.ResponseWriter, r *http.Request, err error) {
            w.Header().Set("Content-Type", "application/json")
            w.WriteHeader(http.StatusUnauthorized)
            json.NewEncoder(w).Encode(map[string]string{"error": err.Error()})
        },
    )))
    sub.Handle("myservice", "GET", "/api/data", handleData)
})

Chain

Combine multiple authenticators into a single chain that tries each in order:

chain := auth.Chain{jwtService, sessionService, apiKeyService}
router.UseMiddlewareFunc(auth.Middleware(chain))

JWT Service

Stateless JWT authentication supporting HS256 (HMAC) and RS256 (RSA).

// HS256
svc := jwt.New(jwt.WithConfig(&jwt.Config{
    JWT: jwt.Settings{
        SigningKey:    "secret",
        SigningMethod: "HS256",
        Expiry:        15 * time.Minute,
        Issuer:        "my-app",
    },
}))

// RS256 (verify-only, no signing)
svc := jwt.New(jwt.WithConfig(&jwt.Config{
    JWT: jwt.Settings{
        SigningMethod: "RS256",
        PublicKeyPath: "/path/to/public.pem",
        Expiry:        15 * time.Minute,
    },
}))
// Sign a token
token, _ := jwtService.Sign("user-123", map[string]any{"role": "admin"})

// Verify a token
claims, _ := jwtService.Verify(token)
fmt.Println(claims.Subject, claims.CustomClaims["role"])

Session Service

Server-side session authentication backed by a database.

svc := session.New(session.WithConfig(&session.Config{
    Session: session.Settings{
        CookieName:     "session_id",
        SessionTTL:     24 * time.Hour,
        ExtendOnAccess: true,
        CookieSecure:   true,
        CookieHTTPOnly: true,
    },
}))
// Create a session (returns *session.Session)
sess, _ := sessionService.Create(ctx, "user-123", metadata, r)
sessionService.SetCookie(w, sess)

// Revoke
sessionService.Revoke(ctx, principal)
sessionService.RevokeAll(ctx, "user-123")

API Key Service

API key authentication with SHA-256 hashing and scope support.

svc := apikey.New(apikey.WithConfig(&apikey.Config{
    APIKey: apikey.Settings{
        HeaderName: "X-API-Key",
        Prefix:     "gas_",
        KeyLength:  32,
    },
}))
// Generate a key. The plaintext key is returned exactly once; only its hash is
// stored. The returned KeyInfo mirrors the persisted row (id, subject, name,
// scopes, metadata, prefix, expiry, createdAt).
fullKey, info, _ := apiKeyService.Generate(ctx, "user-123", "my-key", []string{"read", "write"})

// Optional: attach metadata and/or an expiration
fullKey, info, _ = apiKeyService.Generate(ctx, "user-123", "ci-token", []string{"read"},
    apikey.WithTTL(24*time.Hour),
    apikey.WithMetadata(map[string]any{"env": "prod"}),
)
// Also available: apikey.WithExpiresAt(t time.Time)

// List non-sensitive key info (active keys only by default)
keys, _ := apiKeyService.List(ctx, "user-123")

// Include revoked (soft-deleted) keys — they carry a non-nil KeyInfo.DeletedAt
allKeys, _ := apiKeyService.List(ctx, "user-123", apikey.WithIncludeRevoked())

// Revoke
apiKeyService.Revoke(ctx, principal)
apiKeyService.RevokeAll(ctx, "user-123")

// Run API key operations atomically with your own writes. The caller owns the
// tx lifecycle; the Provider returned by WithTx is scoped to that transaction
// and must not be cached.
_ = dbProv.WithTx(ctx, nil, func(tx *sql.Tx) error {
    if err := userRepo.WithTx(tx).Insert(ctx, user); err != nil {
        return err
    }
    _, _, err := apiKeyService.WithTx(tx).Generate(ctx, user.ID, "initial", []string{"read"})
    return err
})

Token Service

Single-use, time-limited tokens for magic links, email verification, and password resets. Tokens are hashed before storage -- the raw token is returned exactly once.

svc := token.New(token.WithConfig(&token.Config{
    Token: token.Settings{
        DefaultTTL:  15 * time.Minute,
        TokenLength: 32,
    },
}))
// Issue a token
rawToken, _ := tokenService.Issue(ctx, "user-123", "email-verify", 15*time.Minute)

// Verify and consume (single-use)
subject, _ := tokenService.Verify(ctx, rawToken, "email-verify")
// second call returns token.ErrTokenInvalid

Config

If WithConfig is not provided, all services automatically bind configuration from the gas.ConfigProvider injected via DI.

JWT config
Field Default Description
JWT.SigningKey HMAC key for HS256
JWT.SigningMethod HS256 Signing algorithm (HS256 or RS256)
JWT.PublicKeyPath RSA public key PEM path (RS256)
JWT.PrivateKeyPath RSA private key PEM path (RS256 signing)
JWT.Issuer Expected iss claim
JWT.Audience Expected aud claim
JWT.Expiry 15m Default token lifetime
Session config
Field Default Description
Session.CookieName session_id Cookie name
Session.CookiePath / Cookie path
Session.CookieDomain Cookie domain
Session.CookieSameSite Lax SameSite attribute
Session.CookieSecure true Secure flag
Session.CookieHTTPOnly true HttpOnly flag
Session.SessionTTL 24h Session lifetime
Session.ExtendOnAccess true Extend TTL on each authentication
Session.CleanupInterval 1h Background cleanup interval (0=disabled)
API key config
Field Default Description
APIKey.HeaderName X-API-Key HTTP header for the API key
APIKey.Prefix Prefix prepended to generated keys
APIKey.KeyLength 32 Random bytes per key (minimum 16)
Token config
Field Default Description
Token.DefaultTTL 15m Default token lifetime (ttl=0 fallback)
Token.TokenLength 32 Random bytes per token (minimum 16)
Token.CleanupInterval 1h Background cleanup interval (0=disabled)

Database Support

Session, API key, and token services support PostgreSQL, MySQL, and SQLite. The dialect is selected automatically based on the gas.DatabaseProvider driver name.

Testing

The authtest package provides mock implementations for use in tests:

import "github.com/gasmod/gas-auth/authtest"

mock := &authtest.MockAuthenticator{}
mock.AuthenticateFn = func(ctx context.Context, r *http.Request) (gas.Principal, error) {
    return auth.NewPrincipal("user-1", auth.SchemeJWT, "tok-1", nil), nil
}

// Assert calls
if mock.CallCount("Authenticate") != 1 {
    t.Error("expected one Authenticate call")
}

Available mocks: MockAuthenticator, MockAuthorizer, MockRevoker.

Sentinel Errors

auth.ErrUnauthenticated  // no valid credentials provided
auth.ErrForbidden        // principal lacks permission
auth.ErrCredentialsExpired // credentials have expired
auth.ErrCredentialRevoked  // credentials have been revoked

token.ErrTokenInvalid    // token does not exist or has been consumed
token.ErrTokenExpired    // token has expired

Documentation

Overview

Package auth provides authentication, authorization, and credential management for the Gas ecosystem.

Package auth provides authentication and authorization for the Gas ecosystem: JWT, server-side sessions, API keys, and single-use tokens. Sub-packages live under jwt, session, apikey, and token.

See the module README for usage examples and design rationale.

SPDX-License-Identifier: MIT

Index

Constants

View Source
const (
	// SchemeJWT is the scheme for JWT-authenticated principals.
	SchemeJWT = "jwt"
	// SchemeSession is the scheme for session-authenticated principals.
	SchemeSession = "session"
	// SchemeAPIKey is the scheme for API-key-authenticated principals.
	SchemeAPIKey = "apikey"
)

Scheme constants identify the authentication method used by a principal.

Variables

View Source
var (
	// ErrUnauthenticated indicates that no valid credentials were provided.
	ErrUnauthenticated = errors.New("unauthenticated")
	// ErrForbidden indicates that the principal lacks permission for the action.
	ErrForbidden = errors.New("forbidden")
	// ErrCredentialsExpired indicates that the presented credentials have expired.
	ErrCredentialsExpired = errors.New("credentials expired")
	// ErrCredentialRevoked indicates that the presented credentials have been revoked.
	ErrCredentialRevoked = errors.New("credential revoked")
)

Sentinel errors returned by authenticators and middleware.

Functions

func Middleware

func Middleware(provider gas.Authenticator, opts ...MiddlewareOption) func(http.Handler) http.Handler

Middleware extracts and validates credentials from the request using the given authenticator. On success it sets the principal in the request context via gas.WithPrincipal and calls the next handler. On failure it invokes the OnError handler or writes a 401 Unauthorized response.

func RequireScheme

func RequireScheme(scheme string) func(http.Handler) http.Handler

RequireScheme enforces that the authenticated principal uses a specific authentication scheme (e.g. "jwt", "session", "apikey"). It reads the principal from context. If absent or the scheme doesn't match, it writes a 403 Forbidden response.

Types

type BasePrincipal

type BasePrincipal struct {
	// contains filtered or unexported fields
}

BasePrincipal is a concrete implementation of gas.Principal.

func NewPrincipal

func NewPrincipal(subject, scheme, credentialID string, meta gas.PrincipalMetadata) *BasePrincipal

NewPrincipal creates a new BasePrincipal. If meta is nil, an empty gas.BasePrincipalMetadata is used to avoid nil dereferences on Metadata().Value().

func (*BasePrincipal) CredentialID

func (p *BasePrincipal) CredentialID() string

CredentialID returns the identifier for the specific credential used.

func (*BasePrincipal) Metadata

func (p *BasePrincipal) Metadata() gas.PrincipalMetadata

Metadata returns the principal's metadata.

func (*BasePrincipal) Scheme

func (p *BasePrincipal) Scheme() string

Scheme returns the authentication method (e.g. "jwt", "session", "apikey").

func (*BasePrincipal) Subject

func (p *BasePrincipal) Subject() string

Subject returns the stable user identifier.

type Chain

type Chain []gas.Authenticator

Chain is a composite authenticator that tries each authenticator in order. It satisfies gas.Authenticator.

func (Chain) Authenticate

func (c Chain) Authenticate(ctx context.Context, r *http.Request) (gas.Principal, error)

Authenticate tries each authenticator in order. It returns the first successful principal. If all fail, it returns the last error. If the chain is empty, it returns ErrUnauthenticated.

type MiddlewareOption

type MiddlewareOption func(*middlewareOptions)

MiddlewareOption configures the behavior of Middleware.

func WithOnError

func WithOnError(fn func(w http.ResponseWriter, r *http.Request, err error)) MiddlewareOption

WithOnError sets a custom error handler that is called when authentication fails. The handler receives the original error and is responsible for writing the HTTP response. When not set, the middleware writes a plain 401 Unauthorized response.

Directories

Path Synopsis
Package apikey provides API key authentication for the Gas ecosystem.
Package apikey provides API key authentication for the Gas ecosystem.
db
Package authtest provides mock implementations of the gas authentication and authorization interfaces for use in tests.
Package authtest provides mock implementations of the gas authentication and authorization interfaces for use in tests.
internal
cryptoutil
Package cryptoutil provides shared cryptographic helpers used across gas-auth sub-packages.
Package cryptoutil provides shared cryptographic helpers used across gas-auth sub-packages.
testutil
Package testutil provides shared test helpers and mock implementations for gas-auth integration and unit tests.
Package testutil provides shared test helpers and mock implementations for gas-auth integration and unit tests.
Package jwt provides a stateless JWT authenticator for the Gas ecosystem.
Package jwt provides a stateless JWT authenticator for the Gas ecosystem.
Package session provides server-side session authentication for the Gas ecosystem.
Package session provides server-side session authentication for the Gas ecosystem.
db
Package token provides single-use, time-limited tokens for the Gas ecosystem.
Package token provides single-use, time-limited tokens for the Gas ecosystem.
db

Jump to

Keyboard shortcuts

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