My Identity Server is using identityserver4 framework (http://localhost:9000). And I register the client on Identity Server as below.
clients.Add(
new Client
{
ClientId = "customer.api",
ClientName = "Customer services",
AllowedGrantTypes = GrantTypes.HybridAndClientCredentials,
RequireConsent = false,
AllowAccessTokensViaBrowser = true,
RedirectUris = { "http://localhost:60001/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:60001/signout-callback-oidc" },
ClientSecrets = new List<Secret>
{
new Secret("testsecret".Sha256())
},
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
IdentityServerConstants.StandardScopes.OfflineAccess,
"customerprivatelinesvn.api",
},
AllowOfflineAccess = true,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedCorsOrigins = { "http://localhost:60001" }
});
Here is the authentication on my client app (http://localhost:60001).
private void AddAuthentication(IServiceCollection services)
{
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = "Cookies";
options.DefaultChallengeScheme = "oidc";
})
.AddCookie()
.AddOpenIdConnect("oidc", options =>
{
Configuration.GetSection("OpenIdConnect").Bind(options);
});
}
"OpenIdConnect": {
"SignInScheme": "Cookies",
"Authority": "http://localhost:9000/",
"RequireHttpsMetadata": false,
"ClientId": "customer.api",
"ClientSecret": "testsecret",
"Scope": [ "customerprivatelinesvn.api", "offline_access" ],
"CallbackPath": "/signin-oidc",
"ResponseType": "code id_token token",
"GetClaimsFromUserInfoEndpoint": true,
"SaveTokens": true
}
HomeController of client app
[Authorize]
public class HomeController : Controller
{
public IActionResult Index()
{
return View();
}
}
Here are the Cookies of client app after user logs in.
I try to implement the signout action as below
public class AccountController : Controller
{
public async Task<IActionResult> Signout()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
return RedirectToAction("Index", "Home");
}
}
But when user signs out, it doesn't call the endsession endpoint of identity server. I look at the traffic of fiddler, there is no request to identity server.
My expectation is when user signs out, it will call endsession endpoint of identity server and redirect to logout link of identity server as below.
We can do this easily on MVC application by calling OwinContext signout
private void LogoutOwin(IOwinContext context)
{
context.Authentication.SignOut();
}
But the signout method doesn't work anymore on ASP.NET Core 2.
Note: I'm calling the signout action from an AJAX post because my client app is angular 5 app.
Does anyone know how to implement the signout correctly on ASP.NET Core 2?
Thank you very much.
Regards,
Kevin
To allow the signing out to occur use the following Logout action:
public async Task Logout()
{
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
}
This is exactly what the quickstart says to use (http://docs.identityserver.io/en/release/quickstarts/3_interactive_login.html). You (and I) have been too clever. I looked at the action in the tutorial and thought 'That's not complete, it doesn't return an action result, lets redirect back to my page'.
Actually what happens is HttpContext.SignOutAsync("oidc");
sets a default ActionResult (Which is to redirect to the OpenIdConnect provider to complete the sign-out). By specifying your own with return RedirectToAction("Index", "Home");
you override this, so the sign-out action never happens.
From this answer, the way you specify a redirect URL after the logout is completed is by using AuthenticationProperties
public async Task Logout()
{
await context.SignOutAsync("Cookies");
var prop = new AuthenticationProperties
{
RedirectUri = "/logout-complete"
};
// after signout this will redirect to your provided target
await context.SignOutAsync("oidc", prop);
}
On Net Core 2.0 change your code to use the enumerations CookieAuthenticationDefaults and OpenIdConnectDefaults
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie()
.AddOpenIdConnect(SetOpenIdConnectOptions);
private static void SetOpenIdConnectOptions(OpenIdConnectOptions options)
{
options.ClientId = "auAuthApp_implicit";
options.Authority = "http://localhost:55379/";
options.SignInScheme = "Cookies";
options.RequireHttpsMetadata = false;
options.SaveTokens = true;
options.ResponseType = "id_token token";
options.GetClaimsFromUserInfoEndpoint = true;
}
and...
public async Task<IActionResult> Logout()
{
await HttpContext.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme);
await HttpContext.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme);
return RedirectToAction("Index", "Home");
}
I can resolve my problem now.
1) Return SignOutResult will call endsession endpoint.
2) Change AJAX post to submit form.
public class AccountController : Controller
{
public IActionResult Signout()
{
return new SignOutResult(new[] { "oidc", "Cookies" });
}
}
<form action="/Account/Signout" id="signoutForm" method="post" novalidate="novalidate">
<ul class="nav navbar-nav navbar-right">
<li><a href="javascript:document.getElementById('signoutForm').submit()">Sign out</a></li>
</ul>
</form>
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With