Skip to content
This repository has been archived by the owner on Dec 13, 2022. It is now read-only.

Issue with Single Log-out (SLO) #474

Closed
jonas-stjernquist opened this issue Nov 10, 2016 · 6 comments
Closed

Issue with Single Log-out (SLO) #474

jonas-stjernquist opened this issue Nov 10, 2016 · 6 comments
Labels

Comments

@jonas-stjernquist
Copy link

Hi,

I'm having a issue when performing single log-out in IdentityServer4.

I have two MVC clients configured to use the hybrid flow.

The cookie middleware in each client is using different CookieName in the CookieAuthenticationOptions.

Should different clients share same cookie name or do they need to have separate session cookies?

I have a HomeController.cs with a Logout action method i each client

        public IActionResult Logout()
        {
            return new SignOutResult(new List<string>() { "cookie", "oidc" });
        }

Theses steps describes my issue:

  1. Visit a controller action with the [authorize] filter in mvcclienta.
  2. User gets redirected to IdentityServer4 login page.
  3. User is now authenticated and gets a session in both IdentityServer4 and mvcclienta.
  4. User then browses a controller action with the [authorize] filter in mvcclientb.
  5. Since the user already have a session in IdentityServer4 he don't get prompted to login in IdentityServer4 login page.
  6. User is now authenticated and gets a session in mvcclientb.
  7. Currently the user have the following cookies stored in the browser:
    idsvr
    idsvr.session
    companyname.auth.mvcclienta
    companyname.auth.mvcclientaC1
    companyname.auth.mvcclientaC2
    companyname.auth.mvcclientb
    companyname.auth.mvcclientbC1
    companyname.auth.mvcclientbC2
    .AspNetCore.Antiforgery.xxxxxxxx
  8. User then push button to invoke HomeController Logout action method.
  9. User gets prompted in IdentityServer4

Logout
Would you like to logout of IdentityServer?

  1. User press Yes button and a page displays the following

Logout You are now logged out
Click here to return to the MVC Client A application.

The results of the 10 above steps is that the user session in IdentityServer4 is gone and also the session in mvcclienta. But the user is still authenticated and has a session in mvcclientb. The user have the following cookies stored in the browser:
companyname.auth.mvcclientb
companyname.auth.mvcclientbC1
companyname.auth.mvcclientbC2
.AspNetCore.Antiforgery.xxxxxxxx

I can see a failure in the kestrel log of mvcclientb

fail: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectMiddleware[48]
The remote signout request was ignored because the 'sid' parameter didn't match the expected value, which may indicate an unsolicited logout.

Here is the logs from the above 10 steps:
kestrel logs - mvcclienta.txt
kestrel logs - mvcclientb.txt
kestrel logs - identityserver4.txt

Clients.cs in IdentityServer4 configuration

    public class Clients
    {
        public static IEnumerable<Client> Get()
        {
            return new List<Client>
            {
                new Client
                {
                    ClientId = "mvcclienta",
                    ClientName = "MVC Client A",
                    Enabled = true,
                    RequireConsent = false,
                    ClientSecrets = new List<Secret>
                    {
                        new Secret("secretmvcclienta".Sha256())
                    },
                    AllowedGrantTypes = GrantTypes.Hybrid,
                    AllowAccessTokensViaBrowser = false,
                    RedirectUris = new List<string>
                    {
                        "http://localhost:5003/signin-oidc"
                    },
                    LogoutUri = "http://localhost:5003/signout-oidc",
                    PostLogoutRedirectUris = new List<string>
                    {
                        "http://localhost:5003/"
                    },
                    AllowedScopes = new List<string>
                    {
                        StandardScopes.OpenId.Name,
                        StandardScopes.Profile.Name,
                        StandardScopes.Email.Name,
                        StandardScopes.Roles.Name
                    }
                },
                new Client
                {
                    ClientId = "mvcclientb",
                    ClientName = "MVC Client B",
                    Enabled = true,
                    RequireConsent = false,
                    ClientSecrets = new List<Secret>
                    {
                        new Secret("secrectmvcclientb".Sha256())
                    },
                    AllowedGrantTypes = GrantTypes.Hybrid,
                    AllowAccessTokensViaBrowser = false,
                    RedirectUris = new List<string>
                    {
                        "http://localhost:5004/signin-oidc"
                    },
                    LogoutUri = "http://localhost:5004/signout-oidc",
                    PostLogoutRedirectUris = new List<string>
                    {
                        "http://localhost:5004/"
                    },
                    AllowedScopes = new List<string>
                    {
                        StandardScopes.OpenId.Name,
                        StandardScopes.Profile.Name,
                        StandardScopes.Email.Name,
                        StandardScopes.Roles.Name
                    }
                }
            };
        }
    }

Startup.cs of MVC client "mvcclienta"

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (_hostingEnvironment.IsDevelopment())
            {
                loggerFactory.AddConsole();
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationScheme = "cookie",
                CookieName = "companyname.auth.mvcclienta",
                CookieHttpOnly = true,
                CookieSecure = _hostingEnvironment.IsDevelopment() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always,
                AutomaticAuthenticate = true,
                AutomaticChallenge = false,
                ExpireTimeSpan = TimeSpan.FromMinutes(60)
            });
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            var oidcOptions = new OpenIdConnectOptions
            {
                AuthenticationScheme = "oidc",
                SignInScheme = "cookie",
                Authority = "http://localhost:5001/",
                RequireHttpsMetadata = _hostingEnvironment.IsDevelopment() ? false : true,
                PostLogoutRedirectUri = "http://localhost:5003/",
                ClientId = "mvcclienta",
                ClientSecret = "secretmvcclienta",
                ResponseType = "code id_token",
                SaveTokens = true,
                GetClaimsFromUserInfoEndpoint = true,  
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                }
            };
            oidcOptions.Scope.Clear();
            oidcOptions.Scope.Add("openid");
            oidcOptions.Scope.Add("profile");
            oidcOptions.Scope.Add("email");
            oidcOptions.Scope.Add("roles");
            app.UseOpenIdConnectAuthentication(oidcOptions);
            app.UseMvcWithDefaultRoute();
        }

Startup.cs of MVC client "mvcclientb" (equal to mvcclienta beside different ports and replaced the string mvcclienta with mvcclientb for different property settings)

public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
        {
            if (_hostingEnvironment.IsDevelopment())
            {
                loggerFactory.AddConsole();
                app.UseDeveloperExceptionPage();
            }
            else
            {
                app.UseExceptionHandler("/Home/Error");
            }
            app.UseStaticFiles();
            app.UseCookieAuthentication(new CookieAuthenticationOptions
            {
                AuthenticationScheme = "cookie",
                CookieName = "companyname.auth.mvcclientb",
                CookieHttpOnly = true,
                CookieSecure = _hostingEnvironment.IsDevelopment() ? CookieSecurePolicy.SameAsRequest : CookieSecurePolicy.Always,
                AutomaticAuthenticate = true,
                AutomaticChallenge = false,
                ExpireTimeSpan = TimeSpan.FromMinutes(60)
            });
            JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
            var oidcOptions = new OpenIdConnectOptions
            {
                AuthenticationScheme = "oidc",
                SignInScheme = "cookie",
                Authority = "http://localhost:5001/",
                RequireHttpsMetadata = _hostingEnvironment.IsDevelopment() ? false : true,
                PostLogoutRedirectUri = "http://localhost:5004/",
                ClientId = "mvcclientb",
                ClientSecret = "secretmvcclientb",
                ResponseType = "code id_token",
                SaveTokens = true,
                GetClaimsFromUserInfoEndpoint = true,  
                TokenValidationParameters = new TokenValidationParameters
                {
                    NameClaimType = "name",
                    RoleClaimType = "role"
                }
            };
            oidcOptions.Scope.Clear();
            oidcOptions.Scope.Add("openid");
            oidcOptions.Scope.Add("profile");
            oidcOptions.Scope.Add("email");
            oidcOptions.Scope.Add("roles");
            app.UseOpenIdConnectAuthentication(oidcOptions);
            app.UseMvcWithDefaultRoute();
        }
@jonas-stjernquist
Copy link
Author

JWT Header & Payload

{
"alg": "RS256",
"kid": "09c773bd7ccb4ccdeb8398ee9fedf5a4a4b099f2a559338acbe991fd9272683b",
"typ": "JWT"
}
{
"nbf": 1478765478,
"exp": 1478765778,
"iss": "http://localhost:5001",
"aud": "mvcclienta",
"nonce": "636143622713626008.N2Q3NWNkYjAtNmVmOC00NjJjLTkzNzYtYWRjNmM5NmQ2ZDg2ZTdjOTkyNTEtYjc0Ny00NzA5LTgxY2YtNWNiN2RjYzNlMDNk",
"iat": 1478765478,
"sid": "1b938e630078d1e1d74c734d8b7637750ec4506a3efa25aea4081a1d9d26ea56",
"sub": "JohnDoe GUID",
"auth_time": 1478765477,
"idp": "local",
"amr": [
"pwd"
]
}

@brockallen
Copy link
Member

Should different clients share same cookie name or do they need to have separate session cookies?

Normally different clients are distinct apps, and might even run on distinct domains, so they normally have their own cookies (and don't know about one another).

@jonas-stjernquist
Copy link
Author

I updated to IDSvr4 RC3 yesterday and it actually fixed this issue, probably thanks to the fix in #441

The two clients session cookies are removed if I logout from one of the clients.

If I look at the "sid" claim in each client they now have the same value, which results in successful single log-out (SLO).

@alexandranb
Copy link

Hi,

I also have a problem with the single sign out.... If i sign out from my IdentityServer, I'm still logged in the clients. How can I solve this?

Thanks

@alexandranb
Copy link

Also, this happens only in production....on localhost it works fine.

@lock
Copy link

lock bot commented Jan 14, 2020

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.

@lock lock bot locked as resolved and limited conversation to collaborators Jan 14, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Labels
Projects
None yet
Development

No branches or pull requests

3 participants