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.
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.
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.