Skip to content

Changelog

Source: en/reference/changelog.md · Live: https://docs.1pass.dev/en/reference/changelog LLM-sanitized: internal links absolutized, VitePress containers → admonitions, line numbers in the Jump-to Index reference this rendered file (1-indexed).

📍 Jump-to Index

  • L33-L41: ## 2026-08-12 — Native app-to-app handoff moves to open.1pass.dev (Swift SDK v1.3.0)
  • L42-L50: ## 2026-07-08 — SDK Swift v1.1.0 — device PAK exchange, refresh verification, revoke/disconnect primitives
  • L51-L59: ## 2026-07-02 — SDK v1.0.1 security hardening (at_hash + JWKS kty)
  • L60-L71: ## 2026-06-24 — Passkey Phase 1–3
  • L72-L98: ## 2026-05-28 — RP registration hurdles (Phase 2–5e) + Agent Quickstart
    • L76-L81: ### Docs / agent surface
    • L82-L91: ### RP registration hurdles (server-side, autoDeploy=off, manual deploy)
    • L92-L94: ### Migration
    • L95-L98: ### Tests
  • L99-L131: ## 2026-05-15
    • L110-L120: ### 2026-05-11 — Added target_session_token PoP
    • L121-L131: ### 2026-05-11 — canonical resolution applied to the Easy Bracket RP
  • L132-L161: ## 2026-04-27 — Security review wave (Phase 1-14)
    • L137-L140: ### Critical
    • L141-L148: ### High
    • L149-L154: ### Medium
    • L155-L161: ### Summary of SDK user impact
  • L162-L178: ## v0.1-alpha (2026-04-22 sync)
  • L179-L185: ## Known limitations
  • L186-L192: ## Roadmap (β)

Changelog

2026-08-12 — Native app-to-app handoff moves to open.1pass.dev (Swift SDK v1.3.0)

The /oauth/authorize Universal Link claim on api.1pass.dev exists for native app-to-app SSO, but it had the side effect of intercepting web RPs' browser OAuth flows on the same URL — a login started in Chrome would get grabbed by the logi app and the callback would land in a different browser, failing with state mismatch. We split the URL space onto a dedicated bouncer host to fix this structurally.

  • Swift SDK v1.3.0 — the first-try app-to-app open now targets open.1pass.dev/oauth/authorize, while the ASWebAuthenticationSession fallback keeps loading api.1pass.dev. Additive — existing integrations need nothing beyond a version bump. issuer, token exchange, and JWKS stay bound to api (moving them would break token exchange, so the SDK splits only the authorize handoff leg). Staging/self-hosted issuers are never derived onto the bouncer — the SDK keeps the issuer host as-is, with nativeAuthorizeHost available for explicit override. The BFF primitive authorize(startURL:) (backend-assembled URLs) also ships in this tag; the host split does not apply to it (the backend stays authoritative).
  • open.1pass.dev/oauth/authorize landing page — if the Universal Link does not fire, a static landing page is shown (deliberately no auto-redirect — a redirect re-triggers the Universal Link in Safari) and the user taps through to the consent screen on api. The query string is forwarded byte-for-byte — this also fixes the existing /auth bouncer defect that dropped non-whitelisted parameters (login_hint, ui_locales, …) and re-encoded +.
  • AASA cleanup — removed dead claims that opened the app with nothing to show (/oauth/callback*, /auth/callback*, /activate/*, /qr/pair/*, and more). No live deep link is affected.
  • Transition — both hosts currently keep their claims, so nothing breaks for existing integrations. Removing the api claim (fully freeing the web flow) will be announced separately once older app builds have cycled through store updates. Manual-integration migration guidance: iOS (Swift) and Universal Links.

2026-07-08 — SDK Swift v1.1.0 — device PAK exchange, refresh verification, revoke/disconnect primitives

The Swift SDK now absorbs three backend auth calls that RPs used to hand-roll. All changes are additive (existing symbol signatures unchanged) — existing v1.0.x integrations keep working with no code change, and the new surface is opt-in.

  • LogiAuth.verify(_:) — verifies the id_token from LogiAuthStorage.refresh()'s LogiAuthResult using the same JWKS cache and RS256 verification as signIn(), promoting it to a verified LogiSession. Trusting a refreshed token's sub/email without verification reopens, on the refresh path, the signature gap that signIn closes — always route cold-launch session restore through verify. (A refresh response carries no nonce, so nonce is not checked; at_hash is present-only.)
  • LogiDeviceKey (LogiAuthStorage module, actor) — exchanges the OAuth JWT for a device-bound PAK (logi_pak_...) via POST /api/v1/me/device_keys/exchange. The /api/v1/* endpoints require a PAK, not the JWT, so RPs that need this exchange now use the SDK instead of a hand-rolled copy. It persists device_uuid/device_secret idempotently to the Keychain and coalesces concurrent calls. When migrating an existing install, inject the legacy keychainService to preserve the stored credential (otherwise the server mints a fresh orphan DeviceCredential, with the 10/h exchange rate-limit and 409 device_claimed_by_other_user risk).
  • LogiAuthStorage.revokeRefreshToken() / disconnectApp(pak:) — backchannel primitives. revokeRefreshToken() invalidates the stored refresh_token server-side (RFC 7009; a public client sends client_id only; best-effort). disconnectApp(pak:) disconnects the RP via DELETE /api/v1/me/connected_apps/{client_id} (Bearer=PAK, profile:write) and returns true only on 2xx or 404, false on 401/403/5xx/timeout — so the caller can preserve local credentials and retry (preserving account deletion's "server success → local wipe" invariant). signOut() remains a local-only wipe; its contract is unchanged.
  • Integration guide: iOS (Swift). SPM from: "1.1.0", tag v1.1.0. All existing v1.0.1 golden vectors pass (non-breaking).

2026-07-02 — SDK v1.0.1 security hardening (at_hash + JWKS kty)

Non-breaking security hardening applied across all 7 SDKs (swift · android · browser · node · flutter · ruby · python).

  • at_hash binding — the id_token's at_hash claim (base64url_nopad(SHA256(access_token UTF-8 bytes)[first 16 bytes]), OIDC §3.1.3.6) is verified against the access_token (present-only). Code-exchange SDKs wire the access_token internally to confirm the access_token↔id_token binding before returning the session; standalone verification can pass it via an accessToken argument. Rejected with at_hash_mismatch.
  • JWKS kty filter — signing-key selection now filters kty=="RSA" + use∈{null,sig} + alg∈{null,RS256} before the kid match, so an EC (or other) key mixed into the JWKS still resolves the correct RSA signing key — robust to future key-type expansion. See the JWKS reference.
  • Swift — fail-closed CSPRNG (SecRandomCopyBytes) + duplicate OAuth callback parameter guard. Android — re-verify the rotated id_token before persisting on refresh.
  • Published: npm @logi-auth/browser@1.0.1 · @logi-auth/server@1.0.1, gem logi_auth 1.0.1, pub.dev logi_auth 1.0.1, SPM · JitPack v1.0.1 tags. All existing v1.0.0 golden vectors still pass (non-breaking).

2026-06-24 — Passkey Phase 1–3

Server behavior for passkey login and approval flows that external RPs need to understand.

  • WebAuthn L3 metadata storage — stores backup_eligible, backup_state, backup_state_changed_at, and aaguid per passkey. RPs can inspect sync eligibility and AAGUID through passkeys:read responses.
  • sign_count anomaly WORM audit — when the authenticator counter goes backward, authentication is not failed solely for that signal, but an append-only audit event is written to AuthenticationAuditLog.
  • Web Conditional UI login — adds a passkey login path based on /api/v1/passkeys/authentication_options and /api/v1/passkeys/authenticate. When the user selects a passkey in the browser Conditional UI, logi creates the web session.
  • App-approval passkey step-up — users with approval_mode=passkey approve push approval requests with a UV-bearing passkey assertion. The challenge is bound to the PushApprovalRequest to prevent reuse.
  • Unauthenticated passkey rate limits — documents the unauthenticated limits that RPs adding their own passkey login UI need to know. POST /api/v1/passkeys/authentication_options is capped at 20/min/IP, and POST /api/v1/passkeys/authenticate is capped at 15/min/IP + 30/min/credential. See Rate Limits for the exact values.
  • Last-passkey deletion lockout prevention — blocks deleting the last active passkey while passkey-only approval mode is enabled, so the user does not lose their approval path.
  • Authenticate hardening / throttle parity — prevents malformed credential JSON from surfacing as a 500, and aligns the per-credential throttle with JSON body parse/rewind handling.

2026-05-28 — RP registration hurdles (Phase 2–5e) + Agent Quickstart

Prevents AI agents from mass-producing RPs (Relying Parties) through automation, plus a docs cleanup around a single entry point.

Docs / agent surface

  • New Agent Quickstart/guide/agent-quickstart (Korean, 308 lines) + .en (English mirror). An 8-step execution order + automatic track detection + the F1–F8 pitfalls + a self-verify checklist. The ## ⭐ Agents start here section at the top of llms.txt exposes this page as the single entry point.
  • Webhook signature templatestemplates/webhook (rails.rb / express.ts / nextjs-route.ts). Dual signature format + 5-minute replay window + timing-safe compare are baked in. Blocks the // TODO: verify signature anti-pattern.
  • "📋 Copy for LLM" button — top-right of every docs page. Copies the current page's .md mirror to the clipboard + a 2-second toast. Localized, dark-mode-aware, and mobile-friendly.
  • @1pass/mcp v0.2.0 — adds 3 read-only docs tools (fetch_quickstart / get_doc / list_docs). No auth required. Fetch logi docs in natural language from Claude Desktop, Cursor, and Codex.

RP registration hurdles (server-side, autoDeploy=off, manual deploy)

  • identity_verified_level gate — an unverified developer is blocked from registering an RP. The JSON response includes verify_url. The console page shows a permanent banner + an "✉️ Send verification email" CTA (Phase 5c).
  • created_via channel recordingweb | cli | mcp | api | rake. Self-identified via the X-Logi-Client-Type header. Permanently recorded in the audit log.
  • Automated risk assessment (creation_risk_flags jsonb)wildcard_redirect_uri / sensitive_scope / unlisted_scope / high_volume_developer (≥5 in 7 days) / new_developer_account (within 7 days of signup) / suspicious_app_name (placeholder regex). These flags are evaluated at registration time and permanently recorded in the database.
  • Race-safe high-volume countCurrent.user.with_lock { evaluate + save } serializes concurrent registrations. Even with 5 firing at once, exactly 1 passes.
  • Rack::Attack throttle — POST /developer/applications at 10/h + 30/d per developer session. POST /api/v1/applications has the same cap per PAK token (blocking CLI/MCP/automated mass production). Applied alongside the global api/ip 60/min for defense in depth.
  • High-risk approval ack gate — when wildcard_redirect_uri or sensitive_scope appears alone, or 2+ flags accumulate, an admin can't approve without an explicit risk_acknowledged: true. The web console replaces the button with "⚠ Approve despite known risk," and a Turbo confirm dialog shows the flag list. The iOS admin API (/api/v1/admin/applications/:id/approve) returns HTTP 409 risk_acknowledgement_required when it's missing. ApplicationSubmission#approve! + auto_approve_during_beta share the same gate.
  • AdminAlertMailer high-risk alert — when creation_risk_flags is non-empty, the mailer runs deliver_later. A 30-minute per-developer cooldown (Rails.cache) + a rescue on enqueue failure. Recipients are configured via the RP_HIGH_RISK_ALERT_TO env (operator-only).
  • Stronger admin queue UI — each app row shows a risk badge + the created_via label. A "⚠ Has risk signals" quick filter. At the top, a risk-statistics card for the last 7 days (total new / containing risk signals / ack-pending counts + the top-5 flag distribution).

Migration

  • 20260528130250_add_creation_audit_to_oauth_applicationsoauth_applications.created_via (string, default web) + creation_risk_flags (jsonb, default []) + a partial index on jsonb_array_length(creation_risk_flags) > 0.

Tests

  • Model/request/mailer combined: 102+ examples PASS (Phase 2–5e integration regression).
  • 6 rounds of Codex review (Phase 2 / 3 / 4 / 5a / 5b / 5b-followup) — every BLOCK resolved.

2026-05-15

  • LogiAuth Swift SDK 0.1.2 — added redirect_uri scheme/host/path verification to handleCallback (ainote 2026-05-15 incident fix)
  • @logi-auth/browser@0.1.0 released on npm — a zero-deps PKCE SDK for SPAs
  • Expanded OIDC Discovery docsresponse_modes_supported, ui_locales_supported, service_documentation, revocation_endpoint_auth_methods_supported (basic/post; none excluded), claims_parameter_supported / request_parameter_supported / request_uri_parameter_supported / require_request_uri_registration (explicitly false)
  • /diagnose endpoint restored — added the missing DiagnoseController + view (RP self-diagnosis)
  • docs.1pass.dev track split — added /tracks/{mobile,web,api}, introduced a line-numbered jump index in llms.txt
  • Bidirectional Cross-Host Handoff API added (PR #28-32) + global logout
  • Console SSO Phase 3 (start.1pass.dev) + desktop Web SSO Phase 1+2 introduced (Mac Safari Universal Link bouncer)
  • multi-role join table introduced — role separation per host (Phase 1)

2026-05-11 — Added target_session_token PoP

Added a new credential path so a user who holds both account sessions on the same device can merge accounts without the OTP email round-trip.

  • New endpoint POST /api/v1/me/merge/session-token — issues a 5-minute-TTL token from the target session
  • The existing POST /api/v1/me/merge accepts either target_session_token or otp_code
  • Providing both credentials at once yields 422 conflicting_credentials
  • Audit log marker: merged_via=session_token (distinct from OTP's merged_via=otp)

For the full flow, see the session-token PoP guide.

2026-05-11 — canonical resolution applied to the Easy Bracket RP

Easy Bracket (RP, https://easy-bracket-lookbook.onrender.com) applied canonical_user_ids resolution — which reflects logi account merges — to its production environment. The ENFORCE_CANONICAL_RESOLUTION=true flip is complete (currently 0 merged users — it will start working automatically from the first future merge).

  • Every Pundit policy checks ownership against user.canonical_user_ids
  • ActionCable / background jobs / the PttChannel model are canonicalized
  • Push-notification fanout deduplicates canonical ids
  • Device tokens / API tokens are invalidated immediately on a user.merged webhook

Other RPs can integrate the same pattern. See the RP Integration Guide.

2026-04-27 — Security review wave (Phase 1-14)

A batch of hardening based on a full-codebase security review. For the detailed items, see docs/todo/security-review-findings-2026-04-27.md.

Critical

  • P0-1 Device bootstrap re-authenticationdevice_secret_digest + dual-mode (bootstrap → secret revealed once, refresh → digest verification). Blocks re-issuing a PAK for an existing anonymous account using only its device_uuid.
  • P0-2 Identity reporter capabilityidentity_verification_reporter flag + identity_verification_methods allowlist. Resolves the issue where an arbitrary OAuth app could change a trust level.

High

  • Rate limiting — 8 rack-attack throttles (across login/oauth/devices/identity/otp/passkeys/api)
  • Webhook SSRF — both registration and dispatch do DNS verification, blocking private/link-local/multicast addresses (IPv4+IPv6). To defend against DNS rebinding, connect directly to the verified IP.
  • Webhook secret redesign — store webhook_secret_plaintext with ActiveRecord encryption. When the BCrypt fallback is used, add an X-Logi-Secret-Deprecated header + Deprecation (RFC 9745).
  • Passkey UV policy — introduced purpose=step_up → enforces user_verification: required. otp_verified_at is set only based on whether UV actually happened (authenticator_data.user_verified?).
  • OAuth authorize return — when not logged in, save request.fullpath to return_to_after_authenticating → preserves state/scope/redirect_uri/code_challenge.
  • Session fixation — call reset_session on successful login (preserve only the necessary keys, then re-store them).

Medium

  • New identity:level scope — the behavior where identity_verified_level was automatically included in profile is deprecated. New apps must explicitly request identity:level. Existing apps stay compatible via legacy_identity_claim=true (a 6-month migration period).
  • CSP report-only enableddefault-src 'self', frame-ancestors 'none', /csp-violation-report sink. The switch to enforce comes after analyzing violation logs.
  • CORS — dynamic verification of oauth_applications.allowed_origins. The wildcard + credentials combination is not used.
  • Secret expiration policyclient_secret_expires_at / webhook_secret_expires_at with a 1-year TTL + an audit log alert 30 days before. Issuing a token with an expired client_secret yields invalid_client.

Summary of SDK user impact

  • ⚠️ EncryptedSharedPreferences (androidx.security:security-crypto 1.1.0) is deprecated — don't use it in new SDKs. Prefer DataStore + Tink or Ackee Guardian.
  • ⚠️ Specifying the iOS Keychain option is required: kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly (without the suffix, it syncs to iCloud Keychain).
  • ⚠️ On flutter_secure_storage 10.0+, you must set up Android backup exclusion (backup_rules.xml) yourself.
  • ⚠️ The webhook receiving side must handle the X-Logi-Secret-Deprecated: true header + a rotation notice.
  • New mobile guides: iOS, Android, Flutter, React Native.

v0.1-alpha (2026-04-22 sync)

Completed milestones (see GitHub tags):

  • m0-bootstrap — Rails 8 scaffold, Cloudflare/Render checklist
  • m1-auth — basic auth (role + device_uuid + lockout)
  • m2-oauth-core — OAuth 2.0 + PKCE S256 + JWKS + Refresh rotation
  • m3-developer-portal — Developer Portal + Admin (iOS 26 Liquid Glass)
  • m4-consent — consent screen + consent records
  • m5-cli — Personal API Keys + the logi CLI (Ruby/Thor)
  • m7-otp — TOTP 2FA + backup codes + a sensitive-operation gate
  • m8-m10-security-observability — Passkey + Login Logs + Webhooks
  • m12-m13 — Suspicious Detection + Admin Audit
  • m6-ios-scaffold / m6-m11-ios-mcp — iOS app + MCP server
  • m14-docs — VitePress + Scalar docs site + OpenAPI 3.1
  • m15-android-scaffold / m15-complete — Android app + Play Integrity round-trip + Sentry
  • 2026-04-22/oauth/revoke (RFC 7009), /oauth/introspect (RFC 7662), a recurring job to purge sign-in history after 90 days

Known limitations

  • Production deployment to Render / Cloudflare / GitHub Pages is not complete yet
  • Push notifications (APNs/FCM) not implemented
  • Play Integrity production decode and ANDROID_APP_CERT_SHA256 injection not yet complete
  • The iOS associated domain migration to api.1pass.dev is complete (v0.4, 2026-04-22)

Roadmap (β)

  • β1: dynamic scope + required marking (in progress)
  • β2: custom claims (User#custom_claims implemented)
  • β3: sign-in history alerts + APNs/FCM
  • β5: mobile production hardening (Play Integrity decode, cert fingerprint, associated domain cleanup)

최종 수정:

Identity가 제품의 신뢰를 만듭니다.