From dd0381ded467a489c35b0b693af7bd28cc21a1a9 Mon Sep 17 00:00:00 2001 From: John Wang Date: Fri, 6 Nov 2026 14:27:18 -0800 Subject: [PATCH 1/9] feat(systemauth): read a principal's profile from the server Server.Principal returns a human principal from social login's directory, such as its display name and avatar. Co-Authored-By: Claude Opus 5.5 --- identity/systemauth/social_login.go | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/identity/systemauth/social_login.go b/identity/systemauth/social_login.go index 4132a6ca..d9c63d6b 100644 --- a/identity/systemauth/social_login.go +++ b/identity/systemauth/social_login.go @@ -770,6 +770,16 @@ func (s *Server) CurrentLoginSession(r *http.Request) (*LoginSession, error) { return s.social.currentSession(r) } +// Principal returns a human principal's profile from the directory social +// login uses, such as its display name and avatar. It returns +// ErrPrincipalNotFound when there is none or social login is not enabled. +func (s *Server) Principal(ctx context.Context, id uuid.UUID) (*HumanPrincipal, error) { + if s.social == nil { + return nil, ErrPrincipalNotFound + } + return s.social.directory.GetPrincipal(ctx, id) +} + // ValidateReturnTo checks a place to send a person after an action, by the // policy sign-in uses for return_to: an absolute path on this host, or a URL // on an allowed redirect origin. It returns the URL to use, or From 4571bcaac14a4fccb9f26519aa4666cad649acc6 Mon Sep 17 00:00:00 2001 From: John Wang Date: Fri, 6 Nov 2026 14:27:19 -0800 Subject: [PATCH 2/9] fix(console): show the signed-in person's name and picture The console's session carried only the person's ID, so its profile menu showed no name or picture where the applications show both. An optional Options.Profile adds the name, email and avatar to the session, and systemauthsvc fills it from SystemAuth's directory. Co-Authored-By: Claude Opus 5.5 --- console/console.go | 27 ++++++++++++++++++++++++++- identity/systemauthsvc/console.go | 11 +++++++++++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/console/console.go b/console/console.go index 44b0726f..21ac316a 100644 --- a/console/console.go +++ b/console/console.go @@ -67,6 +67,14 @@ type Organization struct { Apps map[string]string `json:"apps,omitempty"` } +// Profile is how the signed-in person appears in the console's chrome: the +// name and picture in the profile menu. +type Profile struct { + Name string + Email string + AvatarURL string +} + // App is an application in the deployment's catalog. type App struct { ID string `json:"id"` @@ -92,6 +100,10 @@ type Options struct { // Apps lists the deployment's application catalog, for the console's // product switcher. Nil lists none. Apps func(ctx context.Context) ([]App, error) + // Profile returns the person's name, email and picture, for the + // profile menu, as the applications show them. Nil, or an error, shows + // the person without them; the console still works. + Profile func(ctx context.Context, principal uuid.UUID) (Profile, error) // Connections, if set, serves the connections section. Connections *connections.Connections // WebDir holds the built console pages. Empty serves only the API. @@ -198,6 +210,9 @@ type wireSession struct { type wireUser struct { ID string `json:"id"` + Name string `json:"name,omitempty"` + Email string `json:"email,omitempty"` + AvatarURL string `json:"avatar_url,omitempty"` Memberships []wireMembership `json:"memberships"` } @@ -248,9 +263,19 @@ func (c *console) session(w http.ResponseWriter, r *http.Request) { } catalog = append(catalog, apps...) } + user := &wireUser{ID: pid.String(), Memberships: ms} + if c.o.Profile != nil { + p, err := c.o.Profile(r.Context(), pid) + if err != nil { + // The profile menu then shows no name or picture. + c.o.Logger.Error("console: reading the profile", "principal", pid, "error", err) + } else { + user.Name, user.Email, user.AvatarURL = p.Name, p.Email, p.AvatarURL + } + } writeJSON(w, http.StatusOK, wireSession{ Authenticated: true, UserID: pid.String(), - User: &wireUser{ID: pid.String(), Memberships: ms}, + User: user, Apps: catalog, }) } diff --git a/identity/systemauthsvc/console.go b/identity/systemauthsvc/console.go index e512dca5..212379ea 100644 --- a/identity/systemauthsvc/console.go +++ b/identity/systemauthsvc/console.go @@ -26,6 +26,7 @@ func (s *Service) mountConsole(c *Config) error { ReturnTo: s.server.ValidateReturnTo, SignOut: s.server.EndLoginSession, Apps: s.consoleApps, + Profile: s.consoleProfile, Connections: s.conns, WebDir: c.Console.WebDir, LoginPath: systemauth.LoginPath, @@ -83,3 +84,13 @@ func (s *Service) consoleApps(context.Context) ([]console.App, error) { } return out, nil } + +// consoleProfile is the signed-in person's name, email and avatar, as the +// applications' tokens carry them. +func (s *Service) consoleProfile(ctx context.Context, principal uuid.UUID) (console.Profile, error) { + p, err := s.server.Principal(ctx, principal) + if err != nil { + return console.Profile{}, err + } + return console.Profile{Name: p.DisplayName, Email: p.Email, AvatarURL: p.AvatarURL}, nil +} From e54e8061edec3f5eecce0f38c94f29f6977e15dc Mon Sep 17 00:00:00 2001 From: John Wang Date: Fri, 6 Nov 2026 14:27:19 -0800 Subject: [PATCH 3/9] test: cover the console's profile and Server.Principal Co-Authored-By: Claude Opus 5.5 --- console/console_test.go | 22 ++++++++++++++++++++ identity/systemauth/social_login_test.go | 26 ++++++++++++++++++++++++ 2 files changed, 48 insertions(+) diff --git a/console/console_test.go b/console/console_test.go index 58ac223d..39ea182d 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -43,6 +43,7 @@ type env struct { admin uuid.UUID member uuid.UUID sessionError error + profileError error signedOut bool } @@ -79,6 +80,12 @@ func newEnv(t *testing.T, webDir string, withConnections bool) *env { Apps: func(context.Context) ([]console.App, error) { return []console.App{{ID: "portfolio", Name: "Portfolio", URL: "https://portfolio.example.com", Order: 3}}, nil }, + Profile: func(_ context.Context, p uuid.UUID) (console.Profile, error) { + if e.profileError != nil { + return console.Profile{}, e.profileError + } + return console.Profile{Name: "Ada Admin", Email: "ada@example.com", AvatarURL: "https://img.example.com/ada.png"}, nil + }, ReturnTo: func(raw string) (string, error) { if strings.HasPrefix(raw, "/") && !strings.HasPrefix(raw, "//") || strings.HasPrefix(raw, "https://app.example.com/") { return raw, nil @@ -169,6 +176,9 @@ func TestSession(t *testing.T) { Apps []console.App User struct { ID string + Name string + Email string + AvatarURL string `json:"avatar_url"` Memberships []struct { OrganizationID string `json:"organization_id"` OrganizationName string `json:"organization_name"` @@ -183,6 +193,10 @@ func TestSession(t *testing.T) { if !s.Authenticated || s.User.ID != e.admin.String() || len(s.User.Memberships) != 1 { t.Fatalf("session: %+v", s) } + // The profile menu shows the person as the applications do. + if s.User.Name != "Ada Admin" || s.User.Email != "ada@example.com" || s.User.AvatarURL != "https://img.example.com/ada.png" { + t.Errorf("profile: %+v", s.User) + } m := s.User.Memberships[0] if m.OrganizationID != e.org.String() || m.OrganizationName != "Acme" || m.Role != "admin" || m.Applications[console.AppID] != "admin" { t.Fatalf("membership: %+v", m) @@ -193,6 +207,14 @@ func TestSession(t *testing.T) { t.Fatalf("products: %+v, catalog %+v", m.Applications, s.Apps) } + // A profile that cannot be read leaves it out; the session still works. + e.profileError = errors.New("directory down") + resp, body = e.do(t, e.admin, http.MethodGet, console.SessionPath, nil, nil) + if resp.StatusCode != http.StatusOK || !strings.Contains(body, `"authenticated":true`) || strings.Contains(body, `"name":"Ada`) { + t.Fatalf("without a profile: %d %s", resp.StatusCode, body) + } + e.profileError = nil + e.sessionError = errors.New("store down") if resp, _ := e.do(t, e.admin, http.MethodGet, console.SessionPath, nil, nil); resp.StatusCode != http.StatusInternalServerError { t.Fatalf("a failing session store: %d", resp.StatusCode) diff --git a/identity/systemauth/social_login_test.go b/identity/systemauth/social_login_test.go index bb425835..aba1dd39 100644 --- a/identity/systemauth/social_login_test.go +++ b/identity/systemauth/social_login_test.go @@ -863,3 +863,29 @@ func TestEndLoginSession(t *testing.T) { t.Errorf("without social login: %v", err) } } + +func TestServerPrincipal(t *testing.T) { + ctx := context.Background() + dir := NewMemoryPrincipalDirectory() + s := newSocialTestServer(t, newFakeGitHub(t), dir) + p, err := ResolveExternalLogin(ctx, dir, ExternalLogin{Provider: "google", Subject: "g-1", Email: "g@example.com", + EmailVerified: true, EmailAuthoritative: true, Name: "Grace", AvatarURL: "https://img.example.com/g.png"}, time.Now()) + if err != nil { + t.Fatal(err) + } + got, err := s.Principal(ctx, p.ID) + if err != nil || got.DisplayName != "Grace" || got.Email != "g@example.com" || got.AvatarURL != "https://img.example.com/g.png" { + t.Fatalf("Principal = %+v, %v", got, err) + } + if _, err := s.Principal(ctx, uuid.New()); !errors.Is(err, ErrPrincipalNotFound) { + t.Errorf("an unknown principal: %v", err) + } + + plain, err := NewEmbedded(Config{Issuer: testIssuer}) + if err != nil { + t.Fatal(err) + } + if _, err := plain.Principal(ctx, p.ID); !errors.Is(err, ErrPrincipalNotFound) { + t.Errorf("without social login: %v", err) + } +} From ae476d249c14184a155c03175f91cd1a6426d4b8 Mon Sep 17 00:00:00 2001 From: John Wang Date: Fri, 6 Nov 2026 14:28:35 -0800 Subject: [PATCH 4/9] docs(console): describe the person's profile in the session Co-Authored-By: Claude Opus 5.5 --- docs/guides/console.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/guides/console.md b/docs/guides/console.md index 48fcbdcc..9d95f92e 100644 --- a/docs/guides/console.md +++ b/docs/guides/console.md @@ -51,7 +51,7 @@ The session routes under `/console/bff` speak the format `@plexusone/auth`'s | Call | Does | |---|---| -| `GET /console/bff/session` | The person and their organizations, personal first, and the deployment's app catalog. Each organization grants the application `console` with the person's organization role, besides their roles in the products, so the console's product switcher links back to them. Without a session: `{"authenticated":false}` | +| `GET /console/bff/session` | The person, with their name, email and avatar as the applications show them, and their organizations, personal first, and the deployment's app catalog. Each organization grants the application `console` with the person's organization role, besides their roles in the products, so the console's product switcher links back to them. Without a session: `{"authenticated":false}` | | `GET /console/bff/auth/login?return_to=` | Sends the person to `/login`, and back to `return_to`, a path on this host | | `GET /console/bff/auth/{provider}?return_to=` | The same, straight to that provider's sign-in | | `POST /console/bff/auth/logout` | Signs the person out of SystemAuth, as `POST /logout` does, and answers `204` | From bdabab88062825edf8bb383c49a69f2820e19717 Mon Sep 17 00:00:00 2001 From: John Wang Date: Fri, 6 Nov 2026 14:28:35 -0800 Subject: [PATCH 5/9] docs(release): add v0.32.1 changelog and release notes Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.json | 31 +++++++++++++++++++++++++++++++ CHANGELOG.md | 22 +++++++++++++++++++++- docs/releases/changelog.md | 22 +++++++++++++++++++++- docs/releases/index.md | 6 +++++- docs/releases/v0.32.1.md | 22 ++++++++++++++++++++++ mkdocs.yml | 1 + 6 files changed, 101 insertions(+), 3 deletions(-) create mode 100644 docs/releases/v0.32.1.md diff --git a/CHANGELOG.json b/CHANGELOG.json index 65cc893c..b22386db 100644 --- a/CHANGELOG.json +++ b/CHANGELOG.json @@ -8,6 +8,37 @@ "grokify" ], "releases": [ + { + "version": "v0.32.1", + "date": "2026-11-06", + "added": [ + { + "description": "`systemauth.Server.Principal` returns a human principal's profile from social login's directory", + "commit": "dd0381d" + }, + { + "description": "`console.Options.Profile` supplies the signed-in person's name, email and avatar to the console's session", + "commit": "4571bca" + } + ], + "fixed": [ + { + "description": "The organization console's profile menu showed no name or picture; its session now carries them, filled by `systemauthsvc` from SystemAuth's directory", + "commit": "4571bca" + } + ], + "tests": [ + { + "description": "The console's session with and without a profile, and `Server.Principal` with and without social login", + "commit": "e54e806" + } + ], + "documentation": [ + { + "description": "The console guide describes the person's profile in the session" + } + ] + }, { "version": "v0.32.0", "date": "2026-11-05", diff --git a/CHANGELOG.md b/CHANGELOG.md index e59d5ca7..4b61376b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,25 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro ## [Unreleased] +## [v0.32.1] - 2026-11-06 + +### Added + +- `systemauth.Server.Principal` returns a human principal's profile from social login's directory ([`dd0381d`](https://github.com/plexusone/systemforge/commit/dd0381d)) +- `console.Options.Profile` supplies the signed-in person's name, email and avatar to the console's session ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) + +### Fixed + +- The organization console's profile menu showed no name or picture; its session now carries them, filled by `systemauthsvc` from SystemAuth's directory ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) + +### Documentation + +- The console guide describes the person's profile in the session + +### Tests + +- The console's session with and without a profile, and `Server.Principal` with and without social login ([`e54e806`](https://github.com/plexusone/systemforge/commit/e54e806)) + ## [v0.32.0] - 2026-11-05 ### Highlights @@ -1855,7 +1874,8 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro - GitHub Actions lint workflow with golangci-lint ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5)) - Dependabot configuration for Go modules and GitHub Actions ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5)) -[unreleased]: https://github.com/plexusone/systemforge/compare/v0.32.0...HEAD +[unreleased]: https://github.com/plexusone/systemforge/compare/v0.32.1...HEAD +[v0.32.1]: https://github.com/plexusone/systemforge/compare/v0.32.0...v0.32.1 [v0.32.0]: https://github.com/plexusone/systemforge/compare/v0.31.0...v0.32.0 [v0.31.0]: https://github.com/plexusone/systemforge/compare/v0.30.5...v0.31.0 [v0.30.5]: https://github.com/plexusone/systemforge/compare/v0.30.4...v0.30.5 diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md index e59d5ca7..4b61376b 100644 --- a/docs/releases/changelog.md +++ b/docs/releases/changelog.md @@ -9,6 +9,25 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro ## [Unreleased] +## [v0.32.1] - 2026-11-06 + +### Added + +- `systemauth.Server.Principal` returns a human principal's profile from social login's directory ([`dd0381d`](https://github.com/plexusone/systemforge/commit/dd0381d)) +- `console.Options.Profile` supplies the signed-in person's name, email and avatar to the console's session ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) + +### Fixed + +- The organization console's profile menu showed no name or picture; its session now carries them, filled by `systemauthsvc` from SystemAuth's directory ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) + +### Documentation + +- The console guide describes the person's profile in the session + +### Tests + +- The console's session with and without a profile, and `Server.Principal` with and without social login ([`e54e806`](https://github.com/plexusone/systemforge/commit/e54e806)) + ## [v0.32.0] - 2026-11-05 ### Highlights @@ -1855,7 +1874,8 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro - GitHub Actions lint workflow with golangci-lint ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5)) - Dependabot configuration for Go modules and GitHub Actions ([`e2f98d5`](https://github.com/plexusone/systemforge/commit/e2f98d5)) -[unreleased]: https://github.com/plexusone/systemforge/compare/v0.32.0...HEAD +[unreleased]: https://github.com/plexusone/systemforge/compare/v0.32.1...HEAD +[v0.32.1]: https://github.com/plexusone/systemforge/compare/v0.32.0...v0.32.1 [v0.32.0]: https://github.com/plexusone/systemforge/compare/v0.31.0...v0.32.0 [v0.31.0]: https://github.com/plexusone/systemforge/compare/v0.30.5...v0.31.0 [v0.30.5]: https://github.com/plexusone/systemforge/compare/v0.30.4...v0.30.5 diff --git a/docs/releases/index.md b/docs/releases/index.md index d9085df4..36247c90 100644 --- a/docs/releases/index.md +++ b/docs/releases/index.md @@ -4,7 +4,11 @@ Release notes, one file per version, newest first. The [changelog](changelog.md) is generated from `CHANGELOG.json` and lists every change with its commit. -The latest release, [v0.32.0](v0.32.0.md), lets a manager replace a +The latest release, [v0.32.1](v0.32.1.md), shows the signed-in person's +name and picture in the organization console, as the applications do. It +adds no breaking change. + +Before it, [v0.32.0](v0.32.0.md) let a manager replace a managed principal's login tokens in one call, so a lost printed card is retired and reprinted in one step. Its sign-in methods page shows what is linked and links back to the application. It adds no breaking change. diff --git a/docs/releases/v0.32.1.md b/docs/releases/v0.32.1.md new file mode 100644 index 00000000..a94bc531 --- /dev/null +++ b/docs/releases/v0.32.1.md @@ -0,0 +1,22 @@ +# v0.32.1 + +**Release Date:** 2026-11-06 + +The organization console shows the signed-in person the way the +applications do. + +- **Name and picture in the console.** The console's session, + `GET /console/bff/session`, now carries the person's name, email and + avatar, so its profile menu shows their picture instead of a placeholder. + `console.Options.Profile` supplies them; `systemauthsvc` fills it from + SystemAuth's directory, the same source as the `name` and `picture` + claims. Without it, or when it fails, the console works as before. +- **`systemauth.Server.Principal`** returns a human principal's profile + from social login's directory. + +The release adds no breaking change. + +## Upgrading + +Nothing is required. A deployment running the console through +`systemauthsvc` shows the person's name and picture after upgrading. diff --git a/mkdocs.yml b/mkdocs.yml index 4e89054c..0a7449c8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -169,6 +169,7 @@ nav: - Releases: - Overview: releases/index.md - Changelog: releases/changelog.md + - v0.32.1: releases/v0.32.1.md - v0.32.0: releases/v0.32.0.md - v0.31.0: releases/v0.31.0.md - v0.30.5: releases/v0.30.5.md From 846ed7b4f34a757179152876d5198619150bf6ba Mon Sep 17 00:00:00 2001 From: John Wang Date: Sat, 7 Nov 2026 07:00:27 -0800 Subject: [PATCH 6/9] build(deps): require connectforge v0.7.0 connectforge v0.7.0 keeps what a validation found in a connection's health, so the connections API returns it with each connection. Co-Authored-By: Claude Opus 5.5 --- go.mod | 4 ++-- go.sum | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/go.mod b/go.mod index 09056ee5..88307da7 100644 --- a/go.mod +++ b/go.mod @@ -28,7 +28,7 @@ require ( github.com/jackc/pgx/v5 v5.12.0 github.com/mattn/go-sqlite3 v1.14.52 github.com/ory/fosite v0.49.0 - github.com/plexusone/connectforge v0.6.0 + github.com/plexusone/connectforge v0.7.0 github.com/plexusone/keyforge v0.2.0 github.com/plexusone/omni-aws v0.11.0 github.com/plexusone/omnimail v0.1.0 @@ -235,7 +235,7 @@ require ( github.com/subosito/gotenv v1.6.0 // indirect github.com/valyala/fastjson v1.6.10 // indirect github.com/yaronf/httpsign v0.5.2 // indirect - github.com/zclconf/go-cty v1.19.0 // indirect + github.com/zclconf/go-cty v1.20.0 // indirect github.com/zclconf/go-cty-yaml v1.2.0 // indirect go.opencensus.io v0.24.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect diff --git a/go.sum b/go.sum index da2cf586..30c42cd8 100644 --- a/go.sum +++ b/go.sum @@ -531,8 +531,8 @@ github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587 h1:xzZOeCMQLA/W198ZkdVdt4EKFKJtS26B773zNU377ZY= github.com/planetscale/vtprotobuf v0.6.1-0.20240917153116-6f2963f01587/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= -github.com/plexusone/connectforge v0.6.0 h1:s/1upvVidylAd9cbafnxoAX+rVrhSIgtnjpgar1VZZ0= -github.com/plexusone/connectforge v0.6.0/go.mod h1:QJKDTQYcfAiguR9PIRcugR7aNnA6wnxGhCcT5CGUigg= +github.com/plexusone/connectforge v0.7.0 h1:ypR3yXqAVLkJ6CSKCqk6tieCxmchktVpqpKycfmO1Hw= +github.com/plexusone/connectforge v0.7.0/go.mod h1:s6gApsI+mzXxFlhjCdbv4a4Y1l9MPkYUCmQk3xVcHjg= github.com/plexusone/keyforge v0.2.0 h1:Dr/KfFuQBAi2hW/fZkqe5u/QLQ43is4oWOFvUhandFQ= github.com/plexusone/keyforge v0.2.0/go.mod h1:LXDweBp3uodk4iYWmxyqvFptjgEwFpWzzhdB86BQl4U= github.com/plexusone/omni-aws v0.11.0 h1:hQM8wadLpy55K/Q30ci1wcRZ/MHOmHOlYi4ooW7ZFyE= @@ -680,8 +680,8 @@ github.com/yuin/goldmark v1.4.1/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1 github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= -github.com/zclconf/go-cty v1.19.0 h1:IV8WdqYZc2c5rLX9bEoLNXKojBAp0MZPBHMIrCoa/s4= -github.com/zclconf/go-cty v1.19.0/go.mod h1:12W89jGn3JCOIQi7infWr9m80rOkb5RNYJqXMZcN4c8= +github.com/zclconf/go-cty v1.20.0 h1:V2Mh9pu2xWPATCY7US3LnGBNSq/AAZDsqQZmmOAV7DI= +github.com/zclconf/go-cty v1.20.0/go.mod h1:12W89jGn3JCOIQi7infWr9m80rOkb5RNYJqXMZcN4c8= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940 h1:4r45xpDWB6ZMSMNJFMOjqrGHynW3DIBuR2H9j0ug+Mo= github.com/zclconf/go-cty-debug v0.0.0-20240509010212-0d6042c53940/go.mod h1:CmBdvvj3nqzfzJ6nTCIwDTPZ56aVGvDrmztiO5g3qrM= github.com/zclconf/go-cty-yaml v1.2.0 h1:GDyL4+e/Qe/S0B7YaecMLbVvAR/Mp21CXMOSiCTOi1M= From 672550ffcd977e6d17649e52c17edb561f6f0894 Mon Sep 17 00:00:00 2001 From: John Wang Date: Sat, 7 Nov 2026 07:05:43 -0800 Subject: [PATCH 7/9] docs(release): add connection details to v0.32.1 Co-Authored-By: Claude Opus 5.5 --- CHANGELOG.json | 10 ++++++++++ CHANGELOG.md | 5 +++++ docs/releases/v0.32.1.md | 11 ++++++++++- 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.json b/CHANGELOG.json index b22386db..6549fc30 100644 --- a/CHANGELOG.json +++ b/CHANGELOG.json @@ -19,6 +19,10 @@ { "description": "`console.Options.Profile` supplies the signed-in person's name, email and avatar to the console's session", "commit": "4571bca" + }, + { + "description": "The connections API returns each connection's `health.details`, what its last successful check found the credential reaches, such as the Search Console properties an account can read", + "commit": "846ed7b" } ], "fixed": [ @@ -33,6 +37,12 @@ "commit": "e54e806" } ], + "dependencies": [ + { + "description": "Bump `github.com/plexusone/connectforge` from 0.6.0 to 0.7.0", + "commit": "846ed7b" + } + ], "documentation": [ { "description": "The console guide describes the person's profile in the session" diff --git a/CHANGELOG.md b/CHANGELOG.md index 4b61376b..e6bbcf99 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,11 +15,16 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro - `systemauth.Server.Principal` returns a human principal's profile from social login's directory ([`dd0381d`](https://github.com/plexusone/systemforge/commit/dd0381d)) - `console.Options.Profile` supplies the signed-in person's name, email and avatar to the console's session ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) +- The connections API returns each connection's `health.details`, what its last successful check found the credential reaches, such as the Search Console properties an account can read ([`846ed7b`](https://github.com/plexusone/systemforge/commit/846ed7b)) ### Fixed - The organization console's profile menu showed no name or picture; its session now carries them, filled by `systemauthsvc` from SystemAuth's directory ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) +### Dependencies + +- _1 dependency update_ + ### Documentation - The console guide describes the person's profile in the session diff --git a/docs/releases/v0.32.1.md b/docs/releases/v0.32.1.md index a94bc531..3f329356 100644 --- a/docs/releases/v0.32.1.md +++ b/docs/releases/v0.32.1.md @@ -3,7 +3,7 @@ **Release Date:** 2026-11-06 The organization console shows the signed-in person the way the -applications do. +applications do, and connections show what their last check found. - **Name and picture in the console.** The console's session, `GET /console/bff/session`, now carries the person's name, email and @@ -13,6 +13,10 @@ applications do. claims. Without it, or when it fails, the console works as before. - **`systemauth.Server.Principal`** returns a human principal's profile from social login's directory. +- **What a connection reaches.** With connectforge v0.7.0, each + connection's `health.details` in the connections API lists what its + last successful check found, such as the Search Console properties an + account can read. A failed check keeps the last ones. The release adds no breaking change. @@ -20,3 +24,8 @@ The release adds no breaking change. Nothing is required. A deployment running the console through `systemauthsvc` shows the person's name and picture after upgrading. +Existing connections have no details until their next check. + +## Dependencies + +- `github.com/plexusone/connectforge` 0.7.0. From 65a9397a23dcf834dc533ac14aae474b8a68631d Mon Sep 17 00:00:00 2001 From: John Wang Date: Sat, 7 Nov 2026 07:48:46 -0800 Subject: [PATCH 8/9] test(console): give each person in the session test their own profile The profile fake returned the same profile for anyone, so a session filled with another person's profile would still pass. The admin and the member now have distinct profiles, and the member's session is checked for theirs. Co-Authored-By: Claude Opus 5.5 --- console/console_test.go | 28 +++++++++++++++++++++++++++- 1 file changed, 27 insertions(+), 1 deletion(-) diff --git a/console/console_test.go b/console/console_test.go index 39ea182d..8c398f10 100644 --- a/console/console_test.go +++ b/console/console_test.go @@ -84,7 +84,13 @@ func newEnv(t *testing.T, webDir string, withConnections bool) *env { if e.profileError != nil { return console.Profile{}, e.profileError } - return console.Profile{Name: "Ada Admin", Email: "ada@example.com", AvatarURL: "https://img.example.com/ada.png"}, nil + switch p { + case e.admin: + return console.Profile{Name: "Ada Admin", Email: "ada@example.com", AvatarURL: "https://img.example.com/ada.png"}, nil + case e.member: + return console.Profile{Name: "Max Member", Email: "max@example.com", AvatarURL: "https://img.example.com/max.png"}, nil + } + return console.Profile{}, errors.New("no such person") }, ReturnTo: func(raw string) (string, error) { if strings.HasPrefix(raw, "/") && !strings.HasPrefix(raw, "//") || strings.HasPrefix(raw, "https://app.example.com/") { @@ -207,6 +213,26 @@ func TestSession(t *testing.T) { t.Fatalf("products: %+v, catalog %+v", m.Applications, s.Apps) } + // Each person's session carries their own profile. + resp, body = e.do(t, e.member, http.MethodGet, console.SessionPath, nil, nil) + if resp.StatusCode != http.StatusOK { + t.Fatalf("member's session: %d %s", resp.StatusCode, body) + } + var ms struct { + User struct { + ID string + Name string + Email string + AvatarURL string `json:"avatar_url"` + } + } + if err := json.Unmarshal([]byte(body), &ms); err != nil { + t.Fatal(err) + } + if ms.User.ID != e.member.String() || ms.User.Name != "Max Member" || ms.User.Email != "max@example.com" || ms.User.AvatarURL != "https://img.example.com/max.png" { + t.Errorf("member's profile: %+v", ms.User) + } + // A profile that cannot be read leaves it out; the session still works. e.profileError = errors.New("directory down") resp, body = e.do(t, e.admin, http.MethodGet, console.SessionPath, nil, nil) From 7068c3f523394084fb150f65349794852f26a2a5 Mon Sep 17 00:00:00 2001 From: John Wang Date: Sat, 7 Nov 2026 07:48:46 -0800 Subject: [PATCH 9/9] docs(release): add connection details to the site's v0.32.1 changelog Co-Authored-By: Claude Opus 5.5 --- docs/releases/changelog.md | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/docs/releases/changelog.md b/docs/releases/changelog.md index 4b61376b..e6bbcf99 100644 --- a/docs/releases/changelog.md +++ b/docs/releases/changelog.md @@ -15,11 +15,16 @@ and this changelog is generated by [Structured Changelog](https://github.com/gro - `systemauth.Server.Principal` returns a human principal's profile from social login's directory ([`dd0381d`](https://github.com/plexusone/systemforge/commit/dd0381d)) - `console.Options.Profile` supplies the signed-in person's name, email and avatar to the console's session ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) +- The connections API returns each connection's `health.details`, what its last successful check found the credential reaches, such as the Search Console properties an account can read ([`846ed7b`](https://github.com/plexusone/systemforge/commit/846ed7b)) ### Fixed - The organization console's profile menu showed no name or picture; its session now carries them, filled by `systemauthsvc` from SystemAuth's directory ([`4571bca`](https://github.com/plexusone/systemforge/commit/4571bca)) +### Dependencies + +- _1 dependency update_ + ### Documentation - The console guide describes the person's profile in the session