Web api 2.0: constant 401 after following widget tutorial

Good day, were having a problem, we really want to use okta for our sign in process but we just can’t get it to work

My question is twofold.
Firstly the tutorial asks us to install two prerelease packages each from one external source. There is a problem however and that is that one of them is a dependency on the other so they both end up coming from the same source unless you ignore dependencies
(And when dealing with prerelease versions it would be extremely helpful to know what exact versions were confirmed working. It would also make me feel a lot better about using prerelease versions of anything)
I’m not quite sure if this is our problem but all the versioning jenga we have played has at least not helped us so far.

Secondly If that is not the error we had then mayhaps we are doing something wrong in the config itself.

using System;
using System.Collections.Generic;
using System.Linq;
using System.Collections;
using System.Threading;
using System.Threading.Tasks;
using System.Web.Http;
using System.Net;
using System.Net.NetworkInformation;
using System.Web;
using Microsoft.ApplicationInsights.Extensibility;

using Owin;
using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.OAuth;
using Microsoft.Owin.BuilderProperties;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using Microsoft.Owin.Security.Jwt;

using Raknid;
using HRMonitor.Jobs;
using HRMonitor.Models;
using HRMonitor.Providers;
using System.IdentityModel.Tokens.Jwt;
using System.Security.Claims;
using System.Configuration;

[assembly: OwinStartup(typeof(HRMonitor.Startup))]
namespace HRMonitor
{
	public class Startup
	{
		private static CancellationToken _shutdownEvent;
		public static event Action OnShutdown
		{
			add { _shutdownEvent.Register(value); }
			remove { throw new NotSupportedException("Removing OnShutdown listeners is not allowed"); }
		}

		public void Configuration(IAppBuilder app)
		{
			Debug.Init();

			Debug.Log("Initiation sequence activated...");
			try
			{
				using(Debug.StartTimer("Configuration"))
				{
					using(Debug.StartTimer("Initalization"))
					{

						Models.Address.Init();

						EnumRegistrator.RegisterEnums();

						APISetting.Init();

						if(!HR.RunningOnProductionServer)
							TelemetryConfiguration.Active.DisableTelemetry = true;
					}

					bool shouldSendEmails = APISetting.IsTestOrReleaseBuild;

					using(Debug.StartTimer("Initiating Background Jobs"))
					{
						Execute<MeasurementJob>.Frequently();
						Execute<MaintenanceJob>.OnceEveryWeek();
						if(shouldSendEmails)
						{
							Execute<NotificationJob>.Frequently();
							Execute<ForewarningJob>.Frequently();
							Execute<ProgressReportJob>.Frequently();
						}
					}
					using(Debug.StartTimer("Configuring WebAPI"))
					{
						ConfigureOkta(app);
						HttpConfiguration config = new HttpConfiguration();
						WebApiConfig.Register(config);
						app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
						app.UseWebApi(config);

						var properties = new AppProperties(app.Properties);
						_shutdownEvent = properties.OnAppDisposing;
						OnShutdown += () => Debug.Log("shutting down");

						if(shouldSendEmails)
						{
							/* Email setup webhook */
						}
						config.EnsureInitialized();
					}
				}
			}
			catch(Exception e)
			{
				Debug.LogException(e);
				Environment.Exit(0);
			}
		}

		public void ConfigureOkta(IAppBuilder app)
		{
			using (Debug.StartTimer("Configuring okta"))
			{
				var okta = "https://dev-***.oktapreview.com/oauth2/aus**********";

				var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
					okta + "/.well-known/openid-configuration",
					new OpenIdConnectConfigurationRetriever(),
					new HttpDocumentRetriever());

				app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
				{
					AuthenticationMode = AuthenticationMode.Active,
					AllowedAudiences = Sequence.Of("hrmonitorapi"),
					TokenValidationParameters = new TokenValidationParameters
					{
						ValidAudience = "hrmonitorapi",
						ValidIssuer = okta,
						RoleClaimType = ClaimTypes.Role,
						ClockSkew = TimeSpan.FromMinutes(5),
						IssuerSigningKeyResolver = (token, securityToken, identifier, parameters) =>
						{
							var discoveryDocument = Task.Run(() => configurationManager.GetConfigurationAsync()).GetAwaiter().GetResult();
							return discoveryDocument.SigningKeys;
						}
					}
				});
			}
		}
	}
}

We have tried just about everything we could think of and nothing would work. The token is arriving successfully, everything that should be there inside of the issuersigningkeyresolver. But it just won’t authenticate it.

Any help greatly appreciate it, it would be nice if were just missing something simple.

Which widget tutorial are you following?

https://developer.okta.com/quickstart/#/widget/dotnet/aspnet4

This is what we were following to try and get the server code to work. We tried everything we could think of but trying to find anything on this was a real pain as mostly everyone seems to be using .net core.

We are working on a pretty tight deadline and are getting pretty close to needing to scrap this and use something else. But if it would just work okta is absolutely everything we need so that would kind of suck. :frowning:

@nate.barbettini can you take a look at this?

Something that might help is sharing the widget code so we can see what is going on.

Thanks!

Hey @Andri, sorry for the delay. If you’re still fighting this issue, I can help.

Can you use Fiddler or your browser’s network panel to capture the request that’s going to your Web API service? I want to double-check that the token is as expected.

Do you have access to any of the IIS logs for the Web API application? The middleware validation errors should be visible there.

We have this exact same problem.

Here’s a the request headers from Fiddler regarding the failing request into a locally hosted (IIS Express) .NET application:

GET /api//yritykset/1/projektit/1061 HTTP/1.1
Host: localhost:50000
Connection: keep-alive
Pragma: no-cache
Accept: application/json;odata=verbose
Authorization: Bearer eyJraWQiOiJWMU9NMXRXcmNYWklLZWJ1RXJGWi03Q1RpYjd5T20yTlJyS1pvMmZyaHRFIiwiYWxnIjoiUlMyNTYifQ.eyJ2ZXIiOjEsImp0aSI6IkFULjB0WkNMX0Fwb1VLNDBvNGEtaGxFSC14NVZwMVJuaGl3Tk1CbEdseXBRZUUiLCJpc3MiOiJodHRwczovL25vdmFzZXJ2by5va3RhLmNvbSIsImF1ZCI6Imh0dHBzOi8vbm92YXNlcnZvLm9rdGEuY29tIiwic3ViIjoidHV1a2thLmhhYXBhbmllbWlAbm92YXNlcnZvLmZpIiwiaWF0IjoxNTExMjY2OTg1LCJleHAiOjE1MTEyNzA1ODUsImNpZCI6IjBvYTlpbXpqaFo2WlRPZjlPMnA2IiwidWlkIjoiMDB1OTBma2hnU3J5R1RGd3YycDYiLCJzY3AiOlsiZW1haWwiLCJwcm9maWxlIiwiYWRkcmVzcyIsIm9wZW5pZCIsInBob25lIl19.Q-1b-7_MFS4gXnCf1GqMOhIfD4UAS4V8YK56sMgssmnTYIzN9spM7nnl7dtPJHj2WAxgOkaPP2euQ5vJpbQoyRGxM0GCdbRhHhRjSCjiQVBmCvSgbA8I2E5nPOBXhWcJwZ406YEagym8-Sw5tRqqJIVp_E3nCViQgnYAyoBoh7Hxt7tQu3fy8F1vFG5KYxLjEqKpHslBCrPO1Le6nHOCt0uwuGgQ8Xl3aKB2PgY-k3AB_5beXIQ_5YW-IdsSHAIhrLgrU3RsPnqhctiuOKXq57HhJM6wc0f4rYyduSY-nh276-ZeI2sxofI4SZ0lEg94WZ29jBjHQLAKIsS2ODjHeQ
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36
Expires: 0
Referer: http://localhost:50000/forecast/
Accept-Encoding: gzip, deflate, br
Accept-Language: en-US,en;q=0.8,fi-FI;q=0.6,fi;q=0.4,de;q=0.2
Cookie: gsScrollPos-1299=0; gsScrollPos-4014=0; gsScrollPos-4018=0; gsScrollPos-4041=0; gsScrollPos-4002=0; gsScrollPos-823=0; gsScrollPos-1343=; okta-oauth-nonce=zKJZWD79cGDChr4trSs9njYJ5fzxzR5YbphL7AXAJAklLeMlAfCXAJyl7MjMKQKV; okta-oauth-state=Nqnu8gBTnDLHRdE3fJRZH0LAzIsahbpiYfdt04NSErec6HWYAIas2nZbRti4wH3k; BasicAuthentication=1

I have also set up a .NET Core app with Angular 2 and that seems to be working fine with the same Okta application, though that is using the method of redirecting to an Okta login page.

Any pointers?

@tuukka.haapaniemi It’s failing because the token is not being requested by the client correctly. I can’t see your client code, but I’m guessing the issuer is not set correctly. It should look like:

https://{yourOktaDomain}.com/oauth2/default
(with {yourOktaDomain} replaced of course)

The default part is important. That makes sure the OpenID Connect flow goes through the custom Authorization Server called default (you can see this in your Okta dev console, under Authorization Servers). If you leave this part off, the token that’s generated will not validate correctly in your .NET code.

1 Like

@nate.barbettini Thanks for the reply! Your response is spot on. I actually managed to solve this with the help of our local partner. The problem was indeed a wrong issuer in the first token request. Apologies for forgetting to reply back here with the solution.

Funny how Okta support could not spot this mistake, when I showed them all my code… They kept on insisting it was a problem with my redirect url. But good to know these forums contain knowledgeable people!

Glad you got it working! Sorry for the slow reply here. We check the forum as often as we can. :slight_smile:

Let us know if you have any other problems!

Thank you man! Your answer saved me a couple of days!