Sign in with Azure AD B2C to Xamarin Android app

2019-08-02 15:01发布

问题:

After a week of researching authentication principles that would work with Azure AD B2C using the Xamarin to target the Android platform (not Xamarin.Forms), I'm finally asking for a little advice.

I've got an activity with a 'Sign in' button and I would like to log in to Azure on the button's touch event. Ideally I'd want to receive a token after the login steps are completed.

Here is the code that I have so far:

public class MainActivity : Activity
{
    public TaskCompletionSource<bool> ActivityResult { get; set; }
    public const int LocationActivityResult = 110;
    private static string AadInstance = "https://login.microsoftonline.com/{0}.onmicrosoft.com/";

    private PublicClientApplication _publicClientApplication;
    private string _authority;
    protected override void OnCreate(Bundle bundle)
    {
        base.OnCreate(bundle);

        // Set our view from the "main" layout resource
        SetContentView(Resource.Layout.Main);

        //partie pour le sign in 

        EditText editTextEmail = FindViewById<EditText>(Resource.Id.editTextEmail);
        EditText editTextPassword = FindViewById<EditText>(Resource.Id.editTextPassword);
        Button signIn = FindViewById<Button>(Resource.Id.buttonSignIn);

        signIn.Click += async (sender, e) =>
        {

            ConnectivityManager connectivityManager = (ConnectivityManager)GetSystemService(ConnectivityService);
            NetworkInfo networkInfo = connectivityManager.ActiveNetworkInfo;
            if (networkInfo == null)
            {
                Toast.MakeText(this, "Aucune connexion internet", ToastLength.Short).Show();
                Intent intent = new Intent(this.ApplicationContext, typeof(NotInternetActivity));
                intent.SetFlags(ActivityFlags.NewTask);
                StartActivity(intent);
            }
            else
            {

                /////essai pour la connexion
                _authority = string.Format(AadInstance, _azureSettings.Tenant);
                _publicClientApplication = new PublicClientApplication(
                    _authority,
                    _azureSettings.ClientId

                );
                await AcquireTokenAsync();

                /////passe sur la nouvelle actvité

                Intent intent = new Intent(this.ApplicationContext, typeof(PlantsActivity));
                intent.SetFlags(ActivityFlags.NewTask);
                StartActivity(intent);

            }

        };

    }
    Authentication _azureSettings = new Authentication
    {
        ClientId = "ClientId",
        ForgotPasswordPolicy = "ForgotPasswordPolicy",
        SignInOrSignUpPolicy = "SignInOrSignUpPolicy",
        Tenant = "Tenant"

    };

    protected override void OnActivityResult(int requestCode, [GeneratedEnum] Result resultCode, Intent data)
    {
        base.OnActivityResult(requestCode, resultCode, data);
        if (requestCode.Equals(LocationActivityResult))
        {
            if (CrossGeolocator.Current.IsGeolocationEnabled)
                this.ActivityResult.TrySetResult(true);
            else
                this.ActivityResult.TrySetResult(false);
        }
        else
        {
            AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
        }
    }

    public class Authentication
    {
        public string Tenant { get; set; }
        public string ClientId { get; set; }
        public string SignInOrSignUpPolicy { get; set; }
        public string ForgotPasswordPolicy { get; set; }
    }

    public Task<AuthenticationResult> AcquireTokenSilentAsync()
    {
        string[] scopes = { _azureSettings.ClientId };
        var res = _publicClientApplication.AcquireTokenSilentAsync(scopes, "", _authority, _azureSettings.SignInOrSignUpPolicy, false);
        return _publicClientApplication.AcquireTokenSilentAsync(scopes, "", _authority, _azureSettings.SignInOrSignUpPolicy, false);
    }

    public async Task<AuthenticationResult> AcquireTokenAsync()
    {
        string[] scopes = { _azureSettings.ClientId };
        return await _publicClientApplication.AcquireTokenAsync(scopes, "", UiOptions.SelectAccount, string.Empty, null, _authority, _azureSettings.SignInOrSignUpPolicy);
    }
}

I have put everything in the same class for now, just to test the outcomes. Any example that you could give me or any documentation on Xamarin.Android that you could point me too would be very helpful.

Thanks in advance.

回答1:

So after spending weeks on this I finally was able to do it.

So my app now has an Azure AD B2C backend, and I can authenticate from Xamarin Android (native) app and access data from my Easy Tables.

Here is how it works:

First thing you need is to get the token from Azure B2C, so once it is successfull, then authResult will hold the new user where you can access token and username.

PublicClientApplication publicClientApplication = new PublicClientApplication(AuthParameters.Authority, AuthParameters.ClientId);
var authResult = await publicClientApplication.AcquireTokenSilentAsync(AuthParameters.Scopes, "", AuthParameters.Authority, AuthParameters.Policy, false);
//      await Navigation.PushAsync(new SecurePage());
var result = authResult.Token;

            textbox.Text = authResult.User.Name;

Second thing is to send the token to your mobileserviceclient through loginasync..

JObject payload = new JObject();
payload["access_token"] = authResult.Token;
try
{
    var user = await MobileService.LoginAsync(MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory, payload);
}

This is the class AuthParameters which stores the data needed:

public class AuthParameters
{
    public const string Authority = "https://login.microsoftonline.com/YOURSITE.onmicrosoft.com/";
    public const string ClientId = "Client ID from B2C APP";
    public static readonly string[] Scopes = { ClientId };
    public const string Policy = "POLICY_NAME_FROM_B2CTenant";
}

Now on azure portal you should have an active Azure B2C App, and a mobile service client. They should be linked together, the way to link them is through this link

https://developer.xamarin.com/guides/xamarin-forms/cloud-services/authentication/azure-ad-b2c-mobile-app/

Now you should be able to access your easy table normally through the MobileServiceClient



回答2:

That code looks on the right track using MSAL .NET with Azure AD B2C.

The only thing (which may be intentional) worth calling out is your apps use of AcquireTokenAsync and AcquireTokenSilentAsync. Generally, the best pattern is to make the silent call (which will check the token cache for a token and fail if it cannot get you a valid access token), and then upon failure call the normal AcquireTokenAsync.

This will make it such that a user with valid tokens doesn't have to sign in over and over again each time they open the app or your app needs a token.

Microsoft has a code sample that shows how to use MSAL .NET (Xamarin) with Azure AD B2C. As always, the B2C Developer Guide is a great place to look for docs.