Skip to content
This repository has been archived by the owner on Jun 30, 2023. It is now read-only.

Leveraging brokers on Android and iOS

Bogdan Gavril edited this page Apr 9, 2020 · 25 revisions

If you are just getting started with brokers, or already using brokers for authentication, please look into migrating to, or directly starting with, MSAL.NET. As of release 4.3.0, MSAL.NET supports the iOS broker. Learn how to migrate from ADAL to MSAL here. Please note at this time, only iOS broker is supported. Android broker support will come in a future release.

Why use brokers on Xamarin.iOS and Xamarin.Android applications?

On Android and iOS, brokers enable:

  • Single Sign On (SSO). Your users won't need to sign-in to each application
  • Device identification (by accessing the device certificate which was created on the device when it was workplace joined)
  • Application identification verification (is it really outlook which calls me?). The way it works is when an application calls the broker, it passes its redirect url, and the broker verifies it:
    • On iOS, the redirect URL is, for instance, ms-word://com.msft.com, the broker parses and gets the appId (after the //) and verifies it's the same as the appId of the calling app, which it knows (by the OS).
    • On Android the redirect URLs have the following shape msauth://com.msft.word/<base64URL encoded hash>.

To enable one of these features, the application developers need to set the UseBroker Boolean to true in the platform parameters. They also need to implement a delegate to react to the broker calling back the application as described in Platform parameters properties specific to brokers on Android and iOS

Things are slightly different on iOS and Android. here are the details:

Brokered Authentication for iOS

If your Xamarin.iOS app requires conditional access or certificate authentication (currently in preview) support, you must set up your AuthenticationContext and redirectURI to be able to talk to the Microsoft Authenticator app. Make sure that your Redirect URI and application's bundle id is all in lower case.

Enable Broker support on your AuthenticationContext

Broker support is enabled on a per-authentication-context basis. It is disabled by default. You must set the useBroker flag to true in the PlatformParameters constructor if you wish ADAL.NET to call the broker:

public PlatformParameters(UIViewController callerViewController, bool useBroker)

Update AppDelegate to ensure that the broker calls back ADAL.NET

When ADAL.NET calls the broker, it will call back to your application, through the OpenUrl method of AppDelegate. Since ADAL waits for the token from the broker, your application needs to cooperate to call ADAL.NET back. You will do this by updating the AppDelegate.cs file to override the method below.

public override bool OpenUrl(UIApplication application, NSUrl url, string sourceApplication, NSObject annotation)
{            
	if (AuthenticationContinuationHelper.IsBrokerResponse(sourceApplication))
    {
		AuthenticationContinuationHelper.SetBrokerContinuationEventArgs(url);    
    }
	
    return true;
}

This method is invoked every time the application is launched and is used as an opportunity to process the response from the Broker and complete the authentication process initiated by ADAL.Net.

Register a URL Scheme

ADAL.Net uses URLs to invoke the broker and then return back to your app. To finish that round trip you need to register a URL scheme for your app. We recommend making the URL scheme fairly unique to minimize the chances of another app using the same URL scheme.

<key>CFBundleURLTypes</key>
<array>
    <dict>
        <key>CFBundleTypeRole</key>
        <string>Editor</string>
        <key>CFBundleURLName</key>
        <string>com.mycompany.myapp</string>
        <key>CFBundleURLSchemes</key>
        <array>
            <string>mytestiosapp</string>
        </array>
    </dict>
</array>

LSApplicationQueriesSchemes

ADAL uses –canOpenURL: to check if the broker is installed on the device. In iOS 9, Apple locked down what schemes an application can query for. You will need to add “msauth” to the LSApplicationQueriesSchemes section of your info.plist file.

<key>LSApplicationQueriesSchemes</key>
<array>
     <string>msauth</string>
</array>

Register your Redirect URI in the application portal

This adds extra requirements on your redirect URI. Your redirect URI must be in the proper form.

<app-scheme>://<your.bundle.id>
ex: mytestiosapp://com.mycompany.myapp

This Redirect URI needs to be registered on the app registration portal (https://portal.azure.com) as a valid redirect URI for your application. Additionally a second "msauth" form needs to be registered to handle certificate authentication in Azure Authenticator.

msauth://code/<broker-redirect-uri-in-url-encoded-form>
AND
msauth://code/<broker-redirect-uri-in-url-encoded-form>/
ex: msauth://code/mytestiosapp%3A%2F%2Fcom.mycompany.myapp and msauth://code/mytestiosapp%3A%2F%2Fcom.mycompany.myapp/  

Brokered Authentication for Android

If your Xamarin.Android app or your app users requires conditional access or certificate authentication support, you must set up your AuthenticationContext and redirectURI to be able to talk to the Microsoft Authenticator app OR the Company Portal app. Both are brokers on Android. Make sure that your Redirect URI and application's bundle id is all in lower case.

Enable support for Broker on Your AuthencationContext

Leveraging a broker is enabled on a per-authentication-context basis. It is disabled by default. You must set the useBroker flag to true in PlatformParameters constructor if you wish ADAL to call to broker:

public PlatformParameters(Activity callerActivity, bool useBroker)
public PlatformParameters(Activity callerActivity, bool useBroker, PromptBehavior promptBehavior)

Ensure your app has access to the Broker accounts

Google has introduced an extra security measure for apps wanting to access accounts. ADAL needs to list all the "Microsoft" accounts, i.e. all the accounts created by the broker app. Currently, it is your responsibility to ensure your app has access to this - by invoking the Android Account Picker.

string WORK_AND_SCHOOL_TYPE = "com.microsoft.workaccount";
           
// this variable should now have all the accounts in the broker
AccountManager accManager = AccountManager.Get(Application.Context);
Account[] accounts = accManager.GetAccountsByType(WORK_AND_SCHOOL_TYPE);

if (accounts == null || accounts.Length == 0)
{
    // This will pop-up a dialog with an account picker. 
    // MSAL has a much better implementation of this, and does not require this dialog!
    // See the Android docs for customizing the UI https://developers.google.com/android/reference/com/google/android/gms/common/AccountPicker                
    Intent intent = AccountManager.NewChooseAccountIntent(
        null, null, new[] { WORK_AND_SCHOOL_TYPE }, null, null, null, null);
    // Start an activity with this intent, e.g. 
    this.StartActivity(intent);

    // TODO: figure out a way to wait for the user to choose a value in the account picker
}

If you do not do this, silent calls to the broker (e.g. when the access token expires in 1h) will fail and the broker will have to invoked explicitly, degrading the user experience because they have to login again.

As far as we have tested, it is sufficient to perform this call only once for your app.

Update the application manifest

If you target Android versions lower than 23, calling app requires having the following permissions declared in the application manifest(http://developer.android.com/reference/android/accounts/AccountManager.html):

  • GET_ACCOUNTS
  • USE_CREDENTIALS
  • MANAGE_ACCOUNTS

If your application targets Android 23 or above, the USE_CREDENTIALS and MANAGE_ACCOUNTS permissions have been deprecated and GET_ACCOUNTS is under protection level "dangerous". Your app is responsible for requesting the permission for GET_ACCOUNTS. You can reference permission request for API 23.

Request access

Register a Redirect URI for your application

ADAL uses URLs to invoke the broker and then return back to your app. To finish that round trip you need to register a URL scheme for your app. This Redirect URI needs to be registered on the Azure AAD app registration portal as a valid redirect URI for your application.

The redirect URI needed for your application is dependent on the certificate used to sign the APK.

Example: msauth://com.microsoft.xforms.testApp/hgbUYHVBYUTvuvT&Y6tr554365466=

The last part of the URI, hgbUYHVBYUTvuvT&Y6tr554365466=, is the signature that the APK is signed with, base64 encoded. However, during the development phase of your application using Visual Studio, if you are debugging your code without signing the apk with a specific certificate, Visual Studio will sign the apk for you for debugging purposes, giving the APK a unique signature for the machine that it is built on. Thus, each time you build your app on a different machine, you will need to update the redirect URI in the application’s code and the application’s registration in the azure portal in order to authenticate with MSAL.

While debugging, you may encounter an MSAL exception (or log message) stating the redirect URI provided is incorrect. This exception will also provide you with the redirect URI that you should be using with the current machine you are debugging on. You can use this redirect URI to continue developing for the time being.

Once you are ready to finalize your code, be sure to update the redirect URI in the code and on the application's registration in the azure portal to use the signature of the certificate you will be signing the APK with.

In practice this means that you have to register a redirect URI for each member of the team, plus a redirect URI for the production signed version of the APK.

You can also compute this signature yourself, similar to how MSAL does it:

        private string GetRedirectUriForBroker()
        {
            string packageName = Application.Context.PackageName;
            string signatureDigest = this.GetCurrentSignatureForPackage(packageName);
            if (!string.IsNullOrEmpty(signatureDigest))
            {
                return string.Format(CultureInfo.InvariantCulture, "{0}://{1}/{2}", RedirectUriScheme,
                    packageName.ToLowerInvariant(), signatureDigest);
            }

            return string.Empty;
        }

        private string GetCurrentSignatureForPackage(string packageName)
        {
                PackageInfo info = Application.Context.PackageManager.GetPackageInfo(packageName,
                    PackageInfoFlags.Signatures);
                if (info != null && info.Signatures != null && info.Signatures.Count > 0)
                {
                    // First available signature. Applications can be signed with multiple signatures.
                    // The order of Signatures is not guaranteed.
                    Signature signature = info.Signatures[0];
                    MessageDigest md = MessageDigest.GetInstance("SHA");
                    md.Update(signature.ToByteArray());
                    return Convert.ToBase64String(md.Digest(), Base64FormattingOptions.None);
                    // Server side needs to register all other tags. ADAL will
                    // send one of them.
                }
           
        }

You also have the option of acquiring the signature for your package by using the keytool with the following commands:

For Windows: keytool.exe -list -v -keystore "%LocalAppData%\Xamarin\Mono for Android\debug.keystore" -alias androiddebugkey -storepass android -keypass android

For Mac: keytool -exportcert -alias androiddebugkey -keystore ~/.android/debug.keystore | openssl sha1 -binary | openssl base64

Update MainActivity.cs to call back ADAL.NET

When ADAL.NET calls the broker, it will call back your application, through the OnActivityResult method of your Android application MainActivity class. Since ADAL waits for the token from the broker, your application needs to cooperate to call ADAL.NET back. You will do this by updating the MainActivity.cs file to the override method below.

protected override void OnActivityResult(int requestCode, Result resultCode, Intent data)
{
    base.OnActivityResult(requestCode, resultCode, data);
	AuthenticationAgentContinuationHelper.SetAuthenticationAgentContinuationEventArgs(requestCode, resultCode, data);
}

This method is invoked when the activity receives a callback from the webview or the broker application. This code snippet is required to complete the authentication process initiated by ADAL.NET

In Silent calls, make sure the UserIdentifier matches the entry in the Broker

When performing a silent call to fetch a token, ADAL must communicate with the Broker to find out which broker account to fetch a token for. ADAL uses the information you pass in as UserIdentifier to locate the account.

// perform an interactive auth first to get a result
_userName = result.UserInfo.DisplayableId; // this is in UPN (email) format, which the matches the account name on the broker

// later, in silent calls
var userId = new UserIdentifier(_userName, UserIdentifierType.OptionalDisplayableId);

AuthenticationResult result = await _ctx
   .AcquireTokenSilentAsync(
   resource,
   clientId,
   userId,
   new PlatformParameters(this, true)) // you need to create PlatformParamters even in the silent call
.ConfigureAwait(false);

Known issue with Xamarin Android Acquire Token Silent and Intune Company Portal

First reported in this issue, and first noticed in ADAL 5.1 and Intune Company Portal 5.0.4464.0, there can be a cache miss on the acquire token silent call which results in the Company Portal appearing on every acquire token silent call.

Mitigation

Use the tenant GUID version of the authority instead of the string representation. If the string version is used, ADAL will fail during LoadFromCacheCommon because there is a comparison of the authority with the cache key authority. Pass in the GUID value for the authority to resolve the issue.

Example:

Switch from this:

   "https://login.microsoftonline.com/cats.onmicrosoft.com/"

To this:

"https://login.microsoftonline.com/2b972g99-74ef-4vev-b56y-cat0c70bcat2c/"

Broker and Refresh token: a difference between iOS and Android

  • On iOS, when calling the broker, the AuthenticationResult will get both the Access Token and the Refresh Token. This is because on iOS there are no background apps.
  • On Android, when calling the broker, the AuthenticationResult will get only the Access Token. This is because on Android, your application can call the broker in the background, and the broker refreshes the token silently if needed. it does not need to share the refresh token with your app.

Troubleshooting

  • You have configured the use of the broker, but instead of the broker (Authenticator or Company Portal) popping up, the regular browser pops up. This can happen if ADAL is not able to find or invoke the broker, permissions are not set etc. Please enable logging and inspect the logs to find the root cause.
Clone this wiki locally