1

I'm currently begining to work with Xamarin.Auth and I'm facing 2 problems for which I'm stuck since 1 day and half..

I have this piece of code :

public class OAuth
{
    private Account account;
    private AccountStore store;

    // These values do not need changing
    public string Scope;
    public string AuthorizeUrl;
    public string AccessTokenUrl;
    public string UserInfoUrl;

    string clientId;
    string redirectUri;

    private Func<JObject, User> OAuthParser;

    private Action<User> OnCompleted;
    private Action<string> OnError;

    public OAuth()
    {
        account = null;
        store = null;

        Scope = "";
        AuthorizeUrl = "";
        AccessTokenUrl = "";
        UserInfoUrl = "";

        clientId = "";
        redirectUri = "";
    }

    public OAuth Facebook()
    {
        // These values do not need changing
        Scope = "public_profile";
        AuthorizeUrl = "https://m.facebook.com/dialog/oauth/";
        AccessTokenUrl = "";
        UserInfoUrl = "";

        switch (Device.RuntimePlatform)
        {
            case Device.iOS:
                clientId = "<insert IOS client ID here>";
                redirectUri = "<insert IOS redirect URL here>:/oauth2redirect";
                break;

            case Device.Android:
                clientId = "206316176526191";
                redirectUri = "http://www.facebook.com/connect/login_success.html";
                break;
        }

        OAuthParser = ParseFacebookResponse;

        return this;
    }

    public OAuth GooglePlus()
    {
        // These values do not need changing
        Scope = "https://www.googleapis.com/auth/userinfo.email";
        AuthorizeUrl = "https://accounts.google.com/o/oauth2/auth";
        AccessTokenUrl = "https://www.googleapis.com/oauth2/v4/token";
        UserInfoUrl = "https://www.googleapis.com/oauth2/v2/userinfo";

        switch (Device.RuntimePlatform)
        {
            case Device.iOS:
                clientId = "<insert IOS client ID here>";
                redirectUri = "<insert IOS redirect URL here>:/oauth2redirect";
                break;

            case Device.Android:
                clientId = "548539660999-muighch5brcrbae8r53js0ggdad5jt45.apps.googleusercontent.com";
                redirectUri = "com.googleusercontent.apps.548539660999-muighch5brcrbae8r53js0ggdad5jt45:/oauth2redirect";
                break;
        }

        OAuthParser = ParseGooglePlusResponse;

        return this;
    }

    public OAuth2Authenticator Authenticator(Action<User> onCompleted, Action<string> onError)
    {
        OAuth2Authenticator authenticator = new OAuth2Authenticator(
            clientId,
            null,
            Scope,
            new Uri(AuthorizeUrl),
            new Uri(redirectUri),
            new Uri(AccessTokenUrl),
            null,
            false);

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

        OnCompleted = onCompleted;
        OnError = onError;

        return authenticator;
    }

    private async void OnAuthCompleted(object sender, AuthenticatorCompletedEventArgs e)
    {
    OAuth2Authenticator OAuth2Authenticator = sender as OAuth2Authenticator;
    if (OAuth2Authenticator != null)
    {
        OAuth2Authenticator.Completed -= OnAuthCompleted;
        OAuth2Authenticator.Error -= OnAuthError;
    }

    //User user = null;
    if (e.IsAuthenticated)
    {
        // If the user is authenticated, request their basic user data from Google
        // UserInfoUrl = https://www.googleapis.com/oauth2/v2/userinfo
        var request = new OAuth2Request("GET", new Uri(UserInfoUrl), null, e.Account);
        var response = await request.GetResponseAsync();
        if (response != null)
        {
                // Deserialize the data and store it in the account store
                // The users email address will be used to identify data in SimpleDB
                string str = await response.GetResponseTextAsync();
                JObject jobject = JObject.Parse(str);
                User user = OAuthParser(jobject);

                OnCompleted(user);
        }

        if (account != null)
        {
            store.Delete(account, App.AppName);
        }

        await store.SaveAsync(account = e.Account, App.AppName);
    }
}

private void OnAuthError(object sender, AuthenticatorErrorEventArgs e)
{
        OAuth2Authenticator OAuth2Authenticator = sender as OAuth2Authenticator;

    if (OAuth2Authenticator != null)
    {
        OAuth2Authenticator.Completed -= OnAuthCompleted;
        OAuth2Authenticator.Error -= OnAuthError;
    }

    OnError("Authentication error: " + e.Message);
}

    private static User ParseGooglePlusResponse(JObject jobject)
    {
        try
        {
            User user = new User()
            {
                Email = jobject["email"].ToString(),
                Pseudo = jobject["name"].ToString(),
                Firstname = jobject["given_name"].ToString(),
                Surname = jobject["family_name"].ToString(),
                Image = jobject["picture"].ToString(),
                Password = "girafe"
            };
            return user;
        }
        catch (Exception e) 
        { Debug.WriteLine(e.ToString()); }
        return null;
    }

    private static User ParseFacebookResponse(JObject jobject)
    {
        try
        {
            Debug.WriteLine(jobject);
            User user = new User()
            {
                Email = jobject["email"].ToString(),
                Pseudo = jobject["name"].ToString(),
                Firstname = jobject["given_name"].ToString(),
                Surname = jobject["family_name"].ToString(),
                Image = jobject["picture"].ToString(),
                Password = "girafe"
            };
            return user;
        }
        catch (Exception e)
        { Debug.WriteLine(e.ToString()); }
        return null;
    }
}

So, I'm using this class like that:

private void FacebookAuthConnection()
    {
        AuthenticationState.Authenticator = OAuthService.Facebook().Authenticator(OAuthLoginCompleted, OAuthLoginError);
        Presenter.Login(AuthenticationState.Authenticator);
    }

However, the problems are coming.. First, GooglePlus works, but I got an alert on my android phone that says "Chrome Custom Tabs Doesn't Close ...", so the application crashes with an empty stack trace... I searched on the web and the only thing I got is to turn false the last parameter of new OAuth2Authenticator();, which doesn't work since google doesn't allow it from a webview...

So I was like, ok my code works, I get the user infos blablabla, let's try with facebook if it works from the native browser. However, I can't find the parameters URL...

Scope = "";
AuthorizeUrl = "";
AccessTokenUrl = "";
UserInfoUrl = "";
  • Scope seems to be public_profile
  • AuthorizationUrl would be https://m.facebook.com/dialog/oauth/

But what about the 2 others? I have them for Google+.

I'm really stuck and I feel like, each new seconds that passes, I get more and more frustrated...

Thank !


Edit

My actual values :

Scope = "email";
AuthorizeUrl = "https://www.facebook.com/v2.8/dialog/oauth";
AccessTokenUrl = "https://graph.facebook.com/oauth/access_token";
UserInfoUrl = "https://graph.facebook.com/me?fields=email,name,gender,picture\"";

clientId = "XXXX";
redirectUri = "http://www.facebook.com/connect/login_success.html";
Emixam23
  • 3,854
  • 8
  • 50
  • 107

1 Answers1

2

Facebook allows authorization in embedded web views. So what it did is used external auth (in chrome tabs) for google+ (on some devices it just doesn't return to my app after authorization) - like you did. But for Facebook and VKontakte i used xamarin.auth in webviews, it's much less problematic, you have total control on everything but the login view design. Except on UWP ofc, where the total process is still buggy.

Answering the question regarding facebook right params for webview auth process:

// OAuth Facebook
// For Facebook login, configure at https://developers.facebook.com/apps
public static string FacebookClientId = "XXX";
public static string FacebookScope = "email";
public static string FacebookAuthorizeUrl = "https://www.facebook.com/v2.9/dialog/oauth";
public static string FacebookAccessTokenUrl = "https://graph.facebook.com/oauth/access_token";

Used as:

auth = new OAuth2Authenticator(
                        clientId: Constants.FacebookClientId,  
                        scope: "email",
                        authorizeUrl: new Uri("https://www.facebook.com/v2.9/dialog/oauth"), // These values do not need changing
                        redirectUrl: new Uri("http://www.facebook.com/connect/login_success.html")// These values do not need changing
                    );

After what you get user's details with:

var request1 = new OAuth2Request("GET", new Uri("https://graph.facebook.com/me?fields=email,first_name,last_name,gender,picture"), null, eventArgs.Account);
Nick Kovalsky
  • 5,378
  • 2
  • 23
  • 50
  • Thank for the reply :) Pretty weird, I get something like at this link : https://stackoverflow.com/questions/37063685/facebook-oauth-the-domain-of-this-url-isnt-included-in-the-apps-domain . Do you want me to edit my code with the link I'm using? – Emixam23 Aug 31 '17 at 13:18
  • If you passed to the stage described at the above link means you're on a good way. Now time to set everything in your FB control panel correctly what is out of the scope of this thread. – Nick Kovalsky Aug 31 '17 at 14:07
  • Alright so it works, I added the redirect link also in the facebook developers panel, however, when the `OAuth2Authenticator.isUsingNativeUI` is set to `true`, facebook doesn't work when Google+ works fine. Now, if I revert the bool to `false`, facebook works but Google+ no.. any idea? – Emixam23 Aug 31 '17 at 16:28
  • Mate please read my post carefully. I gave you details for webview flow that you could use for facebook and others, but not native flow that you should use for google as it doesn't support webviews anymore. Use different OAuth2Authenticator setups for google and other providers. – Nick Kovalsky Aug 31 '17 at 20:02
  • It works really well, however, I do have a last question.. How can you know if the user has already register with facebook/google+, in the past, in order to register or login into my API? – Emixam23 Sep 01 '17 at 15:12
  • 1
    By creating your own auth api. http://bitoftech.net/2014/06/01/token-based-authentication-asp-net-web-api-2-owin-asp-net-identity/ – Nick Kovalsky Sep 03 '17 at 04:12