Logout Redirect Page

Logout redirect causes this error:

{“errorCode”:“invalid_client”,“errorSummary”:“Invalid value for ‘client_id’ parameter.”,“errorLink”:“invalid_client”,“errorId”:“oae048s3hWISeippGnzrmdjgQ”,“errorCauses”:[]}

Any ideas?

Thanks

Can you please provide more information about your application? Are you using .NET, Java and Spring Boot, Angular, or React, etc?

I have a Web Forms Aspnet aplicattion in VB.net.

My okta configutarion:
Login redirect URIs: http://localhost:16517/authorization-code/callback
Logout redirect URIs: http://localhost:16517/

Key en Web.config
add key=“okta:RedirectUri” value=“http://localhost:16517/authorization-code/callback
add key=“okta:PostLogoutRedirectUri” value=“http://localhost:16517/

Master Page with Logout Event
<asp:LoginStatus
ID=“HeadLoginStatus”
runat=“server”
LogoutAction=“Redirect”
LogoutText=“Cerrar Session”
LogoutPageUrl="~/"
OnLoggingOut=“LoggingStatus_LoggingOut”/>

And the code behind:

Protected Sub LoggingStatus_LoggingOut(ByVal sender As Object, ByVal e As LoginCancelEventArgs)
Context.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType, OpenIdConnectAuthenticationDefaults.AuthenticationType)
End Sub

Does the client ID you have configured work for logging in and you’re only having issues logging out?

Thats correct. With Login no problem.

Hi @nrodrigu,

Can you also post your OIDC configuration?

When logging out you have to pass the id_token_hint param. You can find more info about it here: https://developer.okta.com/docs/reference/api/oidc/#request-parameters-5. Are you passing your Id Token when logging out?

Hi. This is the Startup.vb class:

<Assembly: OwinStartup(“PruebasOkta”, GetType(AspNetWebFormsOkta.Startup))>
Namespace AspNetWebFormsOkta
Public Class Startup
Private ReadOnly _clientId As String = ConfigurationManager.AppSettings(“okta:ClientId”)
Private ReadOnly _redirectUri As String = ConfigurationManager.AppSettings(“okta:RedirectUri”)
Private ReadOnly _authority As String = ConfigurationManager.AppSettings(“okta:OrgUri”)
Private ReadOnly _clientSecret As String = ConfigurationManager.AppSettings(“okta:ClientSecret”)

    Public Sub Configuration(ByVal app As IAppBuilder)
        ConfigureAuth(app)
    End Sub

    Public Sub ConfigureAuth(ByVal app As IAppBuilder)
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie)
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
        app.UseCookieAuthentication(New CookieAuthenticationOptions())
        app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
            .ClientId = _clientId,
            .ClientSecret = _clientSecret,
            .Authority = _authority,
            .RedirectUri = _redirectUri,
            .ResponseType = OpenIdConnectResponseType.CodeIdToken,
            .Scope = OpenIdConnectScope.OpenIdProfile,
            .TokenValidationParameters = New TokenValidationParameters With {
                .NameClaimType = "name"
            },
            .Notifications = New OpenIdConnectAuthenticationNotifications With {
                .AuthorizationCodeReceived = Async Function(n)
                                                 Dim tokenClient = New TokenClient($"{_authority}/v1/token", _clientId, _clientSecret)
                                                 Dim tokenResponse = Await tokenClient.RequestAuthorizationCodeAsync(n.Code, _redirectUri)

                                                 If tokenResponse.IsError Then
                                                     Throw New Exception(tokenResponse.[Error])
                                                 End If

                                                 Dim userInfoClient = New UserInfoClient($"{_authority}/v1/userinfo")
                                                 Dim userInfoResponse = Await userInfoClient.GetAsync(tokenResponse.AccessToken)
                                                 Dim claims = New List(Of Claim)(userInfoResponse.Claims) From {
                                                     New Claim("id_token", tokenResponse.IdentityToken),
                                                     New Claim("access_token", tokenResponse.AccessToken)
                                                 }
                                                 n.AuthenticationTicket.Identity.AddClaims(claims)
                                             End Function
            }
        })
    End Sub
End Class

End Namespace

And the Application configuration:
OKtaLog1 OKtaLog2

Thanks

Hi @nrodrigu,

You will also need to add an additional notification for RedirectToIdentityProvider to append the id_token when you are logging out. Something like this with C#:

  private Task BeforeRedirectToIdentityProviderAsync(RedirectToIdentityProviderNotification<OpenIdConnectMessage, OpenIdConnectAuthenticationOptions> redirectToIdentityProviderNotification)
        {
            // If signing out, add the id_token_hint
            if (redirectToIdentityProviderNotification.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
            {
                if (redirectToIdentityProviderNotification.OwinContext.Authentication.User.FindFirst("id_token") != null)
                {
                    redirectToIdentityProviderNotification.ProtocolMessage.IdTokenHint = redirectToIdentityProviderNotification.OwinContext.Authentication.User.FindFirst("id_token").Value;
                }
            }
}

...

  Notifications = new OpenIdConnectAuthenticationNotifications
                {
                    RedirectToIdentityProvider = BeforeRedirectToIdentityProviderAsync,
                    ....
                },

I hope this helps.

Excuse me where should I put that code, it is not clear to me

Sure! So, in your Startup class when initializing the OIDC middleware, you are already overwriting the OpenIdConnectAuthenticationNotifications by adding your own logic when the authorization code is received (AuthorizationCodeReceived):

New OpenIdConnectAuthenticationNotifications With {
                .AuthorizationCodeReceived  = Async Function(n) 
...

OpenIdConnectAuthenticationNotifications has other delegates that you can also use to provide your custom code, RedirectToIdentityProvider is one of them. You should add another function for RedirectToIdentityProvider to include the id token hint if the request type is Logout. I’m not familiar with VB, but I imagine you can do something like this:

...
.Scope = OpenIdConnectScope.OpenIdProfile,
.TokenValidationParameters = New TokenValidationParameters With {
                .NameClaimType = "name"
 },
.Notifications = New OpenIdConnectAuthenticationNotifications With {
                .AuthorizationCodeReceived  = Async Function(n) 
                // your current code ...

               .RedirectToIdentityProvider = Async Function(n) 
                // your code to send IdTokenHint if logging out as shown in BeforeRedirectToIdentityProviderAsync
...

Hopefully this is clear now.

Hi Laura. I modified the code, this way:

Public Sub ConfigureAuth(ByVal app As IAppBuilder)
        app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType)
        app.UseCookieAuthentication(New CookieAuthenticationOptions())
        app.UseOpenIdConnectAuthentication(New OpenIdConnectAuthenticationOptions With {
            .ClientId = _clientId,
            .ClientSecret = _clientSecret,
            .Authority = _authority,
            .RedirectUri = _redirectUri,
            .ResponseType = OpenIdConnectResponseType.CodeIdToken,
            .Scope = OpenIdConnectScope.OpenIdProfile,
            .PostLogoutRedirectUri = _PostLogoutRedirectUri,
            .TokenValidationParameters = New TokenValidationParameters With {
                .NameClaimType = "name"
            },
            .Notifications = New OpenIdConnectAuthenticationNotifications With {
                .AuthorizationCodeReceived = Async Function(n)
                                                 Dim tokenClient = New TokenClient(_authority & "/v1/token", _clientId, _clientSecret)
                                                 Dim tokenResponse = Await tokenClient.RequestAuthorizationCodeAsync(n.Code, _redirectUri)

                                                 If tokenResponse.IsError Then
                                                     Throw New Exception(tokenResponse.[Error])
                                                 End If

                                                 Dim userInfoClient = New UserInfoClient($"{_authority}/v1/userinfo")
                                                 Dim userInfoResponse = Await userInfoClient.GetAsync(tokenResponse.AccessToken)
                                                 Dim claims = New List(Of Claim)(userInfoResponse.Claims) From {
                                                     New Claim("id_token", tokenResponse.IdentityToken),
                                                     New Claim("access_token", tokenResponse.AccessToken)
                                                 }
                                                 n.AuthenticationTicket.Identity.AddClaims(claims)
                                                 Return
                                             End Function,
                .RedirectToIdentityProvider = Function(n)

                                                  If n.ProtocolMessage.RequestType = OpenIdConnectRequestType.Logout Then
                                                      Dim idTokenClaim = n.OwinContext.Authentication.User.FindFirst("id_token")

                                                      If idTokenClaim IsNot Nothing Then
                                                          n.ProtocolMessage.IdTokenHint = idTokenClaim.Value
                                                      End If
                                                  End If

                                                  Return Task.CompletedTask
                                              End Function
            }
        })
    End Sub

And a concern arises. When debugging the application I see that when I perform the LogOut action (Context.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType, OpenIdConnectAuthenticationDefaults.AuthenticationType)) the function associated with the RedirectToIdentityProvider runs twice.
Now, is my code well generated according to your instructions and why does the above happen?

Thank you

Hi @nrodrigu,

Your code looks good to me. The RedirectToIdentityProvider is an event that runs multiple times, basically, any time you are about to redirect to the Identity Provider (Okta) but by doing this n.ProtocolMessage.RequestType = OpenIdConnectRequestType.Logout you are ensuring that your code inside that if block will run only for logout. Is that code executed twice? Is log out working now?

Hello Laura. Sorry for the delay. Log Out works now. But now I have another problem. When Okta redirects to my website I get the following error:

IDX21323: RequireNonce is ‘System.Boolean’. OpenIdConnectProtocolValidationContext.Nonce was null, OpenIdConnectProtocol.ValidatedIdToken.Payload.Nonce was not null. The nonce cannot be validated. If you don’t need to check the nonce, set OpenIdConnectProtocolValidator.RequireNonce to ‘false’. Note if a ‘nonce’ is found it will be evaluated.

I have added a line of code that I found searching the internet and that was supposed to fix the problem, but it has not solved it.

app.UseKentorOwinCookieSaver()

Any ideas? Thank you

Here is the code in C#:

                RedirectToIdentityProvider = n =>
                {
                    // If signing out, add the id_token_hint
                    if (n.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
                    {
                        var idTokenClaim = n.OwinContext.Authentication.User.FindFirst("id_token");

                        if (idTokenClaim != null)
                        {
                            n.ProtocolMessage.IdTokenHint = idTokenClaim.Value;
                        }

                    }

                    return Task.CompletedTask;
                }
1 Like