使用承载令牌时,具有AD身份验证的Azure函数导致401未经授权

时间:2019-03-18 16:41:48

标签: c# azure azure-active-directory azure-functions

我在C#中有一个非常简单的Azure函数,为此我设置了Azure AD Auth。我刚刚使用Express设置在Function配置中创建了App注册。

public static class IsAuthenticated
{
    [FunctionName("IsAuthenticated")]
    public static async Task<IActionResult> Run(
        [HttpTrigger(AuthorizationLevel.Anonymous, "get", "options", Route = null)]
        HttpRequest req,
        ILogger log)
    {
        return new OkObjectResult("You are " + req.HttpContext.User.Identity.Name);
    }
}

当我在浏览器中访问该功能时,所有功能均按预期工作(如果未登录,则必须登录并重定向到我的API)。但是,如果我尝试在需要Bearer令牌的任何地方访问函数,则会出现401 Unauthorized错误。即使很奇怪,我也无法在Azure门户中执行该功能。

但是毫无疑问地获得了令牌,并将其添加到请求中:

enter image description here

我已经尝试了一些其他方法来解决此问题。首先,我认为这可能是CORS问题(因为我也有其中一些问题),只是将CORS设置为接受*,但没有任何变化。

然后,我已将我的API登录端点添加到重定向中,并尝试将隐式授予设置为也接受Access令牌,但仍无法正常工作。

enter image description here

有什么我忽略的东西吗? App Registration Express配置不应该只与Azure函数一起使用吗?

编辑:

按照@thomas-schreiter的建议在重定向中将URL放入我的函数应用程序并没有改变任何东西(我已经尝试了屏幕截图中的配置,还只是将每个值都放在了自己的位置上。) / p>

enter image description here

编辑2:

我现在也尝试通过手动方式with Postman获取Bearer令牌,但是在调用API时仍然遇到401。

8 个答案:

答案 0 :(得分:7)

这花了很长时间才弄清楚,而且官方文档中对此的信息很少。

但是事实证明,问题是/是Azure函数不支持由oauth2 / v2.0 / Azure API生成的Bearer令牌。由于门户网站使用了这些功能(如果您的广告支持它们的话),因此您很幸运能够在其中运行该功能。

这也解释了为什么我的邮递员请求不起作用,因为我也在使用v2 API。切换到v1后,我可以访问我的API(在使用集成身份验证功能时,邮差不允许您添加resource_id,因此我不得不切换为手动处理所有内容)。

此后,意识到您正在编写JS客户端(在我的情况下为Angular)也不能使用MSAL。因此,可以选择ADAL,其中Angular实现看起来有些尴尬。因此,我决定使用angular-oauth2-oidc,这花了一个小时的时间才能使它与Azure AD很好地配合使用。

但是毕竟,我终于可以访问我的API。

我真的不明白为什么您不允许用户使用Azure AD v2令牌访问Azure Function应用程序,但是至少应该对此进行更好地记录。但是无论如何,我终于可以入睡了。

编辑:在我为此打开一个问题之后,他们添加了一条说明,即Azure Functions不支持v2,希望使其他人的生活更轻松。

https://docs.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad

答案 1 :(得分:5)

我设法使用以下配置使它通过邮递员工作。 重要的一课是在“允许的令牌受众”中进行设置,在这种情况下,邮递员用于获取令牌的“资源”名称应相同。我使用了此处提供的相同代码。在这种情况下,在Azure AD中注册的应用程序也是客户端和资源。通过邮递员进行配置和测试,如下所示

enter image description here

在邮递员中获取令牌

enter image description here

使用邮递员..具有承载令牌的授权标头调用azure函数

enter image description here

答案 2 :(得分:2)

您现在可以使用v2.0令牌!

在配置AAD时,不必选择“快速”,而必须选择“高级”并在URL末尾添加/v2.0部分。

Choose Advanced

这是我在控制台应用程序中使用的代码,用于向用户显示登录提示,然后获取承载令牌以与Azure函数一起使用。

string[] scopes = new string[] { "profile", "email", "openid" };
string ClientId = [clientId of Azure Function];
string Tenant = [tenantId];
string Instance = "https://login.microsoftonline.com/";
var _clientApp = PublicClientApplicationBuilder.Create(ClientId)
    .WithAuthority($"{Instance}{Tenant}")
    .WithDefaultRedirectUri()
    .Build();
var accounts = _clientApp.GetAccountsAsync().Result;

var authResult = _clientApp.AcquireTokenInteractive(scopes)
            .WithAccount(accounts.FirstOrDefault())
            .WithPrompt(Prompt.SelectAccount)
            .ExecuteAsync().Result;
var bearerTokenForAzureFunction = authResult.IdToken;

答案 3 :(得分:2)

在Function App上设置Active Directory身份验证时,请将管理模式设置为高级,并根据需要填写客户端ID和颁发者URL(如果需要,还可以填写客户端密码)。

重要的是,在允许令牌受众下,输入应用程序ID URI 。可以在公开API 选项下的已注册应用程序注册(在广告中)中找到。

这是我无法在Function App上进行身份验证的原因。在添加该令牌受众之前,我总是会得到一个带有有效访问令牌的401。

这个Azure active directory - Allow token audiences帮助我得到了答案,但是花了我一段时间才意识到它指的是什么。请记住,可以在您的应用程序注册中找到应用程序ID URI。

希望对您有帮助!

答案 4 :(得分:2)

如果你像我和原始海报一样用头撞墙,可能是你允许用户从“任何组织目录中的帐户(任何 Azure AD 目录 - 多租户)和个人 Microsoft 帐户(例如Skype、Xbox)。”

请注意,截至 2021 年 5 月,v2.0 可以完美运行。如果您使用 https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token 通过 Postman 获取令牌(如上所述),您将获得一个有效令牌,您可以使用该令牌对您的 AZ 函数进行身份验证。

话虽如此,如果用户通过个人帐户或不在您的 AAD 中的帐户登录,MSAL 发出的令牌调用将使用默认的 Microsoft 租户 ID 请求,而不是您的租户身份证。

这就是我无法验证我的函数的原因。如果您使用租户的 AAD 中的用户登录,MSAL 非常棒且易于使用,一切都将按照文档中的描述工作。

答案 5 :(得分:1)

在AAD应用程序本身中,转到“设置”->“答复URL”,并验证“功能应用程序”的URL是否在列表中,其格式如下:https://mycoolapp.azurewebsites.net。如果不是,则添加它。

如果使用插槽,则必须为两个插槽都添加它。

答案 6 :(得分:1)

我现在唯一能想到的就是允许的观众。

转到“ Active Directory”设置,然后单击“高级”。在允许令牌受众下 添加您确切的函数网址。它可能已经包含了回调URL,但是只需将其替换为仅函数库url,而无需进行任何回调,如图所示。

确保在按ok时,还保存了“身份验证/授权”设置以生效,并在1分钟左右后重试。我使用PostMan进行了测试,并传递了承载令牌,并且有效!

enter image description here

答案 7 :(得分:-1)

我今天正面临着​​同样的问题。问题出在我请求访问令牌时传递的资源ID。

例如,最初我使用功能URL作为资源ID来请求这样的令牌:

var allTrue = t2.All(x=>x.BoolProperty == true);
var allFalse = t2.All(x=>x.BoolProperty == false);
var anyNull = t2.Any(x=>x.BoolProperty.HasValue == false);

虽然这返回了访问令牌,但是使用访问令牌调用我的函数api时,我收到了未经授权的401。

我更改了代码,以将功能应用App ID作为资源传递:

AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync("https://myfunction.azurewebsites.net", "myClientAppIdGUID", new Uri("https://login.live.com/oauth20_desktop.srf"), new PlatformParameters(PromptBehavior.SelectAccount)).Result;

现在一切正常。