.net core 2.1 cookie身份验证不起作用sustainsys SAML2



此问题与IdP启动的Sustainsys.SAML2登录有关-AuthenticateResult没有信息。

我发现,通过SP启动的登录,IdP发布到Saml2/Acs端点,然后重定向到回调方法,在我的情况下,";SamlLoginCallback";。此方法检查SAML身份验证是否成功,如果成功,则向用户的web浏览器写入cookie。此cookie允许用户访问名为GetLoginToSaml的辅助方法,该方法是安全的。这一切都适用于SP启动的工作流。

但是,在IdP启动的工作流中,对GetLoginToSaml的调用由于缺乏授权而失败。我不明白为什么同样的代码在第一种情况下有效,但在后一种情况下无效。

请注意,我确实看到一个名为"的cookie;。AspNetCore.Cookies";在我的Chrome浏览器中。但当我尝试转到GetLoginToSaml方法时,仍然会得到302重定向。这对我来说毫无意义。

以下是编码方法:

[AllowAnonymous]
[HttpPost, HttpGet]
[Route("api/Security/SamlLoginCallback")]
public async Task<IActionResult> SamlLoginCallback(string returnUrl)
{
LogDebugInfo("SamlLoginCallback called with returnUrl of " + returnUrl);
var authenticateResult = await HttpContext.AuthenticateAsync(ApplicationSamlConstants.External);
if (!authenticateResult.Succeeded)
{
LogSamlFailInfo(authenticateResult);
return Unauthorized();
}
// if we get here, we have successful SAML authentication, and should have a username
// (to which we need to add the redirect client ID if configured to user redirect)
var userName = _config.GetValue<bool>("Database:IgnoreRedirect")
? authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier).Value.ToString()
: _config.GetValue<string>("Saml:RedirectClientId") + "." + authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier).Value.ToString();

// required for SAML logout
// see https://stackoverflow.com/questions/58961868/not-able-to-signout-using-saml2-from-sustainsys
var samlLogoutNameIdentifier = authenticateResult.Principal.GetClaimValue(CustomClaimTypes.SAMLLogoutNameIdentifier);
var samlSessionIndex = authenticateResult.Principal.GetClaimValue(CustomClaimTypes.SAMLSessionIndex);
// set temporary cookie, which will be replaced when client calls GetLoginDtoSaml
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, userName),
new Claim(CustomClaimTypes.SAMLLogoutNameIdentifier, samlLogoutNameIdentifier),
new Claim(CustomClaimTypes.SAMLSessionIndex, samlSessionIndex),
};
ClaimsIdentity userIdentity = new ClaimsIdentity(claims, "login");
ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
// https://stackoverflow.com/questions/46243697/asp-net-core-persistent-authentication-custom-cookie-authentication
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions
.SignInAsync(HttpContext, CookieAuthenticationDefaults.AuthenticationScheme, principal, new AuthenticationProperties
{
IsPersistent = false,          
});     
LogDebugInfo("Temporary cookie set in SamlLoginCallback with CookieAuthenticationDefaults.AuthenticationScheme");     
if (!string.IsNullOrEmpty(returnUrl))
{
LogDebugInfo("Redirecting to " + returnUrl);
return Redirect(returnUrl);
}
return this.Ok();
}
[HttpPost]
[Route("api/Security/GetLoginDtoSaml")]
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public async Task<IActionResult> GetLoginDtoSaml()
{
try
{
LogDebugInfo("GetLoginDtoSaml called");
var loginDto = new LoginDto();
loginDto.Username = User.Identity.Name;
loginDto.IsSamlAuthenticated = true;
// pick up claims required for SAML logout
loginDto.SAMLLogoutNameIdentifier = User.GetClaimValue(CustomClaimTypes.SAMLLogoutNameIdentifier);
loginDto.SAMLSessionIndex = User.GetClaimValue(CustomClaimTypes.SAMLSessionIndex);
// Now is when we do a database query to get necessary information for the LoginDTO object
// If the IdP has authenticated the user, but the user does not actually exist in our database,
// this method will throw an error, and we need to log the user out so they can  try to login again as a valid user
var dbValidationSuccess = false;
var dbValidationErrorInfo = "";
try
{
dbValidationSuccess = _securitySvc.ValidateLogin(loginDto);
LogDebugInfo("GetLoginDtoSaml - dbValidationSuccess = " + dbValidationSuccess);
}
catch (Exception ex)
{
dbValidationErrorInfo = ex.Message;
LogError(ex);
}
if (dbValidationSuccess)
{
// update cookie with relevant data
await SetCookie(loginDto);
return StatusCode(Microsoft.AspNetCore.Http.StatusCodes.Status200OK, loginDto);
}
else
{
// log user out of the application
LogDebugInfo("GetLoginDtoSaml - logging user out of application");
await Microsoft.AspNetCore.Authentication.AuthenticationHttpContextExtensions
.SignOutAsync(HttpContext, CookieAuthenticationDefaults.AuthenticationScheme);
throw new DisplayException("Despite SAML Authentication from the Identity Provider, user data was not found in the local database. Please refresh the page to retry. Error: " + dbValidationErrorInfo);
}
}
catch (Exception ex)
{
return HandleError(ex);
}
}

在我的启动方法中:

public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
var usingSAML = Configuration.GetValue<bool>("Authentication:UseSAML");
var usingJWT = Configuration.GetValue<bool>("Authentication:UseJWT");
AuthenticationBuilder authBuilder = null;
if (usingSAML)
{
// primary reference for SAML code: https://github.com/hmacat/Saml2WebAPIAndAngularSpaExample
// found via this SO link: https://stackoverflow.com/questions/55025336/sustainsys-saml2-sample-for-asp-net-core-webapi-without-identity

// added to address bug with Okta integration 
// see https://www.developreference.com/article/10349604/Sustainsys+SAML2+Sample+for+ASP.NET+Core+WebAPI+without+Identity
// and https://stackoverflow.com/questions/63853661/authenticateresult-succeeded-is-false-with-okta-and-sustainsys-saml2/63890322#63890322
services.AddDataProtection()
.PersistKeysToFileSystem(new DirectoryInfo("Logs"));
services.Configure<CookiePolicyOptions>(options =>
{
// see https://stackoverflow.com/questions/59742825/httpcontext-signinasync-fails-to-set-cookie-and-return-user-identity-isauthent
options.ConsentCookie.IsEssential = true;
options.CheckConsentNeeded = context => false;
options.MinimumSameSitePolicy = SameSiteMode.None;

// Some older browsers don't support SameSiteMode.None.
options.OnAppendCookie = cookieContext => SameSite.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
options.OnDeleteCookie = cookieContext => SameSite.CheckSameSite(cookieContext.Context, cookieContext.CookieOptions);
});
authBuilder = services.AddAuthentication(o =>
{
o.DefaultScheme = ApplicationSamlConstants.Application;
o.DefaultSignInScheme = ApplicationSamlConstants.External;
o.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
o.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
authBuilder.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
// see https://stackoverflow.com/questions/46243697/asp-net-core-persistent-authentication-custom-cookie-authentication
options.ExpireTimeSpan = new System.TimeSpan(365, 0, 0, 0, 0);
options.AccessDeniedPath = new PathString("/login");
options.LoginPath = new PathString("/login");
// see https://stackoverflow.com/questions/59742825/httpcontext-signinasync-fails-to-set-cookie-and-return-user-identity-isauthent
options.Cookie.IsEssential = true;
options.Cookie.HttpOnly = true;
//options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.SameSite = SameSiteMode.None;
})
.AddCookie(ApplicationSamlConstants.Application)
.AddCookie(ApplicationSamlConstants.External)
.AddSaml2(options =>
{
options.SPOptions.EntityId = new EntityId(this.Configuration["Saml:SPEntityId"]);
var allowIdpInitiated = Configuration.GetValue<bool>("Saml:AllowIdPInitiated"); 
if (allowIdpInitiated)
{
var siteRoot = this.Configuration["Saml:SiteRoot"];
var siteRootEncoded = WebUtility.UrlEncode(siteRoot + "?idp=y");    // add idp parm so javascript knows what's going on       
var returnUrl = string.Format("{0}/api/Security/SamlLoginCallback?returnUrl={1}", siteRoot, siteRootEncoded);
options.SPOptions.ReturnUrl = new System.Uri(returnUrl);
}   
options.IdentityProviders.Add(
new IdentityProvider(
new EntityId(this.Configuration["Saml:IDPEntityId"]), options.SPOptions)
{
MetadataLocation = this.Configuration["Saml:IDPMetaDataBaseUrl"],
LoadMetadata = true,
AllowUnsolicitedAuthnResponse = allowIdpInitiated,   
});
options.SPOptions.ServiceCertificates.Add(new X509Certificate2(this.Configuration["Saml:CertificateFileName"]));

});
}

}

这里的问题最终被证明是用于写入然后读取cookie的cookie路径中大小写敏感度不一致。因此,在编写cookie时,它无法读取。

最新更新