IdentityServer4 PostLogoutRedirectUri null
Asked Answered
M

8

15

I am attempting to get the implicit flow working for IdentityServer4. Login and logout work correctly, however the PostLogoutRedirectUri is coming back null, despite setting the value where it needs to be set. What I would like is for the logout process to redirect back to my application after the logout is complete.

I am getting the logoutId correctly, and Logout calls BuildLoggedOutViewModelAsync:

[HttpPost]
    [ValidateAntiForgeryToken]
    public async Task<IActionResult> Logout(LogoutInputModel model)
    {
        var vm = await _account.BuildLoggedOutViewModelAsync(model.LogoutId);
...

This method is located in my AccountService.cs class, which then calls the GetLogoutContextAsync of the DefaultIdentityServiceInteractionService:

public async Task<LoggedOutViewModel> BuildLoggedOutViewModelAsync(string logoutId)
    {
        // get context information (client name, post logout redirect URI and iframe for federated signout)
        var logout = await _interaction.GetLogoutContextAsync(logoutId);
...

Which creates a IdentityServer4.Models.LogoutRequest.

The SignOutIFrameUrl string property is set to "http://localhost:5000/connect/endsession/callback?sid=bf112f7785bc860fcc4351893012622e&logoutId=d6649e7f818d9709b2c0bc659696abdf" but nothing else seems to have been populated in the LogoutRequest.

Unfortunately, this means that the PostLogoutRedirectUri is null and the AutomaticRedirectAfterSignOut is also null, and when the LoggedOut.cshtml page is loaded, the signout-callback.js file is never loaded:

@section scripts
{
    @if (Model.AutomaticRedirectAfterSignOut)
    {
        <script src="~/js/signout-redirect.js"></script>
    }
}

Here are my configuration settings.

Config.cs:

public static IEnumerable<Client> GetClients()
    {
        return new List<Client>
        {
            new Client
            {
                ClientId = "implicit.client",
                AllowedGrantTypes = GrantTypes.Implicit,
                AllowAccessTokensViaBrowser = true,
                AllowedScopes = 
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "ContractManagerAPI"
                },
                RedirectUris = { "http://localhost:9000/" },
                PostLogoutRedirectUris = { "http://localhost:9000/" },
                AllowedCorsOrigins = { "http://localhost:9000" },
                RequireConsent = false,

            }                
        };
    }

app.ts (js client):

import {UserManager} from 'oidc-client';
import { inject, Factory } from 'aurelia-framework';

@inject(Factory.of(UserManager))
export class App {
  userManager: UserManager;

  constructor(userManagerFactory){
    let config = {
      authority: 'http://localhost:5000',
      client_id: 'implicit.client',
      response_type: 'id_token token',
      scope: 'openid profile ContractManagerAPI',
      redirect_uri: 'http://localhost:9000/',
      post_logout_redirect_uri: 'http://localhost:9000/'
    };

    this.userManager = userManagerFactory(config);
  }

  login(){
    this.userManager.signinRedirect();
  }

  logout(){
    this.userManager.signoutRedirect();
  }
}

Relevant parts of Startup.cs:

services.AddIdentityServer()
                .AddTemporarySigningCredential()
                .AddInMemoryApiResources(Config.GetApiResources())
                .AddInMemoryClients(Config.GetClients())
                .AddInMemoryIdentityResources(Config.GetIdentityResources())
                .AddContractManagerUserStore()
                .AddProfileService<ContractManagerProfileService>();

Any assistance in figuring out where I'm going wrong would be greatly appreciated.

Thanks!

Massey answered 21/6, 2017 at 19:24 Comment(0)
S
15

pass id_token_hint arg to signoutRedirect()

you can get id_token_hint from the User object returned by signinRedirect();

so lets say you got a variable called "user" in your ts file that got set as a result of the user logging in via signinRedirect().

then you would do...

logout(){
    this.userManager.signoutRedirect({ 'id_token_hint': this.user.id_token });
}
Subvert answered 23/6, 2017 at 16:39 Comment(1)
Also, make sure you are actually sending the Identity Token and not the Access token for id_token_hint. I struggled with this because nowhere did I receive any errors telling me I was sending the wrong token, it just wouldn't work.Afterheat
R
4

I was running into (possibly) the same issue as the OP.

After examining the logs from IdentityServer, I noticed that an exception was being thrown just after the request information containing my client information was sent to the IdentityServer server.

This lead me to this github post which addresses the error directly. The solution was to update to IdentityServer4 v4.1.2. I then re-ran my code and voila! the PostLogoutRedirectUri (and other parameters) were now correctly populated from the var context = await _interaction.GetLogoutContextAsync(logoutId); call.

Richella answered 30/3, 2021 at 14:25 Comment(0)
A
3

fyi, GetLogoutContextAsync returns also a null-PostLogoutRedirectUri when the configured URI in Config.cs does not match with the configured URI of the JS-Client.

If you are using EF core based configuration you need to verify the URI in the ClientPostLogoutRedirectUris table.

Doesn't seem to apply to your problem but worth mentioning.

Actual answered 24/11, 2022 at 12:58 Comment(0)
F
2

Make sure you have these settings properly configured:

public class AccountOptions
        {
            public static bool AllowLocalLogin = true;
            public static bool AllowRememberLogin = true;
            public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30);

            public static bool ShowLogoutPrompt = false;
            public static bool AutomaticRedirectAfterSignOut = true;

            public static bool WindowsAuthenticationEnabled = false;
            // specify the Windows authentication schemes you want to use for authentication
            public static readonly string[] WindowsAuthenticationSchemes = new string[] { "Negotiate", "NTLM" };
            public static readonly string WindowsAuthenticationDisplayName = "Windows";

            public static string InvalidCredentialsErrorMessage = "Invalid username or password";
        }
Fredric answered 22/6, 2017 at 12:45 Comment(0)
T
2

I want to share my experience of solving issues with null PostLogoutRedirectUri value.

To initiate Logout process you must first call SignOut("Cookies", "oidc") on mvc client side. Otherwise you will have null logoutId value on Identity Server side. Example endpoint in my HomeController:

public IActionResult Logout()
{
    return SignOut("Cookies", "oidc");
}

I always had null PostLogoutRedirectUri value in logout context until I added SignInScheme value on mvc client side. These settings of authentication on MVC client side works for me:

var authenticationBuilder = services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "oidc";
});

authenticationBuilder.AddCookie(options =>
{
    options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
    options.Cookie.Name = "identity_server_mvc";
});

authenticationBuilder.AddOpenIdConnect("oidc", options =>
{
    options.Authority = "{IDENTITY_SERVER_URL}";
    options.ClientId = "mvc";
    options.SaveTokens = true;
    options.SignInScheme = "Cookies";
});

You also need to make sure that you have added the PostLogoutRedirectUri value to the client configuration on the Identity Server side:

new Client
{
    ClientId = "mvc",
    AllowedGrantTypes = GrantTypes.Implicit,

    RedirectUris           = { "{CLIENT_URL}/signin-oidc" },
    PostLogoutRedirectUris = { "{CLIENT_URL}/signout-callback-oidc" },

    AllowedScopes =
    {
        IdentityServerConstants.StandardScopes.OpenId,
        IdentityServerConstants.StandardScopes.Profile
    }
}

Hope it helps!

Tammietammuz answered 28/10, 2019 at 23:56 Comment(0)
Y
2

For me what solve the null was the bellow option set up on mvc client

o.SignedOutCallbackPath = new PathString("/signout-callback-oidc");
Yeoman answered 24/2, 2021 at 17:49 Comment(0)
F
0

Try configuring the LogoutUrl for the MVC client!

Fredric answered 21/6, 2017 at 20:27 Comment(4)
hi @JayDeeEss,I tried adding LogoutUrl to the IdentityServer, but no luck. I don't have an MVC client, it's a javascript client. I'm using the oidc-client.js file, and from what I can tell there is no logout_url property.Massey
Hi @user2921364! I created angular client (implicit flow) with your configurations and its working fine. The only thing i can think of is to make sure "AutomaticRedirectAfterSignout" option is set to true in AccountOptions.cs under services. This is how my AccountOptions class looks like: (check in the answer above )Fredric
Hi @JayDeeEss, thanks for your input with this. Turns out it was much less intuitive. I had my new Oidc.UserManager().signRedirectCallback().then... script located on the index.html page. I decided to follow the directions to the 'T' and moved that to it's own callback.html page. Once I did that, it all started working. The biggest clue was that userManager.getUser() was always returning null, even when logged in. I realized something was missing from the login process.Massey
@user2921364 - could you make your comment into an answer that explains what happened and how you fixed it? Experiencing a similar issue and I suspect it's related.Getty
F
0

Go through the warning log message where the sign out request started. The log are useful when debug the issues.

Example warning message appear for my case:

[17:21:57 WRN] Invalid PostLogoutRedirectUri: https://localhost:5511/authentication/logout-callback

The problem encountered here is the redirect uri saved in db has an empty sapce infront causing the PostLogoutRedirectUri was invalid and return null. Just need to remove the empty space for the uri, the problem solved.

Fourscore answered 28/4, 2023 at 0:52 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.