Secure resources using Xamarin, and Intune MAM SDK on iOS

Scenario

Disclaimer: This is my personal blog and not official guide from Microsoft.

There are scenarios when enterprise data needs to be protected with mechanism that is more secure than just authenticating the user.
It can be achieved by deploying App protection policy(ies) and provisioning conditional access to resource. This blog explains how to do it with Xamarin and Intune MAM SDK for iOS platform.

Configuration

Here is the high-level picture of how overall system looks like.
Overview

Azure hosts a protected resource that can be accessed via WebAPI within a defined scope. This resource can be your application or a third party resource such as OneDrive. App can request access token to that resource(scope) from Azure AD using MSAL.NET.

To protect the resource from being accessed in undesired conditions, Azure Active Directory (AAD) ensures that certain conditions are met prior to issuing the access token. These conditions are defined in the Conditional Access Policy.

Conditional Access policy can have one or more access conditions defined in it. One such condition is having App Protection policy. The App protection policy is defined in the endpoint. The policy can be applied to a group of users that are using the App. The detailed steps on how to configure it are illustrated here

Workflow

  • When an App requests access token from Azure AD via MSAL.NET, AAD checks if the desired scope has any applicable conditional acecss policy.
  • The conditional access policy is evaluated. If the conditional access policy includes App Protection Policy, AAD verifies has that the App is compliant with it by verifying the EnrollmentId.
  • If the App is not compliant, AAD returns sub error "protection_policy_required".
  • MSAL.NET will check check the error and throw IntuneAppProtectionPolicyRequiredException.
  • It is the App’s responsibility to catch the exception and make the App compliant. App can use MAM SDK for it as shown in the sample code.
  • After the App becomes compliant, it should call AcquireTokenSilent method for obtaining the token.
  • MSAL.NET will then internally retrieve the enrollmentID and pass it to the AAD.
  • With valid enrollmentId, AAD will return access token for the resource. UML

Code Snippets

App code that seeks access to protected scope "Hello.World"

            // The following parameters are for sample app in lab4. Please configure them as per your app registration.
            // And also update corresponding entries in info.plist -> IntuneMAMSettings -> ADALClientID and ADALRedirectUri
            string clientId = "bd9933c9-a825-4f9a-82a0-bbf23c9049fd";
            string redirectURI = $"msauth.com.xamarin.microsoftintunemamsample://auth";
            string tenantID = "f645ad92-e38d-4d1a-b510-d1b09a74a8ca";
            string[] Scopes = { "api://a8bf4bd3-c92d-44d0-8307-9753d975c21e/Hello.World" }; // needs admin consent
            string[] clientCapabilities = { "ProtApp" }; // Important: This must be passed to the PCABuilder

           try
            {
                    string authority = $"https://login.microsoftonline.com/{tenantID}/";
                    var pcaBuilder = PublicClientApplicationBuilder.Create(clientId)
                                                                        .WithRedirectUri(redirectURI)
                                                                        .WithIosKeychainSecurityGroup("com.microsoft.adalcache")
                                                                        .WithLogging(MSALLogCallback, LogLevel.Verbose)
                                                                        .WithAuthority(authority)
                                                                        .WithClientCapabilities(clientCapabilities)
                                                                        .WithHttpClientFactory(new HttpSnifferClientFactory())
                                                                        .WithBroker(true);
                    PCA = pcaBuilder.Build();
                }

                // attempt silent login.
                // If this is very first time and the device is not enrolled, it will throw MsalUiRequiredException
                // If the device is enrolled, this will succeed.
                var authResult = await DoSilentAsync(Scopes).ConfigureAwait(false);
                ShowAlert("Success Silent 1", authResult.AccessToken);
            }
            catch (MsalUiRequiredException _)
            {
                // This executes UI interaction
                try
                {
                    var interParamBuilder = PCA.AcquireTokenInteractive(Scopes)
                                                .WithParentActivityOrWindow(this)
                                                .WithUseEmbeddedWebView(true);

                    var authResult = await interParamBuilder.ExecuteAsync().ConfigureAwait(false);
                    ShowAlert("Success Interactive", authResult.AccessToken);
                }
                catch (IntuneAppProtectionPolicyRequiredException ex)

App code catches the exception and calls MAM SDK to make the App compliant. It will wait for the compliance.

            catch (IntuneAppProtectionPolicyRequiredException ex)
            {
                _manualReset.Reset();

                IntuneMAMComplianceManager.Instance.RemediateComplianceForIdentity(ex.Upn, false);
                _manualReset.WaitOne();
            }

After the App becomes compliant, it will be notified in a delegate. The delegate will set the flag on the semaphore.

        public async override void IdentityHasComplianceStatus(string identity, IntuneMAMComplianceStatus status, string errorMessage, string errorTitle)
        {
            if (status == IntuneMAMComplianceStatus.Compliant)
            {
                _manualReset.Set();
            }
        }

When the semaphore is released, App should call the Silent token acquisition method.

        var accts = await PCA.GetAccountsAsync().ConfigureAwait(false);
        var acct = accts.FirstOrDefault();
        if (acct != null)
        {
            try
            {
                var silentParamBuilder = PCA.AcquireTokenSilent(Scopes, acct);
                var authResult = await silentParamBuilder.ExecuteAsync().ConfigureAwait(false);
                ShowAlert("Success Silent 1", authResult.AccessToken);
            }
        }

The complete sample app is here.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s