2

I am trying to use Sitecore 10.0.1, IdentityServer4, with Okta and OpenID Connect. When I click logout I get this error:

{"errorCode":"invalid_client","errorSummary":"A client_id must be provided in the request.","errorLink":"invalid_client","errorId":"oaevKZNtJFrRjqTZ_wCOsx6lA","errorCauses":[]}

Here is the OnRedirectToIdentityProviderForSignOut code:

                options.Events.OnRedirectToIdentityProviderForSignOut += (Func<RedirectContext, Task>)(context =>
                {
                    context.Request.QueryString.Add("client_id", identityProvider.ClientId); // Did not fix issue
                    context.Options.ClientId = identityProvider.ClientId;  // Did not fix issue
                    context.Options.SignedOutRedirectUri = "https://id.myproject.localhost";
                    _logger.LogInformation("Redirect to identity provider for sign out. ClientId: {0}",
                        context?.Options?.ClientId);

                    context.ProtocolMessage.IdTokenHint = context.HttpContext.User.FindFirst("id_token")?.Value;  // This returns null
                    _logger.LogInformation("IdTokenHint: {0}", context.ProtocolMessage.IdTokenHint);

                    return Task.CompletedTask;
                });
                options.Events.OnRedirectToIdentityProvider += (Func<RedirectContext, Task>)(context =>
                {
                    _logger.LogInformation("Redirect to identity provider. IdToken: {0}.",
                        context.ProtocolMessage.IdToken);

                    var first = context.HttpContext.User.FindFirst("idp");
                    if (string.Equals(first?.Value, identityProvider.AuthenticationScheme, StringComparison.Ordinal))
                        context.ProtocolMessage.Prompt = "select_account";
                    return Task.CompletedTask;
                });

I was thinking this might be related to IdTokenHint but I don't seem to have that available.

4
  • 1
    Is this for the CM/Business users or the front website? – Julius A Apr 26 at 20:14
  • Also, it may be helpful if you please share your code around how you are redirecting to the Identity provider. For example, how are you handling the OnRedirectToIdentityProvider event? – Julius A Apr 26 at 20:32
  • This is for the CM/Business users – Robert Mroch Apr 26 at 20:35
  • I added the OnRedirectToIdentityProvider to the post above. – Robert Mroch Apr 26 at 20:37
1

I ran into the same issue and resolved it by storing the IdToken in a persistent storage so it can be retrieved later during logout. In our case it is in Redis and we used the same approach as my answer here, but this can obviously be different depending on your solution. I'm assuming you have access to IdToken in SecurityTokenValidated event?

3

In the AddOpenIdConnect function set options.SaveTokens = true; then you can retrieve the token in the OnRedirectToIdentityProviderForSignOut handler using the GetTokenAsync extension method from the Microsoft.AspNetCore.Authentication namespace.

options.Events.OnRedirectToIdentityProviderForSignOut += (Func<RedirectContext, Task>)(async context =>
{
   context.ProtocolMessage.IdTokenHint = await context.HttpContext.GetTokenAsync(OpenIdConnectResponseType.IdToken);
});
1

I have working code I have used with IdentityServer4 that I believe should work with Okta. The full solution is available on my repo here.

    public void ConfigureServices(IServiceCollection services)
    {
        Ids4DemoIdentityProvider identityProvider = this._appSettings.Ids4DemoIdentityProvider;
        if (!identityProvider.Enabled)
            return;
        this._logger.LogDebug("Configure '" + identityProvider.DisplayName + "'. AuthenticationScheme = " + identityProvider.AuthenticationScheme + ", ClientId = " + identityProvider.ClientId, Array.Empty<object>());
        new AuthenticationBuilder(services).AddOpenIdConnect(identityProvider.AuthenticationScheme, identityProvider.DisplayName, (Action<OpenIdConnectOptions>)(options =>
        {
            options.SignInScheme    = "idsrv.external";
            options.ClientId        = identityProvider.ClientId;
            options.Authority       = identityProvider.Authority;
            options.MetadataAddress = identityProvider.MetadataAddress;
            options.CallbackPath    = "/signin-idsrv";
            options.Events.OnRedirectToIdentityProvider += (Func<RedirectContext, Task>)(context =>
            {
                Claim first = context.HttpContext.User.FindFirst("idp");
                if (string.Equals(first != null ? first.Value : (string)null, identityProvider.AuthenticationScheme, StringComparison.Ordinal))
                    context.ProtocolMessage.Prompt = "select_account";
                return Task.CompletedTask;
            });
        }));
    }

I suggest you try with this and see if this resolves your issue

3
  • We found that the context.ProtocolMessage.IdTokenHint is not set on logout. But if we breakpoint / copy that value on login then breakpoint / pasted the value in logout it works. How can we persist that value for use in logout? – Robert Mroch Apr 27 at 16:14
  • Did quick search and this might help you: Add id_token in list of claims in SecurityTokenValidated callback and used this in RedirectToIdentityProvider callback". Read more here: github.com/IdentityServer/IdentityServer3/issues/2334 – Julius A Apr 27 at 19:00
  • Another one of our developers found this: context.ProtocolMessage.IdTokenHint = await context.HttpContext.GetTokenAsync(OpenIdConnectResponseType.IdToken); – Robert Mroch Apr 30 at 15:05
0

For me the solution was to set SignOutScheme in OpenIdConnectOptions of AddOpenIdConnect:

services.AddAuthentication()
    .AddOpenIdConnect(OpenIDConnectAuthenticationScheme, oidcOptions =>
    {
        oidcOptions.CallbackPath = "/signin-oidc";
        oidcOptions.SignedOutCallbackPath = "/signout-callback-oidc";
        oidcOptions.ResponseType = OpenIdConnectResponseType.Code;  //Code Flow
        oidcOptions.GetClaimsFromUserInfoEndpoint = true;
        oidcOptions.SaveTokens = true;
        oidcOptions.UseTokenLifetime = false;
        oidcOptions.ClaimActions.Add(new AspNetCore.Authentication.OAuth.Claims.MapAllClaimsAction());
        oidcOptions.SignOutScheme = OpenIDConnectAuthenticationScheme;  //<-- Important

        configuration.Bind($"Authentication:OpenIDConnect", oidcOptions);   //Sets Authority, ClientId and ClientSecret

        if (!(oidcOptions.Scope?.Any() ?? false))
        {
            //Add default scopes
            oidcOptions.Scope.Clear();
            oidcOptions.Scope.Add("openid");
            oidcOptions.Scope.Add("profile");
            oidcOptions.Scope.Add("email");
        }
    });

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.