I'm using IdentityServer4 and one of my clients is a .NET 4.5 MVC application. Everything seems to be working as expected. However, I have a problem. Taking the following flow:
- User logs in and gets redirected to the Client application.
- User performs different actions successfully.
- The id token expires (after, for example, 5 minutes).
- User attempts a POST request (for example, a Logout request).
- Since the id token has expired and the Logout action is protected with the
[Authorize]
attribute, thanks toUseOpenIdConnectAuthentication
in theStartup.cs
file, it will automatically go to IdentityServer4 to fetch a new id token before executing any actions. - A call to IdentityServer4 is done and the new id token is successfully returned.
- Now the Logout action is called with a GET method instead of a POST method, which of course, is not an action in my controller.
How can I ensure once the new id token is received, the original request is performed? (In this case, a POST request). Or, in other words, how can I make sure I can log out using an HttpPost Logout action in the MVC client when the id token has already expired?
Just to clarify, the Logout action is just one example. It could be any other POST action.
I suspect I might be missing something here, probably as a consequence of not having a lot of experience with IdentityServer4.
Here's a cleaned-up version of my code, just in case:
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions()
{
Authority = authenticationAuthority,
ClientId = clientId,
ResponseType = "code id_token",
SignInAsAuthenticationType = "Cookies",
Scope = "openid company.profile offline_access",
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
n.RedirectUri = GetRedirectUriWithCorrectScheme(n.Request.Headers);
// use the code to get the access and refresh token
var secret = "mysecret";
var tokenClient = new TokenClient($"{authenticationAuthority}connect/token", clientId,
secret);
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($"{authenticationAuthority}connect/userinfo");
var userInfoResponse = await userInfoClient.GetAsync(tokenResponse.AccessToken);
userInfoResponse.Claims.ToList().ForEach(claim => claims.Add(claim));
claims.Add(new Claim("access_token", tokenResponse.AccessToken));
claims.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
claims.Add(new Claim("refresh_token", tokenResponse.RefreshToken));
n.AuthenticationTicket = new AuthenticationTicket(
new ClaimsIdentity(claims.Distinct(new ClaimComparer()),
n.AuthenticationTicket.Identity.AuthenticationType),
n.AuthenticationTicket.Properties);
},
RedirectToIdentityProvider = n =>
{
// if signing out, add the id_token_hint
if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.LogoutRequest)
{
var idTokenHint = n.OwinContext.Authentication.User.FindFirst("id_token");
if (idTokenHint != null)
{
n.ProtocolMessage.IdTokenHint = idTokenHint.Value;
}
}
return Task.FromResult(0);
}
}
});