C# 使用OIDC启动进行身份验证时GetExternalLoginInfoAsync无法通过 脚本
我正在配置LTI Advantage 它包括三个步骤 在这里,我只想问一下第一步和第二步 第一步: OIDC:初始OpenID Connect(OIDC)启动发生,其中工具被通知即将进行的LTI启动,并发生第三方发起的登录OIDC流。这是一种识别哪个用户被启动的方法,有助于确保没有任何启动被拦截。我们使用LTI启动端点作为登录URL 第二步: JSON Web令牌:在OIDC启动之后,我们执行1.3启动。构建发射有效载荷时,将其作为JWT进行签名。所有LTI消息都作为JWT发送,并由消息的发送者签名。接收方使用由KeyID标识的公钥来验证JWT签名。此令牌的接收方可以验证: 谁寄的 第三步: OAuth2:为了扩展诸如分配和成绩之类的服务,OAuth2令牌用于进行后续的服务API调用,例如从成绩册检索分数或同步新结果 内容没有改变 错误 我当前在收到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发送,并由消息的发送者
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