Files
zeekayandHanzo Dev 8fcaeea752 feat(auth): sign in with Hanzo IAM, beside the wallet
Login was pointed at the upstream identity provider through the Client
Gateway: the browser redirected to CGW /v1/auth/oidc/authorize, CGW held the
client and set its own cookie. Our gateway serves chains and Safes and no auth
surface at all, so that redirect had nowhere to land — and the buttons never
rendered anyway, because they hung off a per-chain OIDC_AUTH flag our chain
config does not carry. Identity is a property of the domain, not of the chain
being viewed.

The app is now the OIDC client. @hanzo/iam does the Authorization Code +
PKCE-S256 flow against the IAM the brand resolves to — lux.id, zoolabs.id,
pars.id, iam.hanzo.ai — with issuer and client id (<org>-<app>) sitting in
@safe-global/brand beside every other brand token. The client is public, so no
secret reaches the browser and none is needed. /auth/callback is the one
registered redirect URI; it spends the code and returns the user to the page
they signed in from, refusing any destination that is not ours.

One button, not one per method: IAM's login page is what offers password,
one-time code, passkey, wallet or social, per what the application enables.
A button per upstream connection could only lie about what the org configured.

Its expiry is the app's session expiry, so a wallet session and an IAM session
are one piece of state rather than two. Sign-out ends every session the browser
holds, IAM's tokens revoked at the issuer before the page goes away — they
would otherwise outlive it and sign the next visitor back in.

The wallet path is untouched and stays the only signer: identity never
approaches a Safe transaction. Injected wallets were already registered;
wallets.test.ts pins that they survive the disabled-wallets filter, so
MetaMask and every other EIP-6963 wallet stays discoverable.

Co-authored-by: Hanzo Dev <[email protected]>
2026-08-13 13:32:21 -07:00
..
2026-08-13 12:21:21 -07:00