Skip to main content

Go

Same patterns as Python. Code is shorter because the JWT story is cleaner.

Most Go services are high-throughput; per-request HTTP calls to IdentSphere add latency you don't want. Validate JWTs locally instead.

go get github.com/golang-jwt/jwt/v5 github.com/MicahParks/keyfunc/v3
package main

import (
"context"
"fmt"
"net/http"
"os"
"time"

"github.com/MicahParks/keyfunc/v3"
"github.com/golang-jwt/jwt/v5"
)

var jwksURL = os.Getenv("IDENTSPHERE_URL") + "/.well-known/jwks.json"

func main() {
// Auto-refreshing JWKS cache
jwks, err := keyfunc.NewDefault([]string{jwksURL})
if err != nil {
panic(err)
}

mux := http.NewServeMux()
mux.Handle("/api/me", authMiddleware(jwks)(http.HandlerFunc(me)))
http.ListenAndServe(":8080", mux)
}

func authMiddleware(jwks keyfunc.Keyfunc) func(http.Handler) http.Handler {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
cookie, err := r.Cookie("identsphere_at")
if err != nil {
http.Error(w, "unauthenticated", http.StatusUnauthorized)
return
}
tok, err := jwt.Parse(cookie.Value, jwks.Keyfunc)
if err != nil || !tok.Valid {
http.Error(w, "invalid token", http.StatusUnauthorized)
return
}
claims := tok.Claims.(jwt.MapClaims)
ctx := context.WithValue(r.Context(), "user", claims)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}

func me(w http.ResponseWriter, r *http.Request) {
claims := r.Context().Value("user").(jwt.MapClaims)
fmt.Fprintf(w, "user=%s org=%s", claims["sub"], claims["org"])
}

That's a complete working auth-validating Go server in ~50 lines.

Pattern 1 — Proxy through IdentSphere

For login / signup / etc., forward to IdentSphere:

func loginHandler(w http.ResponseWriter, r *http.Request) {
req, _ := http.NewRequestWithContext(r.Context(), "POST",
os.Getenv("IDENTSPHERE_URL")+"/v1/auth/login", r.Body)
req.Header = r.Header.Clone()
resp, err := http.DefaultClient.Do(req)
if err != nil {
http.Error(w, err.Error(), http.StatusBadGateway)
return
}
defer resp.Body.Close()
// forward cookies
for _, c := range resp.Cookies() {
http.SetCookie(w, c)
}
w.WriteHeader(resp.StatusCode)
io.Copy(w, resp.Body)
}

Chi router

import "github.com/go-chi/chi/v5"

r := chi.NewRouter()
r.Group(func(r chi.Router) {
r.Use(authMiddlewareChi(jwks))
r.Get("/api/me", me)
})

Gin

Same pattern — wrap jwks.Keyfunc in gin.HandlerFunc. Sample in the examples/gin-go-starter/ directory (coming v1.1).

Caveat: revocation lag

See the Python notes on revocation lag. Same trade-off applies in Go.