Avnology ID
SDKsGo SDKGuides

HTTP Middleware

Build authentication and authorization middleware for Go HTTP servers using the Go SDK.

HTTP Middleware

Protect your Go HTTP APIs with authentication and authorization middleware.

Token validation middleware

package middleware

import (
    "context"
    "net/http"
    "strings"

    avnologyid "github.com/avnology/sdk-go"
)

type contextKey string

const (
    UserIDKey  contextKey = "userID"
    ScopesKey  contextKey = "scopes"
    OrgIDKey   contextKey = "orgID"
)

func RequireAuth(client *avnologyid.Client) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            authHeader := r.Header.Get("Authorization")
            if !strings.HasPrefix(authHeader, "Bearer ") {
                http.Error(w, `{"error":"missing_token"}`, http.StatusUnauthorized)
                return
            }

            token := strings.TrimPrefix(authHeader, "Bearer ")
            result, err := client.OAuth.IntrospectToken(r.Context(), &avnologyid.IntrospectTokenParams{
                Token: token,
            })
            if err != nil || !result.Active {
                http.Error(w, `{"error":"invalid_token"}`, http.StatusUnauthorized)
                return
            }

            ctx := context.WithValue(r.Context(), UserIDKey, result.Sub)
            ctx = context.WithValue(ctx, ScopesKey, strings.Split(result.Scope, " "))
            if result.OrgID != "" {
                ctx = context.WithValue(ctx, OrgIDKey, result.OrgID)
            }

            next.ServeHTTP(w, r.WithContext(ctx))
        })
    }
}

Scope checking middleware

func RequireScope(scopes ...string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            userScopes, _ := r.Context().Value(ScopesKey).([]string)
            scopeSet := make(map[string]bool, len(userScopes))
            for _, s := range userScopes {
                scopeSet[s] = true
            }

            for _, required := range scopes {
                if !scopeSet[required] {
                    http.Error(w, `{"error":"insufficient_scope"}`, http.StatusForbidden)
                    return
                }
            }

            next.ServeHTTP(w, r)
        })
    }
}

Permission checking middleware

func RequirePermission(client *avnologyid.Client, relation string, objectFn func(*http.Request) string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            userID, _ := r.Context().Value(UserIDKey).(string)
            object := objectFn(r)

            allowed, err := client.Permissions.Check(r.Context(), &avnologyid.CheckPermissionParams{
                Subject:  "user:" + userID,
                Relation: relation,
                Object:   object,
            })
            if err != nil || !allowed {
                http.Error(w, `{"error":"forbidden"}`, http.StatusForbidden)
                return
            }

            next.ServeHTTP(w, r)
        })
    }
}

Composing middleware with net/http

mux := http.NewServeMux()

// Public routes
mux.HandleFunc("GET /health", healthHandler)

// Protected routes
authMux := http.NewServeMux()
authMux.HandleFunc("GET /api/profile", profileHandler)
authMux.HandleFunc("GET /api/projects/{id}", projectHandler)

mux.Handle("/api/", RequireAuth(client)(authMux))

See also

On this page