Ethereum
ethereum.org ↗There is no Ethereum OAuth app registration to wait on — users sign an EIP-4361 (SIWE) message with their own wallet and you verify it server-side in roughly an afternoon. If your stack insists on OAuth-shaped tokens, a third-party bridge (oidc.login.xyz) mints a client_id and secret from one curl, still with no review.
01Pick your path
02Time & effort
- DEVPick the auth shape15–45 minutessourced[S7][S15]Decision rule: does anything downstream require a client_id/client_secret or an id_token? If yes, bridge; if no, plain SIWE.
- DEVRegister OIDC client (bridge path only)5–15 minutessourced[S15]One unauthenticated curl to /register returns client_id and client_secret. No queue, no review.
- DEVNonce, sign, verify1–3 hourssourced[S7]The siwe library + ethers/wagmi examples cover both client signing and server verification end to end.
- DEVSessions, JWT, wallet UI0.5–2 hourssourced[S7]Two documented patterns: JWT after verification, or server-side session keyed to the verified address.
- PRODSecurity review before launch1–2 hourssourced[S7]Nonce uniqueness, domain binding, expiration window, chain ID correctness, and confirming no private key ever reaches your server.
- PRODDeploy and watch RPC spend30–60 minutesinferred[S14][S5]No approval step exists; the real production risk is managed-RPC rate limiting, not a review gate.
03Steps
Get a working connection
Anything needing a client_id/client_secret or an id_token means the bridge path; otherwise plain SIWE is fewer moving parts and one less third party in the trust chain.
◆ prepared by agent · Auth-path decision ruleUse raw SIWE when: you own the backend, users have wallets, you only need identity + sessions. Use an OIDC bridge (oidc.login.xyz) when: your stack/vendor already speaks OIDC, you need an id_token + JWKS, or procurement demands a client_id/client_secret pair. Never: search for an ethereum.org app registration — none exists.
siwe is the official TypeScript implementation by Spruce; siwe-rs and siwe-go exist for other backends. ethers gives you BrowserProvider/verifyMessage. Wallet connector libraries handle the connect modal.
“siwe(opens in a new tab) - Official TypeScript implementation by Spruce” — [S7] verified verbatim
◆ prepared by agent · Installnpm i siwe ethers # or wagmi + RainbowKit / ConnectKit / AppKit for the connect modal
Generate the nonce server-side and store it in the session so the /verify call can compare it. This is the replay protection.
“Issue a nonce and store it in the session so /verify can check it later” — [S7] verified verbatim
◆ prepared by agent · Nonce endpointapp.get('/api/auth/nonce', (req, res) => { req.session.nonce = generateNonce() res.json({ nonce: req.session.nonce }) })Build the EIP-4361 message with domain, address, uri, version, chainId and nonce, then have the wallet sign it. The signature is produced inside the wallet; your app never sees the key.
◆ prepared by agent · Client-side sign-inconst message = new SiweMessage({ domain: window.location.host, address, statement: 'Sign in to My Dapp', uri: window.location.origin, version: '1', chainId: 1, nonce, }) const signature = await signer.signMessage(message.prepareMessage())Verify signature + stored nonce, then issue a JWT or set the verified address on the session. That session is what your agent-facing APIs should check.
◆ prepared by agent · Server verificationconst { success, data } = await siweMessage.verify({ signature, nonce: req.session.nonce }) if (success) { req.session.address = data.address }Response returns client_id, client_secret, registration_access_token and registration_client_uri. Endpoints: authorize https://oidc.login.xyz/authorize, token /token, userinfo /userinfo, discovery /.well-known/openid-configuration.
“The response will look something like this which includes the Client ID, secret, access token, and Client URI.” — [S15] verified verbatim
↗ oidc.login.xyz/register◆ prepared by agent · Dynamic client registrationcurl -X POST https://oidc.login.xyz/register \ -d '{"redirect_uris":["https://<your-base-url>/v1/oauth/callback"]}'Supported claims are exactly sub, aud, exp, iat, iss, preferred_username, picture. Map sub → user id and preferred_username → display name; don't invent claims that aren't in the discovery document.
“You can map sub, preferred_username, and picture to Descope attributes.” — [S15] verified verbatim
↗ oidc.login.xyz/.well-known/openid-configuration
Make it production-ready
Work through the documented security notes — nonce, domain, expiration, SIWE-when-possible, never expose private keys — plus your own session scoping for agent access.
“Always use a nonce - prevents replay attacks where an old signature is reused” — [S7] verified verbatim
◆ prepared by agent · Pre-launch checklist[ ] Nonce generated server-side, single-use, stored in session [ ] Domain + URI set from the real request host, not user input [ ] Expiration timestamp set on the SIWE message [ ] Chain ID matches the network you verify against [ ] Signature verified server-side; address lowercased before compare [ ] No private key or seed phrase ever transmitted or logged [ ] Agent sessions are scoped JWTs; fund-moving actions require a fresh user signature
04Requirements, scopes & nuances
You'll need
- ▸An HTTPS origin — the domain is embedded in the SIWE message to prevent phishing, so a stable hostname is required. sourced ❝[S7]
- ▸A backend that issues nonces, stores them in the session, and verifies the signature against the claimed address. sourced ❝[S7]
- ▸A siwe implementation — TypeScript (siwe, by Spruce), siwe-rs (Rust), or siwe-go — or manual recovery via ethers' verifyMessage. sourced ❝[S7]
- ▸An end user with an Ethereum wallet (MetaMask, Rainbow, WalletConnect, etc.); the wallet is the login and holds the private key. inferred[S6][S7]
- ▸Session infrastructure: either JWT issuance after verification or a server-side session cookie holding the verified address. sourced ❝[S7]
- ▸Bridge path only: a client_id and client_secret from https://oidc.login.xyz/register, plus your provider's callback URL. sourced ❝[S15]
Gotchas
- !"Ethereum OAuth" is a category error: ethereum.org documents SIWE (EIP-4361) as the replacement for username/password and OAuth flows. Sessions start with an off-chain wallet signature, not an authorization code. sourced ❝[S7]
- !The only OAuth-shaped endpoints in these sources belong to a third party, not Ethereum: oidc.login.xyz exposes /register, /authorize, /token, /userinfo, /jwk with scopes openid and profile. inferred[S15]
- !Client registration at oidc.login.xyz is a plain unauthenticated POST — anyone gets a client_id/secret. Treat it as plumbing, not as a trust or identity signal about the app. inferred[S15]
- !Three security requirements are non-optional: always use a nonce (replay), include the domain (cross-site signature reuse), and check expiration. SIWE libraries handle all three. inferred[S7]
- !A valid signature authenticates a person; it does not authorize spending. Every token transfer or contract write needs its own wallet-approved transaction, so an agent's "permissions" are really session scopes + per-action prompts. inferred[S7]
- !The SIWE message embeds a Chain ID, so a signature collected on mainnet (1) is not valid on a testnet or L2 — separate nonce/session handling per network. sourced ❝[S7]
- !Rate limiting is account-wide and halts traffic for the remainder of the day once the daily credit quota is exhausted; quota resets at 00:00 UTC. sourced ❝[S14]
| Scope / permission | Tier | Note | Evidence |
|---|---|---|---|
| openid | basic | Minimum scope for the oidc.login.xyz bridge to issue an ID token; nothing broader is offered. | sourced ❝[S15] |
| profile | basic | Second of only two scopes the bridge supports; unlocks claims sub, preferred_username, picture. | sourced ❝[S15] |
| SIWE Resources (scoped URIs) | basic | The SIWE-native substitute for scopes: optional URIs in the signed message define what access the signature is for. You define them; there is no central scope registry to apply to. | sourced ❝[S7] |
| eth_accounts / wallet permissions | gated | Address disclosure is a wallet-gated permission (wallet_requestPermissions), approved by the user in the wallet, not granted by a token endpoint. | inferred[S10][S3] |
| Transaction signing (transfers, contract writes) | restricted | No OAuth scope exists for this. Every write is a separate wallet signing event the user must approve; an agent cannot pre-authorize it via an access token. | inferred[S7] |
05If you get blocked
→ There is nothing to register with. Implement wallet sign-in: nonce → wallet signs a SIWE (EIP-4361) message → your backend verifies the signature against the claimed address.
→ Ship an embedded-wallet path (Web3Auth SDK sign-up is described as free) so non-wallet users still get an address + signature.
→ Issue a short-lived JWT/session after signature verification and scope it to specific API actions; always route fund-moving actions back to a user-signed transaction.
→ Configure a custom OAuth provider pointing at https://oidc.login.xyz/authorize, /token, /userinfo, with scopes openid + profile.
→ Cache older blocks, avoid polling in a tight loop, use eth_subscribe for new blocks, or raise the plan; alerts exist at 75/85/100% of daily credits.
06Evidence: what was tested, sourced, and still uncertain
Sources read
- [S1] Ethereum development documentation | ethereum.org official
- [S2] JSON-RPC API | ethereum.org official
- [S3] EIP-1193: Ethereum Provider JavaScript API official
- [S4] WalletConnect Documentation - WalletConnect Pay Docs official
- [S5] Infura documentation official
- [S6] Ethereum wallets official
- [S7] Authentication on Ethereum official
- [S8] Get allowlisted | MetaMask developer documentation official
- [S9] Create an Ethereum Web3 wallet in Android | MetaMask developer documentation official
- [S10] MetaMask Connect documentation official
- [S11] Wallet Verification Dashboard and API Keys official
- [S12] Understand reviews | Linea official
- [S13] List your app on marketplace - Chainstack official
- [S14] Avoid rate limiting official
- [S15] Ethereum Wallet OAuth Provider | Descope Documentation official
- [composio] Composio — Managed OAuth apps directory official
Still uncertain
- ? No probes were run at all, so nothing here is live-tested — not the guessed ethereum.org/authorizeEndpointGuess URL, not oidc.login.xyz.
- ? Every time estimate is inferred; no source states how long SIWE or a bridge integration takes.
- ? oidc.login.xyz appears only via a third-party vendor page (Descope) and a spec-adjacent source; its operator, availability, SLA and long-term support were not verified here.
- ? The sources do not state whether any entity (Ethereum Foundation or otherwise) runs a public OAuth/OIDC IdP for Ethereum — the documentation consistently frames wallet signatures as the auth mechanism instead.
- ? Wallet-by-wallet support for SIWE 'Resources' scoping is not documented in these sources; assume uneven support until you test.
- ? No source gives concrete numbers for what an AI agent may do on a user's behalf after SIWE — that boundary is a product/legal decision, not a protocol one.
- ? Generated automatically; no app was registered and no review was submitted.