Get Started | iOS Sample Code | macOS Sample Code | Library reference | Support |
---|
The MSAL library for iOS and macOS gives your app the ability to begin using the Microsoft Identity platform by supporting Azure Active Directory and Microsoft Accounts in a converged experience using industry standard OAuth2 and OpenID Connect. The library also supports Azure AD B2C for those using our hosted identity management service.
let config = MSALPublicClientApplicationConfig(clientId: "<your-client-id-here>")
let scopes = ["your-scope1-here", "your-scope2-here"]
if let application = try? MSALPublicClientApplication(configuration: config) {
#if os(iOS)
let viewController = ... // Pass a reference to the view controller that should be used when getting a token interactively
let webviewParameters = MSALWebviewParameters(authPresentationViewController: viewController)
#else
let webviewParameters = MSALWebviewParameters()
#endif
let interactiveParameters = MSALInteractiveTokenParameters(scopes: scopes, webviewParameters: webviewParameters)
application.acquireToken(with: interactiveParameters, completionBlock: { (result, error) in
guard let authResult = result, error == nil else {
print(error!.localizedDescription)
return
}
// Get access token from result
let accessToken = authResult.accessToken
// You'll want to get the account identifier to retrieve and reuse the account for later acquireToken calls
let accountIdentifier = authResult.account.identifier
})
}
else {
print("Unable to create application.")
}
NSError *msalError = nil;
MSALPublicClientApplicationConfig *config = [[MSALPublicClientApplicationConfig alloc] initWithClientId:@"<your-client-id-here>"];
NSArray<NSString *> *scopes = @[@"your-scope1-here", @"your-scope2-here"];
MSALPublicClientApplication *application = [[MSALPublicClientApplication alloc] initWithConfiguration:config error:&msalError];
#if TARGET_OS_IPHONE
UIViewController *viewController = ...; // Pass a reference to the view controller that should be used when getting a token interactively
MSALWebviewParameters *webParameters = [[MSALWebviewParameters alloc] initWithAuthPresentationViewController:viewController];
#else
MSALWebviewParameters *webParameters = [MSALWebviewParameters new];
#endif
MSALInteractiveTokenParameters *interactiveParams = [[MSALInteractiveTokenParameters alloc] initWithScopes:scopes webviewParameters:webParameters];
[application acquireTokenWithParameters:interactiveParams completionBlock:^(MSALResult *result, NSError *error) {
if (!error)
{
// You'll want to get the account identifier to retrieve and reuse the account
// for later acquireToken calls
NSString *accountIdentifier = result.account.identifier;
NSString *accessToken = result.accessToken;
}
else
{
// Check the error
}
}];
You can use CocoaPods to install MSAL
by adding it to your Podfile
under target:
use_frameworks!
target 'your-target-here' do
pod 'MSAL'
end
You can use Carthage to install MSAL
by adding it to your Cartfile
:
github "AzureAD/microsoft-authentication-library-for-objc" "master"
You can also use Git Submodule or check out the latest release and use as framework in your application.
- Register your app in the Azure portal
- Make sure you register a redirect URI for your application. It should be in the following format:
msauth.[BUNDLE_ID]://auth
- Add a new keychain group to your project Capabilities. Keychain group should be
com.microsoft.adalcache
on iOS andcom.microsoft.identity.universalstorage
on macOS.
See more information about keychain groups and Silent SSO for MSAL.
- Add your application's redirect URI scheme to your
Info.plist
file, it will be in the format ofmsauth.[BUNDLE_ID]
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>msauth.[BUNDLE_ID]</string>
</array>
</dict>
</array>
- Add
LSApplicationQueriesSchemes
to allow making call to Microsoft Authenticator if installed.
Note that "msauthv3" scheme is needed when compiling your app with Xcode 11 and later.
<key>LSApplicationQueriesSchemes</key>
<array>
<string>msauthv2</string>
<string>msauthv3</string>
</array>
See more info about configuring redirect uri for MSAL
- To handle a callback, add the following to
appDelegate
:
func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
return MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: options[UIApplication.OpenURLOptionsKey.sourceApplication] as? String)
}
- (BOOL)application:(UIApplication *)app
openURL:(NSURL *)url
options:(NSDictionary<UIApplicationOpenURLOptionsKey,id> *)options
{
return [MSALPublicClientApplication handleMSALResponse:url
sourceApplication:options[UIApplicationOpenURLOptionsSourceApplicationKey]];
}
Note, that if you adopted UISceneDelegate on iOS 13+, MSAL callback needs to be placed into the appropriate delegate method of UISceneDelegate instead of AppDelegate. MSAL handleMSALResponse:sourceApplication:
must be called only once for each URL. If you support both UISceneDelegate and UIApplicationDelegate for compatibility with older iOS, MSAL callback would need to be placed into both files.
func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
guard let urlContext = URLContexts.first else {
return
}
let url = urlContext.url
let sourceApp = urlContext.options.sourceApplication
MSALPublicClientApplication.handleMSALResponse(url, sourceApplication: sourceApp)
}
- (void)scene:(UIScene *)scene openURLContexts:(NSSet<UIOpenURLContext *> *)URLContexts
{
UIOpenURLContext *context = URLContexts.anyObject;
NSURL *url = context.URL;
NSString *sourceApplication = context.options.sourceApplication;
[MSALPublicClientApplication handleMSALResponse:url sourceApplication:sourceApplication];
}
- Make sure your application is signed with a valid development certificate. While MSAL will still work in the unsigned mode, it will behave differently around cache persistence.
Use the client ID from your app listing when initializing your MSALPublicClientApplication object:
let config = MSALPublicClientApplicationConfig(clientId: "<your-client-id-here>")
let application = try? MSALPublicClientApplication(configuration: config)
NSError *msalError = nil;
MSALPublicClientApplicationConfig *config = [[MSALPublicClientApplicationConfig alloc] initWithClientId:@"<your-client-id-here>"];
MSALPublicClientApplication *application = [[MSALPublicClientApplication alloc] initWithConfiguration:config error:&msalError];
#if os(iOS)
let viewController = ... // Pass a reference to the view controller that should be used when getting a token interactively
let webviewParameters = MSALWebviewParameters(authPresentationViewController: viewController)
#else
let webviewParameters = MSALWebviewParameters()
#endif
let interactiveParameters = MSALInteractiveTokenParameters(scopes: scopes, webviewParameters: webviewParameters)
application.acquireToken(with: interactiveParameters, completionBlock: { (result, error) in
guard let authResult = result, error == nil else {
print(error!.localizedDescription)
return
}
// Get access token from result
let accessToken = authResult.accessToken
// You'll want to get the account identifier to retrieve and reuse the account for later acquireToken calls
let accountIdentifier = authResult.account.identifier
})
#if TARGET_OS_IPHONE
UIViewController *viewController = ...; // Pass a reference to the view controller that should be used when getting a token interactively
MSALWebviewParameters *webParameters = [[MSALWebviewParameters alloc] initWithAuthPresentationViewController:viewController];
#else
MSALWebviewParameters *webParameters = [MSALWebviewParameters new];
#endif
MSALInteractiveTokenParameters *interactiveParams = [[MSALInteractiveTokenParameters alloc] initWithScopes:scopes webviewParameters:webParameters];
[application acquireTokenWithParameters:interactiveParams completionBlock:^(MSALResult *result, NSError *error) {
if (!error)
{
// You'll want to get the account identifier to retrieve and reuse the account
// for later acquireToken calls
NSString *accountIdentifier = result.account.identifier;
NSString *accessToken = result.accessToken;
}
else
{
// Check the error
}
}];
Our library uses the ASWebAuthenticationSession for authentication on iOS 12 by default. See more information about default values, and support for other iOS versions.
guard let account = try? application.account(forIdentifier: accountIdentifier) else { return }
let silentParameters = MSALSilentTokenParameters(scopes: scopes, account: account)
application.acquireTokenSilent(with: silentParameters) { (result, error) in
guard let authResult = result, error == nil else {
let nsError = error! as NSError
if (nsError.domain == MSALErrorDomain &&
nsError.code == MSALError.interactionRequired.rawValue) {
// Interactive auth will be required
return
}
return
}
// Get access token from result
let accessToken = authResult.accessToken
}
NSError *error = nil;
MSALAccount *account = [application accountForIdentifier:accountIdentifier error:&error];
if (!account)
{
// handle error
return;
}
MSALSilentTokenParameters *silentParams = [[MSALSilentTokenParameters alloc] initWithScopes:scopes account:account];
[application acquireTokenSilentWithParameters:silentParams completionBlock:^(MSALResult *result, NSError *error) {
if (!error)
{
NSString *accessToken = result.accessToken;
}
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
}
}];
Occasionally user interaction will be required to get a new access token, when this occurs you will receive a MSALErrorInteractionRequired
error when trying to silently acquire a new token. In those cases call acquireToken:
with the same account and scopes as the failing acquireTokenSilent:
call. It is recommended to display a status message to the user in an unobtrusive way before invoking interactive acquireToken:
call.
For more information, please see MSAL error handling guide.
Microsoft has recently released a new plug-in that uses the newly announced Apple feature called Enterprise Single Sign-On. Microsoft Enterprise SSO plug-in for Apple devices offers the following benefits:
- Comes delivered in Microsoft Authenticator app automatically and can be enabled by any MDM.
- Provides seamless SSO for Active Directory joined accounts across all applications that support Apple's Enterprise Single Sign-On feature.
- COMING SOON: Provides seamless SSO across Safari browsers and applications on the device.
MSAL 1.1.0 and above will use Microsoft Enterprise SSO plug-in automatically instead of the Microsoft Authenticator app when it is active on the device. To use Microsoft Enterprise SSO plug-in in your tenant, you need to enable it in your MDM profile.
See more information about configuring Microsoft Enterprise SSO plug-in for your device here
If your app needs to support just one signed-in user at a time, MSAL provides a simple way to read the signed in account. This API must be also used when you are building an application to run on devices that are configured as shared devices - meaning that a single corporate device is shared between multiple employees. Employees can sign in to their devices and access customer information quickly. When they are finished with their shift or task, they will be able to sign-out of all apps on the shared device.
Here is a code snippet that shows how you can retrieve current account. You must call API every time when your app comes to foreground or before performing a sensitive operation to detect any signed-in account changes.
let msalParameters = MSALParameters()
msalParameters.completionBlockQueue = DispatchQueue.main
application.getCurrentAccount(with: msalParameters, completionBlock: { (currentAccount, previousAccount, error) in
// currentAccount is the currently signed in account
// previousAccount is the previously signed in account if any
})
MSALParameters *parameters = [MSALParameters new];
parameters.completionBlockQueue = dispatch_get_main_queue();
[application getCurrentAccountWithParameters:parameters
completionBlock:^(MSALAccount * _Nullable account, MSALAccount * _Nullable previousAccount, NSError * _Nullable error)
{
// currentAccount is the currently signed in account
// previousAccount is the previously signed in account if any
}];
MSAL also provides a public API to query multiple accounts, granted that they exist in the MSAL cache.
-
Make sure the umbrella header MSAL-umbrella.h is imported (just MSAL for Swift)
-
Create config, then use it to initialize an application object
-
Also initialize MSALAccountEnumerationParameters object with the account identifier. Each MSALAccount object has a parameter called "identifier", which represents the unique account identifier associated with the given MSALAccount object. We recommend using it as the primary search criterion.
-
Then invoke the API "accountsFromDeviceForParameters" from the application object using the enumeration parameter. If you have multiple accounts in MSAL cache, it will return an array containg MSALAccounts that have the account identifier you specified in the previous step.
-
Once the MSAL account is retrieved, invoke acquire token silent operation
#import MSAL //Make sure to import MSAL
let config = MSALPublicClientApplicationConfig(clientId:clientId
redirectUri:redirectUri
authority:authority)
guard let application = MSALPublicClientApplication(configuration: config) else { return }
let accountIdentifier = "9f4880d8-80ba-4c40-97bc-f7a23c703084.f645ad92-e38d-4d1a-b510-d1b09a74a8ca"
let parameters = MSALAccountEnumerationParameters(identifier:accountIdentifier)
var scopeArr = ["https://graph.microsoft.com/.default"]
if #available(iOS 13.0, macOS 10.15, *)
{
application.accountsFromDeviceForParameters(with: parameters, completionBlock:{(accounts, error) in
if let error = error
{
//Handle error
}
guard let accountObjs = accounts else {return}
let tokenParameters = MSALSilentTokenParameters(scopes:scopeArr, account: accountObjs[0]);
application.acquireTokenSilentWithParameters(with: tokenParameters, completionBlock:{(result, error) in
if let error = error
{
//handle error
}
guard let resp = result else {return} //process result
})
})
}
//import other key libraries
#import "MSAL-umbrella.h" //Make sure to import umbrella file
MSALPublicClientApplicationConfig *config = [[MSALPublicClientApplicationConfig alloc] initWithClientId:clientId
redirectUri:redirectUri
authority:authority];
MSALPublicClientApplication *application = [[MSALPublicClientApplication alloc] initWithConfiguration:config error:&error];
MSALAccountEnumerationParameters *parameters = [[MSALAccountEnumerationParameters alloc] initWithIdentifier:@"9f4880d8-80ba-4c40-97bc-f7a23c703084.f645ad92-e38d-4d1a-b510-d1b09a74a8ca"]; //init with account identifier
NSArray<NSString *> *scopeArr = [[NSArray alloc] initWithObjects: @"https://graph.microsoft.com/.default",nil]; //define scope
if (@available(iOS 13.0, macOS 10.15, *)) //Currently, this public API requires iOS version 13 or greater.
{
[application accountsFromDeviceForParameters:parameters
completionBlock:^(NSArray<MSALAccount *> * _Nullable accounts, __unused NSError * _Nullable error)
{
if (error)
{
//Log error & return
}
if (accounts)
{
NSLog(@"hi there");
MSALSilentTokenParameters *tokenParameters = [[MSALSilentTokenParameters alloc] initWithScopes:scopeArr account:accounts[0]];
[application acquireTokenSilentWithParameters:tokenParameters
completionBlock:^(MSALResult * _Nullable result, NSError * _Nullable error)
{
if (error)
{
//Log Error & return
}
if (result)
{
//process result
}
}
];
}
}];
}
Use following code to read current device configuration, including whether device is configured as shared:
application.getDeviceInformation(with: nil, completionBlock: { (deviceInformation, error) in
guard let deviceInfo = deviceInformation else {
return
}
let isSharedDevice = deviceInfo.deviceMode == .shared
// Change your app UX if needed
})
[application getDeviceInformationWithParameters:nil
completionBlock:^(MSALDeviceInformation * _Nullable deviceInformation, NSError * _Nullable error)
{
if (!deviceInformation)
{
return;
}
BOOL isSharedDevice = deviceInformation.deviceMode == MSALDeviceModeShared;
// Change your app UX if needed
}];
To signout account from your app, call MSAL's signout API. You can also optionally sign out from the browser. When MSAL is running on a shared device, signout API will signout globally from all apps on user's device.
let account = .... /* account retrieved above */
let signoutParameters = MSALSignoutParameters(webviewParameters: self.webViewParamaters!)
signoutParameters.signoutFromBrowser = false
application.signout(with: account, signoutParameters: signoutParameters, completionBlock: {(success, error) in
if let error = error {
// Signout failed
return
}
// Sign out completed successfully
})
MSALAccount *account = ... /* account retrieved above */;
MSALSignoutParameters *signoutParameters = [[MSALSignoutParameters alloc] initWithWebviewParameters:webViewParameters];
signoutParameters.signoutFromBrowser = NO;
[application signoutWithAccount:account signoutParameters:signoutParameters completionBlock:^(BOOL success, NSError * _Nullable error)
{
if (!success)
{
// Signout failed
return;
}
// Sign out completed successfully
}];
iOS - MSAL supports iOS 11 and above.
macOS - MSAL supports macOS (OSX) 10.12 and above.
MSAL Objective-C is designed to support smooth migration from ADAL Objective-C library. For detailed design and instructions, follow this guide
Our wiki is intended to document common patterns, error handling and debugging, functionality (e.g. logging, telemetry), and active bugs. You can find it here.
We use Stack Overflow with the community to provide support. We highly recommend you ask your questions on Stack Overflow first and browse existing issues to see if someone has asked your question before.
If you find a bug or have a feature request, please raise the issue on GitHub Issues.
To provide a recommendation, visit our User Voice page.
We enthusiastically welcome contributions and feedback. You can clone the repo and start contributing now.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact opencode@microsoft.com with any additional questions or comments.
This library controls how users sign-in and access services. We recommend you always take the latest version of our library in your app when possible. We use semantic versioning so you can control the risk associated with updating your app. As an example, always downloading the latest minor version number (e.g. x.y.x) ensures you get the latest security and feature enhanements but our API surface remains the same. You can always see the latest version and release notes under the Releases tab of GitHub.
If you find a security issue with our libraries or services please report it to secure@microsoft.com with as much detail as possible. Your submission may be eligible for a bounty through the Microsoft Bounty program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting this page and subscribing to Security Advisory Alerts.
Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT License (the "License").