.NET MVC client_id missing in logout

Hi I’m developing a MVC application that will support both the default ASP.NET identity for users and Okta external authentication for employees.

When I log in to Okta for first time, the application lets me create a local identity account to sync the two together. The log in portion seems to working well.

I looked at this project samples-aspnet/okta-hosted-login at master · okta/samples-aspnet · GitHub and implemented the code with the .net identity but something is wrong with the log out.

When I try to log out, I’m getting this error on the /oauth2/v1/logout url:
{“errorCode”:“invalid_client”,“errorSummary”:“A client_id must be provided in the request.”,“errorLink”:“invalid_client”,“errorId”:“oaeflMoiwOGQ6-0YJPOMSfImg”,“errorCauses”:}

Reading up it says the logout needs a id_token_hint passed, but I’m confused on how to do that. Can someone help point me in the right direction?

Startup.Auth.cs
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext(ApplicationUserManager.Create);
app.CreatePerOwinContext(ApplicationSignInManager.Create);

        app.UseCookieAuthentication(new CookieAuthenticationOptions
        {
            AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
            LoginPath = new PathString("/Account/Login"),
            Provider = new CookieAuthenticationProvider
            {
                OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser>(
                    validateInterval: TimeSpan.FromMinutes(30),
                    regenerateIdentity: (manager, user) => user.GenerateUserIdentityAsync(manager))
            }
        });            
        app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);

        app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));

        app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);

        // OKTA
        app.UseOktaMvc(new OktaMvcOptions()
        {
            OktaDomain = ConfigurationManager.AppSettings["okta:OktaDomain"],
            ClientId = ConfigurationManager.AppSettings["okta:ClientId"],
            ClientSecret = ConfigurationManager.AppSettings["okta:ClientSecret"],
            RedirectUri = ConfigurationManager.AppSettings["okta:RedirectUri"],
            PostLogoutRedirectUri = ConfigurationManager.AppSettings["okta:PostLogoutRedirectUri"],
            GetClaimsFromUserInfoEndpoint = true,
            Scope = new List<string> { "openid", "profile", "email" },
            AuthorizationServerId = "",
        });
    }
}

Log off
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult LogOff()
{
if (HttpContext.User.Identity.IsAuthenticated)
{
AuthenticationManager.SignOut(DefaultAuthenticationTypes.ApplicationCookie, CookieAuthenticationDefaults.AuthenticationType, OktaDefaults.MvcAuthenticationType);
}

	return RedirectToAction("Index", "Home");
}

PostLogout
public ActionResult PostLogout()
{
return RedirectToAction(“Index”, “Home”);
}

Hey there,

I’m having the same issue lol. I was able to find this StackExchange post where one of the answers has a simple solution of adding an OnRedirectToIdentityProviderForSignOut event handler that just adds the missing id_token, However, this solution didn’t work for me, but I’m using Blazor rather than MVC, so maybe you’ll have better luck with it.

@ekornmeyer @DapperDeer
Hi, when logging out, yes, you need pass the id_token_hint param. You can refer more details here: OpenID Connect & OAuth 2.0 API | Okta Developer.
If you still have questions, you can refer the sample code here.
Logout Redirect Page - #8 by laura.rodriguez

Appreciate the response, what should I/we do if that sample code doesn’t fix our issue?

For reference, here’s my Authentication options code

        var events = new OpenIdConnectEvents();
		events.OnRedirectToIdentityProviderForSignOut += async context => {
			context.ProtocolMessage.IdTokenHint = await context.HttpContext.GetTokenAsync(OpenIdConnectResponseType.IdToken);
		};

		optionsAction ??= options => {
			options.SaveTokens = true;
			options.Authority = authenticationOptions.Authority;
			options.ClientId = authenticationOptions.ClientId;
			options.ResponseType = OidcConstants.ResponseTypes.Code;
			options.Scope.Add(IdentityServerConstants.StandardScopes.Profile);
			options.Scope.Add(IdentityServerConstants.StandardScopes.OpenId);
			options.Events = events;
		};

and for parity’s sake, here’s a small variation of the event handler thanks to rcorkery from the forum thread you linked, Lijia.

        var events = new OpenIdConnectEvents {
			OnRedirectToIdentityProvider = context => {
				// If signing out, add the id_token_hint
				if (context.ProtocolMessage.RequestType == OpenIdConnectRequestType.Logout)
				{
					var idTokenClaim = context.HttpContext.User.FindFirst("id_token");

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

				return Task.CompletedTask;
			}
		};

and my Logout action

        await this._signInManager.SignOutAsync();
		await this.HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme);
		await this.HttpContext.SignOutAsync(IdentityServerConstants.DefaultCookieAuthenticationScheme);
		this._logger.LogInformation("User logged out.");
		if (returnUrl != null)
		{
			return SignOut(new AuthenticationProperties { RedirectUri = returnUrl }, OktaDefaults.MvcAuthenticationScheme);
		}

		return SignOut(OktaDefaults.MvcAuthenticationScheme);

Unfortunately neither of those variations have worked, any further advice would be very much appreciated.

Hi Lijia, I followed that link from Laura and I can’t figure out how to implement that code into what I’m using either. I’m using the OktaMvc package and it doesn’t seem to have Notifications nor an option to include the id_token_hint param

I’ve finally figured out what was wrong, hopefully my answer will help you too, ekornmeyer.

In your ExternalLogin action (I scaffolded ASP.NET Core Identity so in my ExternalLogin.cshtml.cs file I have a method called OnGetCallbackAsync()) you need to explicitly store the authentication tokens from the external login provider (Okta).

		var properties = new AuthenticationProperties();
		properties.StoreTokens(info.AuthenticationTokens);
		properties.IsPersistent = true;

You may have to play with it a bit such as using AddLoginAsync to associate the Okta account with your database’s account and SignInAsync to associate the users with the new AuthenticationProperties object, but hopefully this will help you and anyone else who comes across this problem.

This link may also be helpful, it’s where I pulled the snippet from above.
Persist additional claims and tokens from external providers in ASP.NET Core | Microsoft Docs

@ekornmeyer Please check @DapperDeer’s sample and see if you can figure it out. If you need us to look into your code, you can create a support ticket through an email to support@okta.com.

Glad you got it worked out @DapperDeer! I realized I don’t actually want to log them out of Okta as we use that across many different applications, so I’m just going with the local account logout now. I noticed after I log out locally, it doesn’t let me log back in but I think that has to do with the refresh tokens I need to enable.