Emixam23
Emixam23

Reputation: 3964

Xamarin Auth - Facebook & GooglePlus

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 = "";

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";

Upvotes: 1

Views: 542

Answers (1)

Nick Kovalsky
Nick Kovalsky

Reputation: 6502

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);

Upvotes: 2

Related Questions