Identity Server 4 - 注销 - 传递附加数据
Identity Server 4 - Logout - Passing Additional Data
当用户在某些情况下注销时,我想在注销页面上向他们显示一条消息。要启用此功能,我希望能够在注销时将可选参数从客户端发送到 Identity Server / Authority 站点。
虽然我有标准的注销流程,但我在处理这种情况时遇到了障碍,因为信息似乎很少,而且建议的解决方案不起作用。
根据我的阅读,'state' 参数是传递此信息的正确方法,但目前尚未通过。 AcrValues 仅用于以其他方式发送信息。
我下面的简单实现只是将状态查询字符串项添加到结束会话端点。但是,当我检查我的客户端用于转到身份服务器实例的查询字符串时,它丢失了。
重定向(discoveryResponse.EndSessionEndpoint+"&state=foo")
很高兴收到任何帮助!
MVC 客户端的当前流程:
请注意;为简洁起见,删除了一些代码。
从客户端控制器发起注销,state=foo:
public class LogoutController : Controller
{
public ActionResult Index()
{
Request.GetOwinContext().Authentication.SignOut();
var discoveryClient = new DiscoveryClient(clientConfig.Authority) { Policy = {RequireHttps = false} };
var discoveryResponse = discoveryClient.GetAsync().Result;
var tokenClaim = ((ClaimsIdentity)User.Identity).FindFirst("id_token");
return Redirect(discoveryResponse.EndSessionEndpoint+ "?id_token_hint="+ tokenClaim + "&state=foo");
}
}
RedirectToIdentityProvider 为请求调用:
IdTokenHint 和 PostLogoutRedirectUri 已正确设置和传递。
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType != OpenIdConnectRequestType.LogoutRequest)
return Task.FromResult(0);
var idTokenHint = n.OwinContext.Authentication.User.FindFirst(OpenIdConnectClaimType.IdToken);
if (idTokenHint == null) return Task.FromResult(0);
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
n.OwinContext.Response.Cookies.Append("IdentityServerPostLogoutReturnUri",
n.ProtocolMessage.PostLogoutRedirectUri);
n.ProtocolMessage.PostLogoutRedirectUri =
n.Options.PostLogoutRedirectUri;
return Task.FromResult(0);
}
}
URL生成(不是缺少'state'项):
权威网站注销页面:
这是我希望能够访问状态参数的地方。
public class LogoutController : Controller
{
public async Task<ViewResult> Index(string logoutId)
{
if (logoutId == null) throw new Exception("Missing logoutId");
var logoutRequest = await interactionService.GetLogoutContextAsync(logoutId);
var vm = new LoggedOutViewModel(logoutRequest, logoutId);
if (!string.IsNullOrWhiteSpace(httpContextService.GetCookieValue(PostLogoutReturnUriCookieKey)))
{
vm.PostLogoutRedirectUri = httpContextService.GetCookieValue(PostLogoutReturnUriCookieKey);
httpContextService.ClearCookie(PostLogoutReturnUriCookieKey);
}
await httpContextService.SignOutAsync();
return View("Index", vm);
}
}
我进行了更深入的研究,发现问题是由 Microsoft.Owin.Security.OpenIdConnect 中间件中的以下几行引起的。
protected override async Task ApplyResponseGrantAsync()
{
AuthenticationResponseRevoke signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode);
if (signout != null)
{
// snip
var notification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
{
ProtocolMessage = openIdConnectMessage
};
await Options.Notifications.RedirectToIdentityProvider(notification);
// This was causing the issue
if (!notification.HandledResponse)
{
string redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
{
_logger.WriteWarning("The logout redirect URI is malformed: " + redirectUri);
}
Response.Redirect(redirectUri);
}
}
}
为了防止中间件在检测到注销消息时覆盖重定向,需要在 RedirectToIdentityProvider 事件中调用 'HandleResponse' 方法中的以下行。
这允许将原始的 'state' 查询字符串项传递给 Identity Server 并使用交互服务将其拉出。
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
// Snip
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// Snip
},
RedirectToIdentityProvider = n =>
{
// Snip
n.HandleResponse(); // The magic happens here
}
}
当用户在某些情况下注销时,我想在注销页面上向他们显示一条消息。要启用此功能,我希望能够在注销时将可选参数从客户端发送到 Identity Server / Authority 站点。
虽然我有标准的注销流程,但我在处理这种情况时遇到了障碍,因为信息似乎很少,而且建议的解决方案不起作用。
根据我的阅读,'state' 参数是传递此信息的正确方法,但目前尚未通过。 AcrValues 仅用于以其他方式发送信息。
我下面的简单实现只是将状态查询字符串项添加到结束会话端点。但是,当我检查我的客户端用于转到身份服务器实例的查询字符串时,它丢失了。
重定向(discoveryResponse.EndSessionEndpoint+"&state=foo")
很高兴收到任何帮助!
MVC 客户端的当前流程:
请注意;为简洁起见,删除了一些代码。
从客户端控制器发起注销,state=foo:
public class LogoutController : Controller
{
public ActionResult Index()
{
Request.GetOwinContext().Authentication.SignOut();
var discoveryClient = new DiscoveryClient(clientConfig.Authority) { Policy = {RequireHttps = false} };
var discoveryResponse = discoveryClient.GetAsync().Result;
var tokenClaim = ((ClaimsIdentity)User.Identity).FindFirst("id_token");
return Redirect(discoveryResponse.EndSessionEndpoint+ "?id_token_hint="+ tokenClaim + "&state=foo");
}
}
RedirectToIdentityProvider 为请求调用:
IdTokenHint 和 PostLogoutRedirectUri 已正确设置和传递。
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
Notifications = new OpenIdConnectAuthenticationNotifications
{
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType != OpenIdConnectRequestType.LogoutRequest)
return Task.FromResult(0);
var idTokenHint = n.OwinContext.Authentication.User.FindFirst(OpenIdConnectClaimType.IdToken);
if (idTokenHint == null) return Task.FromResult(0);
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
n.OwinContext.Response.Cookies.Append("IdentityServerPostLogoutReturnUri",
n.ProtocolMessage.PostLogoutRedirectUri);
n.ProtocolMessage.PostLogoutRedirectUri =
n.Options.PostLogoutRedirectUri;
return Task.FromResult(0);
}
}
URL生成(不是缺少'state'项):
权威网站注销页面:
这是我希望能够访问状态参数的地方。
public class LogoutController : Controller
{
public async Task<ViewResult> Index(string logoutId)
{
if (logoutId == null) throw new Exception("Missing logoutId");
var logoutRequest = await interactionService.GetLogoutContextAsync(logoutId);
var vm = new LoggedOutViewModel(logoutRequest, logoutId);
if (!string.IsNullOrWhiteSpace(httpContextService.GetCookieValue(PostLogoutReturnUriCookieKey)))
{
vm.PostLogoutRedirectUri = httpContextService.GetCookieValue(PostLogoutReturnUriCookieKey);
httpContextService.ClearCookie(PostLogoutReturnUriCookieKey);
}
await httpContextService.SignOutAsync();
return View("Index", vm);
}
}
我进行了更深入的研究,发现问题是由 Microsoft.Owin.Security.OpenIdConnect 中间件中的以下几行引起的。
protected override async Task ApplyResponseGrantAsync()
{
AuthenticationResponseRevoke signout = Helper.LookupSignOut(Options.AuthenticationType, Options.AuthenticationMode);
if (signout != null)
{
// snip
var notification = new RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions>(Context, Options)
{
ProtocolMessage = openIdConnectMessage
};
await Options.Notifications.RedirectToIdentityProvider(notification);
// This was causing the issue
if (!notification.HandledResponse)
{
string redirectUri = notification.ProtocolMessage.CreateLogoutRequestUrl();
if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute))
{
_logger.WriteWarning("The logout redirect URI is malformed: " + redirectUri);
}
Response.Redirect(redirectUri);
}
}
}
为了防止中间件在检测到注销消息时覆盖重定向,需要在 RedirectToIdentityProvider 事件中调用 'HandleResponse' 方法中的以下行。
这允许将原始的 'state' 查询字符串项传递给 Identity Server 并使用交互服务将其拉出。
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
// Snip
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
// Snip
},
RedirectToIdentityProvider = n =>
{
// Snip
n.HandleResponse(); // The magic happens here
}
}