This Blog’s API Requirements
Although the technology has changed, our API Goals have not. Previously we carefully discussed API Design Aspects and met these requirements:
- Externalize Token Validation from our API code
- Cope automatically with Token Signing Key renewal
- Collect User Data Points from the Token and Central + Product Data
- Use Claims Caching of the above data points to make our API efficient
- Solid Error Handling for our OAuth 2.0 Secured API
- A Productive Developer Setup
Obviously your company may prefer a different solution, but I hope the points I’ve raised make you aware of some important considerations.
Recall that the first time an access token is received by our API it will be processed according to the following pattern to collect claims:
Claims will then be cached until the token’s expiry time so that the claims handling for all subsequent API calls is a fast cache lookup:
Third Party Security Library
We will use the Identity Model OAuth 2.o Introspection Library, which will perform the validation and claims management for us.
Note that when using C# we need to ensure that the caching is thread safe, so we are using a tested library rather than writing this code ourselves.
Downloading the Code Sample
Download code from GitHub via this command. The sample uses SSL so that we can also cover any .Net Core specific SSL issues:
- git clone https://github.com/garyarcher36/authguidance.websample.netcoreapi
First you need to download and install the .Net Core SDK which can be installed on Windows, Mac OS or Linux:
To get fully set up I would recommend first following the Initial SPA Code Sample Setup:
- Ignore step 9 since our API no longer uses Node JS
- Register https:// URLs for our SPA and API rather than http:// URLs
Next you will need to configure SSL Browser Trust for our Company Root SSL Certificate.
View Code in an IDE
When you open a C# file in Visual Studio Code it will prompt you to install the C# extension, which provides editor features such as Intellisense:
Update API Configuration
Our API configuration now looks like this, and you will need to update the 3 OAuth fields to match your Okta setup.
Build the API
As for our earlier samples, we will run the API from the command line. Instead of npm commands we will now use dotnet commands:
- The API was created with ‘dotnet new webapi‘
- We use ‘dotnet build’ to get dependencies and build our code
I’m impressed with the leanness of the new Microsoft tooling. In particular the default project templates and CSPROJ files reference only a handful of dependencies:
Run the API
We can then run the API as an administrator user via one of these commands, and it will listen on the standard firewall friendly SSL port:
- On Windows use dotnet run as an administrative user
- On Mac OS or Linux use sudo dotnet run
In production you would run your API as a low privilege user, and there are various ways to enable this. Our objective though is just to enable real world HTTPS traffic on a developer PC.
Testing our API
We can test our API in isolation by typing a data URL into the browser without a token, after which we get the expected 401 response.
A better method of course is to run our SPA, which behaves identically to previous samples and calls the API after login:
Our .Net Core API implements equivalent logging to our earlier Node JS API. Introspection and data lookup only occurs when a token is first received. On subsequent API calls our API claims are retrieved from a memory cache:
Viewing API Requests to Okta
To view SSL requests from our API to Okta we now need to update our API configuration to indicate useProxy = true:
I can now run a tool such as Charles to view the introspection requests during development:
If you run into problems where the proxy is not capturing requests, see our earlier Proxy Configuration section.
Handling Introspection Errors
Finally, let’s intentionally cause an API error, as for our Node JS application, by setting invalid Introspection Client Details in our API configuration:
This results in an error when the Identity Model Library calls Okta:
Our API logs full error details, and creates an Error ID of 38595. In a real API this information would be persisted to a database or log files.
Our API returns a 500 response to the UI and a JSON error object so that the UI can handle the error in a controlled manner.
The UI then renders the error object, which enables Technical Support Staff to quickly look up details for ID=38595 from the API’s logs:
Where Are We?
We have a working code sample that demonstrates our Preferred API Architecture and it works equivalently to our Node JS version.
We are using a good security library that does the heavy lifting for us, and we’ve been able to easily implement debugging, error handling and logging.