Skip to content

davidbz/go-oidc-auth

Repository files navigation

Go OIDC Authentication Library

Go library for OpenID Connect authentication and JWT management in distributed systems.

Built with Vibe Coding: This entire project was developed using vibe-based planning and programming.

Features

  • Multi-Provider Support: Works with Azure AD, Google, Okta, Auth0, or any OIDC provider
  • Distributed JWT Architecture: Issue internal JWTs with JWKS endpoints for service verification
  • Multi-Instance Ready: Supports horizontal scaling with shared key management
  • HTTP Middleware: Simple JWT verification middleware for web services
  • Production Security: Built-in security best practices and proven cryptography
  • Clean Go Design: Follows Go conventions with context-first interfaces

Installation

go get github.com/davidbz/go-oidc-auth

Quick Start

JWT-Only Usage

Perfect for microservices that need to issue and verify tokens:

package main

import (
    "context"
    "fmt"
    "time"
    
    "github.com/davidbz/go-oidc-auth/pkg/jwt"
)

func main() {
    config := jwt.Config{
        Issuer:   "https://auth.mycompany.com",
        Audience: "myapp-api",
        Expiry:   30 * time.Minute,
    }
    
    // Create signer and verifier
    signer, _ := jwt.NewSigner(config)
    keySet, _ := signer.GetJWKS(context.Background())
    verifier := jwt.NewVerifier(config, *keySet)
    
    // Issue JWT
    claims := jwt.Claims{
        Subject:  "user123",
        Email:    "user@company.com",
        Provider: "internal",
        Custom: map[string]interface{}{
            "role": "admin",
        },
    }
    
    token, _ := signer.Sign(context.Background(), claims)
    fmt.Printf("JWT: %s...\n", token[:50])
    
    // Verify JWT
    verifiedClaims, _ := verifier.Verify(context.Background(), token)
    fmt.Printf("Verified user: %s\n", verifiedClaims.Email)
}

Multi-Instance Deployments

For distributed deployments where multiple instances need consistent JWT operations:

package main

import (
    "context"
    "fmt"
    "os"
    
    "github.com/davidbz/go-oidc-auth/pkg/jwt"
)

func main() {
    config := jwt.Config{
        Issuer:   "https://auth.mycompany.com",
        Audience: "myapp-api",
        Expiry:   30 * time.Minute,
    }
    
    // Load shared private key (all instances use the same key)
    privateKeyPEM := loadPrivateKeyFromSecureStorage() // Your key management
    
    // Create signer with shared key
    signer, _ := jwt.NewSignerWithPEM(config, privateKeyPEM)
    keySet, _ := signer.GetJWKS(context.Background())
    verifier := jwt.NewVerifier(config, *keySet)
    
    // Now all instances can verify each other's JWTs
    claims := jwt.Claims{
        Subject:  "user123",
        Email:    "user@company.com", 
        Provider: "internal",
        Custom: map[string]interface{}{
            "role": "admin",
            "instance": os.Getenv("INSTANCE_ID"),
        },
    }
    
    token, _ := signer.Sign(context.Background(), claims)
    verifiedClaims, _ := verifier.Verify(context.Background(), token)
    
    fmt.Printf("Token verified across instances: %s\n", verifiedClaims.Email)
}

Full OIDC Authentication

Complete authentication flow with external providers:

import (
    "github.com/davidbz/go-oidc-auth/pkg/auth"
    "github.com/davidbz/go-oidc-auth/pkg/config"
    "github.com/davidbz/go-oidc-auth/pkg/providers"
    "github.com/davidbz/go-oidc-auth/pkg/jwt"
)

func main() {
    // Configure providers
    config := config.Config{
        JWT: jwt.Config{
            Issuer:   "https://myapp.com",
            Audience: "myapp-users",
            Expiry:   30 * time.Minute,
        },
        Providers: []providers.ProviderConfig{
            {
                Name:         "azure",
                ClientID:     "your-client-id",
                ClientSecret: "your-client-secret",
                IssuerURL:    "https://login.microsoftonline.com/tenant/v2.0",
                RedirectURI:  "http://localhost:8080/auth/callback",
                Scopes:       []string{"openid", "profile", "email"},
            },
        },
    }
    
    // Setup authentication manager
    signer, _ := jwt.NewSigner(config.JWT)
    keySet, _ := signer.GetJWKS(context.Background())
    verifier := jwt.NewVerifier(config.JWT, *keySet)
    
    registry := providers.NewRegistry()
    manager := auth.NewManager(config, registry, signer, verifier)
    
    // Register providers and handle auth flow
    manager.RegisterProvider(context.Background(), config.Providers[0])
    
    // In your HTTP handlers:
    // loginURL, _ := manager.GetLoginURL(ctx, "azure", "secure-state")
    // tokenResponse, _ := manager.HandleCallback(ctx, "azure", code, state)
}

Service-to-Service Authentication

Verify JWTs from other services:

import (
    "context"
    "net/http"
    
    "github.com/davidbz/go-oidc-auth/pkg/jwt"
    "github.com/lestrrat-go/jwx/v2/jwk"
)

// Fetch public keys from auth service
resp, _ := http.Get("https://auth.company.com/.well-known/jwks.json")
defer resp.Body.Close()

keySet, _ := jwk.ParseReader(resp.Body)

config := jwt.Config{
    Issuer:   "https://auth.company.com",
    Audience: "myapp-api",
}
verifier := jwt.NewVerifier(config, keySet)

// Verify incoming JWT
claims, err := verifier.Verify(context.Background(), tokenString)

HTTP Middleware

Easily protect HTTP endpoints with JWT verification:

import (
    "context"
    "net/http"
    "time"
    
    "github.com/davidbz/go-oidc-auth/pkg/jwt"
    "github.com/davidbz/go-oidc-auth/pkg/middleware"
)

func main() {
    // Setup JWT config and verifier
    jwtConfig := jwt.Config{
        Issuer:   "https://auth.mycompany.com",
        Audience: "myapp-api",
        Expiry:   30 * time.Minute,
    }
    
    // Create signer and get key set
    signer, _ := jwt.NewSigner(jwtConfig)
    keySet, _ := signer.GetJWKS(context.Background())
    verifier := jwt.NewVerifier(jwtConfig, *keySet)
    
    // Create middleware
    authMiddleware, _ := middleware.New(middleware.Config{
        Verifier: verifier,
    })
    
    // Protect specific routes
    mux := http.NewServeMux()
    mux.Handle("/api/users", authMiddleware.Handler(usersHandler))
    mux.Handle("/api/admin", authMiddleware.Handler(adminHandler))
    mux.HandleFunc("/public", publicHandler) // No auth required
    
    http.ListenAndServe(":8080", mux)
}

func usersHandler(w http.ResponseWriter, r *http.Request) {
    // Extract verified claims from context
    claims, ok := middleware.ClaimsFromContext(r.Context())
    if !ok {
        http.Error(w, "No claims", http.StatusInternalServerError)
        return
    }
    
    // Access user attributes
    userID := claims.Subject
    email := claims.Email
    name := claims.Name
    provider := claims.Provider
    
    // Access custom claims
    role, _ := claims.Custom["role"].(string)
    
    // Use claims for business logic...
}

Supported Providers

Works with any OIDC-compliant provider:

  • Microsoft Azure AD / Entra ID
  • Google Identity Platform
  • Okta, Auth0, Keycloak
  • AWS Cognito
  • Custom OIDC providers

Examples

# Run examples  
go run ./examples/jwt-only        # Single instance JWT operations
go run ./examples/multi-instance  # Multi-instance deployment
go run ./examples/real-oidc        # Production OIDC setup

Development

# Build and test
go build ./...
go test ./...
golangci-lint run ./...

Architecture

pkg/
├── auth/           # Authentication manager and orchestration
├── providers/      # OIDC provider implementations
├── jwt/            # JWT signing and verification
├── middleware/     # HTTP middleware for JWT verification
└── config/         # Configuration types

See agents.md for detailed architecture and development context.

About

No description or website provided.

Topics

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

No packages published

Languages