Skip to content

bug: Custom OAuth login succeeds server-side every time but app loops back to login screen (iOS, 4.75.0) #7556

Description

@chintan-kaiv

Describe the bug

Logging in via a Custom OAuth provider (self-hosted Keycloak, standard OIDC) on the mobile app succeeds completely on the server side every single time, but the app never leaves the login screen — it silently re-initiates the entire OAuth handshake every ~15-20 seconds in an infinite loop. Web login against the exact same Custom OAuth provider/server works correctly on the first try.

Environment

  • App version: 4.75.0
  • Platform: iOS (native app, "RC Mobile")
  • Server version: Rocket.Chat 8.6.1
  • Auth method: Custom OAuth pointed at a self-hosted Keycloak instance (standard OIDC — /auth, /token, /userinfo endpoints)
  • Login Style: Tried both Popup and Redirect in Admin → OAuth → (custom provider) — no change in mobile behavior either way
  • 2FA: Disabled account-wide for testing — no change in behavior

Steps to reproduce

  1. Configure a Custom OAuth provider against a standard OIDC/Keycloak server.
  2. Open the RC Mobile app (iOS), tap the custom OAuth login button.
  3. Authenticate against the identity provider (Keycloak login page).
  4. Observe: the auth popup/browser session closes with no visible error, but the app stays on the login screen.
  5. Wait ~15-20 seconds without doing anything — a brand new OAuth handshake (fresh authorization code) starts automatically, completes silently (no re-prompt, since the IdP still has an active session), and the app still doesn't log in. This repeats indefinitely.

What I verified server-side (ruling out server/IdP/proxy causes)

Traced the full request chain end-to-end across three independent login attempts, all with identical results:

  1. GET https://sso.example.com/realms/<realm>/protocol/openid-connect/auth?client_id=<id>&redirect_uri=https%3A%2F%2Fchat.example.com%2F_oauth%2F<provider>&response_type=code&state=<state>&scope=openid
    → Keycloak returns 302 immediately (existing IdP session, no interactive prompt needed after the first attempt).
  2. RocketChat's server exchanges the code: POST https://sso.example.com/realms/<realm>/protocol/openid-connect/token → succeeds.
  3. GET https://sso.example.com/realms/<realm>/protocol/openid-connect/userinfo → succeeds, returns the full identity (email, name, sub, etc.).
  4. RocketChat's CustomOAuth logs "Identity response" with the correct user identity.
  5. RocketChat's login-validation callbacks fire cleanly, in order, with no exception: afterValidateNewOAuthUseronValidateLoginafterValidateLogin.
  6. The app's own REST call — POST /api/v1/login with userAgent: RC Mobile; ios ... and the OAuth credential in the body — returns HTTP 200.

This full sequence (steps 1-6) completed successfully three times in a row across separate login attempts, each with its own fresh authorization code, each getting a clean 200 on /api/v1/login. There is no error, rejection, or exception anywhere in the server-side chain on any attempt. Despite that, the app immediately restarts the same sequence again ~15-20 seconds later, and never transitions out of the login screen.

Decoded state parameter from the request (values redacted, structure intact):

{
  "loginStyle": "redirect",
  "credentialToken": "<redacted>",
  "isCordova": true,
  "redirectUrl": "rocketchat://auth"
}

Note loginStyle is redirect regardless of what the server's Admin → OAuth → Login Style setting is configured to (tried both Popup and Redirect server-side with no change in this value or in mobile behavior) — the mobile app appears to always request redirect style for Custom OAuth, independent of that server setting.

Possibly related

This looks like it could be the same underlying class of bug as #7333 (fixed by #7340: "don't cancel login saga on 2s timeout") — a race/cancellation between successful OAuth completion and the UI state advancing past the login screen — but resurfacing in the newer native-browser-session Custom OAuth flow introduced by #6987 / #7419 (which moved Custom OAuth to ASWebAuthenticationSession/Chrome Custom Tabs and explicitly forced redirect-style login, "activating existing (previously dormant) saga logic"). #7340's fix predates that rework, so it may not cover this newer code path.

Expected behavior

After a successful OAuth completion (confirmed server-side), the app should recognize the returned session and transition to the logged-in state, rather than silently discarding it and re-initiating the OAuth flow.

Additional notes

  • Uninstalling and reinstalling the app does not change the behavior.
  • Force-quitting and reopening the app does not change the behavior.
  • Happy to provide more detail (redacted server logs, HAR-equivalent request traces) on request — omitted here since they contain internal hostnames/identifiers.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions