Repository navigation
Conversation
With auth.guard_identity on (LIBREDESK_AUTH__GUARD_IDENTITY), the API and page middlewares verify guard's x-bastion-identity JWT with platform-kit edgeauth (PLATFORM_KIT_EDGEAUTH_AUDIENCES / _JWKS_URL) and sign the agent in by lower-cased email, as the OIDC callback does: no auto-provisioning (unknown email gets the no-account login error), disabled and non-agent users refused, the identity must hold the libredesk project's admin or agent role, and the agent's Libredesk role stays its permissions. No Libredesk session is minted; CSRF stays checked. API keys are still checked first, and without the header the session path is unchanged. An invalid token is 401, unreachable keys 503. The login is recorded (last login, activity log) once per guard session. The login page signs in through /.well-known/bastion/login and logout ends guard's session at /.well-known/bastion/logout when the server reports auth.guard_identity. Co-Authored-By: Claude Opus 5.5 <[email protected]> Claude-Session: https://claude.ai/code/session_01MwB9uUtkK8BFT7ZGoXwX9u
|
Thanks for opening your first pull request in Libredesk. Please give CONTRIBUTING.md a read if you haven't already, it covers the setup, code style and the checks this PR needs to pass. A maintainer will review this soon. |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What and why
Linear COR-1084 (project "Central auth in guard"). With this change, the fork can trust guard's signed identity instead of running its own SSO. Guard runs the Zitadel login for support.metsa.app (client
libredesk, roles admin|agent) and passes every signed-in request upstream as a 60 s ES256 JWT inx-bastion-identity. Libredesk checks that JWT with platform-kitedgeauth(Verifier.Verifyon the header value, because Libredesk runs on fasthttp and the net/http middleware doesn't apply). It then signs the agent in by email, the same way the OIDC callback does.The feature is off by default:
[auth] guard_identity = true/LIBREDESK_AUTH__GUARD_IDENTITY=trueturns it on, together withPLATFORM_KIT_EDGEAUTH_AUDIENCES(required) andPLATFORM_KIT_EDGEAUTH_JWKS_URL(optional, defaults to guard's in-cluster JWKS).Behaviour
Order in
authenticateUser(auth,tryAuth,perm,authOrSignedURL) and invalidatePageSession(authPage,notAuthPage):csrf_tokenfromserveIndexPagewithout any session login, so nothing changes for the frontend, and we don't have to rely on SameSite reasoning.adminoragent, the libredesk project's roles. This repeats guard's own check so the app also fails closed. Missing role → 403 /oidc_access_denied.GetAgent(0, email); after the first request the agent cache by id is used, re-checked against the email.oidc_no_account. Accounts are still created by hand and the agent's Libredesk role is still what grants permissions; a Zitadel grant only lets someone through the door.oidc_account_disabled/oidc_no_account).agent_loginactivity entry happen once per guard session (sid), tracked in a small bounded in-process set (168 h, at most 10k entries). A restart records each session once more.Pages: a refused identity sends the user to
/?error=<code>&next=.... The login page shows that error instead of redirecting to itself, and a signed-in agent on/goes to the inbox./api/v1/confignow returnsauth.guard_identity. When it is true, the login page shows a single "Sign in" button that goes to/.well-known/bastion/login?return_to=<next>. After a refused sign-in it also shows a "Logout" button to/.well-known/bastion/logout, so the user can switch account. Logout still calls/api/v1/auth/logout(which records the activity) and then goes to guard's logout. No new i18n keys.Removed / kept
The upstream OIDC client code (handlers, provider admin,
oidc.client_*config) is kept. Removing it would make the fork patch large and painful to rebase onto each upstream tag. After cutover the DB provider row gets disabled instead (deploy notes). The patch is one new file (cmd/guard_identity.go), about 30 marked (// metsa) lines incmd/middlewares.go, one line each inmain.go/config.go, a sample-config key, and a small frontend change.go.mod: adds platform-kit; MVS lifts nats-server 2.12.8→2.15.1, nats.go 1.53.1→1.54.0, go-jose 4.1.4→4.1.7 and a few indirect modules.
ENG-1067 (NATS KV): what could shrink
Identity-authenticated requests never create a Libredesk session, so
LIBREDESK_SESSIONSwould only hold break-glass password sessions and OIDC state, which goes away once the provider row is disabled. The bucket could be made smaller, but the fork still binds it at boot and password login uses it, so I did not remove it. The other buckets (widget sessions, help-center cache, rate limits, OTPs, OAuth state for inbox mail) have nothing to do with agent login and stay.Upstream
A proposal issue for a trusted-proxy identity mode (CONTRIBUTING asks for "propose first") is drafted for the maintainer to post. I did not post it. If upstream accepts it, this patch can shrink to configuration.
Tests
go test -count=1 ./...passes against a local Postgres 16 (testutil, port 5433). Newcmd/guard_identity_test.gousesedgeauthtestand covers: a known agent (email case-insensitive, no session cookie,auth_method=guard_identity), the login recorded once per sid, CSRF still enforced, unknown email, empty email, disabled agent, missing role, a garbage token, another host's audience and another issuer's key (401), keys unavailable (503), the page redirect plus the error page being served without a loop,/→ inbox, and an API key still authenticating ahead of any identity.pnpm test:run: 66 files, 1100 tests pass. Newutils/guard-auth.test.jsand a guard case inuseLogout.test.js.pnpm build:mainsucceeds.Pins platform-kit at the COR-1076 branch commit; bump to v0.3.0 once MetsaApp/platform-kit#6 is released, before merging.
Depends on: guard COR-1074 (login, sessions), the deploy-repo guard config for this host.
Cutover
https://support.metsa.app/.well-known/bastion/callback, movesecretReftoguard-production/libredesk-oidc, dropLIBREDESK_OIDC__CLIENT_ID/_SECRET, setLIBREDESK_AUTH__GUARD_IDENTITY=trueandPLATFORM_KIT_EDGEAUTH_AUDIENCES=support.metsa.app, and add the guard client and rules. The public widget, help-center, CSAT and webhook paths areauth: none; media and assets areoptional; everything else is required admin|agent./api/v1/oidc/1/finishredirect URI and theauth.metsa.appegress.Generated by Claude Code