自动创建 Azure AD Web 应用密钥

Automating creation of an Azure AD Web App Key

我们编写了一组 Powershell 脚本来自动执行在 Azure 环境中创建 Web 应用程序新实例的过程。这些脚本的一部分使用 Graph API 创建一些 Azure AD 对象,以及我们用于单点登录的 Auth0 中的相应对象。到目前为止,我们尚未成功以编程方式为 AAD 应用程序创建新密钥。我们已经尝试了多种方法,虽然我们可以看到生成的应用程序确实有一个密钥(并且 Auth0 中的连接对象在其 Client Secret 字段中有该密钥),但我们在尝试进行身份验证时总是会收到此错误:

至此,我们已经授予对应用程序的访问权限以进行单点登录和读取 AAD 中的目录数据。 (编辑:我们通过手动遵循 provisioning_ticket_URL 来完成此操作,该 provisioning_ticket_URL 是 Auth0 API 调用返回的对象的一部分以创建连接对象。此 URL 提示我们输入 Azure 凭据,然后显示此页面:

)

此错误一直存在,直到我们通过 Azure 门户为应用程序手动创建一个新的密钥,保存应用程序,并将新创建的密钥复制到 Auth0 连接中。这样做总能解决问题,但我们希望避免这一额外步骤。

在我们在 API 调用中创建的 AAD 应用程序主体中,我们有定义键的部分:

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

至于这些值,我们已经尝试通过多种不同的方式生成它们,并且它接受 API PUT 调用中的值,但是当我们尝试登录。例如,我们尝试的一种方法是从以下位置插入 $appKeyGUID 和 $appKeyValue 的值:

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

分别写入keyID和value。我在其他地方读到该值应该是 44 个字符长,但是这个不是。

但似乎值本身可能不是问题所在。我已经尝试通过 Azure 门户生成密钥,使用 Graph API GET 调用来检索 keyId,然后将这两个确切值硬编码到应用程序主体中,但登录仍然会产生相同的错误。

知道我哪里出错了吗?

编辑:根据 Philippe 的建议,我尝试通过门户仅更改 AD 应用程序的显示名称,这确实解决了问题。这让我想到,在通过门户保存时正在修复的应用程序主体中的其他地方可能有问题。我在手动保存之前和之后检查了清单,确实存在一个小差异:在 RequiredResourceAccess 部分(我从这里 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 了解到),我有这个:

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

而不是这个,门户将其更改为

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

所以我更改了发送的正文以匹配第二种格式。不幸的是,我们仍然在进行此更改后遇到相同的错误。此外,我验证了在门户网站上进行保存之前和之后清单现在是相同的,应用程序上的 API GET 调用返回的正文也是如此。除了应用程序的定义之外,必须有一些东西 else 门户保存正在更改。

之后,我尝试使用 Graph API 执行两次 PATCH 调用以将显示名称更新为某个内容,然后再将其更改回来,希望它的行为与通过门户网站执行的操作类似并修复问题。我通过门户验证 PATCH 调用确实更改了应用程序的显示名称。可悲的是,这些修改似乎并没有解决问题,我仍然遇到原来的错误。

我们使用 Graph API 调用创建应用程序,如下所示:

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

这里是我们最终用来定义应用程序的 $newappbody。为了故障排除目的,我留下了一些硬编码的东西:

{
  "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"
        }
      ]
    }
  ]
}

这里的最终问题似乎与申请同意有关。 我将在此处详细介绍,但请随时快速阅读本文档,其中描述了我们的同意框架: https://azure.microsoft.com/en-us/documentation/articles/active-directory-integrating-applications/

您在应用程序清单中显示的内容与您的应用程序配置有关,但是,您的应用程序的同意记录不会在那里找到,因此我认为您的 'a/b' 测试不会有成果。

我认为您在这里真正发现的是 Azure 门户在后台有一些 "magic" 如果您是管理员并保存应用程序,它将同意您的应用程序。当您在门户中从头到尾注册应用程序时(同样,作为管理员),我们会在您保存应用程序后自动为您记录同意。这发生在后台,所创建的结果 'objects' 是:租户中您的应用程序的服务主体,以及您作为用户与您的服务主体之间的同意 link。创建的 link 特别是一个 'admin tenant-wide consent' 对象,这与将 "prompt=admin_consent" 作为查询字符串添加到登录名 url 中得到的结果相同。

当您使用其他方法(例如 Graph API 或 AAD PowerShell)创建应用程序时,不会创建这些对象,这将导致您在尝试进行身份验证时看到的那种错误。

此处的解决方案是,您需要先与租户的管理员进行一次交互式登录体验,然后才能让其他人登录该应用程序。在此登录体验期间,您必须让管理员同意您的应用及其所需的权限。在这一次同意体验之后,后续调用 to/from 您的应用程序应该不需要同意,并且您不会 运行 进入您遇到的问题。

总而言之:

  1. 使用您想要的任何方法自动创建应用程序
  2. 然后使用您的申请信息,并使用查询字符串 "prompt=admin_consent"
  3. 创建与管理员的交互式登录
  4. 然后按照您希望的方式使用您的应用程序

谢谢! 肖恩·塔布里齐