Olympus Docs
IntegrateBackends

Gin (Go) integration

Authenticate Gin routes against Olympus

Gin + Olympus via middleware. Token introspection or JWT validation.

Middleware (opaque token)

package main

import (
    "encoding/json"
    "net/http"
    "net/url"
    "strings"

    "github.com/gin-gonic/gin"
)

type TokenInfo struct {
    Active bool   `json:"active"`
    Sub    string `json:"sub"`
    Scope  string `json:"scope"`
}

func OlympusAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        auth := c.GetHeader("Authorization")
        if !strings.HasPrefix(auth, "Bearer ") {
            c.AbortWithStatusJSON(401, gin.H{"error": "missing_token"})
            return
        }
        token := strings.TrimPrefix(auth, "Bearer ")

        form := url.Values{}
        form.Set("token", token)
        req, _ := http.NewRequest("POST",
            os.Getenv("OLYMPUS_ISSUER")+"/admin/oauth2/introspect",
            strings.NewReader(form.Encode()))
        req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
        req.SetBasicAuth(os.Getenv("HYDRA_ADMIN_USER"), os.Getenv("HYDRA_ADMIN_PASS"))

        resp, err := http.DefaultClient.Do(req)
        if err != nil {
            c.AbortWithStatusJSON(500, gin.H{"error": "introspect_failed"})
            return
        }
        defer resp.Body.Close()

        var info TokenInfo
        json.NewDecoder(resp.Body).Decode(&info)
        if !info.Active {
            c.AbortWithStatusJSON(401, gin.H{"error": "inactive_token"})
            return
        }

        c.Set("user_sub", info.Sub)
        c.Set("scopes", strings.Fields(info.Scope))
        c.Next()
    }
}

Wire up

r := gin.Default()
r.Use(OlympusAuth())

r.GET("/api/widgets", func(c *gin.Context) {
    userSub := c.GetString("user_sub")
    scopes := c.GetStringSlice("scopes")
    if !slices.Contains(scopes, "read:widgets") {
        c.AbortWithStatusJSON(403, gin.H{"error": "insufficient_scope"})
        return
    }
    c.JSON(200, gin.H{"user": userSub, "widgets": []string{}})
})

JWT validation (faster)

For JWT access tokens, validate locally:

import (
    "github.com/coreos/go-oidc/v3/oidc"
)

func init() {
    provider, _ := oidc.NewProvider(context.Background(), os.Getenv("OLYMPUS_ISSUER"))
    verifier = provider.Verifier(&oidc.Config{ClientID: "your-audience"})
}

func JWTAuth() gin.HandlerFunc {
    return func(c *gin.Context) {
        token := strings.TrimPrefix(c.GetHeader("Authorization"), "Bearer ")
        idToken, err := verifier.Verify(c.Request.Context(), token)
        if err != nil {
            c.AbortWithStatusJSON(401, gin.H{"error": err.Error()})
            return
        }
        var claims struct {
            Sub   string `json:"sub"`
            Scope string `json:"scope"`
        }
        idToken.Claims(&claims)
        c.Set("user_sub", claims.Sub)
        c.Next()
    }
}

Caching introspection results

import "github.com/patrickmn/go-cache"

var introspectCache = cache.New(30*time.Second, 60*time.Second)

func cachedIntrospect(token string) (*TokenInfo, error) {
    if info, ok := introspectCache.Get(token); ok {
        return info.(*TokenInfo), nil
    }
    info, err := introspect(token)
    if err == nil && info.Active {
        introspectCache.Set(token, info, cache.DefaultExpiration)
    }
    return info, err
}

On this page