MGS
MGS

Reputation: 35

Automating creation of an Azure AD Web App Key

We've written a set of Powershell scripts to automate the process of creating new instances of our web app in our Azure environment. One part of these scripts uses the Graph API to create a few Azure AD objects, as well as corresponding objects in Auth0, which we use for single sign-on. So far, we haven't been successful in programmatically creating a new Key for the AAD Application. We've tried a number of methods, and while we can see that the resulting Application does have a Key in place (and the connection object in Auth0 has that key in its Client Secret field), we always receive this error when trying to authenticate:

error message

By this point, we've already granted access to the app to single sign-on and to read directory data in AAD. (edit: We do this through manually following the provisioning_ticket_URL that is part of the object returned by the Auth0 API call to create the Connection object. This URL prompts us for Azure credentials and then displays this page: grantaccess

)

This error persists until we manually create a new Key for the application through the Azure portal, save the application, and copy the newly created key into the Auth0 connection. Doing so always resolves the problem, but we'd like to avoid this extra step.

Within the body of the AAD application that we're creating in the API call, we have this section that defines the key:

"passwordCredentials": 
  [
    {
        "startDate":  "2016-10-28T20:40:32Z",
        "endDate":  "2017-10-29T20:40:32Z",
        "keyId":  "(a GUID)",
        "value":  "(a Base64 string)"
    }
  ],

As for those values, we've tried to generate them in quite a few different ways, and it accepts the values on the API PUT call, but they all still give us that same error when we try to log in. As an example, one way we tried was to plug in the values from $appKeyGUID and $appKeyValue from this:

$appKeyGUID = [guid]::NewGuid() 
$guidBytes = [System.Text.Encoding]::UTF8.GetBytes($appKeyGUID)
$appKeyValue = [System.Convert]::ToBase64String($guidBytes);

into the keyID and value, respectively. I've read elsewhere that the value should be 44 characters long, though, which this one will not be.

But it seems like the value itself might not be the problem. I've tried generating a key through the Azure portal, using a Graph API GET call to retrieve the keyId, and then hardcoding those two exact values into the application body, but logging in still yields the same error.

Any idea where I'm going wrong?

EDIT: Per Philippe's suggestion, I tried changing only the Display Name of the AD application through the portal, and that did indeed resolve the problem. This led me to think that maybe something was wrong elsewhere within the application body that was being fixed when saving through the portal. I checked the manifest before and after doing that manual save, and there was indeed one small difference: within the RequiredResourceAccess section (of which I learned from here http://www.cloudidentity.com/blog/2015/09/01/azure-ad-permissions-summary-table/ and here https://www.microsoftpressstore.com/articles/article.aspx?p=2473127&seqNum=2), I had this:

{
  "id": "5778995a-e1bf-45b8-affa-663a9f3f4d04",
  "type": "Role"
},
{
  "id": "5778995a-e1bf-45b8-affa-663a9f3f4d04",
  "type": "Scope"
}

Instead of this, which the portal changes it to

{
  "id": "5778995a-e1bf-45b8-affa-663a9f3f4d04",
  "type": "Role,Scope"
},

So I changed the body we're sending to match the second format. Unfortunately, we still are getting the same error with this change in place. Further, I verified that the manifest is now identical before and after making the save on the portal, as is the body returned by an API GET call on the application. There must be something else that the portal save is changing other than the application's definition.

After that, I tried using the Graph API to perform two PATCH calls to update the display name to something and then change it back, hoping that it would behave similarly to doing it through the portal and fix the problem. I verified through the portal that the PATCH calls were indeed changing the display name of the app. Sadly, it seems those edits didn't fix the issue, and I'm still getting the original error.

We create the application with a Graph API call like this:

$uri = "https://graph.windows.net/$waadTenant/applications?api-version=1.6"
$newADApp = (Invoke-RestMethod –Uri $uri –Headers $authHeader –Method POST -Body $newappbody –Verbose)

And here is the $newappbody that we end up using to define the application. I've left some things hard coded for troubleshooting purposes:

{
  "odata.type": "Microsoft.DirectoryServices.Application",
  "displayName": "customer1",
  "homepage": "https://customer1.(our tenant).com",
  "identifierUris":
  [
    "https://customer1.(our tenant).com"
  ],
  "replyUrls":
  [
    "https://(our tenant).auth0.com/login/callback"
  ],
  "passwordCredentials":
  [
    {
        "startDate":  "(a hardcoded date)",
        "endDate":  "(a hardcoded date)",
        "keyId":  "(hardcoded GUID that was previously generated by the portal and extracted through an API GET)",
        "value":  "(hardcoded Base64 like above)"
    }
  ],
  "requiredResourceAccess":
  [
    {
      "resourceAppId": "00000002-0000-0000-c000-000000000000",
      "resourceAccess":
      [
        {
          "id": "5778995a-e1bf-45b8-affa-663a9f3f4d04",
          "type": "Role,Scope"
        },
        {
          "id": "311a71cc-e848-46a1-bdf8-97ff7156d8e6",
          "type": "Scope"
        },
        {
          "id": "6234d376-f627-4f0f-90e0-dff25c5211a3",
          "type": "Scope"
        }
      ]
    }
  ]
}

Upvotes: 1

Views: 471

Answers (1)

Shawn Tabrizi
Shawn Tabrizi

Reputation: 12434

It seems like the ultimate issue here is related to Application Consent. I will go into more details here, but feel free to quickly take a read through this document which describes our Consent Framework: https://azure.microsoft.com/en-us/documentation/articles/active-directory-integrating-applications/

The stuff you are showing in your Application manifest are related to your application configuration, however, the consent record for your app will not be found there, thus I think your 'a/b' testing will not be fruitful.

I think what you are really finding here is that the Azure Portal has some "magic" in the background which will consent to your application if you are an administrator and you save the application. When you register an application from start to finish in the portal (again, as an admin), we record consent for you automatically once you save the app. This happens in the background, and the resulting 'objects' that get created are: A service principal for your app in your tenant, and consent links between you as the user and your service principal. The link that gets created is specifically an 'admin tenant-wide consent' object, which is the same thing you would get if you added "prompt=admin_consent" to your login url as a query string.

When you create an application using other methods, like the Graph API or AAD PowerShell, these objects are not created, which will result in the kind of error you are seeing when you try and authenticate.

The solution here is that you will need to have an interactive login experience one time with an Admin of the tenant before you are able to get other people to sign into the application. During this login experience, you must have the admin consent to your app and the permissions it requires. After this one time consent experience, subsequent calls to/from your application should not require consent, and you will not run into the issue you are having.

So in summary:

  1. Automatically create an App using whatever method you want
  2. Then use your application information, and create an interactive login with the administrator, using the query string "prompt=admin_consent"
  3. Then use your app however you expect it to work

Thanks! Shawn Tabrizi

Upvotes: 2

Related Questions