docs(19.5-03): complete adoption plan

sm-sociallogin-plugin mounted in sm-demo-app and sm-bm-app (Microsoft 365
with the frontend callback, consent and link-ticket views), fonoteka.go
pinned to sm-user-plugin v0.1.4. User setup for the Entra registration;
one unrelated fonoteka route-inventory failure deferred.
This commit is contained in:
Jakub Zych
2026-10-21 00:30:00 +02:00
parent 7f7f5fc171
commit 0a87d64d4e
3 changed files with 297 additions and 0 deletions

View File

@@ -0,0 +1,243 @@
---
phase: 19.5-social-login-in-sm-user-plugin
plan: 03
subsystem: auth
tags: [sociallogin, oauth, microsoft-365, entra, adoption, sm-demo-app, sm-bm-app, fonoteka]
requires:
- phase: 19.5-02
provides: "sm-sociallogin-plugin v0.1.0 (private), sm-user-plugin v0.1.4 (c43ba87), summercms v0.3.1, push go-ahead (release v0.3.1)"
provides:
- "sm-demo-app mounts golem15.sociallogin v0.1.0 next to golem15.oauthserver (go.mod requires it; providers off until credentials are set)"
- "sm-bm-app mounts golem15.sociallogin for Microsoft 365 with tenant and redirect from the environment, DEPLOY.md Entra section"
- "bm frontend: Microsoft 365 button, /auth/oauth/callback, /auth/oauth/rejestracja consent view, profile link through the link ticket"
- "sm-bm-plugin: /oauth reserved from the SPA fallback; rebuilt public/"
- "fonoteka.go pinned to sm-user-plugin c43ba87 (relaxed unlink rule), sociallogin not mounted"
affects: [19.5-04, sm-demo-app, sm-bm-app, sm-bm-plugin, fonoteka.go]
actuals:
tokens: 10560
tasks: 4
commits: 9
plan_head_before: 7f7f5fcb
plan_head_after: 7f7f5fcb
# No code commit in summercms.go (only this SUMMARY). Measured per repository:
# sm-demo-app: git rev-list --count 4b16f50..d5a98d9 = 2 (pushed)
# sm-bm-plugin: git rev-list --count 2536bcb..85cae45 = 2 (pushed)
# sm-bm-app: git rev-list --count 1d446cc..0374c3f = 4 (not pushed)
# fonoteka.go: git rev-list --count 687e25a..f4d5728 = 1 (not pushed)
# tokens: chars/4 over the changed lines of the four repositories, without go.sum and the generated public/ build.
tech-stack:
added: []
patterns:
- "Private shared plugin fetched in module mode through an env-scoped git insteadOf (GIT_CONFIG_COUNT), no global config change"
- "Workspace-level framework replace when a workspace module's go.mod replaces the framework with its own sibling path"
- "SPA OAuth callback view that replaces the route before redeeming a single-use code once"
key-files:
created:
- ../sm-demo-app/sociallogin_test.go
- ../sm-bm-app/sociallogin_test.go
- ../sm-bm-app/config/golem15.sociallogin.yaml
- ../sm-bm-app/frontend/src/lib/oauth.ts
- ../sm-bm-app/frontend/src/lib/oauth.test.ts
- ../sm-bm-app/frontend/src/views/OAuthCallbackView.vue
- ../sm-bm-app/frontend/src/views/OAuthRegisterView.vue
- .planning/phases/19.5-social-login-in-sm-user-plugin/19.5-USER-SETUP.md
- .planning/phases/19.5-social-login-in-sm-user-plugin/deferred-items.md
modified:
- ../sm-demo-app/{.gitmodules,go.mod,go.sum,go.work,summer.yaml,plugins.gen.go,boot_test.go,deploy/env.example,README.md}
- ../sm-bm-app/{.gitmodules,go.mod,go.sum,go.work,summer.yaml,plugins.gen.go,realtime_test.go,scripts/build.sh,deploy/env.example,DEPLOY.md,README.md}
- ../sm-bm-app/frontend/{vite.config.ts,src/router.ts,src/api/user.ts,src/stores/auth.ts,src/views/LoginView.vue,src/views/ProfileView.vue,src/pwa/sw.js,src/pwa/pwa.test.ts}
- ../sm-bm-app/plugins/jz/bm/{static.go,static_test.go,public/}
- ../fonoteka.go/plugins/golem15/user (pointer)
key-decisions:
- "bm keeps providers.microsoft.tenant empty in committed config (empty means common): the MICROSOFT_TENANT legacy variable only fills an empty key, so a committed 'common' would shadow it"
- "sm-demo-app go.mod requires sm-sociallogin-plugin v0.1.0, sm-user-plugin v0.1.4 and summercms v0.3.1 (TestGoModRequiresTags and check-pins.sh demand a tag for every summer.yaml plugin)"
- "sm-bm-app go.work replaces the framework for the whole workspace, because sm-sociallogin-plugin's go.mod replaces it with a sibling path that conflicts with bm's"
- "The bm service worker skips /oauth like the other reserved prefixes"
- "sm-bm-app and fonoteka.go not pushed: both carry other sessions' unpushed commits"
patterns-established:
- "An application that mounts sociallogin reserves /oauth in its SPA fallback and service worker"
requirements-completed: [SC-1, SC-3, D-01, D-04, D-05, D-06, D-11, D-14, D-17, D-18, D-21, D-23, D-24]
coverage:
- id: D1
description: "sm-demo-app mounts sociallogin; Google redirect with prompt=select_account, state and a summer_oauth_google cookie on /oauth; oauth-providers lists google"
requirement: "D-21"
verification:
- kind: integration
ref: "../sm-demo-app/sociallogin_test.go#TestSocialLoginRedirect"
status: pass
- kind: integration
ref: "../sm-demo-app/boot_test.go#TestBootAllPlugins"
status: pass
human_judgment: false
- id: D2
description: "Pitfall 9: /oauth/jwks, /oauth/authorize, /oauth/userinfo stay with oauthserver; GET /oauth/token and /oauth/nope answer 404 without a state cookie"
requirement: "SC-1"
verification:
- kind: integration
ref: "../sm-demo-app/sociallogin_test.go#TestSocialLoginCoexistsWithOAuthServer"
status: pass
human_judgment: false
- id: D3
description: "sm-bm-app: Microsoft redirect to login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize with offline_access from MICROSOFT_* env, provider list {microsoft, Microsoft}, unconfigured provider errors to the callback; /oauth reserved from the SPA fallback"
requirement: "D-04"
verification:
- kind: integration
ref: "../sm-bm-app/sociallogin_test.go#TestSocialLoginMounted"
status: pass
- kind: unit
ref: "../sm-bm-app/plugins/jz/bm/static_test.go#TestStaticReservedPaths"
status: pass
human_judgment: false
- id: D4
description: "bm frontend start URLs and callback parsing (login, register consent, link ticket, return_to encoding, array queries)"
requirement: "D-06"
verification:
- kind: unit
ref: "../sm-bm-app/frontend/src/lib/oauth.test.ts (10 tests)"
status: pass
- kind: other
ref: "npm run typecheck; npm run build; TestEmbeddedPublicHasIndex"
status: pass
human_judgment: false
- id: D5
description: "bm callback, consent and profile link views behave in a browser against a live Entra registration"
requirement: "D-23"
human_judgment: true
rationale: "Needs a real Entra app registration and a browser; no credentials exist in this session"
- id: D6
description: "fonoteka.go builds and passes at sm-user-plugin c43ba87"
requirement: "D-24"
verification:
- kind: other
ref: "go -C ../fonoteka.go vet ./... && build ./... && test -count=1 -short ./..."
status: pass
- kind: integration
ref: "go -C ../fonoteka.go test -count=1 ./plugins/golem15/user/... ./plugins/golem15/fonoteka/... (user plugin all ok; one unrelated cabana inventory failure, deferred-items.md)"
status: pass
human_judgment: true
rationale: "TestPhase09SecurityRoutes fails on five summercms.cabana admin routes unrelated to the user plugin; the verifier should confirm it is pre-existing"
duration: 18min
completed: 2026-10-21
status: complete
---
# Phase 19.5 Plan 03: Adoption in sm-demo-app, sm-bm-app and fonoteka.go Summary
**sm-sociallogin-plugin v0.1.0 mounted in the demo next to the OIDC provider and in bm for Microsoft 365 (tenant and redirect from the environment), the bm student app signs in, registers and links Microsoft through the one-time code and link ticket, and fonoteka.go is pinned green to sm-user-plugin v0.1.4**
## Performance
- **Duration:** about 18 min
- **Started:** 2026-10-20T22:09:59Z
- **Completed:** 2026-10-20T22:28:22Z (2026-10-21 local)
- **Tasks:** 4 of 4
- **Files:** about 45 source files across four repositories, plus the regenerated `public/` build (62 files) in sm-bm-plugin
## Accomplishments
- Demo: `golem15.sociallogin` boots without a database next to all 16 other plugins; GET `/oauth/google` redirects to Google's account chooser with a sealed state cookie; the OIDC provider's `/oauth/*` routes are untouched and its reserved names answer 404 from sociallogin.
- bm: Microsoft 365 with a configurable tenant (default common); `/oauth` reserved from the SPA fallback and the service worker; DEPLOY.md explains the Entra registration, the env values, D-11 (no auto-link by email), linking from the profile and the one-instance cache caveat.
- bm frontend: login button shown only when the provider is configured, a callback view that strips the code from the address bar before redeeming it once, a consent view for first sign-ins (with the unverified-match explanation), and a profile button that links through `oauth-link-ticket`. No new npm dependency.
- fonoteka.go: only the user pointer moved; vet, build and the short suite green; the Docker-backed user plugin suite green.
## Task Commits
**sm-demo-app** (`/media/nvme/dev/golem15/summercms.io/summercms/sm-demo-app`), pushed `4b16f50..d5a98d9`:
1. Task 1a: `9eb8492` chore: bump golem15.user for social sign-in hooks (pointer 7b9faa3 to c43ba87, go.mod `sm-user-plugin v0.1.4`)
2. Task 1b: `d5a98d9` feat: mount golem15.sociallogin
**sm-bm-plugin** (`sm-bm-app/plugins/jz/bm`), pushed `2536bcb..85cae45` before the pointer bump:
3. Task 2: `438c0b9` fix: reserve /oauth for social sign-in
4. Task 3: `85cae45` build: frontend with Microsoft 365 sign-in
**sm-bm-app** (`/media/nvme/dev/golem15/summercms.io/summercms/sm-bm-app`), **not pushed**:
5. Task 2: `bb800a5` chore: bump golem15.user for social sign-in hooks (pointer 24b16b1 to c43ba87, realtime test expectation)
6. Task 2: `117ef8f` feat: mount golem15.sociallogin for Microsoft 365
7. Task 3: `e76f8bc` feat: Microsoft 365 sign-in in the frontend
8. Task 3: `0374c3f` chore: bump jz.bm for /oauth and the rebuilt frontend (pointer 85cae45)
**fonoteka.go** (`/media/nvme/dev/golem15/summercms.io/summercms/fonoteka.go`), **not pushed**:
9. Task 4: `f4d5728` chore: bump golem15.user to the social sign-in hooks (pointer cf5c6b9 to c43ba87; only that path staged)
**Plan metadata:** this SUMMARY commit in summercms.go, `docs(19.5-03): ...` (local).
## Pushes
- **sm-demo-app:** fetched; `origin/master..master` listed only `9eb8492`, `d5a98d9`; pushed. Now in sync.
- **sm-bm-plugin:** fetched; only `438c0b9`, `85cae45`; pushed before `0374c3f`. Now in sync; the committed pointer equals the pushed head.
- **sm-bm-app: left to the user.** `origin/master..master` also holds another session's `3a97157` (bump golem15.presentations, summer theme option) and `1d446cc` (feat: add the summer presentation theme), so it was not pushed.
- **fonoteka.go: left to the user.** 30 other unpushed commits (phase 14.2, 14.1, 12.1 and parity work) sit below `f4d5728`.
- Nothing force-pushed, nothing deployed.
## Checked-out submodule commits
| Repository | Path | Commit |
|---|---|---|
| sm-demo-app | plugins/golem15/sociallogin | adfcfbf (v0.1.0) |
| sm-demo-app | plugins/golem15/user | c43ba87 (v0.1.4) |
| sm-bm-app | plugins/golem15/sociallogin | adfcfbf (v0.1.0) |
| sm-bm-app | plugins/golem15/user | c43ba87 (v0.1.4) |
| sm-bm-app | plugins/jz/bm | 85cae45 |
| fonoteka.go | plugins/golem15/user | c43ba87 (v0.1.4) |
## Verification
- Task 1: `go build ./...`, `go vet .`, `go test -count=1 -short .` green; `TestBootAllPlugins`, `TestSocialLoginRedirect`, `TestSocialLoginCoexistsWithOAuthServer` = 3 PASS; the full root suite with Docker (`go test -count=1 .`) green; `GOWORK=off GOPRIVATE='git.golem15.com/*' go vet ./...` green; `scripts/check-pins.sh` ok (15 modules); `scripts/check-deploy.sh` ok.
- Task 2: `TestStaticReservedPaths` PASS; sm-bm-plugin `go test ./...` green; bm `go build ./...`, `go vet .`, short tests green; `TestSocialLoginMounted` PASS; `GOWORK=off go build ./...` now works (it needed `go mod tidy` before this plan); a simulation of `scripts/build.sh`'s generated go.work vets sociallogin and builds bm.
- Task 3: vitest 17 files / 211 tests green (oauth.test.ts 10 tests); `vue-tsc --noEmit` clean; `vite build` green and byte-identical on a second run; `TestEmbeddedPublicHasIndex` and `TestStaticReservedPaths` = 2 PASS; `go test -short ./plugins/jz/bm/...` green.
- Task 4: fonoteka.go `go vet ./...`, `go build ./...`, `go test -count=1 -short ./...` green. Docker-backed run of `./plugins/golem15/user/... ./plugins/golem15/fonoteka/...` (Docker available, no sudo needed): every sm-user-plugin package ok; the fonoteka plugin packages ok except `TestPhase09SecurityRoutes` (see Issues).
- Acceptance criteria: all pass except `grep -c 'tenant: common' config/golem15.sociallogin.yaml` (0, deviation 1).
## Deviations from Plan
1. **[Rule 1 - Bug] bm config keeps the tenant empty instead of `tenant: common`.** Found in Task 2: the plugin copies `MICROSOFT_TENANT` only onto an empty key (`copyLegacyEnv`), so a committed `common` would silently override the deployment's tenant and `TestSocialLoginMounted` (MICROSOFT_TENANT=organizations) would fail. Empty means common (`DefaultTenant`); the file says so. The acceptance grep for `tenant: common` prints 0. (`117ef8f`)
2. **[Rule 3 - Blocking] sm-demo-app go.mod and go.sum changed** (not in the plan's file list): `TestGoModRequiresTags` and `check-pins.sh` require a tag for every summer.yaml plugin, so the user bump requires `sm-user-plugin v0.1.4` and the mount requires `sm-sociallogin-plugin v0.1.0`, which moves `summercms` to v0.3.1 and adds goth, `golang.org/x/oauth2` and `cloud.google.com/go/compute/metadata` (goth's graph only). (`9eb8492`, `d5a98d9`)
3. **[Rule 3 - Blocking] The private module needs SSH for module-mode fetches.** `go get` over https asked for credentials. Fetched with an env-scoped rewrite (`GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=url.git@git.golem15.com:.insteadOf GIT_CONFIG_VALUE_0=https://git.golem15.com/`), no global git config change; the module is now in this machine's cache. Other machines need the rewrite or a public repository (19.5-USER-SETUP.md).
4. **[Rule 3 - Blocking] sm-bm-app go.work gained `replace summercms => ../summercms.go`.** The plugin's go.mod replaces the framework with its own sibling path, which conflicts with bm's replace in workspace mode. (`117ef8f`)
5. **[Rule 1 - Bug] bm realtime tests expected only the `presentations` namespace.** sm-user-plugin registers a `user` namespace since v0.1.2, so the bump turned `TestRealtimeDefaultDriver` and `TestRealtimeRoutes` red; the expectation is now `[presentations user]`. (`bb800a5`)
6. **[Rule 2 - Missing critical] `scripts/build.sh` lists the sociallogin submodule** so the release build's go.work uses it, vets it and refuses its uncommitted changes. (`117ef8f`)
7. **[Rule 2 - Missing critical] The bm service worker skips `/oauth`.** Its skip list mirrors the reserved prefixes; without it, `/oauth` navigations would go through the worker's offline fallback. `pwa.test.ts` covers `/oauth/microsoft` and its callback. (`e76f8bc`)
8. **[Context] The rebuilt `public/` includes the summer presentation theme** from another session's committed but unpushed sm-bm-app commit `1d446cc` (fonts and CSS from the frontend sources). It is the frontend HEAD's build output; the pushed sm-bm-plugin therefore carries that theme before sm-bm-app's source commit is pushed. (`85cae45`)
9. **[Context] Demo nginx does not route `/oauth` to Go** (`check-deploy.sh` refuses it on purpose), so the demo cannot reach social sign-in live; the env template says so and nothing in deploy/ was changed. Not needed for D-21 (mount and config).
10. **[Context] g15office comment not posted:** no g15office tool in this executor session; the orchestrator posts it on `onunbz3l` (as for plans 01 and 02).
**Total deviations:** 10 (2 bug, 3 blocking, 2 missing critical, 3 context). **Impact:** contracts as planned; the bm tenant is configured only through the environment.
## Issues Encountered
- fonoteka.go `TestPhase09SecurityRoutes` fails in the Docker-backed run: five `summercms.cabana` admin routes (account preferences, reorder, singleton) are missing from fonoteka's route inventory. All belong to cabana, none to golem15.user, so the bump does not cause it. Logged in `deferred-items.md`.
## Known Stubs
None.
## Threat Flags
None beyond the plan's threat model. Templates and config hold no credential values (`grep -cE 'CLIENT_SECRET=[^ ]+'` and `client_secret: *[^ ]+` both 0); the tests use test-only strings.
## User Setup Required
See `19.5-USER-SETUP.md`: Entra registration and four env values for bm, Google credentials for the demo (live use only after the demo's nginx routes `/oauth`), and SSH access for module downloads of the private plugin.
## Next Phase Readiness
- Plan 04 (unit tests and the PHP test map) can run; all three applications build at the new pins.
- Left to the user: push sm-bm-app (after the summer theme commits are cleared for push) and fonoteka.go; deploy bm with the Entra values.
## Self-Check: PASSED
- Files: sm-demo-app/sociallogin_test.go, sm-bm-app/sociallogin_test.go, sm-bm-app/config/golem15.sociallogin.yaml, frontend/src/lib/oauth.ts, oauth.test.ts, OAuthCallbackView.vue, OAuthRegisterView.vue exist.
- Commits: 9eb8492, d5a98d9 (sm-demo-app, on origin), 438c0b9, 85cae45 (sm-bm-plugin, on origin), bb800a5, 117ef8f, e76f8bc, 0374c3f (sm-bm-app), f4d5728 (fonoteka.go) exist.
---
*Phase: 19.5-social-login-in-sm-user-plugin*
*Completed: 2026-10-21*

View File

@@ -0,0 +1,47 @@
# Phase 19.5: User Setup Required
**Generated:** 2026-10-21
**Phase:** 19.5-social-login-in-sm-user-plugin
**Status:** Incomplete
These items are needed only for live use of social sign-in. The tests need no credentials. Claude automated everything else; these need access to external dashboards and the servers.
## Environment Variables
| Status | Variable | Source | Add to |
|--------|----------|--------|--------|
| [ ] | `MICROSOFT_CLIENT_ID` | Entra admin center, App registrations, the bm app, Overview (Application (client) ID) | `.env` of bm on rome |
| [ ] | `MICROSOFT_CLIENT_SECRET` | Entra admin center, App registrations, the bm app, Certificates & secrets (the value, not the ID) | `.env` of bm on rome |
| [ ] | `MICROSOFT_TENANT` | `common`, `organizations` or the university's directory (tenant) ID | `.env` of bm on rome |
| [ ] | `SUMMER_GOLEM15__SOCIALLOGIN__PROVIDERS__MICROSOFT__REDIRECT` | `https://bm.jakubzych.com/oauth/microsoft/callback` (template value in `sm-bm-app/deploy/env.example`) | `.env` of bm on rome |
| [ ] | `SUMMER_GOLEM15__SOCIALLOGIN__PROVIDERS__GOOGLE__CLIENT_ID` (or `GOOGLE_CLIENT_ID`) | Google Cloud console, APIs & Services, Credentials (OAuth client, web) | `/srv/summercms-demo/.env` |
| [ ] | `SUMMER_GOLEM15__SOCIALLOGIN__PROVIDERS__GOOGLE__CLIENT_SECRET` (or `GOOGLE_CLIENT_SECRET`) | same OAuth client | `/srv/summercms-demo/.env` |
## Dashboard Configuration
- [ ] **Entra redirect URI and permissions (bm)**
- Location: Entra admin center, App registrations, the bm app, Authentication and API permissions
- Set to: Web redirect URI `https://bm.jakubzych.com/oauth/microsoft/callback`; delegated Microsoft Graph permissions `openid`, `profile`, `email`, `offline_access`, `User.Read`
- Notes: note the client secret's expiry date; details in `sm-bm-app/DEPLOY.md`, "Microsoft 365 sign-in".
- [ ] **Google OAuth client (demo)**
- Location: Google Cloud console, APIs & Services, Credentials
- Set to: authorised redirect URI `https://demo.summercms.io/oauth/google/callback`
- Notes: the demo's nginx does not route `/oauth` to Go yet (`sm-demo-app/scripts/check-deploy.sh` refuses it), so the demo cannot use social sign-in live until that guard and the nginx site change.
## Access to the private module
- [ ] **Module downloads of sm-sociallogin-plugin**
- The repository is private, so `GOWORK=off` builds (sm-demo-app releases) download it over SSH only. On a machine without the module in its cache, run once: `git config --global url."git@git.golem15.com:".insteadOf "https://git.golem15.com/"`, or make the repository public like the other shared plugins.
## Verification
```bash
curl -sI https://bm.jakubzych.com/oauth/microsoft | grep -i '^location'
```
Expected results:
- The Location points at `login.microsoftonline.com/<tenant>/oauth2/v2.0/authorize`; without credentials it points at `/auth/oauth/callback?error=…`.
---
**Once all items complete:** Mark status as "Complete" at top of file.

View File

@@ -0,0 +1,7 @@
# Deferred items (Phase 19.5)
Out-of-scope discoveries logged by executors; not fixed in this phase.
## From plan 19.5-03
- **fonoteka.go `TestPhase09SecurityRoutes` (plugins/golem15/fonoteka/admin_phase09_security_test.go:52) fails in the Docker-backed run.** The admin route inventory does not list five `summercms.cabana` routes the framework working tree now mounts: `GET`/`PUT /plytadmin/api/v1/account/preferences`, `GET`/`POST /plytadmin/api/v1/{vendor}/{plugin}/{controller}/reorder` and `GET /plytadmin/api/v1/{vendor}/{plugin}/{controller}/singleton`. Every reported route belongs to cabana, none to golem15.user, so the sm-user-plugin bump does not cause it; the inventory needs the framework's newer admin routes (the pattern of fonoteka's earlier "list the post-Phase 9 cabana admin routes" commit).