## Summary
- Route browser OAuth redirects through the configured `redirectMethod`
instead of hardcoded `window.location` calls.
- Keep OAuth redirect APIs pending after navigation starts, including
custom redirect methods.
- Add `cliAuthConfirm` handler URL metadata and custom-page prompt
coverage.
- Update SDK spec text for browser OAuth callback and `returnTo`
behavior.
## Root Cause
OAuth helpers previously combined URL construction with direct browser
navigation. That bypassed configured redirect methods and made it too
easy for public redirect APIs to resolve after navigation started.
## Impact
Browser SDK consumers get consistent redirect behavior across built-in
and custom navigation methods. `returnTo` is handled as the
post-callback destination while the OAuth callback URL remains fixed to
the configured handler route.
## Validation
- `pnpm test run packages/template/src/lib/auth.test.ts`
- `pnpm test run apps/e2e/tests/js/oauth.test.ts`
- `pnpm -C packages/template lint`
- `pnpm -C apps/e2e lint`
- `pnpm -C packages/template typecheck`
- `pnpm -C apps/e2e typecheck`
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Added CLI authorization confirmation page/flow for terminal-based
auth.
* Added optional returnTo parameter for OAuth to control post-auth
redirects.
* Exposed configurable redirect behavior so apps follow the chosen
redirect method.
* **Bug Fixes**
* OAuth callback now uses app navigation/queued redirects and shows a
fallback link instead of forcing location.assign.
* **Tests**
* Added unit and e2e tests covering OAuth URL generation, scope
handling, and CLI auth confirmation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
## Summary
- Extract CLI auth confirmation into a `useCliAuthConfirmation()` hook
(status / error / isLoading / authorize / retry) so custom pages don't
have to reimplement the protocol; `CliAuthConfirmation` now consumes the
hook.
- Make `cliAuthConfirm` a first-class handler URL target — resolved via
`resolveHandlerUrls`, customizable per project, and used by
`promptCliLogin` through a new `buildCliAuthConfirmUrl()` helper.
- Move `StackContext` to its own module so the hook can be unit-tested
with a test double without tripping the client-version sentinel;
register `cliAuthConfirm` in custom-page prompts and the dev-tool
components tab; export the hook + types from `@stackframe/stack`.
## Test plan
- [ ] `pnpm typecheck`
- [ ] `pnpm lint`
- [ ] `pnpm --filter @stackframe/stack test cli-auth-confirm
url-targets`
- [ ] Manually verify default `/handler/cli-auth-confirm` flow + a
project with a custom `cliAuthConfirm` URL
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Adds a CLI authentication confirmation page with clear states
(invalid, authorizing, redirecting, success, error), retry action, and
flows for signed-in and anonymous users.
* CLI login URL generation now derives from the configured handler
target and app base, improving reliability.
* CLI confirmation page exposed in the components/dev UI for previewing.
* **Tests**
* End-to-end and unit tests covering confirmation behaviors and URL
generation.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
- Extended `CliAuthAttempt` with `anonRefreshToken` and a migration.
- CLI `POST /auth/cli` accepts optional `anon_refresh_token` (must be an
anonymous user's refresh token for the current project).
- `POST /auth/cli/complete` supports `mode` `check` (anonymous vs none),
`claim-anon-session` (issue tokens for the linked anonymous session),
and `complete` (bind the browser session's refresh token to the
attempt). Completing clears `anonRefreshToken` on the row. We do **not**
merge anonymous account data into the signed-in user (that behavior was
removed as a security risk; the anonymous user remains unchanged).
- Template CLI confirmation page, stack-cli optional
`STACK_CLI_ANON_REFRESH_TOKEN`, SDK/spec updates, and e2e coverage.
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* CLI login supports attaching anonymous sessions and a multi-mode
confirm/claim/check flow; CLI tools now surface login codes and remove
anon token after use.
* Added interactive CLI auth demo page and a CLI simulator script.
* Client libraries: prompt flow accepts an optional anon token and a
promptLink(url, loginCode) callback.
* **Tests**
* Expanded end-to-end coverage for anonymous CLI sessions,
claim/complete/poll flows, upgrades, and error cases.
* **Documentation**
* Updated prompt CLI docs/spec to describe new options and callback
signature.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
DB migration compat / Back-compat — Current branch migrations with ${{ needs.check-migrations-changed.outputs.base_branch }} branch code (push) Has been cancelled
DB migration compat / Forward-compat — Current branch code with ${{ needs.check-migrations-changed.outputs.base_branch }} branch migrations (push) Has been cancelled
<!--
Make sure you've read the CONTRIBUTING.md guidelines:
https://github.com/stack-auth/stack-auth/blob/dev/CONTRIBUTING.md
-->
<!-- CURSOR_SUMMARY -->
---
> [!NOTE]
> **High Risk**
> Touches authentication and OAuth token/authorize flows and changes how
client requests are validated, so regressions could cause widespread
login/client-access failures. Also includes a data migration that alters
effective security posture for existing projects.
>
> **Overview**
> Adds a **project-level toggle**
(`project.requirePublishableClientKey`) to control whether client
requests/OAuth flows must include a publishable client key, including a
DB migration that backfills existing projects to require it.
>
> Backend auth now treats the publishable client key as *optional when
allowed*, introducing a public sentinel (`__stack_public_client__`) and
returning a new specific error
(`PUBLISHABLE_CLIENT_KEY_REQUIRED_FOR_PROJECT`) across smart request
auth + OAuth `authorize`/`callback`/`token` endpoints.
>
> Dashboard and SDKs update key generation/display and request
construction to handle missing publishable keys, expose an advanced
toggle on the Project Keys page, and extend internal config overrides to
support a new `project` level; E2E/tests and schema fuzzing are expanded
accordingly, and CI adds a forward-compat migration check job when
back-compat fails.
>
> <sup>Written by [Cursor
Bugbot](https://cursor.com/dashboard?tab=bugbot) for commit
5d06c08613. This will update automatically
on new commits. Configure
[here](https://cursor.com/dashboard?tab=bugbot).</sup>
<!-- /CURSOR_SUMMARY -->
<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit
* **New Features**
* Project-level config to require publishable client keys; migration
applied to existing projects.
* **Improvements**
* Auth flows now support optional publishable client keys with explicit
validation and a sentinel for keyless OAuth.
* Dashboard/UI and SDKs handle publishable keys as optional and
conditionally show/generate them.
* Admin/client APIs extended to manage project-level overrides.
* **Bug Fixes**
* Key validation behavior aligned with project config.
* **Tests**
* Expanded E2E and unit tests covering optional/required publishable-key
scenarios.
* **Documentation**
* Spec and knowledge docs updated to describe the sentinel and config
behavior.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
### Summary of Changes
Previously, on the Swift SDK, the `signInWithOAuth` function wasn't
working. In this PR, we fix it by having the `getOAuthUrl` function to
actually redirect correctly. Note that to do so, we updated the
`validRedirectUrl` check on the backend to accept app native redirects
(from our new trusted url scheme). Another thing to note is that we
added functionality to the `TokenStore` abstraction to conditionally
refresh the access token that the user is trying to fetch if it is
expired/close to expiring if possible. `getOAuthUrl` will attempt to get
a valid access token, and thus will rely on our algorithm documented in
`utilities.md`.
The specs serve as the source of truth.
We go further and implement Apple Native sign in. To do so, we have it
hit a new route on the backend and verify the `jwtToken` retrieved by
the sdk against an Apple-provided set of `jwks`. We use jose to do so,
in line with the rest of the codebase.
We take this opportunity to refactor the oauth provider route owing to
the amount of duplicated logic. Additionally, to enable the apple sign
in, users will have to update the Apple authentication method modal on
the dashboard and add accepted bundle ids. These are identifiers for
projects, and we will check the `JWT` on the backend to make sure the
audience is set to an accepted bundleId.
We also update the Apple modal to be more informative.
### Using the new Features
To use the Apple native sign in, users will have to 1) sign up with an
apple developer account, 2) set up their bundleids for their projects by
connecting them to the apple developer account, 3) update the Stack-Auth
Authentication Methods dashboard apple modal with the relevant fields.
Then, trying to sign in with apple with our Swift SDK will use the apple
native sign in.
### UI Changes
Renamed the fields in the apple modal. Added a new field for bundle ids.
See below.
https://github.com/user-attachments/assets/0e760c0e-3198-4818-ac7f-4900d7a125bb
Co-authored-by: Konstantin Wohlwend <n2d4xc@gmail.com>