Authorization server configuration

I have what I believe to be a pretty typical architecture: It’s a React Application frontend with a .NET Core 3.1 backend with APIs. I’ve watched a bunch of videos and done quite a bit of reading but everyone seems to gloss over the configuration of the application on the Okta server. My question is this: When I create a new application on the Okta server and choose OIDC, I believe I have to make it a WebAPI/.NET type that has a Client ID and a Client Secret. I don’t think it will work as an SPA because of my backend needing to do authorization. But in some instances, I’ve seen people create 2 applications on the Okta server: 1 is an SPA type for the frontend and another is the WebAPI one for the backend with the APIs. How should this be configured? Also, when the .NET Core 3.1 backend does the authorization, does it actually hit the Okta authorization server or does it do it locally using supplied keys and so I’d have to provide those Client ID and Client Secret keys in the backend?

It looks like there are many nice questions here; I will answer them one by one based on what I’ve understood. I’ve numbered them down to make them easier to read, and I’ve also formed them into questions to make sure I answer them correctly.

  1. When creating a new application on the Okta server and choosing OIDC, is it necessary to make it a WebAPI/.NET type with a client ID and a client secret?*

Yes, because you are doing client authentication for a trusted app, meaning instead of the user giving out his credentials to authenticate as we usually do in front-end applications (SPA), you are validating an application itself with Okta; hence, you need the Web App for NET Core in Okta, which gives it a client ID and a client secret (sort of like a admin username and password for your Net Core Application). To ensure the application is trusted to access your resources.

  1. Can the application on the Okta server work as an SPA, or does it need to be a WebAPI type considering the backend’s authorization requirements?

The WebApp type is usually used for trusted applications (server-based backend applications) where you are able to secretly store the credentials. (If someone can view your credentials by using Chrome dev tools or as part of the request, that’s not a web app; it should be an environment configuration and behind a server that you only have access to as an admin.) So yes, for the. NET Core app again from point 1.

    • Is it common to create two applications on the Okta server - one as an SPA type for the frontend and another as the WebAPI type for the backend with APIs?

Yes, depending on your architecture, you can have two different applications, one for a single page and one for a web app (backend). However, this is an architecture decision; you may have a single app flow with public endpoints serving data that would require only one of the applications. (Your call basically changes from use case to use case.) The general rule of thumb is to see if your resource (info / data) needs protection; if answer is yes, you would need a specific app type to help add that protection.

  1. How should the configuration be set up when dealing with both frontend (React Application) and backend (.NET Core 3.1) in terms of Okta applications?

You can do this much faster with running the okta samples and following okta guides-

You need two distinct samples, each serving different authentication purposes for various applications. While the forms of authentication differ, the overall flow remains same.

Examples below.

Here is the React Sample : Add user authentication to your React app | Okta Developer
Here is the Backend Server Sample : Add user authentication and Okta Resource Management to your ASP.NET Core app | Okta Developer

  1. When a user accesses your React application (SPA app), users authenticate through Okta, and Okta issues an ID token.
  2. Your React application (SPA) sends this ID token to the .NET Core backend(Web App) whenever it makes a request.
    
  3. In the backend(Web App), you validate the ID token with Okta. This ensures that the user is authenticated.
    

However, the above flow is entirely optional; you can do all of this without using the React library, and based on the change in architecture, you would need to change the implementation.

I hope this helps you get some clarity on the flow. It’s best to tackle this in two steps: creating two different applications and making them work is cognitive overload on aspects of adding authentication and also handling library-related code. The best process is to go one step at a time and move from there.

5 Likes

Thank you for the very thorough reply. It has left me with a few more questions, perhaps because I wasn’t specific enough and just want to be 100% sure that I’m understanding this correctly.

I’ll respond primarily to #4 above: The links you sent are very familiar to me. I have them bookmarked and have read them thoroughly in the past but you’re right, there’s a lot of information there.

To be more specific about what I’ve already implemented:
1 - On the Okta server I have created an OIDC/SPA application that has a Client ID
2 - My frontend, React application that runs in a browser uses the Okta Embedded Widget for username and password entry. So I use the Client ID and the user enters a username and password and I pass that to the Okta server for authentication.
3 - The Okta server successfully returns an id_token and an access_token to me.

So on the frontend, I believe I am using the correct flow.

For the backend, I still need a few clarifications.

4 - I used this to validate the ID Token that I pass to the backend:

(I used a similar article to do it with the accessToken, but as noted below, I’m not sure which to use).

I have successfully implemented that code and can get validation of the idToken by passing it to one of the API endpoints as a parameter and using the code from the link above.

My backend .NET Core service has several API/endpoints that the user can hit. My questions are as follows:

5 - Do I need to pass the idToken to each API as an extra parameter and validate it every time the user hits and endpoint? The answer to this from your response above (section 4.2) appears to be yes. I have to update each of my APIs to accept an idToken as a parameter and validate that before returning a response with data.

6 - Why am I using the idToken to do #5 above? My understanding from what I’ve read is that the idToken doesn’t expire like the accessToken does, so the accessToken should be passed to the backend for authorization and that it would timeout and refreshTokens would need to be used. In fact, in this article, protecting the API endpoints shows the use of accessTokens:

And that would imply, I believe, that my Okta configuration would need not to be an SPA but a .NET WebAPI type.

Can you please clarify these points for me? As I said, I believe I have he basic mechanics of it working but I’m not sure I’m doing it correctly.

Thanks.

Hi there @pmd,

Clarifying on your posts because authentication and authorization can be pretty nuanced, influencing best practices and recommendations.

Is your application a React front-end that calls .NET Core API backend APIs where authentication doesn’t involve the .NET APIs? Or do you have an ASP.NET Core web app with React frontend? The key to identifying which application type to use and whether or not you have a client secret is figuring out whether your .NET backend handles authentication for you or if your React app handles authentication. It appears that your React app handles authentication based on reading that you use the embedded sign-in widget, but I want to make doubly sure.

After we ascertain which Okta application type you’ll need, we can provide info. But in a nutshell, if your application is a SPA, the React app will only send the access token to the APIs. The APIs verify the access token before responding to the request. We can get into authorization fun in more detail, but let’s ensure you have the correct application type set up. :slight_smile:

Here are some example blog posts that might clarify things based on your application type. One is a React app calling an independent Fastify API, one is a SPA hosted within .NET Core application. Unfortunately, I couldn’t find a React MVC, but I did find a Blazor app that should demonstrate the example. And I added a post that details handling tokens in SPAs if we determine that it matches your set up.

Which of these app types best match your set up?

2 Likes

Thanks for the response. I’d like to respond quickly before I go read those articles because I believe the answer is: it’s complicated.

We have 2 versions of our product that I need to talk about:

Today we have a shipping product that has a React frontend that accepts a username and password and passes them back to an ASP.NET Core Web App that handles the authentication using Identity services. Once the user is logged in, the backend gives full access to all of the APIs.

My end goal in this project is to add a single sign on implementation using Okta. To do that, I added the embedded sign on widget to our react frontend and am now using Okta to handle authentication. As I mentioned, I believe that is working for me because I see that I am handed back an id_token and an access_token as expected.

As part of this update, I believe what I also need to do is pass that access token back to my ASP.NET Core Web App via API calls (there are about a dozen different APIs that my React application can call) and use that to restrict access to each endpoint API. But I’m not sure. The validation part in the backend (although I was able to get it to succeed using sample code) eludes me a bit in part because I don’t know what parameters to validate against and whether it goes out to the Okta server for help with validation or whether it handles it all locally based on the known client id, audience and other items.

The complication is this: Because this is a shipping product, I can’t abandon how we already handle user authentication in the backend because it provides other functionality for us including storing dynamic data for the user like progress, user preferences and so on. Further, not all of our customers will have SSO capabilities, so we need to maintain the existing backend authentication for those existing customers in cases where an access_token is not passed to the backend. In those cases we’d use the old username/password method with Identity services.

My intention was to make the new version of the React App use Okta to handle authentication then pass an access_token to the backend login endpoint, which would then do a “soft” login. By that I mean I would make it not care whether it has a password as long as it has a valid access_token, because then I would know that the user had been authenticated by Okta. I don’t know if this is a realistic scenario, of course. Because of that behavior, I believe I would probably have to additionally secure all of the APIs because they’re all endpoints, although I’m not certain.

The final bit that I will be adding (and this goes beyond our discussion a bit but I’m still trying to figure out) is that I want to make the Okta server handle users such that they have role-based permissions in the backend. I believe I can acquire role information from either the access_token or the id_token.

I may be overcomplicating this. It’s possible that all I need to do is authenticate using Okta from the React frontend, pass the access_token (or id_token?) to the backend login API and validate it. Once it’s validated, register the user as we did previously and behave as we always have with complete access to the APIs.

Gocha. Thank you for your detailed clarification.

Your app appears to be a web application, not a true SPA from an Okta application standpoint. I completely understand being unable to change behavior as this is a production application.

As for the backend APIs making subsequent calls, I agree it is complicated because it sounds like you may need to call other applications with user context? Is that correct? I suspect what you wrote in the last paragraph for other application access is probably right, but it might depend on how you currently register users in other applications.

For calling your own APIs, you’d pass in the access token only (the id token should not be sent) for validation and authorization context, including for RBAC. Since I’m not very familiar with the Okta product line that handles this, let me see if I can get someone more knowledgeable about this to respond. :sweat_smile:

@krishna please feel free to jump in if you think I’m misunderstanding something.

1 Like

My backend doesn’t make any subsequent calls to any other locations. The backend’s only job is to handle API calls from the frontend and respond with data from the database or other areas. There’s no interaction with any other applications. It does, however, need to continue to maintain users in the database as we are today. I want to allow the authentication of the users to happen using SSO via Okta instead of the way we’re doing it today with username/password.

Just to reiterate, I control both parts of this application, the frontend, which is a React based application and the backend, which is a .NET Core Web App.

The authentication happens in the frontend/React App. It passes tokens to the backend Web App.

I believe the token that I should be passing is the access token. My code below that I got primarily from one of the Okta Dev pages is really the root of my confusion. I’m not sure what parameters to validate or where the clientID and possibly clientSecret come into play if at all. That will depend upon how the application is configured in the Okta server, I think.

... MyGetOIDCWellknownConfiguration(string issuer, string clientId)
{
	// I don't know how to configure this - it appears to be different for id tokens and access tokens
	var wellKnownEndpoint = $"{issuer}/.well-known/oauth-authorization-server";
	// var wellKnownEndpoint = $"{issuer}/.well-known/openid-configuration?client_id={clientId}";

	var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration> 
		(wellKnownEndpoint, new OpenIdConnectConfigurationRetriever());
            
	return await configurationManager.GetConfigurationAsync();
}
		
... MyValidateToken()
{
	// I don't know how to configure these either - again, it seems to depend upon which token
	// I'm trying to validate
	var issuer = "https://{myOktaDomain}/oauth2/default";
	var audience = "api://default";
			
	var tokenValidator = new JwtSecurityTokenHandler();

	var validationParameters = new TokenValidationParameters
	{
		RequireExpirationTime = true,
		RequireSignedTokens = true,
		ValidateIssuer = true,
		ValidIssuer = $"{issuer}",
		ValidateIssuerSigningKey = true,
		IssuerSigningKeys = oidcWellknownEndpoints.SigningKeys,
		ValidateLifetime = true,
		ClockSkew = TimeSpan.FromMinutes(2),
		ValidateAudience = true,
		ValidAudience = $"{audience}"
	};

	try
	{
		...

		ClaimsPrincipal principal = tokenValidator.ValidateToken(accessToken, 
			validationParameters, out SecurityToken securityToken);

		var validatedToken = (JwtSecurityToken) securityToken;

		...

		return responseValidation;
	}
	catch (Exception ex)
	{
		...
	}
}