ExApps sign after AppAPI auth; Microservices check a static public key or an OIDC issuer and forward the same bearer. Co-authored-by: Cursor <cursoragent@cursor.com>
88 lines
2.2 KiB
Go
88 lines
2.2 KiB
Go
package usertoken
|
|
|
|
import (
|
|
"crypto/rsa"
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"errors"
|
|
"math/big"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/golang-jwt/jwt/v5"
|
|
)
|
|
|
|
func TestOIDCVerifier(t *testing.T) {
|
|
key := testKey(t)
|
|
var srv *httptest.Server
|
|
mux := http.NewServeMux()
|
|
srv = httptest.NewServer(mux)
|
|
t.Cleanup(srv.Close)
|
|
mux.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
|
|
_ = json.NewEncoder(w).Encode(map[string]any{
|
|
"issuer": srv.URL,
|
|
"jwks_uri": srv.URL + "/jwks",
|
|
"id_token_signing_alg_values_supported": []string{"RS256"},
|
|
})
|
|
})
|
|
mux.HandleFunc("/jwks", func(w http.ResponseWriter, r *http.Request) {
|
|
_ = json.NewEncoder(w).Encode(publicJWKS(&key.PublicKey))
|
|
})
|
|
|
|
auth, err := New(t.Context(), Config{
|
|
OIDCIssuer: srv.URL,
|
|
OIDCAudience: "mgmnt",
|
|
GroupsClaim: "identity_groups",
|
|
})
|
|
if err != nil {
|
|
t.Fatalf("New: %v", err)
|
|
}
|
|
now := time.Now()
|
|
groups := []string{"family"}
|
|
raw, err := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.MapClaims{
|
|
"iss": srv.URL,
|
|
"sub": "konrad",
|
|
"preferred_username": "konrad",
|
|
"aud": "mgmnt",
|
|
"iat": now.Unix(),
|
|
"exp": now.Add(time.Minute).Unix(),
|
|
"identity_groups": groups,
|
|
}).SignedString(key)
|
|
if err != nil {
|
|
t.Fatal(err)
|
|
}
|
|
caller, err := auth.Verify(t.Context(), raw)
|
|
if err != nil {
|
|
t.Fatalf("Verify: %v", err)
|
|
}
|
|
if caller.Subject != "konrad" || len(caller.Groups) != 1 || caller.Groups[0] != "family" {
|
|
t.Fatalf("caller = %+v", caller)
|
|
}
|
|
|
|
if _, err := New(t.Context(), Config{
|
|
PublicKey: &key.PublicKey,
|
|
Issuer: "manager",
|
|
Audience: "mgmnt",
|
|
OIDCIssuer: srv.URL,
|
|
}); err == nil {
|
|
t.Fatal("both modes accepted")
|
|
}
|
|
if _, err := auth.Verify(t.Context(), "not-a-token"); !errors.Is(err, ErrUnauthorized) {
|
|
t.Fatalf("bad token err = %v", err)
|
|
}
|
|
}
|
|
|
|
func publicJWKS(pub *rsa.PublicKey) map[string]any {
|
|
return map[string]any{
|
|
"keys": []map[string]string{{
|
|
"kty": "RSA",
|
|
"use": "sig",
|
|
"alg": "RS256",
|
|
"n": base64.RawURLEncoding.EncodeToString(pub.N.Bytes()),
|
|
"e": base64.RawURLEncoding.EncodeToString(big.NewInt(int64(pub.E)).Bytes()),
|
|
}},
|
|
}
|
|
}
|