identityserver4 - 在通过 IdentityServer4 自动登录期间未分配会话 ID (sid),这是什么原因?
问题描述
问题
第一个问题,是什么决定了是否sid
从身份服务器发出声明?
第二个问题,我什至需要一个sid
?我目前包含它,因为它在样本中。
背景故事
我有一个使用 IdentityServer4 进行身份验证的网站和一个不使用的网站。我拼凑了一个解决方案,允许用户登录到 non-identityserver4 站点并单击使用一次性访问代码的链接自动登录到 identityserver4 站点。sid
从非身份服务器站点传输时,除了声明没有从身份服务器传递到身份服务器保护的站点之外,一切似乎都正常工作。如果我直接登录到 identityserver4 安全站点,sid
则它包含在声明中。代码改编自注册后自动登录和/或模拟工作流程的示例。
这是代码:
identityserver4 中的一次代码登录过程
public class CustomAuthorizeInteractionResponseGenerator : AuthorizeInteractionResponseGenerator
{
...
//https://stackoverflow.com/a/51466043/391994
public override async Task<InteractionResponse> ProcessInteractionAsync(ValidatedAuthorizeRequest request,
ConsentResponse consent = null)
{
string oneTimeAccessToken = request.GetAcrValues().FirstOrDefault(x => x.Split(':')[0] == "otac");
string clientId = request.ClientId;
//handle auto login handoff
if (!string.IsNullOrWhiteSpace(oneTimeAccessToken))
{
//https://benfoster.io/blog/identity-server-post-registration-sign-in/
oneTimeAccessToken = oneTimeAccessToken.Split(':')[1];
OneTimeCodeContract details = await GetOTACFromDatabase(oneTimeAccessToken);
if (details.IsValid)
{
UserFormContract user = await GetPersonUserFromDatabase(details.PersonId);
if (user != null)
{
string subjectId = await GetClientSubjectIdAsync(clientId, user.AdUsername);
var iduser = new IdentityServerUser(subjectId)
{
DisplayName = user.AdUsername,
AuthenticationTime = DateTime.Now,
IdentityProvider = "local",
};
request.Subject = iduser.CreatePrincipal();
//revoke token
bool? success = await InvalidateTokenInDatabase(oneTimeAccessToken);
if (success.HasValue && !success.Value)
{
Log.Debug($"Revoke failed for {oneTimeAccessToken} it should expire at {details.ExpirationDate}");
}
//https://stackoverflow.com/a/56237859/391994
//sign them in
await _httpContextAccessor.HttpContext.SignInAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme, request.Subject, null);
return new InteractionResponse
{
IsLogin = false,
IsConsent = false,
};
}
}
}
return await base.ProcessInteractionAsync(request, consent);
}
}
直接登录到 identityserver4 安全站点时的正常登录流程(来自示例)
public class AccountController : Controller
{
/// <summary>
/// Handle postback from username/password login
/// </summary>
[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginInputModel model)
{
Log.Information($"login request from: {Request.HttpContext.Connection.RemoteIpAddress.ToString()}");
if (ModelState.IsValid)
{
// validate username/password against in-memory store
if (await _userRepository.ValidateCredentialsAsync(model.Username, model.Password))
{
AuthenticationProperties props = null;
// only set explicit expiration here if persistent.
// otherwise we reply upon expiration configured in cookie middleware.
if (AccountOptions.AllowRememberLogin && model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration)
};
};
var clientId = await _account.GetClientIdAsync(model.ReturnUrl);
// issue authentication cookie with subject ID and username
var user = await _userRepository.FindByUsernameAsync(model.Username, clientId);
var iduser = new IdentityServerUser(user.SubjectId)
{
DisplayName = user.UserName
};
await HttpContext.SignInAsync(iduser, props);
// make sure the returnUrl is still valid, and if yes - redirect back to authorize endpoint
if (_interaction.IsValidReturnUrl(model.ReturnUrl))
{
return Redirect(model.ReturnUrl);
}
return Redirect("~/");
}
ModelState.AddModelError("", AccountOptions.InvalidCredentialsErrorMessage);
}
// something went wrong, show form with error
var vm = await _account.BuildLoginViewModelAsync(model);
return View(vm);
}
}
Identityserver4 安全站点中的 AuthorizationCodeReceived
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// use the code to get the access and refresh token
var tokenClient = new TokenClient(
tokenEndpoint,
electionClientId,
electionClientSecret);
var tokenResponse = await tokenClient.RequestAuthorizationCodeAsync(
n.Code, n.RedirectUri);
if (tokenResponse.IsError)
{
throw new Exception(tokenResponse.Error);
}
// use the access token to retrieve claims from userinfo
var userInfoClient = new UserInfoClient(
new Uri(userInfoEndpoint).ToString());
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
Claim subject = userInfoResponse.Claims.Where(x => x.Type == "sub").FirstOrDefault();
// create new identity
var id = new ClaimsIdentity(n.AuthenticationTicket.Identity.AuthenticationType);
id.AddClaims(GetRoles(subject.Value, tokenClient, apiResourceScope, apiBasePath));
var transformedClaims = StartupHelper.TransformClaims(userInfoResponse.Claims);
id.AddClaims(transformedClaims);
id.AddClaim(new Claim("access_token", tokenResponse.AccessToken));
id.AddClaim(new Claim("expires_at", DateTime.Now.AddSeconds(tokenResponse.ExpiresIn).ToLocalTime().ToString()));
id.AddClaim(new Claim("refresh_token", tokenResponse.RefreshToken));
id.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
THIS FAILS -> id.AddClaim(new Claim("sid", n.AuthenticationTicket.Identity.FindFirst("sid").Value));
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(id.Claims, n.AuthenticationTicket.Identity.AuthenticationType, "name", "role"),
n.AuthenticationTicket.Properties);
},
}
});
}
}
如果您不想向上滚动,请再次提问
第一个问题,是什么决定了是否sid
从身份服务器发出声明?
第二个问题,我什至需要一个sid
?我目前包含它,因为它在样本中。
解决方案
推荐阅读
- javascript - 我的 clearInterval 代码似乎找不到问题
- python - 用于查找两个给定日期之间差异的 Python 代码还应调用另一个函数来检查有效日期
- python - 用每个点上的标签可视化散点图
- java - 运行使用 jpackage、JavaFX 应用程序生成的 exe 文件时 Hibernate 抛出错误
- react-native - 如何在反应原生android上检测屏幕录像机?
- css - 角度材料选择自动宽度到所选选项
- visual-studio - Visual Studio 2019 - 连接服务参考 - OpenAPI 正在生成重复类型
- windows - 如何在远程 Windows 文件系统上克隆 git repo?
- amazon-web-services - CloudWatch 日志过滤器计数指标值 < 1
- javascript - 为什么 Salesforce 会在标签的 src 中发送其分析数据?