⭐ Quickstart for agents (RP integration runbook)
Where this page sits When an AI agent receives a request like "add logi (1pass) login to this project," fetch this page first. The 60+ other pages on docs.1pass.dev are reference material that branches from this page. This page covers only "what to do, in what order, on which track, and what never to do." Details and code examples are linked out — not a single line of code is inlined here.
Before starting, skim the changelog once (avoids deprecated patterns — see F8).
Step 0 — Auto-detect the track and confirm with the user
Before any real work, lock in exactly one track. Without a confirmed track, the agent will install the wrong SDK.
| File / signature | Track |
|---|---|
Info.plist, *.xcodeproj, Package.swift | 📱 mobile (iOS Swift) |
build.gradle*, AndroidManifest.xml | 📱 mobile (Android Kotlin) |
pubspec.yaml with a flutter: key | 📱 mobile (Flutter) |
package.json with a react-native dependency | 📱 mobile (React Native) |
Gemfile with rails | 🌐 web (Rails) |
package.json with a next dependency | 🌐 web (Next.js) |
package.json with an express dependency | 🌐 web (Express) |
| None of the above — CLI, daemon, or webhook receiver only | 🔧 api |
If multiple signatures match, ask the user — never guess. Have the user confirm the detected track before any work begins.
Track-specific guides: 📱 mobile · 🌐 web · 🔧 api.
Responsibility labels
Each step header carries one of these labels:
- [agent] — work the agent can complete in code on its own
- [human] — work the user must do in a console or secret store. The agent stops and says "your turn — next action: ..."
- [agent + human] — the agent prepares it; the user clicks or approves once
The 8-step execution order
Step 1 · Pick the client type — [agent]
⚠️ Decide before registering. logi locks client type as immutable at registration time (public-vs-confidential is final after registration). A wrong choice means creating a brand new RP.
Decide based on track and whether secrets can be stored:
| Track | Client type | Why |
|---|---|---|
| 📱 mobile | Public + PKCE | client_secret cannot be embedded in an app binary (RFC 8252) |
| 🌐 web (SSR with a backend) | Confidential + PKCE | The server can hold the secret |
| 🌐 web (SPA only, no backend) | Public + PKCE | A browser bundle cannot hide a secret |
| 🔧 api (server-to-server) | Confidential + device flow / PAK | logi does NOT support the client_credentials grant (errors.md) |
Detailed decision rules: oauth/public-vs-confidential, oauth/public-clients, oauth/recommended-architecture.
Step 1.5 · Pick the scopes — [agent]
These define what user data the RP receives from logi. Scopes are pinned at registration via allowed_scopes. Read the drift behavior before deciding.
- Default starting point:
openid profile email(enough for most RPs) - Add as needed:
phone, or a custom namespaced scope - Drift behavior: under the default policy (
block), requesting any scope not inallowed_scopesis rejected withinvalid_scope. Only apps explicitly relaxed tolog_only/alertby an admin get the old silent-drop behavior — keep your authorize request scopes identical to the registered list.
Verification: the scopes you chose match allowed_scopes at registration, and the authorize request in code uses the same list.
Step 2 · Register the RP app — [human] (or [agent + human] when using the CLI)
Using the type and scopes from Step 1 and 1.5, register the RP in the console or via the CLI and obtain a client_id (plus a client_secret for confidential clients). The agent must not store the secret in its own memory — ask the user where to keep it: 1Password, Keychain, or .env.
- Console sign-up and app creation: guide/registering-apps
- CLI automation: cli/apps, cli/login
Verification: client_id starts with logi_. For confidential clients, client_secret starts with logi_secret_.
Step 3 · Drop in the track-specific code — [agent]
This is where the agent writes the most code. Apply only the one page for your track. Merging from multiple track pages at once will pull in deprecated patterns.
| Track / stack | Page |
|---|---|
| iOS Swift | integrations/swift |
| Android Kotlin | integrations/android (with the kotlin addendum) |
| Flutter | integrations/flutter |
| React Native | integrations/react-native |
| Rails 8 | integrations/rails |
| Next.js (App Router) | integrations/nextjs |
| Express.js | integrations/express |
| Login button UI | integrations/buttons |
Verification: the build passes — npm run build, bundle exec rails routes, Xcode build, or equivalent.
Step 4 · Verify the PKCE and state implementation — [agent]
logi only accepts S256 and rejects plain. An SDK used as-is is usually fine, but a hand-rolled implementation must be checked.
- Details: oauth/pkce
- Standard flow: oauth/flow
- Security requirements: guide/security
Verification checkpoints:
code_verifieris URL-safe base64, length in [43, 128] (RFC 7636)code_challenge_method=S256is included in the/oauth/authorizerequestcode_verifieris stored in the session or Keychain and reused for the/oauth/tokencallstateis randomly generated, stored in the session, and verified at the callback (mitigates F3)id_tokenverification checks bothiss="logi"andaud=LOGI_CLIENT_ID(mitigates F4)
Step 5 · Register the redirect_uri values — [agent + human]
🚨 The #1 OAuth error (per OneUptime stats). Cutting corners here guarantees a production failure.
The agent must list every redirect_uri the RP will use and tell the user to register them all in the console:
- Local dev — for example
http://localhost:3000/auth/logi/callback - Staging — for example
https://staging.example.com/auth/logi/callback - Production — for example
https://example.com/auth/logi/callback - Mobile (custom scheme) — for example
com.example.myapp://oauth/1pass/callback
The rule: exact match on everything — trailing slash, case, query, port. Wildcards are rejected.
- Registration verification guide: oauth/redirect-uri-verification
- Troubleshooting: guide/troubleshooting, oauth/troubleshooting
Verification: the URI list registered in the console matches the LOGI_REDIRECT_URI environment variable character-for-character.
Step 6 · Webhook receiver and signature verification — [agent]
🚨 The step agents skip most often. Leaving "verify signature" as a
// TODOexposes the RP to forged webhooks. If aTODOcomment remains, this step is not done.
- Event catalog and receiver skeleton: guide/webhooks
- HMAC signature verification (both formats must be accepted): guide/webhook-verification
- Tested templates (Rails / Express / Next.js): templates/webhook
- Polling fallback: oauth/polling-events-api
- Event consistency: oauth/event-delivery
Verification: the PR must include a test case that returns 401 for requests with an invalid X-Logi-Signature.
Step 6.5 · Handle refresh token rotation — [agent]
logi issues a new refresh token on every response and revokes the entire token family on reuse detection. An RP that ignores rotation will log every user out on a single network failure.
- Standard flow (reuse detection →
400 invalid_grantand family revoke): oauth/flow - Public client rotation behavior: oauth/public-clients
- Storage guide (security.md §4 Refresh Token Storage): guide/security
- Apple and Google upstream refresh policy (separate topic): oauth/refresh-token-rotation
Verification:
- Replace and persist the new
refresh_tokenfrom every/oauth/tokenresponse immediately - Storage: encrypted DB on the server, Keychain (iOS) or Keystore (Android) on mobile, and HttpOnly Secure SameSite=Strict cookies on the web
- A
400 invalid_grantresponse means family revoke or expiry — force re-authentication. Never silently retry forever.
Step 7 · First-time signup form — [agent]
logi returns roughly sub, email, and optionally nickname. If the RP needs more fields (role, organization, terms acceptance, and so on), implement a form that runs only on the first login. Showing it every time breaks the UX.
- Recommended pattern: integrations/first-time-signup
- User identification rules: oauth/sub-policy
- canonical_sub migration: oauth/rp-migration-guide
Verification: logging out and logging back in with the same logi account does not re-show the form.
Step 8 · End-to-end test plus error and rate-limit handling — [agent + human]
Run the full flow in a real browser or device, and handle the error codes and rate limits the RP will hit in production at the code level:
- Scenario checklist: guide/testing
- Pre-incident health check: oauth/rp-health
- Compare against the demo: guide/demo-page-walkthrough
- Error code catalog (with logging format): oauth/errors
- Rate limit policy and backoff guidance: guide/rate-limits
- Response header signals (
X-Logi-*): oauth/response-headers
Verification (full loop):
/auth/logi/startredirects to/oauth/authorize- The user approves via logi app push or QR scan
- The RP callback fires,
/oauth/tokenexchange succeeds, andid_tokenverification passes (issandaud) /oauth/userinforeturns the user object- A
user.updatedevent arrives at the webhook receiver and signature verification passes - On a
429response, the client backs off according toRetry-After(sleep and retry)
Track branching table
How each step differs across tracks, at a glance:
| Step | 📱 mobile | 🌐 web | 🔧 api |
|---|---|---|---|
| 1 client type | Public + PKCE, always | Confidential recommended (Public for SPA-only) | Confidential + device / PAK |
| 1.5 scopes | openid profile email default | openid profile email default | Minimal (profile is usually enough) |
| 2 RP registration | ✅ Public | ✅ Confidential | ✅ Confidential |
| 3 code drop-in | swift / kotlin / flutter / RN | rails / nextjs / express | api track page |
| 4 PKCE + state | Required | Required (both SPA and SSR) | Device flow does not need PKCE |
| 5 redirect_uri | Custom scheme recommended | https URL | N/A (device flow) |
| 6 webhook | ✅ | ✅ | ✅ (most common consumer) |
| 6.5 refresh rotation | ✅ Keychain / Keystore | ✅ HttpOnly cookie | ✅ Encrypted DB |
| 7 first-time signup | ✅ | ✅ | Usually not needed (no UI) |
| 8 e2e | Real device plus KakaoTalk / Naver in-app escape | localhost plus staging | curl alone is enough |
🚨 Known pitfalls (F1-F8) — ordered by frequency
Ignoring this catalog leads directly to production incidents.
F1. redirect_uri mismatch (the #1 OAuth incident)
- Symptoms:
redirect_uri_mismatch,invalid_grant, or the callback page never loads - Causes: trailing slash differences, scheme (
httpvshttps), port, case, query string - Mitigation: oauth/redirect-uri-verification, guide/troubleshooting
F2. Skipping webhook signature verification (// TODO: verify)
- Symptoms: forged webhooks pollute the RP database — typically caught only during a post-incident audit
- Causes: the agent ships the receiver but defers verification
- Mitigation: guide/webhook-verification — both signature formats must be handled. Use a tested template: templates/webhook
F3. Missing or unverified state parameter
- Symptoms: CSRF lets an attacker bind their own logi account to the victim's RP session
- Causes: no
stateon the authorize request, or no comparison against the stored value at the callback - Mitigation: guide/security#state
F4. id_token verification skips iss or aud
- Symptoms: confused-deputy bug — tokens from another OIDC IdP are accepted as logi tokens
- Causes: only the JWT signature is verified; claims are not
- Mitigation: oauth/jwks, guide/security
F5. Refresh token reuse and unhandled family revoke
- Symptoms: a single network failure logs every user out; the client retries
400 invalid_grantforever - Causes: the new refresh token is not persisted after rotation, or family-revoke
400 invalid_grantresponses are retried as generic errors - Mitigation: oauth/flow (reuse detection → family revoke + 400 spec), oauth/public-clients
F6. iOS universal-link host collision
- Symptoms: ainote incident on 2026-05-15 — an unrelated deep link in flight was injected into the OAuth parser, producing a bogus
missingCode - Causes: the RP app claims a universal link on the same host as the IdP
- Mitigation: use a custom scheme callback (
com.example.myapp://oauth/1pass/callback) — integrations/swift, integrations/react-native, guide/universal-links
F7. KakaoTalk and Naver in-app browsers
- Symptoms: OAuth is blocked inside the in-app browser (passkey and SSO do not work)
- Mitigation: use an escape route — guide/demo-page-walkthrough
F8. client_secret in a public client
- Symptoms: the secret is embedded in a mobile app or SPA binary and extracted via reverse engineering
- Mitigation: Public + PKCE only — oauth/public-clients
Additional pitfalls (iOS scheme / Android App Link host sharing) are covered in the pitfalls sections of integrations/swift and integrations/android.
✅ Self-verify checklist before reporting completion
Eight items the agent must confirm itself before telling the user the work is done:
- [ ] Step 1 and 1.5 — the rationale for client type and scope choice is recorded in the PR description and matches the registration result
- [ ] Step 2 —
client_idstarts withlogi_; console or CLI registration is complete; the secret lives in 1Password, Keychain, or.env(never in code or git) - [ ] Step 3 — only one track page was applied; the build passes
- [ ] Step 4 —
code_challenge_method=S256,stateverification, andid_tokenissandaudchecks all pass - [ ] Step 5 — every redirect_uri (local, staging, prod, mobile scheme) is registered in the console and matches the environment variable character-for-character
- [ ] Step 6 — zero
TODOcomments in webhook signature verification code; a 401 test case is included - [ ] Step 6.5 — refresh token rotation works: new refresh token is persisted immediately, and
400 invalid_granttriggers re-authentication (family revoke or expiry) - [ ] Step 7 and 8 — the first-time signup form does not re-appear on re-login; the 6-step e2e flow passes (authorize → approve → callback → token → userinfo → webhook plus 429 backoff)
If even one item is unchecked, report "not done — please continue with the next step" to the user. Do not file a false "complete" report.
❌ Anti-patterns — do not do these
Avoid these before any production push. If the agent's PR contains any of them, self-block the PR.
- ❌ Using the Implicit Flow — logi rejects it. Only Authorization Code + PKCE is supported
- ❌ Using the Password Grant — logi rejects it
- ❌ Exposing
client_secretin a client (mobile binary, SPA bundle, or public git repo) - ❌ Wildcards or partial matches for redirect_uri — only exact matches pass
- ❌ Skipping webhook signature verification or leaving
// TODO: verify - ❌ Using PKCE
plain— onlyS256is accepted - ❌ Omitting or failing to verify the
stateparameter — CSRF exposure (F3) - ❌ Skipping
issoraudinid_tokenverification — token forgery exposure (F4) - ❌ Infinite retry on refresh token failure — ignoring family revoke force-logs-out every user
- ❌ Merging from multiple track pages at once — pulls in deprecated patterns
Maintenance note (avoiding stale skills)
The skills and templates shipped alongside this page go stale over time. At the start of any work, confirm two things:
- Recent entries in reference/changelog — new grant types, deprecated scopes, breaking changes
- guide/ai-assistants — the current skill catalog for Claude Code, Cursor, and Codex
A skill cached for more than three months can ship a deprecated pattern the agent has never seen. Refresh it.
What's next
- Registration → guide/registering-apps
- 5-minute quickstart (for humans) → guide/quickstart
- Pick a track → tracks/index
- AI assistant skill catalog → guide/ai-assistants
- Changelog (fetch before starting work) → reference/changelog