package httpapi import ( "testing" "time" "github.com/go-jose/go-jose/v4" "github.com/go-jose/go-jose/v4/jwt" ) // extractIdentity pulls the display identity plus the thin-account fields // (match_key from the configured claim, roles from groups) out of an id_token. // It does not verify the signature, so a throwaway HS256 key suffices here. func TestExtractIdentity(t *testing.T) { key := []byte("any-throwaway-signing-key-32-bytes!!") sig, err := jose.NewSigner(jose.SigningKey{Algorithm: jose.HS256, Key: key}, (&jose.SignerOptions{}).WithType("JWT")) if err != nil { t.Fatalf("signer: %v", err) } tok, err := jwt.Signed(sig). Claims(jwt.Claims{Subject: "sub-123", Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour))}). Claims(map[string]any{ "preferred_username": "alice", "email": "alice@example.net", "avatar": "https://a/av.png", "groups": []any{"admins", "staff"}, }).Serialize() if err != nil { t.Fatalf("sign: %v", err) } id, err := extractIdentity(tok, "", "email") if err != nil { t.Fatalf("extract: %v", err) } if id.User.ID != "sub-123" || id.User.Name != "alice" || id.User.Avatar != "https://a/av.png" { t.Errorf("user wrong: %+v", id.User) } if id.MatchKey != "alice@example.net" { t.Errorf("match_key: got %q, want alice@example.net", id.MatchKey) } if len(id.Roles) != 2 || id.Roles[0] != "admins" || id.Roles[1] != "staff" { t.Errorf("roles: got %v, want [admins staff]", id.Roles) } // No name claim → falls back to sub; absent match claim → empty (never matches). tok2, _ := jwt.Signed(sig). Claims(jwt.Claims{Subject: "sub-x", Expiry: jwt.NewNumericDate(time.Now().Add(time.Hour))}). Serialize() id2, err := extractIdentity(tok2, "", "email") if err != nil { t.Fatalf("extract2: %v", err) } if id2.User.Name != "sub-x" || id2.MatchKey != "" || len(id2.Roles) != 0 { t.Errorf("fallback wrong: %+v", id2) } }