Open Closed

React Native 400 status code when try to Login #10247


User avatar
0
carl.hallqvist created

I am using ABP 9.2.3 microservice template.

Everything works well for Blazor WebAssemby. However, for the generated React Native app, it always return 400 when trying to login.

[19:48:48 INF] Request starting HTTP/1.1 GET http://auth.staging.myproject.io/__bundles/LeptonX.Global.AFCBFDDBAFF29B1A0580323089206430.js?_v=639019426904413149 - null null
[19:48:48 WRN] Unknown proxy: [::ffff:10.244.1.23]:60176
[19:48:48 INF] The file /__bundles/LeptonX.Global.AFCBFDDBAFF29B1A0580323089206430.js was not modified
[19:48:48 INF] Executing endpoint 'Volo.Abp.AspNetCore.Mvc.ProxyScripting.AbpServiceProxyScriptController.GetAll (Volo.Abp.AspNetCore.Mvc)'
[19:48:48 INF] Route matched with {area = "Abp", action = "GetAll", controller = "AbpServiceProxyScript", page = ""}. Executing controller action with signature Microsoft.AspNetCore.Mvc.ActionResult GetAll(Volo.Abp.AspNetCore.Mvc.ProxyScripting.ServiceProxyGenerationModel) on controller Volo.Abp.AspNetCore.Mvc.ProxyScripting.AbpServiceProxyScriptController (Volo.Abp.AspNetCore.Mvc).
[19:48:48 INF] Identity.Application was not authenticated. Failure message: Unprotect ticket failed
[19:48:48 INF] Executing ContentResult with HTTP Response ContentType of application/javascript
[19:48:48 INF] Executed action Volo.Abp.AspNetCore.Mvc.ProxyScripting.AbpServiceProxyScriptController.GetAll (Volo.Abp.AspNetCore.Mvc) in 2.4121ms
[19:48:48 INF] Executed endpoint 'Volo.Abp.AspNetCore.Mvc.ProxyScripting.AbpServiceProxyScriptController.GetAll (Volo.Abp.AspNetCore.Mvc)'
[19:48:48 INF] Request starting HTTP/1.1 GET http://auth.staging.myproject.io/libs/timeago/locales/jquery.timeago.sv.js?_v=638925170426700683 - null null
[19:48:48 INF] Request finished HTTP/1.1 GET http://auth.staging.myproject.io/Abp/ServiceProxyScript - 200 3003 application/javascript 7.6018ms


1 Answer(s)
  • User Avatar
    0
    AI-Bot created
    Support Team ABP Agent
    1. Solution A 400 at the end of the OpenID Connect login flow in the React Native (Expo) app almost always means one of these is misconfigured for the mobile client in your Auth Server (OpenIddict) and API host:
    • The client registration (redirect URI, post logout redirect URI, allowed grant types)
    • The RedirectAllowedUrls/CorsOrigins on your HttpApi.Host/AuthServer
    • The final callback scheme in the mobile app

    Checklist to fix:

    1. Register a dedicated mobile client in your Auth Server (OpenIddict) with Authorization Code + PKCE
    • In your XxxDomain/IdentityServer/IdentityServerDataSeedContributor (or the OpenIddict seeding equivalent in v9) add a mobile client like:
    // Mobile (React Native) client
      await CreateClientAsync(
          name: "MyApp_Mobile",
          scopes: new[] { "openid", "profile", "email", "offline_access", "MyService" },
          grantTypes: new[] { "authorization_code" },
          secret: null, // PKCE, public client
          requireClientSecret: false,
          redirectUri: "com.myapp.mobile://callback",
          postLogoutRedirectUri: "com.myapp.mobile://signout-callback",
          corsOrigins: new[] { "https://your-api-host", "https://your-auth-host" }
      );
    
    • Run DbMigrator (or your data seeder) so the client is created.
    1. Configure RedirectAllowedUrls and CORS on HttpApi.Host (and/or AuthServer)
    • In appsettings.json of your HttpApi.Host (and AuthServer if separated), include your custom scheme callback URI and any dev hosts you use:
    "App": {
        "SelfUrl": "https://api.staging.myproject.io",
        "CorsOrigins": "https://*.myproject.io,http://localhost:19006,exp://127.0.0.1:19000",
        "RedirectAllowedUrls": "com.myapp.mobile://callback,com.myapp.mobile://signout-callback"
      },
      "AuthServer": {
        "Authority": "https://auth.staging.myproject.io",
        "RequireHttpsMetadata": "true"
      }
    
    • Important: The custom scheme URLs (com.myapp.mobile://callback) must be present in RedirectAllowedUrls, otherwise OpenIddict will reject the authorization code redemption with 400.
    1. Make sure your Expo/React Native app uses the same client and redirect URI
    • In the generated React Native app config (AppConfig.ts or env), set:
      • issuer: https://auth.staging.myproject.io
      • clientId: MyApp_Mobile
      • redirectUri: com.myapp.mobile://callback
      • scopes: openid profile email offline_access [and your API scope]
      • use PKCE and no client secret (public client)
    1. Configure native URL scheme on iOS/Android
    • iOS: Add CFBundleURLSchemes including com.myapp.mobile
    • Android: Set the intent filter (or appAuthRedirectScheme if using react-native-app-auth) to com.myapp.mobile
    • Expo: In app.json/app.config, define scheme: "com.myapp.mobile"
    1. Multi-tenancy header (if applicable)
    • If you use multi-tenancy and the mobile user is not in host, ensure the Abp-TenantId or domain-based tenant resolution matches the login you try. Mismatch can also result in unexpected auth errors.
    1. Clear cookies/session and retry
    • A stale session in the in-app browser can cause “Unprotect ticket failed” in logs. Close the in-app browser, revoke the app session, then retry.
    1. Verify SSL/cert trust on simulator/device
    • If the device/simulator does not trust your staging certificate, the token exchange can fail. Ensure full chain is trusted.

    Why 400 appears:

    • OpenIddict returns 400 when redirect_uri does not match the registered client, when the client is not allowed for the requested flow, or when PKCE/verification fails. The “Unprotect ticket failed” in your logs indicates an invalid/expired/foreign cookie or state in the front-channel, but the final cause for the mobile error is almost always a redirect URI mismatch or missing RedirectAllowedUrls entry.

    Quick minimal set you can copy:

    • Seed client:
    await CreateClientAsync(
        name: "MyApp_Mobile",
        scopes: new[] { "openid", "profile", "email", "offline_access", "MyService" },
        grantTypes: new[] { "authorization_code" },
        requireClientSecret: false,
        redirectUri: "com.myapp.mobile://callback",
        postLogoutRedirectUri: "com.myapp.mobile://signout-callback",
        corsOrigins: new[] { "https://auth.staging.myproject.io", "https://api.staging.myproject.io" }
      );
    
    • HttpApi.Host appsettings.json:
    "App": {
        "CorsOrigins": "https://*.myproject.io,http://localhost:19006,exp://127.0.0.1:19000",
        "RedirectAllowedUrls": "com.myapp.mobile://callback,com.myapp.mobile://signout-callback"
      }
    
    • React Native (Expo) OAuth config:
    export const oAuthConfig = {
        issuer: 'https://auth.staging.myproject.io',
        clientId: 'MyApp_Mobile',
        redirectUri: 'com.myapp.mobile://callback',
        scopes: ['openid', 'profile', 'email', 'offline_access', 'MyService'],
      };
    

    If you apply the above, the React Native login should complete and the token exchange should return 200 with tokens instead of 400.

    1. Sources
    • ABP Support thread explaining 401/login redirect behaviors and headers:
      • https://abp.io/support/questions/3281
    • ABP Community guides showing mobile client registration, grant types, redirect URIs and host settings patterns (use as reference for allowed redirects/CORS and mobile client definition):
      • https://abp.io/support/questions/1989
      • https://abp.io/support/questions/3339

    This response was generated by AI. If it didn’t solve your problem, feel free to post a new answer — our support team will jump in to help you soon.

Boost Your Development
ABP Live Training
Packages
See Trainings
Mastering ABP Framework Book
The Official Guide
Mastering
ABP Framework
Learn More
Mastering ABP Framework Book
Made with ❤️ on ABP v10.1.0-preview. Updated on December 17, 2025, 07:08
1
ABP Assistant
🔐 You need to be logged in to use the chatbot. Please log in first.