Skip to content

[Auth] Support HTTPS App Links/Universal Links as OAuth redirect URI on React Native (preferredRedirectUrl not passed through) #14869

Description

@shivennn

Is this related to a new or existing framework?

React Native

Is this related to a new or existing API?

Authentication

Is this related to another service?

  • Amazon Cognito (Hosted UI / OAuth 2.0 flows) - CloudFront (HTTPS endpoint for App Link verification) - S3 (hosting .well-known/assetlinks.json and apple-app-site-association)

Describe the feature you'd like to request

On Android, using custom URL schemes (e.g., myapp://auth/callback) as OAuth redirect URIs is vulnerable to authorization code interception by malicious apps that register the same scheme. Android shows a disambiguation dialog ("Open with...") allowing any app claiming the scheme to steal the auth code.

The industry-standard mitigation (per RFC 8252 §8.2) is to use HTTPS App Links (Android) / Universal Links (iOS) as the redirect URI. These are verified against a .well-known/assetlinks.json file on the domain, ensuring only the legitimate app (matching package name + SHA-256 signing certificate) can handle the redirect.

Amplify v6 has the preferredRedirectUrl option in signInWithRedirect() and getRedirectUrl.native.js has logic to handle it — but the parameter is never passed through the internal call chain, making HTTPS redirects impossible without patching.

Request: Wire preferredRedirectUrl through signInWithRedirect()oauthSignIn()getRedirectUrl() so that HTTPS App Links work as OAuth redirect URIs on React Native.

Describe the solution you'd like

A one-line fix in signInWithRedirect.js to pass preferredRedirectUrl through:

Current code (@aws-amplify/auth/dist/cjs/providers/cognito/apis/signInWithRedirect.js):

return oauthSignIn({
    oauthConfig: authConfig.loginWith.oauth,
    clientId: authConfig.userPoolClientId,
    provider,
    idpIdentifier,
    customState: input?.customState,
    preferPrivateSession: input?.options?.preferPrivateSession,
    options: {
        loginHint: input?.options?.loginHint,
        lang: input?.options?.lang,
        nonce: input?.options?.nonce,
        prompt: input?.options?.prompt,
    },
    authSessionOpener: input?.options?.authSessionOpener,
    // ❌ preferredRedirectUrl is NOT passed here
});

Proposed fix:

return oauthSignIn({
    oauthConfig: authConfig.loginWith.oauth,
    clientId: authConfig.userPoolClientId,
    provider,
    idpIdentifier,
    customState: input?.customState,
    preferPrivateSession: input?.options?.preferPrivateSession,
    options: {
        loginHint: input?.options?.loginHint,
        lang: input?.options?.lang,
        nonce: input?.options?.nonce,
        prompt: input?.options?.prompt,
    },
    authSessionOpener: input?.options?.authSessionOpener,
    preferredRedirectUrl: input?.options?.preferredRedirectUrl,  // ✅ ADD THIS
});

And in oauthSignIn function signature, add preferredRedirectUrl to the destructured params:

const oauthSignIn = async ({ oauthConfig, provider, idpIdentifier, clientId, customState, preferPrivateSession, options, authSessionOpener, preferredRedirectUrl, }) => {

And pass it to getRedirectUrl:

const redirectUri = (0, oauth_1.getRedirectUrl)(oauthConfig.redirectSignIn, preferredRedirectUrl);

The getRedirectUrl.native.js already has the logic to handle preferredRedirectUrl — it just never receives it.

Describe alternatives you've considered

Alternative 1: Patch via postinstall script (current workaround)

We currently patch getRedirectUrl.native.js via a postinstall.sh script to prefer HTTPS URLs when available:

// Patched: prefer HTTPS over custom scheme
const httpsRedirectUrl = redirects?.find(redirect => redirect.startsWith('https://'));
if (httpsRedirectUrl) {
    return httpsRedirectUrl;
}
return appSchemeRedirectUrl;

Downsides: Fragile, breaks on Amplify updates, requires custom build scripts.

Alternative 2: Change default behavior on React Native

Instead of always defaulting to the custom scheme, getRedirectUrl.native.js could prefer HTTPS when available (since HTTPS is strictly more secure than custom schemes on mobile).

Alternative 3: Remove custom scheme requirement

Currently, getRedirectUrl.native.js throws invalidAppSchemeException if no custom scheme URL exists in the config. For apps that ONLY want HTTPS redirects, this validation should be optional.

Additional context

Security Impact

This is a security vulnerability in the current implementation. On Android:

  • Any malicious app can register myapp://auth/callback
  • When Cognito redirects with the auth code, Android shows "Open with" dialog
  • Malicious app can intercept the authorization code
  • Auth code can be exchanged for access/refresh tokens (PKCE mitigates but doesn't eliminate risk on Android < 12)

Working Implementation

We built a complete working implementation with:

  • CloudFront + S3 hosting .well-known/assetlinks.json for App Link verification
  • Custom authSessionOpener using Linking.openURL (Chrome dispatches App Links correctly)
  • Patched getRedirectUrl.native.js to prefer HTTPS
  • Custom sign-out flow (Amplify's signOut() also needs the redirect URL fix)
  • Tested with a malicious app installed — no interception possible with HTTPS App Links

Affected Files (3 total for the fix)

  1. packages/auth/src/providers/cognito/apis/signInWithRedirect.ts — pass preferredRedirectUrl through
  2. packages/auth/src/providers/cognito/utils/oauth/getRedirectUrl.native.ts — already has the logic (no change needed)
  3. packages/auth/src/providers/cognito/utils/oauth/handleOAuthSignOut.native.ts — same fix needed for sign-out redirect

References

Enterprise Use Case

For regulated environments (finance, healthcare, government), custom URL schemes are not acceptable because:

  • They cannot be verified/owned (any app can claim them)
  • They don't meet security audit requirements for OAuth flows
  • Industry standards (RFC 8252, FAPI) recommend HTTPS-based redirects

Is this something that you'd be interested in working on?

👋 I may be able to implement this feature request

The fix is minimal (3 lines across 1-2 files). Happy to submit a PR if the approach is approved


Reproduction Steps (for maintainers)

  1. Create a React Native app with aws-amplify v6.18+
  2. Configure signInWithRedirect with both HTTPS and custom scheme in redirectSignIn
  3. Pass preferredRedirectUrl: 'https://...' in options
  4. Observe that the authorize URL still contains redirect_uri=myapp://... (custom scheme)
  5. Check getRedirectUrl.native.jspreferredRedirectUrl parameter is always undefined

Breaking Change Assessment

This fix is non-breaking and safe for a patch release (e.g., 6.20.1):

  1. preferredRedirectUrl is already a documented API parameter — it's accepted by signInWithRedirect() today, just never wired through internally. No API signature changes required.

  2. getRedirectUrl() already accepts the optional second parameter — the receiving function has the logic implemented; it simply never receives the value from the call site.

  3. Backward-compatible behavior:

    • If preferredRedirectUrl is not passed: behavior is identical to today (picks first URL, or on native, prefers custom scheme). Zero impact on existing apps.
    • If preferredRedirectUrl is passed: the parameter now works as documented. This is a bug fix, not a behavior change.
  4. Only theoretical "break": An app that passes preferredRedirectUrl but unknowingly depends on it being ignored (relying on the default URL selection). This would be an app depending on buggy behavior — not a valid backward-compatibility concern.

Conclusion: This qualifies as a bug fix, not a feature or breaking change. No major version bump needed.

Is this something that you'd be interested in working on?

  • [✔️ ] 👋 I may be able to implement this feature request
  • [ ❌ ] ⚠️ This feature might incur a breaking change

Metadata

Metadata

Assignees

No one assigned

    Labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions