Tag Archives: azure

Safely Storing Azure App Connection Secrets

Microsoft’s Azure AD surfaces a wide variety of capabilities that can be accessed programmatically via RESTful web APIs. The Azure Graph and its successor the Microsoft Graph are two of the more comprehensive APIs that enable the manipulation of most AAD objects. Using these APIs requires obtaining an OAuth access token to send with API requests. AAD uses two object types to provide the mechanism for obtaining OAuth tokens: Applications and Service Principals. An Application object can span multiple AAD tenants whereas the Service Principal is the tenant-specific representation of the application.

To obtain an OAuth access token, one must call the Microsoft authorization server endpoint to request it. This call must be authenticated by providing the Application’s client ID and client secret. These two values are the app’s credentials and must be protected as you would protect any other privileged credentials. This becomes a problem if you have automated tasks that need to connect to Azure. How can these credentials be safely stored?

The naive programmer would just embed these values into the task’s code. This is especially egregious if the code is checked into a source library because then the secrets will be in the change history even if they are later removed from the code. The first refinement is to put the secrets into a task configuration file. That’s fine as long as the configuration file is never checked into the source library or otherwise stored in an insecure fashion.

It turns out there is a much better solution. X509 certificates are designed to store secret keys and can be used for AAD OAuth token requests. The first step, after a suitable certificate is created1, is to add the certificate as an app access key2 using either the Azure Portal GUI4 or PowerShell. Then you need to install the cert, with private key, into the cert store of the server on which the task will run. Make certain to grant private key access to the service account being used to run the task. Refer to the article in the second footnote for an example of using this technique in C# code.

Using Certificate-based Authentication From PowerShell

The AzureAD PowerShell module wraps the functionality of the MS Graph. A connection to Azure must be made before any of the AzureAD commandlets can be called. The Connect-AzureAD3 commandlet is used to do this. In reality, what it is doing is obtaining and storing an OAuth access token in the PS session. I use the following bit of code to do this:

Import-Module AzureAD
# Check if there is a connection to AAD. If this call throws, then make the connection.
try {
    Get-AzureADCurrentSessionInfo | Out-Null
catch {
    # Use cert-based auth via the AAD app. Ensure that the
    # task context account has access to the cert private key.
    Write-Output "Connecting to AAD"
    $tenantId = ''
    $appId = ''
    $certThumbprint = ''
    Connect-AzureAD -TenantId $tenantId -ApplicationId $appId -CertificateThumbprint $certThumbprint

First this code tries to make an AAD call (Get-AzureADCurrentSessionInfo). That call will fail and throw an exception if there is no valid OAuth token. If that is the case, then the code in the catch block will make the connection and obtain the token using the certificate. Note that you will need to fill in the missing tenantId, appId, and certThumbprint values.

The try/catch setup allows you to run this code multiple times in the same PS session such that it will only attempt to make a connection if there isn’t a valid token.

Unfortunately there are still many functions related to Exchange Online and other Office 365 apps that are not currently manageable by the AzureAD module. For these operations one must revert to the older MSOnline PS module. It employs the Connect-MsolService commandlet to make the connection to Azure but it does not support using certificates to store an app secret.

Using a certificate as the Azure app secret store has many advantages. It provides multiple levels of security. The private key is stored only on the server running the tasks. The private key is protected by ACLs that only grant access to the accounts that are configured to have access. I recommend storing the certificate with the private key set as exportable but protected by a password. That way you can move the tasks and the cert to another server if needed. If you are truly paranoid you can skip this step since it is pretty easy to repeat the steps of creating a self-signed cert and adding it as a secret key to the Application object.


  1. A self-signed certificate is fine for this use. Creating a self-signed cert is pretty straightforward. This article explains one way to do this: https://blogs.msdn.microsoft.com/davidhardin/2013/08/27/azure-management-certificate-public-key-private-key/
  2. This article uses a sample daemon app to describes using a certificate for API access and goes into the details of every step: https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-daemon-certificate-credential/
  3. The MS docs for the Connect-AzureAD commandlet also show how to do this from PowerShell although I wasn’t able to get every step of the sample to work as expected. https://docs.microsoft.com/en-us/powershell/module/azuread/connect-azuread?view=azureadps-2.0
  4. The Azure Portal GUI for setting a certificate as an Application secret Key:

    Note the “Upload Public Key” button that enables selecting a cert file that contains the public key.

ADFS and Office Modern Authentication, What Could Possibly Go Wrong?

We’ve recently thrown the load balancer switch to send users to our new ADFS 4.0 farm rather than the old ADFS 2.x farm. My first baby steps in this process were documented in a prior post.1 It turns out that was just the beginning of this long, tortured journey. Things got very complicated when we started getting errors from users of Outlook hitting our Office 365 Exchange Online. In my prior post I explain how SAML Tracer can be helpful. However, you can’t use a browser-based HTTP debugger/tracer with a thick client like Outlook. In these cases Fiddler is your friend.

Many of the Office 2016 apps (and some of the Office 2013 apps with the right updates and registry settings) can use what Microsoft likes to call Modern Authentication. This is nothing but a lame pseudonym for OpenID Connect. OIDC, as it is abbreviated, uses a web-API friendly exchange to authenticate users. This is in contrast with the older and well established SAML and WS-Trust authentication protocols which are SOAP-based. We don’t (yet) use MFA with Office 365 so the settings I discussed in the prior article don’t apply to it.

Older versions of the Office thick clients use basic authentication with Office 365. The app puts up a credential dialog and then sends the user’s credentials to the O365 service where the actual authentication against Azure AD takes place. The user credentials are protected by TLS. This means that the user has to enter their credentials each time they start the app unless they choose to have the credentials stored locally. The biggest downside to this is that those locally stored credentials can easily be harvested by malware.

How does OIDC change the authentication flow? Newer Office apps open a window that hosts a browser which the app directs to the address of the OIDC provider (OP) configured during auto-discovery. The OP puts up a web form to collect the user’s credentials and, after validating them, returns two JSON web tokens. One is an app authentication token, the other is a refresh token which can be used by the app to request a new auth token when the current one expires. Thus the user’s credentials are never stored locally. The app and refresh tokens could be replayed but they are bound to the app so their loss would be far less damaging.

The Office 365 OP is the familiar https://login.windows.net and/or login.microsoftonline.com which both sit in front of Azure Active Directory (AAD). Things get more complicated when ADFS is in the mix and it really is a bit of a mess when your ADFS is using a SAML Claims Trust Provider (CTP). The UW, like many higher-ed institutions, uses the community developed Shibboleth SAML IdP and our ADFS is configured with it as the CTP. This means we get an authentication flow that transitions between 3 different protocols. The initial step from the Office app uses OIDC. AAD then calls ADFS using WS-Trust. ADFS then translates the WS-Trust call into a SAML protocol call to Shibboleth and the whole process unwinds as the security tokens are returned.2 As you can see there are lots of places where things can go haywire.

Our first sign of something amiss was users reporting this error when they attempted to sign on after the switch to ADFS 4.0.

ADFS error

Error shown by ADFS

Ah-ha, there is an Activity ID. I can look that up in the ADFS event logs to get more detail. Except that the logs didn’t say anything other than there had been an authentication failure. Not very helpful. Here is where breaking out Fiddler becomes necessary. As an aside I recommend running Fiddler from an otherwise unused machine because it captures all network traffic. Your typical workstation is going to be way too noisy which will clutter the network capture with lots of extraneous traffic. I use a virtual machine for this purpose so that nothing is running other than the app (usually Outlook) and Fiddler.

I’m not going to spend time describing how to use Fiddler. There are lots of web articles on that topic.3 What I discovered is that Azure AD (AAD) was sending a WS-Trust request to ADFS with a URL query parameter of:4


ADFS then sends this same URI as the SAML AuthnContextClassRef to Shibboleth. This parameter value is not a part of the SAML spec so Shib returned an error and ADFS then displayed its error.

If you recall from my prior post there is an ADFS CTP property called CustomMfaUri that gets applied in the MFA case. Unfortunately Microsoft did not create a corresponding non-MFA property. I’ve asked them to consider creating a CustomDefaultUri property. We’ll see if that gains any traction.

At this point I called Microsoft Premier Support to find out what, if anything, could be done to fix this. The support engineer took a look at the Fiddler trace and pointed out something I hadn’t noticed. Outlook 2016 was adding a “prompt=login” parameter to its OIDC login request. AAD translates this into the WS-Trust wauth parameter. He told me that this AAD behavior is configurable and that I should follow example 3 of this article https://docs.microsoft.com/en-us/windows-server/identity/ad-fs/operations/ad-fs-prompt-login. That indeed fixed the issue. Wauth is an optional WS-Trust parameter. Password authentication is the default in the absence of this parameter. The SAML AuthnRequest created by ADFS when there is no wauth has no AuthnContextClassRef so Shib also defaults to password authentication.

Our celebration of success was short-lived as other users continued to have similar login problems. What we discovered is that different versions of Office and Outlook 2016 have different “modern auth” behavior. The click-to-run version downloaded from the Office 365 site sends the prompt=login parameter. However, the MSI volume licensed version we distribute to campus is an older build of Office 2016 and it instead sends a different OIDC parameter: “amr_values=pwd”. There is no AAD property to configure the behavior with this parameter, it results in the above wauth being sent to ADFS. As far as we can tell there is no update that can be applied to the MSI version to have it change its behavior to send the “prompt=login” parameter. At this point the MS support engineer had no suggestions.

I’m thinking we have 3 options. The first is to convince everyone with the MSI version to uninstall it and install the c2r version. This is a non-starter because campus IT has no real authority; there is no way to force people to upgrade. We still have thousands of people running Windows 7 and a few even with XP! The next option to consider was writing an F5 load balancer iRule to do a URL rewrite to remove the wauth parameter. That solution would not work in the long run because we want to use AAD conditional access to do a structured roll-out of MFA. Removing the wauth would negate the requirement to use MFA. We could detect this specific wauth and only remove it but now the iRule is becoming fairly complex. So the third option was to ask the Shibboleth team to accept the Microsoft URI as a legitimate proxy for the PasswordProtectedTransport URI which is defined by the SAML spec.5

The Shib team made the change and now things are working properly. To quote Jim the lead engineer “We frequently have to make accommodations for vendor apps that are not spec compliant.” A better solution would be for Microsoft to more fully support SAML-spec IdPs as CTPs. Another solution would be to do password hash sync from AD to AAD so that AAD could handle the login without ADFS or Shibboleth in the mix. However we are concerned about introducing a second login portal to our campus users. We have enough of a problem with phishing and this would only complicate matters. We’ll see which way we end up going.