繁体   English   中英

如何在 .NET Core 2.1 中使用 Azure AD 身份验证和 OpenIdConnect 交换 access_token 的授权代码?

[英]How to exchange authorization code for access_token using Azure AD Authentication and OpenIdConnect in .NET Core 2.1?

我是多租户应用程序的新手,并且已经搜索了几天关于如何在.NET Core 2.1 中本地获取 access_token 。 到目前为止,我发现的所有内容都是 2.0 或更早版本,并且发布的任何方法都不存在于 2.1 中。

我创建了一个 Microsoft Graph 助手,它接受一个字符串access_token并将获取用户详细信息。 我只是尝试在收到授权代码(OnAuthorizationCodeReceived 事件)后使用access_token调用帮助程序。

我觉得这最多应该是一个单行或简短的片段,我似乎无法找到解决方案。

这是我希望发生这种情况的 Azure AD 扩展:

using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using System;
using System.Threading.Tasks;

namespace Microsoft.AspNetCore.Authentication
{
    public static class AzureAdAuthenticationBuilderExtensions
    {        
        public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder)
            => builder.AddAzureAd(_ => { });

        public static AuthenticationBuilder AddAzureAd(this AuthenticationBuilder builder, Action<AzureAdOptions> configureOptions)
        {
            builder.Services.Configure(configureOptions);
            builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, ConfigureAzureOptions>();
            builder.AddOpenIdConnect();
            return builder;
        }

        private class ConfigureAzureOptions: IConfigureNamedOptions<OpenIdConnectOptions>
        {
            private readonly AzureAdOptions _azureOptions;

            public ConfigureAzureOptions(IOptions<AzureAdOptions> azureOptions)
            {
                _azureOptions = azureOptions.Value;
            }

            public void Configure(string name, OpenIdConnectOptions options)
            {
                options.ClientId = _azureOptions.ClientId;
                options.Authority = $"{_azureOptions.Instance}";
                options.UseTokenLifetime = true;
                options.CallbackPath = _azureOptions.CallbackPath;
                options.RequireHttpsMetadata = true;
                options.ResponseType = OpenIdConnectResponseType.CodeIdToken;
                options.SaveTokens = true;
                options.GetClaimsFromUserInfoEndpoint = true;
                options.TokenValidationParameters.ValidateIssuer = true;
                options.TokenValidationParameters.IssuerValidator = ValidateIssuer;
                options.Events.OnAuthenticationFailed = AuthenticationFailed;
                options.Events.OnAuthorizationCodeReceived = AuthorizationCodeReceived;
            }

            // TODO check tenant against database for authorized tenants
            private string ValidateIssuer(string issuer, SecurityToken securityToken, TokenValidationParameters validationParameters)
            {
                if (false)
                {
                    //throw new SecurityTokenInvalidIssuerException();

                    // how do i get my db context here if there's no context in the scope?
                    // var db = context.HttpContext.RequestServices.GetRequiredService<RdmsContext>(); <-- something like this
                }

                // allowed
                return issuer;
            }

            private static Task AuthenticationFailed(
                AuthenticationFailedContext context)
            {
                context.HandleResponse();

                string message = Uri.EscapeUriString(context.Exception.Message);
                context.Response.Redirect($"/Home/Error?message={message}");
                return Task.CompletedTask;
            }

            private static async Task AuthorizationCodeReceived(
                AuthorizationCodeReceivedContext context)
            {
                string authorizationCode = context.ProtocolMessage.Code;
                string idToken = context.ProtocolMessage.IdToken;

                // ProtocolMessage has AccessToken property, but it's null.

                // Exchange authorization code for access_token here
                string accessToken = ...

                var userDetails = MyProject.Helpers.Graph
                    .GetUserDetailsAsync(accessToken);

                context.HandleCodeRedemption(accessToken, idToken);
            }

            public void Configure(OpenIdConnectOptions options)
            {
                Configure(Options.DefaultName, options);
            }
        }
    }
}

经过大量的反复试验,我终于找到了缺失的部分。

首先,我将options.ResponseType更改为OpenIdConnectResponseType.IdTokenToken ,据我所知,它返回一个 IdToken 以及一个令牌(访问令牌)。 这需要提供将使用访问令牌的资源。

因此,我还添加了值为"https://graph.microsoft.com" options.Resource

我还删除了options.GetClaimsFromUserInfoEndpoint = true;

我还必须更新 Azure 中的应用程序清单以将oauth2AllowImplicitFlow更改为true

最后,我取代了OnAuthorizationCodeRecevied事件与OnTokenValidated从我打电话给我的Microsoft Graph帮助的地步。

这种更改组合导致成功接收访问令牌,然后我可以将其提供给我的 Microsoft Graph 帮助程序并获得我需要的内容。

最终的Configure方法现在看起来像这样:

public void Configure(string name, OpenIdConnectOptions options)
{
    options.ClientId = _azureOptions.ClientId;
    options.Resource = "https://graph.microsoft.com";
    options.Authority = $"{_azureOptions.Instance}";
    options.UseTokenLifetime = true;
    options.CallbackPath = _azureOptions.CallbackPath;
    options.RequireHttpsMetadata = true;
    options.ResponseType = OpenIdConnectResponseType.IdTokenToken;
    options.SaveTokens = true;
    options.TokenValidationParameters.ValidateIssuer = true;
    options.TokenValidationParameters.IssuerValidator = ValidateIssuer;
    options.Events.OnAuthenticationFailed = AuthenticationFailed;
    options.Events.OnTokenValidated = TokenValidatedAsync;
}

并且TokenValidatedAsync现在具有在TokenValidatedContext.ProtocolMessage.AccessToken找到的访问令牌:

private static async Task TokenValidatedAsync(
    TokenValidatedContext context)
{
    string accessToken = context.ProtocolMessage.AccessToken;
    Graph.User userDetails = await MyProject.Helpers.Graph
        .GetUserDetailsAsync(accessToken);
}

从这里我可以使用 Microsoft Graph 用户详细信息执行我需要的操作。

我找不到任何工作示例,因此我将其留在这里以供将来参考。

非常感谢,我想知道如何获取 Microsoft 图形的 access_token,但我使用的是 Microsoft.AspNetCore.Authentication.AzureAD.UI(它抽象了所有 OpenId 配置),我认为options.UseTokenLifetime = trueoptions.SaveTokens = true; 帮助访问context.ProtocolMessage.AccessToken

再次感谢你

暂无
暂无

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

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM