Skip to main content

AuthzX + Chi

Add authorization to your Chi API in 5 minutes.

Install

go get github.com/authzx/authzx-go

Set up the client

package main

import (
"os"
authzx "github.com/authzx/authzx-go"
"github.com/go-chi/chi/v5"
)

var client = authzx.NewClient(os.Getenv("AUTHZX_API_KEY"))

Create middleware

Chi uses standard net/http middleware — the Go SDK's HTTPMiddleware works directly:

r := chi.NewRouter()

r.Route("/documents/{id}", func(r chi.Router) {
r.Use(client.HTTPMiddleware("document", "read", "X-User-ID"))
r.Get("/", getDocument)
})

Custom middleware

For more control over subject extraction and resource ID:

func AuthzMiddleware(resourceType, action 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.Header.Get("X-User-ID")
if userID == "" {
http.Error(w, `{"error":"unauthorized"}`, 401)
return
}

resourceID := chi.URLParam(r, "id")

allowed, err := client.Check(r.Context(),
authzx.Subject{ID: userID, Type: "user"},
action,
authzx.Resource{ID: resourceID, Type: resourceType},
)
if err != nil {
http.Error(w, `{"error":"authorization check failed"}`, 500)
return
}
if !allowed {
http.Error(w, `{"error":"forbidden"}`, 403)
return
}

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

Apply to routes

r := chi.NewRouter()

r.Route("/documents/{id}", func(r chi.Router) {
r.With(AuthzMiddleware("document", "read")).Get("/", getDocument)
r.With(AuthzMiddleware("document", "write")).Put("/", updateDocument)
r.With(AuthzMiddleware("document", "delete")).Delete("/", deleteDocument)
})

Group-level middleware

Protect an entire route group:

r.Route("/admin", func(r chi.Router) {
r.Use(AuthzMiddleware("admin-panel", "access"))
r.Get("/users", listUsers)
r.Get("/settings", getSettings)
})

Full response with reason

func getDocument(w http.ResponseWriter, r *http.Request) {
resp, err := client.Authorize(r.Context(), &authzx.AuthorizeRequest{
Subject: authzx.Subject{ID: r.Header.Get("X-User-ID")},
Resource: authzx.Resource{ID: chi.URLParam(r, "id"), Type: "document"},
Action: "read",
})
if err != nil {
http.Error(w, `{"error":"authorization failed"}`, 500)
return
}
if !resp.Allowed {
w.WriteHeader(403)
json.NewEncoder(w).Encode(map[string]string{"error": "forbidden", "reason": resp.Reason})
return
}

json.NewEncoder(w).Encode(map[string]string{
"id": chi.URLParam(r, "id"),
"policy": resp.PolicyID,
"path": resp.AccessPath,
})
}

Using the local agent

var client = authzx.NewClient("", authzx.WithBaseURL("http://localhost:8181"))

No API key needed. Same middleware, sub-millisecond responses.