Package 

Class OAuthHandler


  • 
    public final class OAuthHandler
    
                        

    Manages OAuth authentication flows using Chrome Custom Tabs.

    zerohash uses a confidential-client OAuth architecture: the authorization code → token exchange happens entirely server-side inside connection-service, which holds the client_secret. The Android SDK never sees the raw authorization code; connection-service validates the OAuth callback, performs the token exchange, and then redirects to the web app's oauth-callback page which in turn fires connectsdk-oauth://callback?connectionId=<uuid>.

    PKCE (RFC 7636) is designed for public clients that perform the token exchange on-device (e.g., a native app calling the token endpoint directly). It protects against an intercepted authorization code being exchanged by a third party. Here the token exchange is handled server-side by connection-service using client_secret — a stronger form of client authentication. Adding a code_challenge from the SDK without a matching code_verifier from connection-service would break the token exchange if the OAuth provider enforces PKCE when the parameter is present. Additionally, the OAuth authorization URL is already constructed by connection-service, which appends its own state token; prepending another state from the SDK creates a duplicate parameter that breaks connection-service's own CSRF protection.

    CSRF protection via state is already implemented server-side: connection-service generates a 256-bit random state token (GenerateState() in utils.go), stores it against the pending connection in MongoDB, and verifies it when the OAuth provider redirects back. The final redirect to connectsdk-oauth://callback only carries connectionId — the state is fully consumed by the backend and never forwarded to the SDK. Validating a state that is never present in the callback would unconditionally reject every OAuth response.

    The primary mitigation for custom-scheme hijacking is that the callback only delivers a connectionId (not an authorization code or tokens). Connection-service has already completed the token exchange before this callback fires. A malicious app that intercepts the intent receives only a reference UUID; it cannot exchange that for credentials without proper authentication against the zerohash API.

    Additional defense in depth from the post-merge review:

    • The exported intent-filter lives on OAuthCallbackActivity, not on xyz.connect.sdk.ui.WebViewActivity, so the JWT-bearing activity is not reachable from external apps.

    • This handler validates that connectionId is a well-formed UUID before forwarding it — a malicious app firing the intent cannot inject arbitrary strings.

    • Provider-supplied error_description is sanitised to the RFC 6749 §5.2 error-code allowlist before being surfaced to integrators.

    F-007: OAuth URLs and connection IDs are only logged in debug builds.

    • Constructor Detail

      • OAuthHandler

        OAuthHandler(Activity activity)
    • Method Detail

      • startOAuthFlow

         final Unit startOAuthFlow(String url, OAuthHandler.OAuthCallback callback)

        Start OAuth flow with Chrome Custom Tabs.

        The authorization URL is passed through unchanged — connection-service has already appended the state token and any other required parameters to the URL before the web app forwarded it to the SDK via the navigate message.

        Parameters:
        url - OAuth authorization URL constructed by connection-service
        callback - Callback for OAuth results
      • handleCallback

         final Boolean handleCallback(Intent intent)

        Handle OAuth callback from redirect.

        Validates scheme and host of the incoming intent, then extracts the connectionId from query parameters or fragment. Rejects non-UUID connectionIds (F-003 review) and sanitises provider-supplied errors to standard OAuth 2.0 codes (F-013 review).

        Parameters:
        intent - Intent containing the callback URL
      • clear

         final Unit clear()

        Clear current state (e.g., on activity destroy).