WardenAuthAgent Security
PricingDocsCompareBlogLearnChangelog
Sign inGet started free
All posts
Tutorial
July 31, 2025
11 min read

Fine-Grained Authorization in Go with WardenAuth

Add context-aware access control to a Go service using the WardenAuth Go SDK: initialize the client, write net/http middleware, propagate context, and check permissions on the request path — idiomatic Go throughout.


This guide adds fine-grained authorization to a Go service using the WardenAuth Go SDK. Every SDK method is context-aware, so cancellation and deadlines propagate cleanly through your request path. We will build net/http middleware that enforces a permission before the handler runs.

Step 1: Install

bash
go get github.com/ecarrizo/warden-auth-go

Step 2: Initialize the Client

go
package main

import (
    "os"
    ac "github.com/ecarrizo/warden-auth-go"
)

func newClient() *ac.Client {
    return ac.NewClient(ac.ClientConfig{
        APIKey:  os.Getenv("RBAC_API_KEY"),
        BaseURL: os.Getenv("RBAC_API_URL"),
    })
}

Step 3: Authorization Middleware

The middleware takes the resource and action, resolves the subject and scope from the request, and calls the PDP with the request context:

go
func RequirePermission(client *ac.Client, resource, action string) func(http.Handler) http.Handler {
    return func(next http.Handler) http.Handler {
        return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
            subjectID := r.Context().Value(userIDKey).(string) // set by your auth middleware
            scopeID := r.Header.Get("X-Workspace-Id")

            result, err := client.AccessCheck.Check(r.Context(), ac.AccessCheckInput{
                SubjectID: subjectID,
                ScopeID:   scopeID,
                Resource:  resource,
                Action:    action,
            })
            if err != nil {
                http.Error(w, "authorization error", http.StatusBadGateway)
                return
            }
            if !result.Allowed {
                http.Error(w, "forbidden", http.StatusForbidden)
                return
            }
            next.ServeHTTP(w, r)
        })
    }
}

Step 4: Wire It Into Routes

go
func main() {
    client := newClient()
    mux := http.NewServeMux()

    deleteInvoice := http.HandlerFunc(handleDeleteInvoice)
    mux.Handle("DELETE /invoices/{id}",
        RequirePermission(client, "invoice", "delete")(deleteInvoice))

    http.ListenAndServe(":8080", mux)
}

Step 5: Ad-Hoc Checks in Handlers

For decisions that depend on runtime data, call the SDK directly and always pass the request context:

go
func handleExport(client *ac.Client) http.HandlerFunc {
    return func(w http.ResponseWriter, r *http.Request) {
        res, err := client.AccessCheck.Check(r.Context(), ac.AccessCheckInput{
            SubjectID: userID(r), ScopeID: scopeID(r),
            Resource: "report", Action: "export",
        })
        if err != nil || !res.Allowed {
            http.Error(w, "forbidden", http.StatusForbidden)
            return
        }
        // ...stream the export
    }
}
Always pass r.Context() rather than context.Background(). If the client disconnects, the authorization call is cancelled with the request — no wasted work.

Summary

You now have idiomatic, context-aware authorization in Go: reusable middleware for the common case and direct SDK calls for the rest. Create an API key to get started.


Back to blogTry WardenAuth free →
© 2026 ecarrizo. All rights reserved.
PricingDocsCompareBlogLearnChangelogStatusGlossaryContactTermsPrivacy