简体   繁体   English

使用不记名令牌时,带有 AD 身份验证的 Azure 函数导致 401 Unauthorized

[英]Azure Function with AD auth results in 401 Unauthorized when using Bearer tokens

I have a very simple Azure function in C# for which I've setup Azure AD Auth.我在 C# 中有一个非常简单的 Azure 函数,为此我设置了 Azure AD Auth。 I've just used the Express settings to create an App registration in the Function configuration.我刚刚使用 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);
    }
}

When I access the function in my browser everything works as expected (if not logged in I have to login and get redirected to my API).当我在浏览器中访问该功能时,一切都按预期工作(如果未登录,我必须登录并重定向到我的 API)。 But if I try to access my function anywhere a Bearer token is needed I get an 401 Unauthorized error.但是,如果我尝试在需要不记名令牌的任何地方访问我的函数,我会收到401 Unauthorized错误。 Even weirder I also can't execute the function in the Azure Portal.更奇怪的是,我也无法在 Azure 门户中执行该功能。

But the token was aquired without a problem and added to the request:但是令牌被毫无问题地获取并添加到请求中:

在此处输入图像描述

I've tried a few different things to solve this problem.我尝试了一些不同的方法来解决这个问题。 First I thought maybe it's a CORS problem (since I've also had a few of those) and just set CORS to accept *, but nothing changed.首先,我认为这可能是 CORS 问题(因为我也遇到过一些问题),只是将 CORS 设置为接受 *,但没有任何改变。

Then I've added my API login endpoints to the redirect and tried setting the implicit grant to also accept Access tokens, it's still not working.然后我将我的 API 登录端点添加到重定向并尝试将隐式授权设置为也接受访问令牌,它仍然无法正常工作。

在此处输入图像描述

Is there anything I've overlooked?有什么我忽略的吗? Shouldn't the App registration express config just work with azure functions?应用程序注册快速配置不应该只与 azure 函数一起使用吗?

EDIT:编辑:

Putting the URL to my function app in the redirects as suggested by @thomas-schreiter didn't change anything (I've tried the config in the screenshot and also just putting each of those values on it's own).按照@thomas-schreiter的建议,将指向我的函数应用程序的 URL 放在重定向中并没有改变任何东西(我已经尝试了屏幕截图中的配置,并且只是将这些值中的每一个都放在了它自己的位置上)。

在此处输入图像描述

EDIT 2:编辑2:

I've now also tried to aquire an Bearer token the manual way with Postman , but I still run into a 401 when calling my API.我现在还尝试使用 Postman手动获取 Bearer 令牌,但在调用我的 API 时仍然遇到 401。

UPDATE 2020-05-12: According to ambrose-leung 's answer further below you can now add a custom issuer URL which should potentially enable you to use v2 tokens. 2020 年 5 月 12 日更新:根据ambrose-leung在下面的回答您现在可以添加自定义发行者 URL,这可能使您能够使用 v2 令牌。 I haven't tried this myself, but maybe this will provide useful for someone in the future.我自己还没有尝试过,但也许这会对将来的某人有用。 (If his answer helped you please give him an upvote and maybe leave a comment 😉) (如果他的回答对你有帮助,请给他一个赞,或者留下评论😉)


This took forever to figure out, and there is very little information about this in the offical documentations.这花了很长时间才弄明白,官方文档中关于这方面的信息很少。

But it turns out the problem was/is that Azure Functions don't support Bearer tokens generated by the oauth2/v2.0/ Azure API.但事实证明,问题是/是 Azure Functions 不支持由 oauth2/v2.0/Azure API 生成的不记名令牌。 Since the portal uses those (if your AD supports them) you are out of luck to be able to run the function in there.由于门户使用这些(如果您的 AD 支持它们),您很不走运,无法在其中运行该功能。

This also explains why my postman requests didn't work, because I was also using the v2 api.这也解释了为什么我的邮递员请求不起作用,因为我也在使用 v2 api。 After switching to v1 I could access my API (Postman doesn't allow you to add a resource_id when you use the integrated auth feature, so I had to switch to handling everything manually).切换到 v1 后,我可以访问我的 API(当您使用集成的身份验证功能时,Postman 不允许您添加 resource_id,因此我不得不切换到手动处理所有内容)。

After that came the realisation that you can't use MSAL either if you are writing a JS client (Angular in my case).之后意识到如果您正在编写 JS 客户端(在我的情况下是 Angular),您也不能使用 MSAL。 So one alternative is ADAL, where the Angular implementation looks kind of awkward.所以另一种选择是 ADAL,其中 Angular 实现看起来有点尴尬。 So I decided to use angular-oauth2-oidc which took another hour of tinkering to get it to play nicely with Azure AD.所以我决定使用 angular-oauth2-oidc,这又花了一个小时的修补才让它与 Azure AD 很好地配合使用。

But after all that I can finally access my API.但毕竟我终于可以访问我的 API。

I really don't understand why you wouldn't allow users to access Azure Function Apps with Azure AD v2 tokens, but at least this should be so much better documented.我真的不明白为什么你不允许用户使用 Azure AD v2 令牌访问 Azure Function Apps,但至少这应该有更好的记录。 But whatever, I can finally go to sleep.但无论如何,我终于可以睡觉了。

EDIT: After I opend an issue for this, they added a note that v2 isn't supported by Azure Functions, hopefully making life easier for other people.编辑:在我为此打开一个问题后,他们添加了一条说明 Azure Functions 不支持 v2,希望能让其他人的生活更轻松。

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

I managed to get it working through postman using following configuration.我设法使用以下配置通过邮递员使其工作。 Important lesson was setting in "Allowed token audiences" and "resource" name used in postman to acquire token should be same in this case.重要的教训是在“允许的令牌受众”中设置和邮递员用于获取令牌的“资源”名称在这种情况下应该相同。 I used the same code provided here in question.我使用了此处提供的相同代码。 in this case app registered in Azure AD is a client and resource as well.在这种情况下,在 Azure AD 中注册的应用程序也是客户端和资源。 configuration and testing through postman as follows通过postman配置和测试如下

在此处输入图片说明

Acquire token in postman在邮递员中获取令牌

在此处输入图片说明

Calling azure function using Postman .. Authorization header with bearer token使用 Postman 调用 azure 函数 .. 带有不记名令牌的授权标头

在此处输入图片说明

You can now use v2.0 tokens!您现在可以使用 v2.0 代币了!

Instead of choosing 'Express' when you configure AAD, you have to choose 'Advance' and add the /v2.0 part at the end of the URL.在配置 AAD 时,不要选择“Express”,而必须选择“Advance”并在 URL 末尾添加 /v2.0 部分。

选择高级

This is the code that I use in my console app to present the user with a login prompt, then take the bearer token for use with the Azure Function.这是我在控制台应用程序中使用的代码,用于向用户显示登录提示,然后获取不记名令牌以与 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;

When setting up your Active Directory authentication on your Function App, set management mode to advanced and fill in the Client ID and Issuer URL as required (and the client secret if necessary).在您的 Function App 上设置 Active Directory 身份验证时,将管理模式设置为高级并根据需要填写客户端 ID 和颁发者 URL(以及必要时填写客户端密码)。

Importantly, under the Allowed Token Audiences , enter the Application ID URI .重要的是,在Allowed Token Audiences 下,输入Application ID URI This can be found in your registered App Registration (in your AD) under the Expose an API option.这可以在您注册的应用程序注册(在您的 AD 中)中的公开 API选项下找到。

This is what I was missing to get authentication working on my Function App.这是我在 Function App 上进行身份验证时所缺少的。 Before I added that token audience, I would always get a 401 with a valid access token.在添加该令牌受众之前,我总是会收到带有有效访问令牌的 401。

This Azure active directory - Allow token audiences helped me get my answer but it took me a while to realise what it was referring to.这个Azure 活动目录 - 允许令牌受众帮助我得到了答案,但我花了一段时间才意识到它指的是什么。 Remember, it's the Application ID URI that can be found within your App Registration.请记住,它是可以在您的应用程序注册中找到的应用程序 ID URI。

I hope it helps!我希望它有帮助!

If you are banging your head against the wall like myself and the original poster, it may be that you are allowing users to sign in from "Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (eg Skype, Xbox)."如果您像我和原始海报一样用头撞墙,则可能是您允许用户从“任何组织目录中的帐户(任何 Azure AD 目录 - 多租户)和个人 Microsoft 帐户(例如 Skype、 Xbox)”。

Note that as of May 2021, v2.0 works perfectly.请注意,截至 2021 年 5 月,v2.0 可以完美运行。 If you use https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token to get a token with Postman (as described above), you will get a valid token that you can use to auth your AZ Function with.如果您使用https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token通过 Postman 获取令牌(如上所述),您将获得一个有效的令牌,您可以使用该令牌对您的 AZ 函数进行身份验证.

With that said, IF a user is signed in via a personal account or an account not within your AAD , the token call made by MSAL is requested with the default Microsoft tenant id, NOT your tenant id.话虽如此,如果用户通过个人帐户或不在您的 AAD 中的帐户登录,则 MSAL 进行的令牌调用将使用默认的 Microsoft 租户 ID 进行请求,而不是您的租户 ID。

THIS is why I was unable to auth my function.这就是我无法验证我的函数的原因。 If you are logged in with a user in your tenant's AAD, MSAL is amazing and easy to use and everything will work as described in the documentation.如果您使用租户的 AAD 中的用户登录,MSAL 非常棒且易于使用,一切都将按照文档中的描述工作。

In the AAD app itself, go to Settings -> Reply URLs and verify that the url of the Function App is in the list, which has the following format: https://mycoolapp.azurewebsites.net .在 AAD 应用本身中,转到“设置”->“回复 URL”并验证函数应用的 url 是否在列表中,其格式如下: https://mycoolapp.azurewebsites.net If it isn't, then add it.如果不是,则添加它。

If you use slots, you have to add it for both slots.如果您使用插槽,则必须为两个插槽添加它。

The only thing i can think of right now is Allowed Audience.我现在唯一能想到的是允许观众。

Go to Your Active directory settings and click Advance.转到您的活动目录设置,然后单击高级。 Under Allowed Token Audience Add your exact function url.Allowed Token Audience 下添加您的确切功能网址。 It might already be there with call back url but Simply replace it with only function base url without any call back as mentioned in the picture.它可能已经与回调 url 一起存在,但只需将其替换为仅函数基 url 而不像图片中提到的任何回调。

Make sure when you press ok , you also save your Authentication / Authorization setting to take effect and try again after 1min or so.确保当您按 ok 时,您还保存了您的身份验证/授权设置以使其生效,并在 1 分钟左右后重试。 I tested using PostMan and passing bearer token and it works !我使用 PostMan 进行了测试并传递了不记名令牌,并且可以正常工作!

在此处输入图片说明

I'm facing the exact same issue today.我今天面临完全相同的问题。 The issue turned out to be the resource id that I was passing when requesting the access token.问题原来是我在请求访问令牌时传递的资源 ID。

For example, initially I was requesting a token like this, using the function URL as the resource id:例如,最初我请求这样的令牌,使用函数 URL 作为资源 ID:

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

While this returned an access token, I was receiving a 401 unauthorized when using the access token to call my function api.虽然这返回了一个访问令牌,但我在使用访问令牌调用我的函数 api 时收到了未经授权的 401。

I changed my code to pass my function apps App Id as the resource:我更改了代码以将我的函数应用程序 App Id 作为资源传递:

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

Everything works fine now.现在一切正常。

对我来说,当我添加范围时解决了这个问题: [clientId]/.default per this article

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

相关问题 PostAsync(承载身份验证)后未经授权的HttpClient 401 - HttpClient 401 Unauthorized after PostAsync (Bearer Auth) Azure AD API 请求401未授权 - Azure AD API request 401 Unauthorized ASP.NET Core SignalR使用Azure AD返回401 Unauthorized - ASP.NET Core SignalR returns 401 Unauthorized using Azure AD 使用Azure AD身份验证获取承载访问令牌以访问o365资源 - Obtaining bearer access token to access o365 resource, using Azure AD auth Jmeter - Azure AD - Bearer Token - 配置函数 - Jmeter - Azure AD - Bearer Token - Configuration Function Azure AD-HTTP / 1.1 401未经授权-Windows Service - Azure AD - HTTP/1.1 401 Unauthorized - Windows Service Azure App Service的本地调试版本中的身份验证导致401未经授权 - Authentication in local debug build of Azure App Service results in 401 Unauthorized 使用来自 Azure AD 的承载令牌保护 ASP.Net Core 3.1 API - Secure ASP.Net Core 3.1 API with Bearer Tokens from Azure AD AspNet.Core、IdentityServer 4:在使用 JWT 不记名令牌与 SignalR 1.0 进行 websocket 握手期间未经授权 (401) - AspNet.Core, IdentityServer 4: Unauthorized (401) during websocket handshake with SignalR 1.0 using JWT bearer token Azure AD Jwt Bearer令牌 - Azure AD Jwt Bearer token
 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM