I'm using the Asp Net Identity and the EF Core combined sample, everything works correctly, database, seeding, api call except for when i try to log out from the IS page. It does not delete the .AspNetCore.Cookies which is the one keeping the user logged in on client
` ///
/// Handle logout page postback
///
[HttpPost]
[ValidateAntiForgeryToken]
public async Task
{
// build a model so the logged out page knows what to display
var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await _signInManager.SignOutAsync();
// raise the logout event
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
}
// check if we need to trigger sign-out at an upstream identity provider
if (vm.TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
}
return View("LoggedOut", vm);
}
On MVC Client it works correctly, logs out from both sides
public IActionResult Logout()
{
return SignOut("Cookies", "oidc");
}
I tried to do
// delete local authentication cookie
await HttpContext.SignOutAsync("Cookies");
await HttpContext.SignOutAsync("oidc");
but gives me an exception cause i didn't add these on AddAuthentication on the IS...
Can you add your start up specifically the AddAuthentication section.
Hi , thanks for replying. This is basically the same file as the sample of Asp.Net Identity and EF Core combined
```
public class Startup
{
public IConfiguration Configuration { get; }
public IHostingEnvironment Environment { get; }
public Startup(IConfiguration configuration, IHostingEnvironment environment)
{
Configuration = configuration;
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("DefaultConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(connectionString));
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders();
services.AddMvc().SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_1);
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = "Windows";
iis.AutomaticAuthentication = false;
});
var builder = services.AddIdentityServer(options =>
{
options.Events.RaiseErrorEvents = true;
options.Events.RaiseInformationEvents = true;
options.Events.RaiseFailureEvents = true;
options.Events.RaiseSuccessEvents = true;
})
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = b =>
b.UseSqlServer(connectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
// this enables automatic token cleanup. this is optional.
options.EnableTokenCleanup = true;
})
.AddAspNetIdentity<ApplicationUser>();
if (Environment.IsDevelopment())
{
builder.AddDeveloperSigningCredential();
}
else
{
throw new Exception("need to configure key material");
}
services.AddAuthentication()
.AddGoogle(options =>
{
// register your IdentityServer with Google at https://console.developers.google.com
// enable the Google+ API
// set the redirect URI to http://localhost:5000/signin-google
options.ClientId = "copy client ID from Google here";
options.ClientSecret = "copy client secret from Google here";
});
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
app.UseStaticFiles();
app.UseIdentityServer();
app.UseMvcWithDefaultRoute();
}
}`
try
await _signInManager.SignOutAsync();
if that doesn't work you can go with
// delete local authentication cookie
await HttpContext.SignOutAsync();
// Clear the existing external cookie to ensure a clean login process
wait HttpContext.SignOutAsync(IdentityConstants.ApplicationScheme);
// Clear the existing external cookie to ensure a clean login process
await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);
Remember this is NOT going to log a user out of their Google account. Google doesn't support third party logout. Its just going to log them out of your application.
I tried this and it does not delete the AspNetCore.Cookies which keeps the user logged in the client application.
I tried again just downloading the samples https://github.com/IdentityServer/IdentityServer4.Samples
and using a fresh Identity + EF Core combined solution and problem still persists.
Logging out from Identity Server does not log out from client.
This is the configuration on client, not sure if it could change anything
``
new Client
{
ClientId = "mvc",
ClientName = "MVC Client",
AllowedGrantTypes = GrantTypes.Hybrid,
ClientSecrets =
{
new Secret("secret".Sha256())
},
RedirectUris = { "http://localhost:5002/signin-oidc" },
PostLogoutRedirectUris = { "http://localhost:5002/signout-callback-oidc" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
},
AllowOfflineAccess = true
},`
Clearing the cookie in IdentityServer won't logout the user "auto-magically" at other clients, check the docs
https://identityserver4.readthedocs.io/en/latest/topics/signout.html
That makes sense, I haven't implemented any of this. However i can't seem to find any of this in any identity server 4 sample on the official github, would be nice to see a working example
Looks like the issue is that in the quickstarts layout lhttps://github.com/IdentityServer/IdentityServer4.Samples/blob/master/Quickstarts/Combined_AspId_and_EFStorage/src/IdentityServer/Views/Shared/_Layout.cshtml
the link to the logout controller does not pass the logoutId argument
@if (!string.IsNullOrWhiteSpace(name))
{
<ul class="nav navbar-nav">
<li class="dropdown">
<a href="#" class="dropdown-toggle" data-toggle="dropdown">@name <b class="caret"></b></a>
<ul class="dropdown-menu">
<li><a asp-action="Logout" asp-controller="Account">Logout</a></li>
</ul>
</li>
</ul>
}
...which the AccountController expects
```[HttpGet]
public async Task
{
// build a model so the logout page knows what to display
var vm = await BuildLogoutViewModelAsync(logoutId);
if (vm.ShowLogoutPrompt == false)
{
// if the request for logout was properly authenticated from IdentityServer, then
// we don't need to show the prompt and can just log the user out directly.
return await Logout(vm);
}
return View(vm);
}
As a result vm.TriggerExternalSignout is false which means the extenal signout does not get called.
/// Handle logout page postback
///
[HttpPost]
[ValidateAntiForgeryToken]
public async Task
{
// build a model so the logged out page knows what to display
var vm = await BuildLoggedOutViewModelAsync(model.LogoutId);
if (User?.Identity.IsAuthenticated == true)
{
// delete local authentication cookie
await _signInManager.SignOutAsync();
// raise the logout event
await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName()));
}
// check if we need to trigger sign-out at an upstream identity provider
if (vm.TriggerExternalSignout)
{
// build a return URL so the upstream provider will redirect back
// to us after the user has logged out. this allows us to then
// complete our single sign-out processing.
string url = Url.Action("Logout", new { logoutId = vm.LogoutId });
// this triggers a redirect to the external provider for sign-out
return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme);
}
return View("LoggedOut", vm);
}
```
I'll raise a new issue
I am in the process of going though the samples now i will add this to #3178
Thank you Linda, really appreciate your help. I also have created https://github.com/IdentityServer/IdentityServer4/issues/3212
Looks like the issue is that in the quickstarts layout lhttps://github.com/IdentityServer/IdentityServer4.Samples/blob/master/Quickstarts/Combined_AspId_and_EFStorage/src/IdentityServer/Views/Shared/_Layout.cshtml
the link to the logout controller does not pass the logoutId argument
@if (!string.IsNullOrWhiteSpace(name)) { <ul class="nav navbar-nav"> <li class="dropdown"> <a href="#" class="dropdown-toggle" data-toggle="dropdown">@name <b class="caret"></b></a> <ul class="dropdown-menu"> <li><a asp-action="Logout" asp-controller="Account">Logout</a></li> </ul> </li> </ul> }...which the AccountController expects
public async Task<IActionResult> Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); }As a result vm.TriggerExternalSignout is false which means the extenal signout does not get called.
/// Handle logout page postback /// </summary> [HttpPost] [ValidateAntiForgeryToken] public async Task<IActionResult> Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await _signInManager.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); }I'll raise a new issue
You've got a point here! I think.
The problem is that BuildLogoutViewModelAsync, when logoutid is null (as is in the case of the IS account controller), will not set LoggedOutViewModel.SignOutIframeUrl.
This means that if the user signs-out in the IS backoffice it will not trigger the front-channel logout on the clients as it does between clients.
Anyone can tell me how to fix that please?
I have the same issue, except I'm using a JavaScript Client with OIDC. I can see that the endSession contains both the id_token_hint and post_logout_redirect_uri in the debug logs, I can see an error:
Client request:
http://localhost:5002/connect/endsession?id_token_hint=ey_redacted_EQ&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A8080
Server logs:
{"ClientId": "js", "ClientName": "JavaScript Client", "SubjectId": "0exxx93", "PostLogOutUri": null, "State": null, "Raw": {"id_token_hint": "eyxxx
[INF] End session request validation failure: Invalid post logout URI
[ERR] Error processing end session request Invalid request
I assume I've done something wrong, but have not scratched open the solution yet.
PS: related reference https://identityserver4.readthedocs.io/en/latest/endpoints/endsession.html
This thread has been automatically locked since there has not been any recent activity after it was closed. Please open a new issue for related bugs.
Most helpful comment
Clearing the cookie in IdentityServer won't logout the user "auto-magically" at other clients, check the docs
https://identityserver4.readthedocs.io/en/latest/topics/signout.html