Setting up OpenID Connect for .NET Core Web API



我有一个简单的应用程序,它使用Angular作为前端,.NET Core Web API作为后端服务。现在我想保护我的 WEB API 层。我虽然我可以为此目的使用OpenID Connect。但是所有在线示例或文档都使用一些身份管理系统(Keycloak,Okta(,但我只想使用SQL数据库中的用户数据。

因此,我从Angular点击了WEB API,根据发送的用户详细信息生成令牌(使用OpenID?i 只能使用令牌来授权用户。我想使用 OpenID,以便以后如果需要,可以使用其他一些身份管理系统。

我在 WEB API 中的启动类

services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(o =>
{
o.ClientId = "sa";
o.ClientSecret = "sa";
o.Authority = "https://localhost:44352";
o.GetClaimsFromUserInfoEndpoint = true;
o.RequireHttpsMetadata = true;
});

我添加了一个具有授权属性的控制器,并添加了一个测试方法,以查看当我从 Swagger 点击它时会发生什么

我看到以下错误

IOException: IDX20804: Unable to retrieve document from: 'https://localhost:44352/.well-known/openid-configuration'

我不确定错误是什么。

我也想问一下我这样做是否正确。我是否可以使用相同的 API(Authority?((o.Authority = "https://localhost:44352";)( 来验证/获取令牌(。

OpenIDConnect 需要的绝对是实现 oidc-spec 的服务器。.well-known/...url 是 oidc 发现规范的一部分,像 identityserver 和 keycloak 这样的服务器实现了该规范。它提供了其他端点的标准化列表,用于检索令牌,获取用户信息,获取logouturi等。

您的 API 没有这样的端点,所以这就是错误所说的。

如果你想自己实现整个oidc规范,那就去做吧,但我不推荐它,它有点复杂。在我看来,.net core只实现了openidconnect-client,只要它实现了规范,您就可以自由选择服务器实现。

或者,看看 JwtBearer,这是一种更轻量级的身份验证方法,更容易实现(而且,最好的是:你以后可以很容易地更改为 oidc(。好的起点可能是这些博客文章。

services.AddAuthentication(options => 
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
}).AddCookie(CookieAuthenticationDefaults.AuthenticationScheme,
options =>
{
options.LoginPath = new PathString("/");
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.Cookie = new CookieBuilder()
{
SecurePolicy = CookieSecurePolicy.SameAsRequest,
Path = "/"
};
options.SlidingExpiration = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(sessionTimeout);
}).AddOpenIdConnect(options =>
{
options.ClientId = Configuration.GetValue<string>("oidc:ClientId");
options.ClientSecret = Configuration["oidc:ClientSecret"];
options.CallbackPath = new PathString("/auth/callback");
options.GetClaimsFromUserInfoEndpoint = true;
options.Authority = Configuration["oidc:Authority"];
options.SignedOutRedirectUri = "/";
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.UseTokenLifetime = true;
var signingKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(Configuration["oidc:ClientSecret"]));
options.TokenValidationParameters = new TokenValidationParameters
{
RequireSignedTokens = true,
IssuerSigningKey = signingKey,
ValidateAudience = true,
ValidateIssuer = true,
ValidateIssuerSigningKey = true,
};
options.ResponseType = OpenIdConnectResponseType.Code;
options.AuthenticationMethod = OpenIdConnectRedirectBehavior.RedirectGet;
options.Scope.Clear();
options.Scope.Add("openid");
options.Scope.Add("email");
options.Scope.Add("profile");
options.Events = new OpenIdConnectEvents()
{
OnTicketReceived = context =>
{
var identity = context.Principal.Identity as ClaimsIdentity;
if (identity != null)
{
if (!context.Principal.HasClaim(c => c.Type == ClaimTypes.Name) &&
identity.HasClaim(c => c.Type == "name"))
identity.AddClaim(new Claim(ClaimTypes.Name, identity.FindFirst("name").Value));
if (context.Properties.Items.ContainsKey(".TokenNames"))
{
string[] tokenNames = context.Properties.Items[".TokenNames"].Split(';');
foreach (var tokenName in tokenNames)
{
string tokenValue = context.Properties.Items[$".Token.{tokenName}"];
identity.AddClaim(new Claim(tokenName, tokenValue));
}
}
}
var cp = new ClaimsPrincipal(identity);
context.Principal = cp;
return Task.CompletedTask;
},
//OnTokenValidated = context =>
//{
//    ClaimsIdentity identity = (ClaimsIdentity)context.Principal.Identity;
//    Claim Name = identity.FindFirst("preferred_username");
//    Claim gender = identity.FindFirst(ClaimTypes.Gender);
//    Claim sub = identity.FindFirst(ClaimTypes.NameIdentifier);
//    var claimsToKeep = new List<Claim> { Name, gender, sub };
//    var newIdentity = new ClaimsIdentity(claimsToKeep, identity.AuthenticationType);
//    context.Principal = new ClaimsPrincipal(newIdentity);
//    return Task.FromResult(0);
//},
OnAuthenticationFailed = context =>
{
context.Response.Redirect("/");
context.HandleResponse();
return Task.CompletedTask;
},
OnRedirectToIdentityProvider = context =>
{
context.ProtocolMessage.SetParameter("pfidpadapterid", Configuration["oidc:pfidpadapterid"]);
return Task.FromResult(0);
}
};
});

最新更新