Debugging an Office 365 ADFS/SSO issue when accessing Office Store in browser

We recently came across an issue with a customer where they had configured a standard SSO experience with Office 365 using ADFS and it was working perfectly except for a specific use case.   When a user accesses the office store via the Office 365 portal (e.g. portal.office.com/store) they got into an endless SSO login loop.  Specfically, they would see the following:

  1. Connection to Portal.Office.com
  2. Redirection to login.microsoftonline.com
  3. Redirection to adfs.customerdomain.com (automatically signed in because of WIA SSO)
  4. Redirection to login.microsftonline.com
  5. Redirection to Portal.Office.com\Store page but loads partially and then redirection to login.microsoftonline.com
  6. Redirection to adfs.customerdomain.com (automatically signed in because of WIA SSO)
  7. Rinse and repeat steps 4-6 ad nauseum

Normally, steps 1-4 is expected, because what is normally happening here in laymen’s terms are:

  1. Portal.office.com provides a response to the user’s browser saying “please provide sign in credentials, and do so via redirecting to this url”
  2. login.microsoftonline.com provides a reponse to the user’s browser saying “we know you are trying to access @customerdomain.com resources, which is federated, so please connect to adfs.customerdomain.com and get a valid auth token to present to us”
  3. User connects to adfs.customerdomain.com, and because it’s in it’s trusted sites list, and trusted sites is configured to perform windows integrated auth (WIA), the user’s browser uses the computers cached kerberos/ntlm auth token to sign into ADFS.  ADFS responds with a valid SAML token which the user can present to Azure AD.
  4. User connects back to login.microsoftonline.com and presents the auth token.  From there it is validated and an auth token browser cookie is created.
  5. At this point, the user would normally then connect to portal.office.com and access the relevant resources.

Now this was certainly the case for the majority of services in Office 365, including the main portal, Exchange Online, Sharepoint etc.   It was just the Office Store that was the problem, and bizarrely it was doing a partial load and then getting into the loop.

The resolution to the problem was discovered by doing a Fiddler trace of the sign in traffic.

First, we confirmed the normal ADFS SSO components were working (highlighted in red)

  1. Connection to login.microsoftonline.com
  2. Redirection to our ADFS servers (sts.{domain}.com) against the WIA path
  3. Connection back to login.microsoftonline.com with the SAML token response
  4. Subsequent connection to portal.office.com/store which is what we’re trying to access

ADFS-SSO-Process

The cause of the looping however can be seen further down in the fiddler trace, shown here:

Store-Office-NoAuth

From this we can see what would be an unexpected connection to a different domain url, store.office.com, and looking at it we see an authentication error, and no auth cookies are being presented to it.

A quick inventory of the configuration on the client gave us the answer as to why.   While the customer had done the common practice of including the key Office 365 URLs into their trusted sites (office365.com, outlook.com, sharepoint.com etc.), they did not include store.office.com.   This in itself is not specifically a problem, BUT they also had ‘mixed’ mode set up where their ‘Internet Zone’ was set to run in Protected Mode, while their ‘Trusted Sites’ and ‘Intranet Zone’ were configured to have Protected Mode turned off.

For a bit of background around what IE Protected Mode is, refer to this article, but the short version is, Protected mode is a security feature in IE which ensures that sites run in an ‘isolated instance’.  An effect of this isolation is that sites in protected mode cannot access the regular cookie cache.

As such, there was a ‘zone mismatch’ between the two, and what effectively was happening was:

  1. ADFS sign in was working as expected, and the user was successfully accessing portal.office.com resources and creating an auth cookie
  2. Portal.office.com/store however was actually loading content that was hosted under the site store.office.com
  3. store.office.com needed authentication credentials in order to correctly serve the content.  But because it was not included in trusted sites, that content request was running in ‘protected mode’.
  4. Because of the isolation, it couldn’t see the valid auth token which was stored in the regular cookie cache, and so triggers an auth sign in request to login.microsoftonline.com to get one.  And thus begins the endless authentication cycle.

The simple fix for this was simply adding the *.office.com into the trusted sites zone to ensure that it did not execute in protected mode.   Once we did it, we could see that when a connection to store.office.com is made, the appropriate auth token is presented and the page load works as expected:

Store-Office-Auth

Now, I’m not personally aware of a Microsoft guidance in terms of what should go into trusted sites for Office 365 functionality, but generally at Kloud we recommend the following to be used to avoid SSO issues:

  • *.sharepoint.com
  • *.office.com
  • *.microsoftonline.com
  • *.lync.com
  • *.outlook.com
  • URL of ADFS Server (e.g. sts.customerdomain.com)

Hopefully that gives you a bit of insight around the thinking, components and tools needed in debugging SSO issues with ADFS and Office 365!

Office 365 – AADSTS50008: SAML token is invalid

If you’ve made it to this post because you are troubleshooting your AD FS sign in with Office 365 due to “AADSTS50008: SAML token is invalid” I still recommend you do all the standard troubleshooting steps provided in this article below the image:

AADSTS50008https://support.microsoft.com/en-us/kb/3015526

Generally speaking, if you’re getting issued a token from your AD FS server and Microsoft’s STS is stopping you from logging in, it would be because of your token signing certificate:

Has your Token-Signing Certificate changed since you last told Microsoft?

 

It’s never a bad idea to re-run the following:

Update-MsolFederatedDomain -DomainName [verified domain]

Executing this MSOL cmdlet will get Microsoft’s STS service to check your Metadata, which in turn will update any certificate changes you may have made. If you’re really clever you can go to your WS-Federation Metadata and check what you’re telling the world is your public token signing certificate.

  1. Navigate to https://<AD FS Namespace>.com.au/federationmetadata/2007-06/federationmetadata.xml in Internet Explorer.
  2. Search for KeyDescriptor use=”signing” in the metadata document.
  3. If you copy the raw certificate data from <X509Certificate> into a text file and save with extension ‘cer‘ you can validate it matches what’s in your AD FS console > Service > Certificates > Token-Signing

Good, we have that under control. I experienced an issue the other day where I still got the error post running this cmdlet and performing the above nifty check….. Head scratches. Firstly, I know that my AD FS services has processed my sign on attempt and given me the required information to take to Office 365 and log in. Secondly, I know that my signing certificate is correct. Still the error remains;

We received a bad request.
Additional technical information:
Correlation ID: 82121251-3634-4afb-8014-fb5298d6f2c9
Timestamp: 2016-03-04 00:25:35Z
AADSTS50008: SAML token is invalid

My issue was a little unique but worth a notable mention on the internet. In my scenario, If I left my browser page up with the error shown above for a length of time, suddenly if my browser refreshed I would get dropped into whatever it was I was trying to authenticate to. So my authentication was successful with Microsoft after a point in time. I highlight those words cause I literally had to say them out loud to myself before I found the fix. Looking at the AD FS servers where my token is issued from, I took a quick look at the time. I then took a look at the time on the AD workstation I was trying from, they both seemed to be in synchronisation with each other.  What about from my BYOD Surface that I use at Kloud which uses a internet time source?

Great Scott! 7 minutes difference!

My token was being accepted by Office 365, but only once Microsoft had caught up to the future from which I had came from. This isn’t unreasonable from Microsoft to not allow my issued token until that time had actually happened in Microsoft cloud land. This wasn’t an issue that showed its face as easily with internal applications as all domain controllers and workstations were following orders from the time source in the domain. The problem was that the domain couldn’t synchronise with a internet time source at the time master. So it was slowly but surely sneaking ahead. Once we had come back from the future, the issue with ‘AADSTS50008: SAML token is invalid’ was resolved and authentication was instantaneous on the first attempt once again.

I don’t want to put the fear of the ‘internet time gods’ on you, I believe that there is some kind of threshold that Microsoft will allow. Just not in 10 minutes time or maybe tomorrow, in the future.  Possibly the magic number for token timestamp issuance is plus or minus 5 minutes. Once I rolled the AD FS servers time back within a couple of minutes/seconds of the internet time gods, tokens were accepted. Now over to the AD and Network teams to restore order on a wider scale.

Sharing Azure SSO Access Tokens Across Multiple Native Mobile Apps

This blog post is the fourth and final in the series that cover Azure AD SSO in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory
  2. How to Best handle AAD access tokens in native mobile apps
  3. Using Azure SSO tokens for Multiple AAD Resources From Native Mobile Apps
  4. Sharing Azure SSO Access Tokens Across Multiple Native Mobile Apps (this post).

Introduction

Most enterprises have more than one mobile app and it’s not unusual for these mobile apps to interact with some back-end services or APIs to fetch and update data. In the previous posts of this series we looked at how to manage access to APIs and share tokens as a way to enable a mobile app to interact with multiple AAD-secured resources.

This post will cover how to share access tokens to AAD resources across multiple mobile apps. This is very useful if the enterprise (or any mobile app vendor) wants to provide convenience for the users by not asking them to authenticate on every mobile app the user has.

Once a mobile user logs into Azure AD and gets a token we want to reuse the same token with other apps. This is suitable for some scenarios, but it might not be ideal for apps and resources that deal with sensitive data – the judgement is yours.

Sharing Tokens Across Multiple Mobile Apps

Moving on from previous posts it is now time to enable our mobile apps to share the access token. In this scenario we are covering iOS devices (iOS 6 and above), however other mobile platforms provide similar capabilities too. So what are the options for sharing data on iOS:

KeyChain Store

iOS offers developers a simple utility for storing and sharing keys, this is called SecKeyChain. The API has been part of the iOS platform since before iOS 6, but in iOS 6 Apple integrated this tool with iCloud, to make it even easier to push any saved passwords and keys to Apple iCloud, and then share them on multiple devices.

We could use iOS SecKeyChain to store the token (and the refreshToken) once the user logs in on any of the apps. When the user starts using any of the other apps, we check the SecKeyChain first before attempting to authenticate the user.

public async Task AsyncInit(UIViewController controller, ITokensRepository repository)
{
	_controller = controller;
	_repository = repository;
	_authContext = new AuthenticationContext(authority);
}

public async Task&lt;string&gt; RefreshTokensLocally()
{
	var refreshToken = _repository.GetKey(Constants.CacheKeys.RefreshToken, string.Empty);
	var authorizationParameters = new AuthorizationParameters(_controller);

	var result = &quot;Refreshed an existing Token&quot;;
	bool hasARefreshToken = true;

	if (string.IsNullOrEmpty(refreshToken)) 
	{
		var localAuthResult = await _authContext.AcquireTokenAsync(
			resourceId1, clientId, 
                        new Uri (redirectUrl), 
                        authorizationParameters, 
                         UserIdentifier.AnyUser, null);

		refreshToken = localAuthResult.RefreshToken;
		_repository.SaveKey(Constants.CacheKeys.WebService1Token, localAuthResult.AccessToken, null);


		hasARefreshToken = false;
		result = &quot;Acquired a new Token&quot;; 
	} 

	var refreshAuthResult = await _authContext.AcquireTokenByRefreshTokenAsync(refreshToken, clientId, resourceId2);
	_repository.SaveKey(Constants.CacheKeys.WebService2Token, refreshAuthResult.AccessToken, null);

	if (hasARefreshToken) 
	{
		// this will only be called when we try refreshing the tokens (not when we are acquiring new tokens. 
		refreshAuthResult = await _authContext.AcquireTokenByRefreshTokenAsync(refreshAuthResult.RefreshToken,  clientId,  resourceId1);
		_repository.SaveKey(Constants.CacheKeys.WebService1Token, refreshAuthResult.AccessToken, null);
	}

	_repository.SaveKey(Constants.CacheKeys.RefreshToken, refreshAuthResult.RefreshToken, null);

	return result;
}

Some of the above code will be familiar from previous posts, but what has changed is that now we are passing ITokenRepository which would save any tokens (and refreshTokens) once the user logs in to make them available for other mobile apps.

I have intentionally passed an interface (ITokenRepository) to allow for different implementations, in case you opt to use a different approach for sharing the tokens. The internal implementation of the concrete TokenRepository is something like this:

public interface ITokensRepository 
{
	bool SaveKey(string key, string val, string keyDescription);
	string GetKey(string key, string defaultValue);
	bool SaveKeys(Dictionary&lt;string,string&gt; secrets);
}

public class TokensRepository : ITokensRepository
{
	private const string _keyChainAccountName = &quot;myService&quot;;

	public bool SaveKey(string key, string val, string keyDescription)
	{
		var setResult = KeychainHelpers.SetPasswordForUsername(key, val, _keyChainAccountName, SecAccessible.WhenUnlockedThisDeviceOnly, false );

		return setResult == SecStatusCode.Success;
	}

	public string GetKey(string key, string defaultValue)
	{
		return KeychainHelpers.GetPasswordForUsername(key, _keyChainAccountName, false) ?? defaultValue;
	}
		
	public bool SaveKeys(Dictionary&lt;string,string&gt; secrets)
	{
		var result = true;
		foreach (var key in secrets.Keys) 
		{
			result = result &amp;&amp; SaveKey(key, secrets [key], string.Empty);
		}

		return result;
	}
}

iCloud

We could use Apple iCloud to push the access tokens to the cloud and share them with other apps. The approach would be similar to what we have done above with the only difference being in the way we are storing these keys. Instead of storing them locally, we push them to Apple iCloud directly. As the SecKeyChain implementation above does support pushing data to iCloud, I won’t go through the implementation details here and simply note the option is available for you.

Third Party Cloud Providers (ie Azure)

Similar to the previous option, but offer more flexibility. This is a very good solution if we already are already using Azure Mobile Services for our mobile app. We can create one more table and then use this table to store and share access tokens. The implementation of this could be similar to the following:

public async Task&lt;string&gt; RefreshTokensInAzureTable()
{
	var tokensListOnAzure = await tokensTable.ToListAsync();
	var tokenEntry = tokensListOnAzure.FirstOrDefault();
	var authorizationParameters = new AuthorizationParameters(_controller);

	var result = &quot;Refreshed an existing Token&quot;;
	bool hasARefreshToken = true;

	if (tokenEntry == null) 
	{
		var localAuthResult = await _authContext.AcquireTokenAsync(resourceId1, clientId, new Uri (redirectUrl),  authorizationParameters, UserIdentifier.AnyUser, null);

		tokenEntry = new Tokens {
			WebApi1AccessToken = localAuthResult.AccessToken,
			RefreshToken = localAuthResult.RefreshToken,
			Email = localAuthResult.UserInfo.DisplayableId,
			ExpiresOn = localAuthResult.ExpiresOn
		};
		hasARefreshToken = false;
		result = &quot;Acquired a new Token&quot;; 
	} 
		
	var refreshAuthResult = await _authContext.AcquireTokenByRefreshTokenAsync(tokenEntry.RefreshToken, 
                                                                                    clientId, 
                                                                                    resourceId2);
	tokenEntry.WebApi2AccessToken = refreshAuthResult.AccessToken;
	tokenEntry.RefreshToken = refreshAuthResult.RefreshToken;
	tokenEntry.ExpiresOn = refreshAuthResult.ExpiresOn;

	if (hasARefreshToken) 
	{
		// this will only be called when we try refreshing the tokens (not when we are acquiring new tokens. 
		refreshAuthResult = await _authContext.AcquireTokenByRefreshTokenAsync (refreshAuthResult.RefreshToken, clientId, resourceId1);
		tokenEntry.WebApi1AccessToken = refreshAuthResult.AccessToken;
		tokenEntry.RefreshToken = refreshAuthResult.RefreshToken;
		tokenEntry.ExpiresOn = refreshAuthResult.ExpiresOn;
	}

	if (hasARefreshToken)
		await tokensTable.UpdateAsync (tokenEntry);
	else
		await tokensTable.InsertAsync (tokenEntry);

	return result;
}

Words of Warning

Bearer Tokens

Developers need to understand Bearer Tokens when using Azure AD authentication. Bearer Tokens mean anybody who has the token (bearer of the token) could access and interact with your AAD resource. This offers high flexibility but it could also be a security risk if your key was exposed somehow. This needs to be thought of when implementing any token sharing mechanism.

iOS SecKeyChain is “Secure”

iOS SecKeyChain is “Secure”, right? No, not at all. Apple calls it secure, but on jail-broken devices, you could see the key store as a normal file. Thus, I would highly recommend encrypting these access tokens and any key that you might want to store before persisting it. The same goes for iCloud, Azure, or any of the other approaches we went through above.

Apple AppStore Verification

If you intend on submitting your app to Apple AppStore, then you need to be extra careful with what approach you take to share data between your apps. For enterprises (locally deployed apps), you have the control and you make the call based on your use case. However, Apple has a history of rejecting apps (ie PastePane) for using some of iOS APIs in “an unintended” manner.

I hope you found this series of posts useful, and as usual, if there is something not clear or you need some help with similar projects that you are undertaking, then get in touch, and we will do our best to help. I have pushed the sample code from this post and the previous ones to GitHub, and can be found here

Has.

This blog post is the fourth and final in the series that cover Azure AD SSO in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory
  2. How to Best handle AAD access tokens in native mobile apps
  3. Using Azure SSO tokens for Multiple AAD Resources From Native Mobile Apps
  4. Sharing Azure SSO Access Tokens Across Multiple Native Mobile Apps (this post).

Using Azure SSO Tokens for Multiple AAD Resources From Native Mobile Apps

This blog post is the third in a series that cover Azure Active Directory Single Sign-On (SSO) authentication in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory
  2. How to Best handle AAD access tokens in native mobile apps
  3. Using Azure SSO tokens for Multiple AAD Resources From Native Mobile Apps (this post)
  4. Sharing Azure SSO access tokens across multiple native mobile apps.

Introduction

In an enterprise context it is highly likely there are multiple web services that your native mobile app needs to consume. I had exactly this scenario at one of my clients who asked if they could maintain the same SSO token in the background in the mobile app and use it for accessing multiple web services. I spent some time digging through the documentation and conducting some experiments to confirm some points and this post is to share my findings.

Cannot Share Azure AD Tokens for Multiple Resources

The first thing that comes to mind is to use the same access token for multiple Azure AD resources. Unfortunately this is not allowed. Azure AD issues a token for a certain resource (which is mapped to an Azure AD app). When we call AcquireToken, we need to provide a single resourceID. The result is the token can only be used for resource matching the supplied identifier.

There are ways where you could use the same token (as we will see later in this post), but it is not recommended as it complicates operations logging, authentication process tracing, etc. Therefore it is better to look at the other options provided by Azure and the ADAL library.

Use Refresh-Token to Acquire Tokens for Multiple Resources

The ADAL library supports acquiring multiple access tokens for multiple resources using a “refresh token”. This means once a user is authenticated, the ADAL’s authentication context is able to generate an access token to multiple resources without authenticating the user again. This is covered briefly by the MSDN documentation. A sample implementation to retrieve this token is shown below.

public async Task<string> RefreshTokens()
{
	var tokenEntry = await tokensRepository.GetTokens();
	var authorizationParameters = new AuthorizationParameters (_controller);

	var result = "Refreshed an existing Token";
	bool hasARefreshToken = true;

	if (tokenEntry == null) 
	{
		var localAuthResult = await _authContext.AcquireTokenAsync (
			resourceId1, 
                        clientId, 
                        new Uri (redirectUrl), 
                        authorizationParameters, 
                        UserIdentifier.AnyUser, 
                        null);

		tokenEntry = new Tokens {
			WebApi1AccessToken = localAuthResult.AccessToken,
			RefreshToken = localAuthResult.RefreshToken,
			Email = localAuthResult.UserInfo.DisplayableId,
			ExpiresOn = localAuthResult.ExpiresOn
		};
		hasARefreshToken = false;
		result = "Acquired a new Token"; 
	} 

	var refreshAuthResult = await _authContext.AcquireTokenByRefreshTokenAsync(
                                tokenEntry.RefreshToken, 
                                clientId, 
                                resourceId2);

	tokenEntry.WebApi2AccessToken = refreshAuthResult.AccessToken;
	tokenEntry.RefreshToken = refreshAuthResult.RefreshToken;
	tokenEntry.ExpiresOn = refreshAuthResult.ExpiresOn;

	if (hasARefreshToken) 
	{
		// this will only be called when we try refreshing the tokens (not when we are acquiring new tokens. 
		refreshAuthResult = await _authContext.AcquireTokenByRefreshTokenAsync (
                                                     refreshAuthResult.RefreshToken, 
                                                     clientId, 
                                                     resourceId1);

		tokenEntry.WebApi1AccessToken = refreshAuthResult.AccessToken;
		tokenEntry.RefreshToken = refreshAuthResult.RefreshToken;
		tokenEntry.ExpiresOn = refreshAuthResult.ExpiresOn;
	}

	await tokensRepository.InsertOrUpdateAsync (tokenEntry);

	return result;
}

As you can see from above, we check if we have an access token from previous calls, and if we do, we refresh the access tokens for both web services. Notice how the _authContext.AcquireTokenByRefreshTokenAsync() method provides an overloading parameter that takes a resourceId. This enables us to get multiple access tokens for multiple resources without having to re-authenticate the user. The rest of the code is similar to what we have seen in the previous two posts.

ADAL Library Can Produce New Tokens For Other Resources

In the previous two posts we looked at ADAL and how it uses the TokenCache. Although ADAL does not support persistent caching of tokens yet on mobile apps, it still uses the TokenCache for in-memory caching. This enables ADAL to generate new access tokens if the AuthenticationContext still exists from previous authentication calls. Remember in the previous post we said it is recommended to keep a reference to the authentication-context? Here it comes in handy as it enables us to generate new access tokens for accessing multiple Azure AD resources.

var localAuthResult = await _authContext.AcquireTokenAsync (
                                   resourceId2, 
                                   clientId, 
                                   new Uri(redirectUrl),
                                   authorizationParameters,
                                   UserIdentifier.AnyUser, 
                                   null
                                 );

Calling AcquireToken() (even with no refresh token) would give us a new access token to the requested resource. This is due to ADAL checking if we have a refresh token in-memory which ADAL then uses that to generate a new access token for the resource.

An alternative

The third alternative is the simplest (but not necessarily the best). In this option, we can use the same access token to consume multiple Azure AD resources. To do this, we need to use the same Azure AD app ID when setting the two APIs for authentication via Azure AD. This requires some understanding of how the Azure AD authentication happens on our web apps.

If you refer to Taiseer Joudeh’s tutorial you will see that in our web app, we need to tell the authentication framework what our Authority is and the Audience (Azure AD App Id). If we set up both of our web APIs to use the same Audience (Azure AD app Id) we link them both into the same Azure AD application which allows use of the same access token to use both web APIs.

// linking our web app authentication to an Azure AD application
private void ConfigureAuth(IAppBuilder app)
{
	app.UseWindowsAzureActiveDirectoryBearerAuthentication(
		new WindowsAzureActiveDirectoryBearerAuthenticationOptions
		{
			Audience = ConfigurationManager.AppSettings["Audience"],
			Tenant = ConfigurationManager.AppSettings["Tenant"]
		});
}
<appSettings>
    <add key="Tenant" value="hasaltaiargmail.onmicrosoft.com" />
    <add key="Audience" value="http://my-Azure-AD-Application-Id" />	
</appSettings>

As I said before, this is very simple and requires less code, but could cause complications in terms of security logging and maintenance. At the end of the day, it depends on your context and what you are trying to achieve.

Conclusion

We looked at how we could use Azure AD SSO with ADAL to access multiple resources from native mobile apps. As we saw, there are three main options, and the choice could be made based on the context of your app. I hope you find this useful and if you have any questions or you need help with some development that you are doing, then just get in touch.

This blog post is the third in a series that cover Azure Active Directory Single Sign On (SSO) Authentication in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory
  2. How to Best handle AAD access tokens in native mobile apps
  3. Using Azure SSO tokens for Multiple AAD Resources From Native Mobile Apps (this post)
  4. Sharing Azure SSO access tokens across multiple native mobile apps.

How to Best Handle Azure AD Access Tokens in Native Mobile Apps

This blog post is the second in a series that cover Azure Active Directory Single Sign On (SSO) Authentication in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory
  2. How to Best handle AAD access tokens in native mobile apps (this post)
  3. Using Azure SSO access token for multiple AAD resources from native mobile apps
  4. Sharing Azure SSO access token across multiple native mobile apps.

In my previous post, I talked about authenticating mobile app users using Azure AD SSO. In this post, I will explore how to take this further to persist the access token to interact with Azure AD.

Let’s assume that we have an API and a mobile app that consumes it. In order to secure the interaction between our mobile app and the API, we can register both the app and API with Azure AD and let Azure handle the authentication for us. Let’s take a look.

Securing the Web App

To start with, we will implement an authentication mechanism in our API. We can create use a vanilla Web API project from Visual Studio and implement Azure AD authentication on that. Our focus isn’t on this so for a good reference see Taiseer Joudeh’s detailed tutorial.

Securing the Mobile App

In the previous post, I showed how we could use Azure AD with ADAL to authenticate users on native mobile apps. The remainder of this post will assume that you have followed the previous post and you have this part ready. If you are not sure, please revisit my previous post.

Setting up the Permissions in AAD

We have seen how to secure our apps with AAD, now we need to authorise the mobile app to access our Web API. To do this we first need to expose the webApi permissions on Azure AD. We can navigate to AAD/Applications/our-web-app, then click on download manifest. This will give us a copy of the configuration of this API in AAD (in a simple JSON file). We need to modify the permission section and add the following section to tell Azure AD that this web app can be accessed by other AAD apps.

AAD app manifest configuration

AAD app manifest configuration

appPermissions": [
    {
      "claimValue": "user_impersonation",
      "description": "Allow the application full access to the service on behalf of the signed-in user",
      "directAccessGrantTypes": [],
      "displayName": "Have full access to the service",
      "impersonationAccessGrantTypes": [
        {
          "impersonated": "User",
          "impersonator": "Application"
        }
      ],
      "isDisabled": false,
      "origin": "Application",
      "permissionId": "place a NEW GUID here",
      "resourceScopeType": "Personal",
      "userConsentDescription": "Allow the application full access to the service on your behalf",
      "userConsentDisplayName": "Have full access to the service"
    }
  ]

We can update the file then upload it to update the permissions settings to enable AAD to allow access to this API just like any other permissions that it manages. You could read more on Azure AD impersonation and permission settings on MSDN. Note that you need to choose a NEW GUID for the permission id.

Now we need to configure our native mobile app in Azure AD to have access to our Web API. This is very simple and is shown in the screenshot below. In the list of permissions on the left, we now have more permissions that we can grant to the mobile app. Whatever name you gave to your mobile app will appear there along with the type of permissions that you have configured. In my case, I have named it MobileServices1 and that is what is appearing there.

Azure AD app permission settings

Azure AD app permission settings

Token Expiry and Caching

Setting the permissions and configuration above will allow our mobile app to authenticate users and manage the access of the API. This access is managed by the token that Azure AD issues when a user authenticates successfully. If the mobile app interacts with the API frequently, then we need to always have a valid token for all our requests. The question is how to keep a valid request token in the native mobile app?

The answer is depends on what you are trying to do – if you are implementing a highly secure mobile app you might want to always check with Azure and maybe ask the user to login every time the token expires. AAD access tokens expire after one hour by default. This means the default behaviour would be to ask the user to login every hour which is OK for some mobile apps, but it is certainly not the normal flow you see in many apps. So what should we do if we wanted to only ask the user to login once, or only occasionally (say, once every 3 months)? To do that, we would then need to manage the access tokens and automatically refresh it.

ADAL comes with the TokenCache class that is designed to manage caching of tokens so that consumers don’t need to go back to Azure AD every time the mobile app asks for a new token. Unfortunately for us persistent caching of tokens is not supported in the release this post is based on (ADAL 3.0.11). This means that ADAL will only cache the token in memory which means that once an app restarts (or is backgrounded in iOS) you will lose your access token. Therefore, we need to manage the token, and refresh it on our own in the background.

There are many ways that you could do this, a simple way is to always check token validity before we access the API. If our token isn’t valid then we could check for the Refresh Token. Azure AD gives us a refresh token to use when our access token is about to expire. This means that when we ask AAD for a new token and provide this refresh token, AAD will give us a new token without asking the user to re-authenticate.

By Default, Azure AD refresh tokens are valid for 14 days. This means as long as we refresh the actual token even once in this period then we do not need to re-authenticate. Another security constraint that Azure AD imposes is that the access token can only be refreshed for a maximum period of 90 days (i.e. 90 days after the initial issuance of the access and refresh tokens, the end user will have to sign themselves in again).

Alright, time to write some code. The code snippet below shows how you could structure your API calls from your mobile app. Notice that we always call either AcquireToken() or AcquireTokenByRefreshToken() before every call. This is to ensure that we always have a valid token before we send a request to the API. This could even be optimised further by checking if the access token still valid, then we skip the token refreshing call. I will leave this as an exercise for you to implement. In the next release of ADAL hopefully the TokenCache would be implemented, and then we would not need to do this.


public async Task<string> GetResultFromWebApi(string apiCallPath)
{
	var token = await AcquireOrRefreshToken ();
	using (var httpClient = new HttpClient())
	{
		httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);
		HttpResponseMessage response = await httpClient.GetAsync(apiBaseAddress + apiCallPath);
		return await response.Content.ReadAsStringAsync();
	}
}

private async Task<string> AcquireOrRefreshToken()
{
	var refreshToken = _storage.Get<string> (Constants.CacheKeys.RefreshToken);
	AuthenticationResult authResult = null;

	if (string.IsNullOrEmpty (refreshToken)) 
	{
		authResult = await _authContext.AcquireTokenAsync (
			resourceId, clientId, new Uri (redirectUrl), new AuthorizationParameters (_controller), UserIdentifier.AnyUser, null);

	} 
        else 
        {
		authResult = await _authContext.AcquireTokenByRefreshTokenAsync (refreshToken, clientId);
	}

	// when calling refresh token, the UserInfo would be null
	if (authResult.UserInfo != null)
		_storage.Save<string> (Constants.CacheKeys.Email, authResult.UserInfo.DisplayableId);

	_storage.Save<string> (Constants.CacheKeys.Token, authResult.AccessToken);
	_storage.Save<string> (Constants.CacheKeys.ExpireOn, authResult.ExpiresOn.ToString("dd MMM HH:mm:ss"));
	_storage.Save<string> (Constants.CacheKeys.RefreshToken, authResult.RefreshToken);

	return authResult.AccessToken;
}

That’s it! Now your mobile app would keep interacting with the API using a valid token. And if you are concerned about what happens when the user account is disabled, or the password is changed, then well done, you are following the topic properly. Azure AD, would either try to re-authenticate the user again (by showing the login screen), or gives an error. So we need to add some error handling to our code to catch these types of exceptions and handle them properly in the mobile app.

I hope you find this blog post useful and I would love to hear from you if you have a question or comment. In the next blog post, we will look at how we could use the same token for accessing multiple resources registered in Azure AD.

This blog post is the second in a series that cover Azure Active Directory Single Sign On (SSO) Authentication in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory
  2. How to Best handle AAD access tokens in native mobile apps (this post)
  3. Using Azure SSO access token for multiple AAD resources from native mobile apps
  4. Sharing Azure SSO access token across multiple native mobile apps.

Implementing Azure Active Directory SSO (Single Sign on) in Xamarin iOS apps

This blog post is the first in a series that cover Azure Active Directory Single Sign On (SSO) Authentication in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory (this post)
  2. How to Best handle AAD access tokens in native mobile apps
  3. Using Azure SSO access token for multiple AAD resources from native mobile apps
  4. Sharing Azure SSO access token across multiple native mobile apps.

Brief Start

Two weeks ago the Azure AD (AAD) team released the Active Directory Authentication Library (ADAL) to enable developers to implement SSO functionality leveraging AAD. This was great news for me as I have few clients who are keen on having this feature in their apps and in this blog post I will share my experience in implementing Azure AD SSO in Xamarin.iOS apps.

Mobile Service vs AAD Authentication

First things first, if you are using Azure Mobile Services, then authentication could be handled for you by Azure Mobile Services itself. All you need to do is to pass a reference of your RootViewController to the library and call LoginAsync() as follows:


// Initialize the Mobile Service client with your URL and key
client = new MobileServiceClient (applicationURL, applicationKey, this);
user = client.LoginAsync (_controller, MobileServiceAuthenticationProvider.WindowsAzureActiveDirectory);

This will return a User object that you can use for future calls. This is slightly different from what we are going to talk about in this blog post. For further details on handling Azure Mobile Services authentication, you can check out this tutorial from the MSDN library.

Azure Active Directory Authentication

This blog post is to show you how you could authenticate users against Azure AD which can be useful in many cases. You may have a mobile app and only want users in Active Directory (on-prem or Azure) to use this app or you might have an API or a website and you share some functionality with your mobile users using a native mobile app. In both cases, you could use ADAL to enable Azure AD to handle the user authentication for you. This is quite handy for the following reasons:

  1. Less code development and maintenance for you as Azure handles it by itself
  2. Guaranteed functionality and less bugs as it is a well structured/tested library from a trusted source (Azure team)
  3. No need to further update your APIs when Azure API/SDKs are changed
  4. Extra features like token caching and token refresh operations.

Lack of Documentation

I have had few issues with the documentation on Azure when trying to implement SSO on Xamarin.iOS – the Azure documentation refers to classes and methods that do not exist in the ADAL. As another example, this tutorial seems to be taken from the native iOS implementation of Azure SSO without any update to match the release of ADAL. Anyway, enough complaining, for this reason we have this blog post. 🙂

Implementation

To implement SSO, I will assume that I have a native app, and I want to authenticate users against my AAD before they can use this app. For that, I would need to first register my native app in Azure AD so it shows as below.

Adding an App to Azure AD

Adding an App to Azure AD

  1. Click Add at the bottom of the screen to open the Add Application dialog
  2. Provide a name for your application registration so you can find it later. Click Next
  3. Provide a unique Redirect URI for your Application. Click the Finish (tick) button. Note that this must be a valid URI and is how Azure AD will identify your application during authentication requests.

Once we have created our application, let’s get the following details:

Authority
This represents the authority of your AAD, and it follows the format of https://login.windows.net/your-tenant-name.

ClientId
This is the unique client identifier of the native mobile app that we just created. See the screenshot below.

Azure AD app configurations

Azure AD app configurations

Redirect Uri
This is the unique redirect Id of the app that we just created as shown in the screenshot above.

Resource Id
This represents the resource URI of the app that we are trying to access. So if we are trying to access some functionality of a web API that is also registered with AAD, then this web app Id in Azure AD.

Once we have all the info above all we need to do is to write few lines of code. First we should install the ADAL Nuget Package in our solution. At the time of writing, the Nuget package version is 3.0.1102… and is in pre-release which means you need to allow pre-release packages in your Nuget settings.

Time to write some code. The small snippet below shows how to authenticate the user and get a token.
For the sake of this blog post, we only need to authenticate the user to azure. We will get the token and save it for future use, I have another post that talks about using this token. You can find it here.

        
const string authority = "https://login.windows.net/your-tenant-name";
const string resourceId = "your-resource-id";
const string clientId = "your-native-app-client-id-on-AAD";
const string redirectUrl = "your-awsome-app-redirect-url-as-on-AAD";

QSTodoService ()
{
    // this line if very important as it enables the ADAL library to do all 
    // IoC injection and other magic based on the platform that you are in. 
    AdalInitializer.Initialize ();
}

public async Task AsyncInit(UIViewController controller, MySimpleStorage storage)
{
    _storage = storage;
    _controller = controller;

    _authContext = new AuthenticationContext (authority);
}

public async Task<string> RefreshTokens()
{
    var refreshToken = _storage.Get<string> (Constants.CacheKeys.RefreshToken);
    AuthenticationResult authResult = null;
    var result = "Acquired a new Token"; 

    if (string.IsNullOrEmpty (refreshToken)) 
    {
       authResult = await _authContext.AcquireTokenAsync (
                    resourceId, clientId, new Uri (redirectUrl), new AuthorizationParameters (_controller), UserIdentifier.AnyUser, null);

    } else {
       authResult = await _authContext.AcquireTokenByRefreshTokenAsync (refreshToken, clientId);
       result = "Refreshed an existing Token";
    }

    if (authResult.UserInfo != null)
       _storage.Save<string> (Constants.CacheKeys.Email, authResult.UserInfo.DisplayableId);

    _storage.Save<string> (Constants.CacheKeys.Token, authResult.AccessToken);
    _storage.Save<string> (Constants.CacheKeys.RefreshToken, authResult.RefreshToken);

    return result;
}

As you can see, it is very simple. You could keep a reference to your AuthenticationContext in your app. In fact, it is recommended that you do so, for later use as the aggressive GC on Monotouch might dispose of it quickly.

Note that I am storing the token and the refresh token as I mentioned above, but you do not need to do that if you only using the library to authenticate once. In the next blog post, I will show how you could manage these tokens for further interaction with another app that is also using AAD for authentication.

I hope you found this series of posts useful, and as usual, if there is something not clear or you need some help with similar projects that you are undertaking, then get in touch, and we will do our best to help. I have pushed the sample code from this posts series to GitHub, and can be found here

This blog post is the first in a series that cover Azure Active Directory Single Sign On (SSO) Authentication in native mobile applications.

  1. Authenticating iOS app users with Azure Active Directory (this post)
  2. How to Best handle AAD access tokens in native mobile apps
  3. Using Azure SSO access token for multiple AAD resources from native mobile apps
  4. Sharing Azure SSO access token across multiple native mobile apps.

I Keep Getting Prompted to Authenticate in SharePoint Online!

Every once in a while I come across a problem which is vexing and irritating and equally, an absolute joy when it is finally resolved.

The Scene

This particular issue had been on-going for some time with one of my customers with reports coming in from various people saying that they were being prompted to authenticate when opening an Office document in SharePoint Online. “I’ve already logged in so why do I keep getting these prompts?!” – It’s a fair question, especially since the organisation had implemented AD FS to reduce the frequency of authentication dialogue boxes. It seemed that Microsoft was not only being overprotective but overbearing as well. Here’s the dialogue I’m talking about by the way:

Figure 1 – Overprotective

Along with this issue, the customer had started to map network drives to SharePoint. Let’s face it, it’s easier to manage files in a folder structure than in the SPO web interface, and with improvements over the last few years with SharePoint mapping network drives actually works, or at least is should work. While it was working some of the time, there were reports of access issues to these mapped drives, and a different dialogue box which while uglier, on first glance appeared more promising from a technical perspective. A recommendation for a fix and a reference to a KB article!

Figure 2 – Ugly but useful

There are a plethora of articles, wikis, forum posts, etc out there which all talk about adding your SharePoint Online sites to your browser’s Trusted Sites list. The referenced KB article covers it off nicely, and I’ve added the link in case you’ve not come across it, here.

In my particular case the SharePoint Online domains were all happily existing in the IE Trusted Sites list (I checked about 30 times during my investigations)

I’ve mentioned that AD FS is in play, further a redirection URL http://Intranet.KloudySky.com.au is used with a Smart Link in this particular environment to provide a more seamless login experience. Until very recently you were not able to customise the Office 365 Portal page, https://login.microsoftonline.com. Check out this article for more info on how to brand the Sign-In Page with company branding.

While you can now brand the Sign-In page to your heart’s content (if you have an Azure subscription along with your O365 subscription), you’ll need to type in your UPN at least once (normally not an experience IT wants to establish for a new Intranet rollout), and so smart links are not yet dead.

There’s a couple of good resources for creating SmartLinks, including one written by a Kloudy here and a good Office365 community article here which discusses some of the components of the link.

The Solution

I’ve set the scene, and for most of you you’ve probably bypassed everything above and come straight to this point. For those of you who took the slow route of reading from the top; Thanks for sticking around!

The problem I was seeing was due to a very simple issue really. You need to check the check box which says “Keep me signed in”. It even says so in the ugly helpful dialogue box above. The thing is, if you’re using AD FS and SmartLinks you don’t actually hit the Office 365 login page and so don’t get the opportunity to sign in. We need to pass the “Keep me signed in” option to the AD FS server somehow, and the only way to do that is to encode it in the URL.

How do we do that? As it turns out there’s a way!

At the end of the SmartLink is the LoginOptions attribute.

LoginOption=3 will pass the “Keep me signed in” option as off (Not checked)

LoginOption=1 will pass the “Keep me signed in” option as on (Checked)

Don’t keep me signed in. Boo!!!
https://myadfs.Kloudysky.com.au/adfs/ls/?wa=wsignin1.0&wtrealm=urn:federation:MicrosoftOnline&wctx=%5BwctxValue%5D%26wreply%3Dhttps%253A%252F%252FKloudySky%252Esharepoint%252Ecom%252F%255Fforms%252Fdefault%252Easpx%26lc%3D1033%26id%3D123456%26%26LoginOptions%3D3

Keep me signed in. Yay!!!
https://myadfs.Kloudysky.com.au/adfs/ls/?wa=wsignin1.0&wtrealm=urn:federation:MicrosoftOnline&wctx=%5BwctxValue%5D%26wreply%3Dhttps%253A%252F%252FKloudySky%252Esharepoint%252Ecom%252F%255Fforms%252Fdefault%252Easpx%26lc%3D1033%26id%3D123456%26%26LoginOptions%3D1

I only made this change on the Internal SmartLinks by the way. I’ve left the SmartLinks published to the Internet with LoginOptions=3, we don’t really want people on untrusted PCs and devices to remain logged into Office 365 services.

 

I’d like to make a big thankyou to Carolyn at Microsoft PSS for helping me resolve this issue.