-
Notifications
You must be signed in to change notification settings - Fork 145
Migrating from ADAL Objective C to MSAL Objective C
Microsoft Azure Active Directory Authentication Library (ADAL Objective-C) was created to work with the Azure Active Directory v1.0 endpoint.
The Microsoft Authentication Library (MSAL) is meant to work with the Microsoft Identity platform (also known as Azure Active Directory v2.0 endpoint).
Microsoft Identity platform has a few key differences with Azure Active Directory v1.0 endpoint.
- ADAL supports only work and school accounts
- MSAL supports work and school accounts, as well as personal Microsoft accounts (MSA) such as hotmail.com, outlook.com, and msn.com for Microsoft identity platform endpoint. MSAL also supports Azure Active Directory B2C
- The Microsoft Identity Platform endpoint is built in compliance with OAuth 2.0 and OIDC standards.
- Azure Active Directory v1.0 endpoint requires all permissions to be declared in advance on the application registration. This means those permissions are static.
- The Microsoft Identity Platform endpoint supports requesting permissions dynamically. This means app can ask for a bare minimum of permissions upfront and requesting more over time when you add additional app functionalities.
You can read more about Azure Active Directory v1.0 and the Microsoft Identity Platform differences here
The public API of MSAL SDKs reflects a few key differences between Azure Active Directory endpoints.
ADAuthenticationContext is the first object to create for all ADAL scenarios. Developers should create new instances of ADAuthenticationContext per AAD cloud and a tenant (authority) combination. Same ADAuthenticationContext could be used to get tokens for multiple public client applications.
In MSAL, the main object to interact with is MSALPublicClientApplication which is modeled after OAuth 2.0 Public Client. It means one instance of MSALPublicClientApplication can be used to interact with multiple AAD clouds and tenants without needing to create new instance for each authority. For most apps, however, one instance of MSALPublicClientApplication is sufficient.
In ADAL, to acquire tokens from Azure Active Directory v1.0 endpoint, developer has to provide a resource identifier. A resource can define a number of scopes or oAuth2Permissions that it understands, allowing client apps to request tokens from that resource for a certain set of scopes.
In MSAL, instead of a single resource identifier, developer needs to provide a set of scopes.
A scope is generally represented by a resource identifier followed by a permission name (resource/permission, e.g. https://graph.windows.net/directory.write)
There're two ways to provide scopes in MSAL:
- Provide an exact list of permissions that your apps needs:
e.g.
@[@"https://graph.windows.net/directory.read", @"https://graph.windows.net/directory.write"]
In this case, app needs directory.read and directory.write permissions for a certain functionality. End user will be asked for a consent for those two permissions if user hasn't consented to them yet. Application might receive additional permissions that end user has already consented for the application. End user will only be prompted to consent for new permissions.
- Use
/.default
scope
This is a built-in scope for every application that refers to the static list of permissions configured on the application registration. Requesting this scope provides the same functionality as the v1.0 endpoint through resource parameter, and is therefore useful for migrating apps from ADAL to MSAL with minimum changes.
In order to use /.default
scope, append /.default
string to your resource identifier (e.g. https://graph.microsoft.com/.default
. Please note that if your resource ends with a slash (/
), you should still append /.default
string with a starting slash. This means that the resulting scope will have a double slash in it.
You can read more information about using the "/.default" scope here
ADAL Objective-C only supports UIWebView/WKWebView for iOS and WebView for macOS. MSAL for iOS supports more technologies for displaying web content when requesting an authorization code.
By default, MSAL will use ASWebAuthenticationSession on iOS 12+ devices, which is Apple's recommended web component for authentication, and provides SSO benefits through cookie sharing.
However, depending on the desired end user experience and app requirements, developer may choose to use a different web component. See detailed documentation on supported web view types here.
When migrating from ADAL Objective-C to MSAL, WKWebView will provide the most similar end user experience with ADAL on iOS.
As a result of ADAL acquireToken and acquireTokenSilent calls, developer receives ADUserInformation object containing a list of claims from the id_token
that represents account being authenticated. Additionally, ADUserInformation also returns userId
property that is generally based on upn
claim. After initial interactive token acquisition, ADAL expects developer to provide userId
in all silent calls.
ADAL did not provide an API to retrieve known user identities
at a later point, and relied on developer saving and managing those accounts.
Similarly to ADAL, MSAL returns account information which holds a list of claim from the id_token
in its result object (MSALResult).
However, MSAL also provides a set of APIs that allows listing all accounts known to MSAL at a later point without having to perform token acquisition. MSAL also provides a set of APIs to remove accounts, which effectively marks that account as not accessible to the app. After account removal, next token acquisition will fail and request user to perform interactive token acquisition. Account removal only applies to the client application that initiated it, and will not remove account from other apps running on the device, nor from system browser.
Additionally, MSAL also returns account identifier that can be used to request a token silently at a later point. However, the account identifier is not displayable and is opaque to the app (homeAccountId
).
When migrating from ADAL Objective-C SDK, application would normally have ADAL's userId
stored and it doesn't have homeAccountId
that is required by MSAL. As a one time migration step, developer can query MSAL account by ADAL's userId using the following API:
- (nullable MSALAccount *)accountForUsername:(nonnull NSString *)username error:(NSError * _Nullable __autoreleasing * _Nullable)error;
This API will read both MSAL's and ADAL's cache and find the account by ADAL userId (UPN).
If account is found, developer should use the account to perform silent token acquisition. First silent token acquisition will effectively "upgrade" the account and developer will get MSAL compatible account identifier in MSAL result (homeAccountId
). After that, only homeAccountId
should be used for following account lookups through following API:
- (nullable MSALAccount *)accountForHomeAccountId:(nonnull NSString *)homeAccountId error:(NSError * _Nullable __autoreleasing * _Nullable)error;
Although it's possible to continue using ADAL's userId for all operations in MSAL, since userId is based on UPN, it is subject to multiple limitations that result in bad user experience (e.g. user has to sign in again if UPN changes). Therefore, it's highly advisable for all apps to be using non displayable homeAccountId
for all operations.
You can read more about cache state migration here
MSAL Objective-C can have SSO through unified cache with following SDKs:
- ADAL Objective-C 2.7.x+
- MSAL.NET for Xamarin 2.4.x+
- ADAL.NET for Xamarin 4.4.x+
SSO is achieved through iOS keychain sharing and is only available between apps published by the same Apple Developer account.
SSO through iOS keychain sharing is the only silent SSO type.
MSAL Objective-C also supports 2 other types of SSO:
- SSO through web browser. MSAL supports ASWebAuthenticationSession, which provides SSO through cookies with other apps on the device and Safari browser.
- SSO through Authentication broker. On iOS device, Microsoft Authenticator acts as the Authentication broker. It allows complying with certain conditional access policies (e.g. require compliant device), and provides SSO for registered devices. Latest MSAL SDKs starting with 0.3.0 support broker by default.
MSAL introduced a few calling pattern changes over ADAL.
- Similarly to ADAL, acquireTokenSilent call always results in a silent request.
- Differently from ADAL, acquireToken call always results in an interactive request. Interactive request means that user will be presented with user actionable UI either through web view or Microsoft Authenticator. Depending on SSO state inside webview/Microsoft Authenticator, user might or might be not prompted to enter credentials.
- In ADAL, acquireToken with AD_PROMPT_AUTO would first attempt to perform silent token acquisition, and only show UI if silent request failed. In MSAL, this logic can be achieved by first calling acquireTokenSilent and only calling acquireToken if silent acquisition fails. This allows developers to customize user experience before starting interactive token acquisition.
MSAL provides better clarity over errors that can be handled in runtime, and errors that should result in a generic error message for the end user. There are very limited number of errors developer should handle:
- MSALErrorInteractionRequired: The user must perform an interactive request. This can be caused by a multitude of reasons including expired auth session or additional auth requirements.
- MSALErrorServerDeclinedScopes: The request was not fully completed and some scopes were not granted access to. This can be caused by a user declining consent on certain scopes.
More info about MSAL error handling can be found here
MSAL provides support for brokered authentication with Microsoft Authenticator starting with version 0.3.0. Microsoft Authenticator enables support for conditional access scenarios.
Please follow next steps to enable broker for your application:
- Register a broker compatible Redirect URI format for the application. Broker compatible Redirect URI format is
msauth.<app.bundle.id>://auth
. Please replace<app.bundle.id>
with your application's bundle ID. If you're migrating from ADAL and your application was already broker capable, there's nothing extra you need to do. Your previous Redirect URI is fully compatible with MSAL, so you can skip to step 3. - Add your application's redirect URI scheme to your info.plist file. For the default MSAL Redirect URI, it will be in the format of
msauth.<app.bundle.id>
.
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.<app.bundle.id></string>
</array>
- Add following schemes to your app's Info.plist under LSApplicationQueriesSchemes:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauth</string>
<string>msauthv2</string>
</array>
- You will need to add the following to your AppDelegate.m file to handle callbacks:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options`
{
return [MSALPublicClientApplication handleMSALResponse:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]];
}
More information about AAD authentication brokers and MSAL can be found [here](TODO, add link)
In ADAL Objective-C developer is required to create separate instances of ADAuthenticationContext for each tenant that the app is requesting tokens for. This is no longer a requirement in MSAL, where developer can create a single instance of MSALPublicClientApplication and use it for any AAD cloud and organization by specifying a different authority for acquireToken and acquireTokenSilent calls.
Intune MAM SDK doesn't yet support MSAL Objective-C. Once it does, you'll need to update to the TODO - add version version of Intune MAM SDK in order to support MSAL.
ADAL Objective-C starting with version 2.7.0 cannot coexist with MSAL in the same application. The main reason is the shared submodule common code. With Objective-C's lack of support for namespaces, if you try to have both ADAL and MSAL frameworks in your application there'll be two instances of the same class with no guarantee on which one will get picked in runtime.
If both SDKs are using same version of the conflicting class, your app will likely just get a warning, but still work. However, if it's a different version, your app might experience unexpected crashes that are difficult to catch.
Due to those reasons, we do not support running ADAL and MSAL in the same production application.
However, if you're just testing and migrating your users from ADAL Objective-C to MSAL Objective-C, you can continue using ADAL Objective-C 2.6.10 - TODO create release. Please note that it's the only version that works with MSAL in the same application. There'll be no new feature updates for this ADAL version, so it should be only used for migration and testing purposes. Your app shouldn't rely on this ADAL version co-existing with MSAL long term, as it's not something we will officially support outside of testing and migration.
No changes are required to your existing AAD application to switch to MSAL and enable AAD accounts. However, if your ADAL based application didn't support brokered authentication, you'll need to register a new Redirect URI for the application to switch to MSAL.
The Redirect URI should be in the following format: msauth.<app.bundle.id>://auth
. Please replace <app.bundle.id>
with your application's bundle ID. The Redirect URI needs to be specified in the Azure Portal
Additionally, to support cert-based authentication an additional Redirect URI needs to be registered in your application and the Azure portal in the following format msauth://code/<broker-redirect-uri-in-url-encoded-form>
ex: msauth://code/msauth.com.microsoft.mybundleId%3A%2F%2Fauth
We recommend all apps to register both Redirect URIs.
If you wish to add support for incremental consent, you can check a list of the APIs and permissions that your app is configured to request access to in your app registration under "API permissions" tab.
If you're migrating from ADAL and wish to support both AAD and MSA accounts, your existing application will need to be updated to support both audiences. It's currently not recommended to update your existing production app to support both AAD and MSA right away. Instead, create another clientID that supports both AAD and MSA for testing first, and only after you have verified that all scenarios work, proceed updating existing app.
You can add MSAL SDK to your app using your preferred package management tool. See detailed instructions here.
Add your application's redirect URI scheme to your info.plist file if it's not already there (for ADAL broker compatible apps it should be already done, so skip this step). For the default MSAL Redirect URI, it will be in the format of msauth.<app.bundle.id>
.
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.<app.bundle.id></string>
</array>
Add following schemes to your app's Info.plist under LSApplicationQueriesSchemes:
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauth</string>
<string>msauthv2</string>
</array>
(If you already have msauth declared, only add msauthv2).
You will need to add the following to your AppDelegate.m file:
- (BOOL)application:(UIApplication *)app openURL:(NSURL *)url options:(NSDictionary<NSString *,id> *)options`
{
return [MSALPublicClientApplication handleMSALResponse:url sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]];
}
This will allow MSAL handle responses from broker and web component. Note that this wasn't necessary in ADAL since it "swizzled" app delegate methods automatically. However, we wanted to avoid doing that error prone operation in MSAL and give application more control.
You can create MSALPublicClientApplication using following code:
NSError *error = nil;
MSALPublicClientApplicationConfig *configuration = [[MSALPublicClientApplicationConfig alloc] initWithClientId:@"<your-client-id-here>"];
MSALPublicClientApplication *application =
[[MSALPublicClientApplication alloc] initWithConfiguration:configuration
error:&error];
After that, call account management API to see if there're any accounts in cache:
NSError *error = nil;
MSALAccount *account = [application accountForHomeAccountId:accountIdentifier error:&error];
or read all of the accounts:
NSError *error = nil;
NSArray<MSALAccount *> *accounts = [application allAccounts:&error];
If account is found, call MSAL acquireTokenSilent API:
MSALSilentTokenParameters *silentParameters = [[MSALSilentTokenParameters alloc] initWithScopes:@[@"<your-resource-here>/.default"] account:account];
[application acquireTokenSilentWithParameters:silentParameters
completionBlock:^(MSALResult *result, NSError *error)
{
if (!error)
{
NSString *accessToken = result.accessToken;
// Use your token
}
else
{
// Check the error
if ([error.domain isEqual:MSALErrorDomain] && error.code == MSALErrorInteractionRequired)
{
// Interactive auth will be required
}
// Other errors may require trying again later, or reporting authentication problems to the user
}
}];
- Customizing Browsers and WebViews
- Logging
- Sovereign clouds
- B2C
- Auth Telemetry (coming soon)
- MSAL questions, bugs and issues (coming soon)
- Redirect URIs
- Requesting individual claims
- Keychain cache
- SSL issues
- iOS 13 and macOS 10.15 support
- Releases
- Roadmap (coming soon)