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
go get github.com/ecarrizo/warden-auth-goStep 2: Initialize the Client
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:
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
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:
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
}
}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.