Asp.net core 如何在.NET Core 2.1中使用Azure AD身份验证和OpenIdConnect为access_令牌交换授权代码?

Asp.net core 如何在.NET Core 2.1中使用Azure AD身份验证和OpenIdConnect为access_令牌交换授权代码?,asp.net-core,.net-core,asp.net-core-mvc,microsoft-graph-api,asp.net-core-2.1,Asp.net Core,.net Core,Asp.net Core Mvc,Microsoft Graph Api,Asp.net Core 2.1,我是多租户应用的新手,几天来一直在谷歌上搜索如何在.NETCore2.1中本机获取access_令牌。到目前为止,我发现的所有内容都是2.0或更早版本,并且发布的方法都不存在于2.1中 我已经创建了一个Microsoft Graph helper,它接受一个字符串access\u token,并将获取用户详细信息。我只是在收到授权代码(OnAuthorizationCodeReceived事件)后,尝试使用access\u令牌调用帮助程序 我觉得这最多应该是一行或一小段,而我似乎找不到解决办法

我是多租户应用的新手,几天来一直在谷歌上搜索如何在.NETCore2.1中本机获取access_令牌。到目前为止,我发现的所有内容都是2.0或更早版本,并且发布的方法都不存在于2.1中

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

我觉得这最多应该是一行或一小段,而我似乎找不到解决办法

这是我的Azure广告扩展,我希望它能够实现:

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);
            }
        }
    }
}
使用Microsoft.AspNetCore.Authentication.OpenIdConnect;
使用Microsoft.Extensions.DependencyInjection;
使用Microsoft.Extensions.Options;
使用Microsoft.IdentityModel.Protocols.OpenIdConnect;
使用Microsoft.IdentityModel.Tokens;
使用制度;
使用System.Threading.Tasks;
命名空间Microsoft.AspNetCore.Authentication
{
公共静态类AzureAdAuthenticationBuilderExtensions
{        
公共静态AuthenticationBuilder AddAzureAd(此AuthenticationBuilder)
=>builder.AddAzureAd({});
公共静态AuthenticationBuilder AddAzureAd(此AuthenticationBuilder,操作配置选项)
{
builder.Services.Configure(配置选项);
builder.Services.AddSingleton();
AddOpenIdConnect();
返回生成器;
}
私有类配置AzureOptions:IConfigureNameOptions
{
私人只读AzureAdOptions _azureOptions;
公共配置azureOptions(IOptions azureOptions)
{
_azureOptions=azureOptions.Value;
}
public void配置(字符串名称、OpenIdConnectOptions选项)
{
options.ClientId=\u azureOptions.ClientId;
options.Authority=$“{u azureOptions.Instance}”;
options.UseTokenLifetime=true;
options.CallbackPath=\u azureOptions.CallbackPath;
options.RequireHttpsMetadata=true;
options.ResponseType=OpenIdConnectResponseType.CodeIdToken;
options.SaveTokens=true;
options.GetClaimsFromUserInfoEndpoint=true;
options.TokenValidationParameters.ValidateIsuer=true;
options.TokenValidationParameters.IssuerValidator=ValidateIssuer;
options.Events.OnAuthenticationFailed=身份验证失败;
options.Events.OnAuthorizationCodeReceived=AuthorizationCodeReceived;
}
//TODO根据数据库检查授权租户的租户
私有字符串ValidateSuer(字符串颁发者、SecurityToken SecurityToken、TokenValidationParameters validationParameters)
{
if(false)
{
//抛出新的SecurityTokenInvalidIssuerException();
//如果作用域中没有上下文,如何在此处获取db上下文?
//var db=context.HttpContext.RequestServices.GetRequiredService();经过多次尝试和错误,我终于找到了缺失的部分

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

因此,我还添加了
选项。Resource
的值为
”https://graph.microsoft.com“

我还删除了
options.GetClaimsFromUserInfoEndpoint=true;

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

最后,我用
OnTokenValidated
替换了
OnAuthorizationCodeReceived
事件,作为调用Microsoft Graph helper的起点

这些更改的组合导致成功接收访问令牌,然后我可以将其馈送给我的Microsoft Graph helper并获得我需要的内容

最后的
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用户详细信息做我需要的事情

我找不到这方面的任何有效示例,因此我将把它留在这里供将来参考。

经过多次尝试和错误,我终于找到了缺失的部分

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

因此,我还添加了
选项。Resource
的值为
”https://graph.microsoft.com“

我还删除了
options.GetClaimsFromUserInfoEndpoint=true;

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

最后,我用
OnTokenValidated
替换了
OnAuthorizationCodeReceived
事件,作为调用Microsoft Graph helper的起点

这些更改的组合导致成功接收访问令牌,然后我可以将其馈送给我的Microsoft Graph helper并获得我需要的内容

最后的
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);
}
从这里我可以做什么