Goal
After starting a local Account Web dev server, developers should be able to complete third-party hosted sign-in without configuring local hosts entries or local HTTPS certificates.
The local Account Web dev server should work in both common modes:
- Account Web dev server + local Account Backend.
- Account Web dev server + staging Account Backend through the dev-server API proxy.
Background
Some external OAuth identity providers only allow one configured callback domain for an application. This makes it hard to reuse the same provider application across production, staging, and development environments whose Account domains differ.
This also makes local Account frontend development painful. For example, WeChat website login will redirect back to the configured callback domain, so pointing that callback to a local dev server would require host mapping and local HTTPS certificate work.
Proposal: Generic Redirect Relay
Add a generic redirect relay endpoint to Account Backend. The endpoint should be independent of OAuth provider logic: it should only validate a target URL against an allowlist and redirect to it while forwarding the other query parameters.
Example:
GET /api/redirect?redirect_to=https%3A%2F%2Faccount-test.example.com%2Fapi%2Fidentity-providers%2Fwechat%2Fcallback&code=...&state=...
The endpoint redirects to:
https://account-test.example.com/api/identity-providers/wechat/callback?code=...&state=...
This lets a provider callback domain configured for production receive the provider callback first, then relay it to staging or a local frontend/dev flow without coupling the relay endpoint to OAuth state handling.
Current Implementation Findings
The existing Account hosted sign-in flow already persists the provider callback URL in provider redirect state. StartIdentityProviderAuthorization validates CallbackURL, sends it to the provider authorization URL, stores it as callbackURL, and HandleIdentityProviderCallback later passes the stored value into provider credential verification. This means redirect URI consistency during provider token exchange is already modeled, as long as the correct provider-facing callback URL is stored at authorization start.
The current HTTP handler for GET /account/identity-providers/{provider}/authorize always computes CallbackURL from ACCOUNT_WEB_BASE_URL via the Account Web API facade path. For local Account Web dev, this is not enough: the provider-facing callback URL may need to be a production redirect relay URL whose redirect_to points back to the local dev server's /api/identity-providers/{provider}/callback.
The current hosted sign-in completion redirect also uses ACCOUNT_WEB_BASE_URL. After a provider callback is handled, HandleAccountIdentityProviderCallback returns an absolute hosted sign-in URL. If a staging backend handles a callback reached through a local dev-server proxy, an absolute staging URL sends the browser back to staging Account Web instead of the local dev server. A relative Location such as /sign-in?clientID=...&requestURI=... would allow the browser to stay on the origin that received the callback, including http://localhost:<port>.
Account Web's local Vite config already has partial support for proxying Account API calls to another backend and overriding the proxied Origin header through VITE_ACCOUNT_WEB_TEST_ORIGIN. This should be verified as part of the full local sign-in flow rather than treated as a separate product feature.
Requirements
- Add a generic HTTP redirect relay endpoint in Account Backend.
- Use a configurable allowlist with full target URL granularity, not host-only matching.
- Accept a fixed control parameter such as
redirect_to.
- Require
redirect_to to be an absolute URL.
- Validate the parsed target URL against the allowlist before redirecting.
- Forward all other query parameters from the current request to the target URL.
- Preserve any query parameters already present in the target URL, then append forwarded parameters.
- Do not forward the
redirect_to control parameter itself.
- Allow
https targets by default.
- Allow
http://localhost and http://127.0.0.1 only when explicitly configured for development.
- Reject protocol-relative URLs, malformed URLs, unsupported schemes, and URLs with userinfo.
- Return a redirect response, preferably
302 Found or 303 See Other.
- Add request logging that records the target origin/path and allowlist result without logging sensitive full query values.
Hosted Sign-In Integration Requirements
- Allow hosted provider authorization to use a provider-facing callback URL that is not simply derived from
ACCOUNT_WEB_BASE_URL.
- Support configuring or passing a provider-facing callback URL such as:
https://account.example.com/api/redirect?redirect_to=http%3A%2F%2Flocalhost%3A5174%2Fapi%2Fidentity-providers%2Fwechat%2Fcallback
- Preserve that exact provider-facing callback URL in provider redirect state so providers that require
redirect_uri during token exchange receive the same value that was used during authorization.
- Keep validation strict. If the provider-facing callback URL is passed from Account Web, validate it before using it. The redirect relay allowlist should validate the final
redirect_to target.
- Change the post-provider-callback redirect back to hosted sign-in to be origin-relative, or otherwise configurable, so a callback handled through a local dev-server proxy returns the browser to local Account Web rather than the backend's configured
ACCOUNT_WEB_BASE_URL.
- Preserve any configured Account Web base path when producing relative hosted sign-in locations.
- Update Account Web dev flow/configuration as needed so the local dev server can generate or select the provider-facing callback URL for local and staging-backend modes.
- Document the local development configuration needed for both modes:
- local Account Web + local Account Backend;
- local Account Web + staging Account Backend via proxy.
Cookie and Origin Notes
The current hosted Account session cookie is __Host-xbuilder-account-session with HttpOnly, Secure, SameSite=Lax, and Path=/. Verify that the local dev-server callback/proxy path can store and send this cookie in the supported local modes. If plain http://localhost cannot reliably receive the current cookie, add an explicit local-development solution rather than requiring manual HTTPS setup.
Cookie-authenticated Account mutations validate the request Origin against ACCOUNT_WEB_BASE_URL. The Account Web dev proxy already supports overriding the proxied Origin header with VITE_ACCOUNT_WEB_TEST_ORIGIN; the final flow should verify that password sign-in, switch account, and provider callback continuation still work when the backend is staging.
OAuth Integration Notes
The relay endpoint itself should not inspect OAuth state, look up OAuth flow records, exchange codes, or understand provider-specific behavior.
However, OAuth flows that use this relay still need to preserve redirect URI consistency for providers that require it during token exchange. For providers such as QQ, Apple, GitHub, Google, and X, the Account provider code currently sends redirect_uri when exchanging an authorization code for tokens. The OAuth flow should therefore reuse the exact redirect URI that was sent to the provider during authorization, which may be the relay URL rather than the final callback URL.
WeChat website login is a useful motivating case because its token exchange does not currently send redirect_uri, while its callback domain restriction still blocks staging/local callback URLs.
Test Coverage
- Redirects to an allowlisted exact target URL.
- Rejects non-allowlisted targets.
- Rejects unsupported schemes and protocol-relative targets.
- Rejects targets with userinfo.
- Preserves existing target query parameters.
- Appends provider callback parameters such as
code, state, error, and error_description.
- Does not forward
redirect_to.
- Supports explicitly configured localhost targets for local development.
- Starts provider authorization from local Account Web and stores the exact provider-facing callback URL used in the provider authorization request.
- Handles provider callback through the local dev-server API proxy and redirects back to local
/sign-in with a relative Location.
- Confirms providers that send
redirect_uri during token exchange receive the original provider-facing callback URL.
- Verifies the full local Account Web + staging Account Backend flow without host-file or local HTTPS certificate setup.
- Verifies the full local Account Web + local Account Backend flow without host-file or local HTTPS certificate setup.
Goal
After starting a local Account Web dev server, developers should be able to complete third-party hosted sign-in without configuring local hosts entries or local HTTPS certificates.
The local Account Web dev server should work in both common modes:
Background
Some external OAuth identity providers only allow one configured callback domain for an application. This makes it hard to reuse the same provider application across production, staging, and development environments whose Account domains differ.
This also makes local Account frontend development painful. For example, WeChat website login will redirect back to the configured callback domain, so pointing that callback to a local dev server would require host mapping and local HTTPS certificate work.
Proposal: Generic Redirect Relay
Add a generic redirect relay endpoint to Account Backend. The endpoint should be independent of OAuth provider logic: it should only validate a target URL against an allowlist and redirect to it while forwarding the other query parameters.
Example:
The endpoint redirects to:
This lets a provider callback domain configured for production receive the provider callback first, then relay it to staging or a local frontend/dev flow without coupling the relay endpoint to OAuth state handling.
Current Implementation Findings
The existing Account hosted sign-in flow already persists the provider callback URL in provider redirect state.
StartIdentityProviderAuthorizationvalidatesCallbackURL, sends it to the provider authorization URL, stores it ascallbackURL, andHandleIdentityProviderCallbacklater passes the stored value into provider credential verification. This means redirect URI consistency during provider token exchange is already modeled, as long as the correct provider-facing callback URL is stored at authorization start.The current HTTP handler for
GET /account/identity-providers/{provider}/authorizealways computesCallbackURLfromACCOUNT_WEB_BASE_URLvia the Account Web API facade path. For local Account Web dev, this is not enough: the provider-facing callback URL may need to be a production redirect relay URL whoseredirect_topoints back to the local dev server's/api/identity-providers/{provider}/callback.The current hosted sign-in completion redirect also uses
ACCOUNT_WEB_BASE_URL. After a provider callback is handled,HandleAccountIdentityProviderCallbackreturns an absolute hosted sign-in URL. If a staging backend handles a callback reached through a local dev-server proxy, an absolute staging URL sends the browser back to staging Account Web instead of the local dev server. A relativeLocationsuch as/sign-in?clientID=...&requestURI=...would allow the browser to stay on the origin that received the callback, includinghttp://localhost:<port>.Account Web's local Vite config already has partial support for proxying Account API calls to another backend and overriding the proxied
Originheader throughVITE_ACCOUNT_WEB_TEST_ORIGIN. This should be verified as part of the full local sign-in flow rather than treated as a separate product feature.Requirements
redirect_to.redirect_toto be an absolute URL.redirect_tocontrol parameter itself.httpstargets by default.http://localhostandhttp://127.0.0.1only when explicitly configured for development.302 Foundor303 See Other.Hosted Sign-In Integration Requirements
ACCOUNT_WEB_BASE_URL.redirect_uriduring token exchange receive the same value that was used during authorization.redirect_totarget.ACCOUNT_WEB_BASE_URL.Cookie and Origin Notes
The current hosted Account session cookie is
__Host-xbuilder-account-sessionwithHttpOnly,Secure,SameSite=Lax, andPath=/. Verify that the local dev-server callback/proxy path can store and send this cookie in the supported local modes. If plainhttp://localhostcannot reliably receive the current cookie, add an explicit local-development solution rather than requiring manual HTTPS setup.Cookie-authenticated Account mutations validate the request
OriginagainstACCOUNT_WEB_BASE_URL. The Account Web dev proxy already supports overriding the proxiedOriginheader withVITE_ACCOUNT_WEB_TEST_ORIGIN; the final flow should verify that password sign-in, switch account, and provider callback continuation still work when the backend is staging.OAuth Integration Notes
The relay endpoint itself should not inspect OAuth
state, look up OAuth flow records, exchange codes, or understand provider-specific behavior.However, OAuth flows that use this relay still need to preserve redirect URI consistency for providers that require it during token exchange. For providers such as QQ, Apple, GitHub, Google, and X, the Account provider code currently sends
redirect_uriwhen exchanging an authorization code for tokens. The OAuth flow should therefore reuse the exact redirect URI that was sent to the provider during authorization, which may be the relay URL rather than the final callback URL.WeChat website login is a useful motivating case because its token exchange does not currently send
redirect_uri, while its callback domain restriction still blocks staging/local callback URLs.Test Coverage
code,state,error, anderror_description.redirect_to./sign-inwith a relativeLocation.redirect_uriduring token exchange receive the original provider-facing callback URL.