C# 使用OIDC启动进行身份验证时GetExternalLoginInfoAsync无法通过 脚本

C# 使用OIDC启动进行身份验证时GetExternalLoginInfoAsync无法通过 脚本,c#,asp.net-core,openid,lti,C#,Asp.net Core,Openid,Lti,我正在配置LTI Advantage 它包括三个步骤 在这里,我只想问一下第一步和第二步 第一步: OIDC:初始OpenID Connect(OIDC)启动发生,其中工具被通知即将进行的LTI启动,并发生第三方发起的登录OIDC流。这是一种识别哪个用户被启动的方法,有助于确保没有任何启动被拦截。我们使用LTI启动端点作为登录URL 第二步: JSON Web令牌:在OIDC启动之后,我们执行1.3启动。构建发射有效载荷时,将其作为JWT进行签名。所有LTI消息都作为JWT发送,并由消息的发送者

我正在配置LTI Advantage

它包括三个步骤

在这里,我只想问一下第一步和第二步

第一步: OIDC:初始OpenID Connect(OIDC)启动发生,其中工具被通知即将进行的LTI启动,并发生第三方发起的登录OIDC流。这是一种识别哪个用户被启动的方法,有助于确保没有任何启动被拦截。我们使用LTI启动端点作为登录URL

第二步: JSON Web令牌:在OIDC启动之后,我们执行1.3启动。构建发射有效载荷时,将其作为JWT进行签名。所有LTI消息都作为JWT发送,并由消息的发送者签名。接收方使用由KeyID标识的公钥来验证JWT签名。此令牌的接收方可以验证: 谁寄的

第三步: OAuth2:为了扩展诸如分配和成绩之类的服务,OAuth2令牌用于进行后续的服务API调用,例如从成绩册检索分数或同步新结果

内容没有改变

错误 我当前在收到
id\u令牌后登录用户时遇到问题

GetExternalLoginInfoAsync
方法不起作用,它正在重定向到身份验证服务器或为空

细节: 当身份验证从外部源开始时,用户始终登录到此流

第一个用户使用6个查询字符串参数重定向到/OidcLogin路由

之后,这两个服务器都被重定向到身份验证服务器

之后,身份验证服务器验证所有输入并重定向到重定向URL

之后,我在
启动
类中验证
Id\u令牌
状态
Nonce

验证完所有内容后,我将重定向到
主页/回调

这里,我使用的是
var externalInfo=await\u signInManager.getexternallogininfosync()在回调中

在回调中使用它会重定向到身份验证服务器

我还尝试在两个不同的OpenIdConnect事件上使用它,即
OnTokenResponseReceived
OnTicketReceived
,但在这两种情况下它都是空的

我知道需要登录用户

此外,我现在在上述两个事件中都获得了
id\u令牌
,但用户尚未登录。httpcontext中没有令牌

调试很困难,因为我无法在本地运行解决方案,因为身份验证是从外部源启动的

OidcLoginController.cs

using AdvantageTool.Models;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;

namespace AdvantageTool.Controllers
{
    public class OidcLoginController : Controller
    {
        private readonly SignInManager<AdvantageToolUser> _signInManager;

        public OidcLoginController(SignInManager<AdvantageToolUser> signInManager)
        {
            _signInManager = signInManager;
        }

        [HttpPost]
        [Consumes("application/x-www-form-urlencoded")]
        public IActionResult Index([FromForm] IFormCollection value)
        {
            var provider = "oidc";
            var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, "/Home/Callback");

            var oidcModel = new OidcModel
            {
                ClientId = value["client_id"],
                Issuer = value["iss"],
                LoginHint = value["login_hint"],
                LtiDeploymentId = value["lti_deployment_id"],
                LtiMessageHint = value["lti_message_hint"],
                TargetLinkUri = value["target_link_uri"]
            };

            properties.Items.Add(nameof(OidcModel.ClientId), oidcModel.ClientId);
            properties.Items.Add(nameof(OidcModel.LoginHint), oidcModel.LoginHint);
            properties.Items.Add(nameof(OidcModel.LtiMessageHint), oidcModel.LtiMessageHint);
            properties.Items.Add(nameof(OidcModel.Issuer), oidcModel.Issuer);
            properties.Items.Add(nameof(OidcModel.LtiDeploymentId), oidcModel.LtiDeploymentId);
            properties.Items.Add(nameof(OidcModel.TargetLinkUri), oidcModel.TargetLinkUri);

            return new ChallengeResult(provider, properties);
        }
    }
}
使用AdvantageTool.Models;
使用Microsoft.AspNetCore.Http;
使用Microsoft.AspNetCore.Identity;
使用Microsoft.AspNetCore.Mvc;
使用System.Threading.Tasks;
命名空间AdvantageTool.Controllers
{
公共类控制器:控制器
{
专用只读签名管理器\u签名管理器;
公共控制器(SignInManager SignInManager)
{
_signInManager=signInManager;
}
[HttpPost]
[使用(“应用程序/x-www-form-urlencoded”)]
公共IActionResult索引([FromForm]IFormCollection值)
{
var provider=“oidc”;
var properties=_signInManager.ConfigureExternalAuthenticationProperties(provider,“/Home/Callback”);
变量oidcModel=新oidcModel
{
ClientId=值[“客户端id”],
发卡机构=值[“iss”],
LoginHint=value[“login\u hint”],
LtiDeploymentId=值[“lti_部署_id”],
LtiMessageHint=值[“lti_message_hint”],
TargetLinkUri=value[“target\u link\u uri”]
};
properties.Items.Add(nameof(OidcModel.ClientId)、OidcModel.ClientId);
properties.Items.Add(nameof(OidcModel.LoginHint)、OidcModel.LoginHint);
properties.Items.Add(nameof(OidcModel.LtiMessageHint)、OidcModel.LtiMessageHint);
properties.Items.Add(名称(OidcModel.Issuer)、OidcModel.Issuer);
properties.Items.Add(nameof(OidcModel.LtiDeploymentId)、OidcModel.LtiDeploymentId);
添加(名称(OidcModel.TargetLinkUri)、OidcModel.TargetLinkUri);
返回新的ChallengeResult(提供程序、属性);
}
}
}
Startup.cs

using AdvantageTool.Models;
using AdvantageTool.Services.LTI;
using AdvantageTool.Utility;
using IdentityModel;
using Microsoft.AspNetCore.Authentication.Cookies;
using Microsoft.AspNetCore.Authentication.OAuth;
using Microsoft.AspNetCore.Authentication.OpenIdConnect;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using System;
using System.Security.Cryptography;
using System.Linq;
using System.Collections.Generic;
using System.Text.Encodings.Web;
using System.Threading.Tasks;
using System.Net.Http;
using Newtonsoft.Json;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using Serilog;
using System.Net;

namespace AdvantageTool
{
    public class Startup
    {
        public Startup(IConfiguration configuration)
        {
            Configuration = configuration;
        }

        public IConfiguration Configuration { get; }

        // This method gets called by the runtime. Use this method to add services to the container.
        public void ConfigureServices(IServiceCollection services)
        {
            services.Configure<CookiePolicyOptions>(options =>
            {
                // This lambda determines whether user consent for non-essential cookies is needed for a given request.
                options.CheckConsentNeeded = context => true;
                options.MinimumSameSitePolicy = SameSiteMode.None;
            });

            services.AddDbContext<ApplicationDbContext>(options =>
                options.UseInMemoryDatabase("Application"));

            services
                .AddDefaultIdentity<AdvantageToolUser>()
                .AddEntityFrameworkStores<ApplicationDbContext>();

            services
                .AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
                .AddCookie(o => o.LoginPath = new PathString("/OidcLogin"))
                .AddOpenIdConnect("oidc", options =>
                {
                    options.SaveTokens = true;

                    options.ClientId = Configuration["OpenIdConfig:ClientId"];
                    options.Authority = Configuration["OpenIdConfig:Issuer"];
                    options.ResponseType = OpenIdConnectResponseType.IdToken;
                    options.ResponseMode = OpenIdConnectResponseMode.FormPost;
                    options.Prompt = OpenIdConnectPrompt.None;
                    options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
                    options.DisableTelemetry = true;

                    options.Configuration = new OpenIdConnectConfiguration
                    {
                        AuthorizationEndpoint = Configuration["OpenIdConfig:AuthorizationEndpoint"],
                        TokenEndpoint = Configuration["OpenIdConfig:TokenEndpoint"],
                        Issuer = Configuration["OpenIdConfig:Issuer"],
                        JwksUri = Configuration["OpenIdConfig:JwksUri"]
                    };

                    options.CallbackPath = new PathString("/LtiTool");

                    options.Scope.Clear();
                    options.Scope.Add(OidcConstants.StandardScopes.OpenId);

                    options.Events = new OpenIdConnectEvents
                    {
                        //OnRemoteFailure = HandleOnRemoteFailure,
                        OnRedirectToIdentityProvider = context =>
                        {
                            var login_hint = context.Properties.Items[nameof(OidcModel.LoginHint)];
                            context.ProtocolMessage.LoginHint = login_hint;

                            var lti_message_hint = context.Properties.Items[nameof(OidcModel.LtiMessageHint)];

                            context.ProtocolMessage.SetParameter("lti_message_hint", lti_message_hint);

                            var clientId = context.Properties.Items[nameof(OidcModel.ClientId)];
                            context.ProtocolMessage.ClientId = clientId;

                            context.ProtocolMessage.Prompt = OpenIdConnectPrompt.None;

                            return Task.CompletedTask;
                        }
                    };

                    // Using the options.TokenValidationParameters, validate four things:
                    //
                    // 1. The Issuer Identifier for the Platform MUST exactly match the value of the iss
                    //    (Issuer) Claim (therefore the Tool MUST previously have been made aware of this
                    //    identifier.
                    // 2. The Tool MUST Validate the signature of the ID Token according to JSON Web Signature
                    //    RFC 7515, Section 5; using the Public Key for the Platform which collected offline.
                    // 3. The Tool MUST validate that the aud (audience) Claim contains its client_id value
                    //    registered as an audience with the Issuer identified by the iss (Issuer) Claim. The
                    //    aud (audience) Claim MAY contain an array with more than one element. The Tool MUST
                    //    reject the ID Token if it does not list the client_id as a valid audience, or if it
                    //    contains additional audiences not trusted by the Tool.
                    // 4. The current time MUST be before the time represented by the exp Claim;
                    options.TokenValidationParameters = new TokenValidationParameters
                    {
                        ValidateTokenReplay = true,
                        ValidateAudience = true,
                        ValidateIssuer = true,
                        RequireSignedTokens = true,
                        ValidateIssuerSigningKey = true,
                        ValidAudience = Configuration["OpenIdConfig:ClientId"],
                        ValidIssuer = Configuration["OpenIdConfig:Issuer"],
                        ValidateLifetime = true,
                        IssuerSigningKeyResolver = (token, securityToken, kid, validationParameters) =>
                        {
                            var keySetJson = new WebClient().DownloadString(Configuration["OpenIdConfig:JwksUri"]);
                            var keySet = JsonConvert.DeserializeObject<JsonWebKeySet>(keySetJson);
                            var key = keySet.Keys.SingleOrDefault(k => k.Kid == kid);

                            return new List<JsonWebKey> { key };
                        },
                        ClockSkew = TimeSpan.FromMinutes(5.0)
                    };
                });

            services.ConfigureApplicationCookie(options => options.Cookie.Name = "AdvantageTool");

            // Prevent X-Frame-Options header from being sent so that the Tool can appear
            // within an iframe on the platform
            services.AddAntiforgery(options => options.SuppressXFrameOptionsHeader = true);

            services.AddHttpClient();

            // Make AccessTokenService available for dependency injection.
            // This is for machine to machine communication auth service
            services.AddTransient<AccessTokenService>();

            services
                .AddControllersWithViews()
                .AddNewtonsoftJson();
        }

        // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
        public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
        {
            if (env.IsDevelopment())
            {
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseDeveloperExceptionPage();
                //app.UseExceptionHandler("/Home/Error");
                // The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
                app.UseHsts();
            }

            app.UseSerilogRequestLogging();
            app.UseHttpsRedirection();
            app.UseStaticFiles();

            app.UseRouting();
            app.UseAuthentication();
            app.UseAuthorization();

            app.UseEndpoints(endpoints =>
            {
                endpoints.MapControllerRoute(
                    name: "default",
                    pattern: "{controller=Home}/{action=Index}/{id?}");
            });
        }
    }
}
使用AdvantageTool.Models;
使用AdvantageTool.Services.LTI;
使用AdvantageTool.Utility;
使用IdentityModel;
使用Microsoft.AspNetCore.Authentication.Cookies;
使用Microsoft.AspNetCore.Authentication.OAuth;
使用Microsoft.AspNetCore.Authentication.OpenIdConnect;
使用Microsoft.AspNetCore.Builder;
使用Microsoft.AspNetCore.Hosting;
使用Microsoft.AspNetCore.Http;
使用Microsoft.EntityFrameworkCore;
使用Microsoft.Extensions.Configuration;
使用Microsoft.Extensions.DependencyInjection;
使用Microsoft.Extensions.Hosting;
使用Microsoft.IdentityModel.Protocols.OpenIdConnect;
使用制度;
使用System.Security.Cryptography;
使用System.Linq;
使用System.Collections.Generic;
使用System.Text.Encodings.Web;
使用System.Threading.Tasks;
使用System.Net.Http;
使用Newtonsoft.Json;
使用Microsoft.IdentityModel.Tokens;
使用System.IdentityModel.Tokens.Jwt;
使用Serilog;
Net系统;
名称空间优势醇
{
公营创业
{
公共启动(IConfiguration配置)
{
配置=配置;
}
公共IConfiguration配置{get;}
//此方法由运行时调用。请使用此方法将服务添加到容器中。
public void配置服务(IServiceCollection服务)
{
配置(选项=>
{
//此lambda确定给定请求是否需要非必要cookie的用户同意。
options.checkApprovered=contex