Xamarin: Redirect from Browser to native App doesn't work

Viewed 2025

I followed this documentation in order to implement an oauth2 authorization code flow in my xamarin app: https://docs.microsoft.com/de-de/xamarin/xamarin-forms/data-cloud/authentication/oauth

In Addition I refered to this Tutorial to get it to work with the IdentityServer4 (I copied the extension of the OAuth2Authenticator class): https://sinclairinat0r.com/2018/12/09/secure-data-access-with-identityserver4-and-xamarin-forms

When I start the Loginflow the App redirects to the browser and I can login and allow access to the user data. Unfortunately the browser (or I guess the operating system) does not redirect to my App. I am guessing there is something wrong with my redirect-url, but I am not sure. Any pointers as to why it does not work would be appreciated.

Redirect Uri is composed of the exact package name in the manifest.xml plus ":/oauth2redirect". Although probably obvious, I want to mention that I replaced the actual url of our identity server with ipadress.

private async void StartOAuth()
        {

            var authenticator = new OAuth2AuthenticatorEx("xamarin", string.Empty, "openid profile",
                new Uri("https://ipadress:5001/connect/authorize"), new Uri("com.companyname.SApp:/oauth2redirect"), new Uri("https://ipadress:5001/connect/token"), null, true);


            authenticator.Completed += OnAuthCompleted;
            authenticator.Error += OnAuthError;

            AuthenticationState.Authenticator = authenticator;

            var presenter = new Xamarin.Auth.Presenters.OAuthLoginPresenter();
            Debug.WriteLine("after presenter");
            presenter.Login(authenticator);

            Debug.WriteLine($"Button StartOAuth clicked!");
        }

This is the corresponding client of the identity server

private static Client XamarinClient() => new Client
        {
            ClientId = "xamarin",
            ClientName = "Xamarin",

            AllowedGrantTypes = GrantTypes.Code,
            AllowAccessTokensViaBrowser = true,
            AlwaysIncludeUserClaimsInIdToken = true,

            RequireClientSecret = false,

            RequirePkce = true,

            AccessTokenType = AccessTokenType.Jwt,
            AccessTokenLifetime = 60,

            // Custom URL for native Apps. The URL below corresponds to bundle identifier 
            // on iOS and package name on Android 
            RedirectUris =
            {
                "com.companyname.SApp:/oauth2redirect",
            },

            AllowedScopes =
            { "openid", "profile" },
        };

Forgot to post my CustomUrlSchemeInterceptorActivity:

using System;
using Android.App;
using Android.Content;
using Android.Content.PM;
using Android.OS;
using SApp.Services;

namespace OAuthNativeFlow.Droid
{
    [Activity(Label = "CustomUrlSchemeInterceptorActivity", NoHistory = true, LaunchMode = LaunchMode.SingleTop)]
    [IntentFilter(
        new[] { Intent.ActionView },
        Categories = new[] { Intent.CategoryDefault, Intent.CategoryBrowsable },
        DataSchemes = new[] { "com.companyname.SApp" },
        DataPath = "/oauth2redirect")]
    public class CustomUrlSchemeInterceptorActivity : Activity
    {
        protected override void OnCreate(Bundle savedInstanceState)
        {
            base.OnCreate(savedInstanceState);

            // Convert Android.Net.Url to Uri
            var uri = new Uri(Intent.Data.ToString());

            // Load redirectUrl page
            AuthenticationState.Authenticator.OnPageLoading(uri);

            Finish();
        }
    }
}

Edit: I changed to Uris to http and used whireshark to catch the server response that should trigger the redirect into the App. It looks fine to me, but maybe it helps:

Frame 5009: 541 bytes on wire (4328 bits), 541 bytes captured (4328 bits) on interface 0
Ethernet II, Src: IntelCor_df:44:bd (28:16:ad:df:44:bd), Dst: Cisco_9f:f0:15 (00:00:0c:9f:f0:15)
Internet Protocol Version 4, Src: x.x.x.x, Dst: x.x.x.x
Transmission Control Protocol, Src Port: 5000, Dst Port: 38378, Seq: 9204, Ack: 4575, Len: 487
Hypertext Transfer Protocol
    HTTP/1.1 302 Found\r\n
        [Expert Info (Chat/Sequence): HTTP/1.1 302 Found\r\n]
        Response Version: HTTP/1.1
        Status Code: 302
        [Status Code Description: Found]
        Response Phrase: Found
    Date: Wed, 05 Jun 2019 07:55:06 GMT\r\n
    Server: Kestrel\r\n
    Content-Length: 0\r\n
        [Content length: 0]
     [truncated]Location: http://ipaddress:5000/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Dxamarin%26redirect_uri%3Dcom.companyname.SApp%253A%252Foauth2redirect%26scope%3Dopenid%2520profile%26response_type%3Dcode
    \r\n
    [HTTP response 4/5]
    [Time since request: 0.079050000 seconds]
    [Prev request in frame: 4993]
    [Prev response in frame: 5002]
    [Request in frame: 5007]
    [Next request in frame: 5010]
    [Next response in frame: 5015]
    [Request URI [truncated]: http://ipaddress:5000/Account/Login?ReturnUrl=%2Fconnect%2Fauthorize%2Fcallback%3Fclient_id%3Dxamarin%26redirect_uri%3Dcom.companyname.SApp%253A%252Foauth2redirect%26scope%3Dopenid%2520profile%26response_type%3Dc]


Update 1: Alright after trying a couple of things and talking to a colleague I think I have at least determined that the identityserver is not part of the issue. Also I have to use https and the mentioned extension to the OAuth2Authenticator class, otherwise I don't get to the point where the browser should redirect to the App. When I get to the point where the browser shows me "you will now be redirected", nothing happens. If I manually close the tab my breakpoint in OnAuthCompleted fires, but the EventArgs are basically empty (account is null and IsAuthenticated is false). The breakpoint in my CustomUrlInterceptor never fires, which probably means either my implementation of the intent for the redirect is the problem or the redirect-uri itself. Unfortunately I am out of ideas. I even tried adding the following to the Manifest.xml manually, taken from the Xamarin.Auth github:

<activity android:label="ActivityCustomUrlSchemeInterceptor"
    android:launchMode="singleTop" android:noHistory="true" android:name="md52ecc484fd43c6baf7f3301c3ba1d0d0c.ActivityCustomUrlSchemeInterceptor">
    <intent-filter>
      <action android:name="android.intent.action.VIEW" />
      <category android:name="android.intent.category.DEFAULT" />
      <category android:name="android.intent.category.BROWSABLE" />
      <data android:path="/oauth2redirect" />
      <data android:scheme="com.companyname.SApp" />
    </intent-filter>
  </activity>

Could a self signed certificate be the issue? Since the configuration of the identityserver changes very frequently at the moment, we haven't deployed it to azure yet. Today I started running the server locally on my notebook and connecting through the local network from the tablet with the app.

Update 2: We did deploy our identityserver4 to azure and sadly not much has changed. For some reason my CustomUrlInterceptor still doesn't get called. I did however find a way to manually trigger it through adb with

adb shell am start -a android.intent.action.VIEW -d "com.companyname.SApp:/oauth2redirect" com.companyname.SApp

Once I do that I get Xamarin.Auth.AuthException: Error authenticating: invalid_grant I am still looking into the reason why I suddenly get a invalid_grant, but the main problem of the browser not giving control back to the App is still there.

Another behaviour that confuses me is when I execute the adb command above while the app is closed it starts the application and immediately shows me a screen, that the app has stopped working. When I execute the command after I get the redirect screen of the browser (basically forcing the redirect when it should happen) I get to my breakpoint in the CustomUrlInterceptor, then I get to the OnAuthError Method and after that the App closes/crashes this. I get why it crashes in the first case but not in the second one.

1 Answers

On File CustomUrlSchemeInterceptorActivity change DataSchemes from "com.companyname.SApp" to yours GoogleRedirectLink: Example: com.googleusercontent.apps.1093596514437-d3rpjj7clslhdg3uv365qpodsl5tq4fn

Related