OAuth 2.0 and OpenID Connect implementation in C# (Authlete)

This article is an introduction to csharp-oauth-server and csharp-resource-server. They are:

csharp-oauth-server : an authorization server and OpenID provider implementation written in C# that supports OAuth 2.0 and OpenID Connect.

csharp-resource-server : a resource server implementation written in C# that includes an implementation of UserInfo Endpoint whose specification is defined in OpenID Connect Core 1.0.

1. Architecture

Both csharp-oauth-server and csharp-resource-server are written with authlete-csharp library (NuGet package “Authlete.Authlete”). authlete-csharp is a library to communicate with Web APIs provided by Authlete.

Authlete is a cloud service that provides Web APIs necessary to implement OAuth 2.0 and OpenID Connect. A distinctive point is that Authlete itself is neither an authorization server nor an OpenID provider but it is a backend service which works behind authorization servers and OpenID providers.

Therefore, although the logic of OAuth 2.0 and OpenID Connect is implemented inside Authlete, a frontend server like csharp-oauth-server is still needed. java-oauth-server and spring-oauth-server are examples of frontend authorization servers, too.

Please read New Architecture of OAuth 2.0 and OpenID Connect Implementation for details about advantages of Authlete’s architecture.

2. Preparation

2.1. API key and API secret

To run csharp-oauth-server and csharp-resource-server, a pair of API key and API secret is required to communicate with the Authlete server. Account registration at Authlete’s signup page will automatically generate one pair of API key and API secret, so necessary steps to get API credentials are just to register an account. See “3.1. API key and API secret to use Authlete APIs” in Spring + OAuth 2.0 + OpenID Connect for details.

2.2. Client ID

According to the specification of OAuth 2.0 (RFC 6749), a client ID is required when you make an authorization request to an authorization server. As one client application is automatically registered on account registration, necessary steps to get a client ID are just to register an account, too. See “3.2. Client ID” in Spring + OAuth 2.0 + OpenID Connect for details.

3. Run

3.1. Start Authorization Server

$ git clone https://github.com/authlete/csharp-oauth-server
$ cd csharp-oauth-server/AuthorizationServer
$ vi authlete.properties
$ dotnet run

The steps above will launch the authorization server (csharp-oauth-server) at localhost:5000.

authlete.properties is the configuration file csharp-oauth-server refers to. Open the file with a text editor and set the API credentials you obtained in “2.1. API key and API secret”.

3.2. Start Resource Server

$ git clone https://github.com/authlete/csharp-resource-server
$ cd csharp-resource-server/ResourceServer
$ vi authlete.properties
$ dotnet run

The steps above will launch the resource server (csharp-resource-server) at localhost:5001.

authlete.properties is the configuration file csharp-resource-server refers to. Open the file with a text editor and set the API credentials you obtained in “2.1. API key and API secret”.

3.3. Authorization Request

csharp-oauth-server exposes an authorization endpoint at /api/authorization. Let’s make an authorization request to the endpoint using Implicit Flow (response_type=token).

Input the following URL into the address bar of your web browser. Don’t forget to replace {Client-ID} with an actual client ID you obtained in “2.2. Client ID”.

http://localhost:5000/api/authorization?response_type=token&client_id={Client-ID}

You will see an authorization page like below.

Authorization Page

Input john and john into the “Login ID” field and “Password” field. If the fields are not shown (this happens if you have already logged in) but you want to see them again, append &prompt=login at the end of the URL of the authorization request.

The web browser will be redirected to https://api.authlete.com/api/mock/redirection/{Service-API-Key} (if you have not changed the configuration of the client application) and the result of the authorization request will be displayed.

Result of Authorization Request

The value next to access_token in the displayed table is the value of the access token issued by the authorization request.

3.4. API Call

Now you have an access token, so let’s make an API call to the resource server.

csharp-resource-server exposes a simple API at /api/time. First, let’s make an API call without an access token. Give -v option to curl in order to see HTTP headers returned from the resource server.

$ curl -v http://localhost:5001/api/time

The authorization will return 400 Bad Request.

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5001 (#0)
> GET /api/time HTTP/1.1
> Host: localhost:5001
> User-Agent: curl/7.54.0
> Accept: */*
>
< HTTP/1.1 400 Bad Request
< Date: Mon, 08 Jan 2018 13:08:24 GMT
< Server: Kestrel
< Content-Length: 0
< Cache-Control: no-store
< Pragma: no-cache
< WWW-Authenticate: Bearer error="invalid_token",error_description="[A057301] The request does not contain a valid access token.",error_uri="https://www.authlete.com/documents/apis/result_codes#A057301"
<
* Connection #0 to host localhost left intact

Note that the error message is written not in the response body but in the WWW-Authenticate header. This is a requirement by RFC 6750 (The OAuth 2.0 Authorization Framework: Bearer Token Usage).

Next, let’s make an API call to /api/time with the access token you obtained in “3.3. Authorization Request”. As /api/time supports all the three ways defined in RFC 6750, you can pass the access token to the API using any one of the means shown below.

RFC 6750, 2.1. Authorization Header Request Field

$ curl -v http://localhost:5001/api/time \
-H 'Authorization: Bearer pZ-NP9xuMOTf5Q_jafvXaWfq3czJp_-EHgDtF-ru90g'

RFC 6750, 2.2. Form-Encoded Body Parameter

$ curl -v http://localhost:5001/api/time \
-d access_token=pZ-NP9xuMOTf5Q_jafvXaWfq3czJp_-EHgDtF-ru90g

RFC 6750, 2.3. URI Query Parameter

curl -v http://localhost:5001/api/time\?access_token=pZ-NP9xuMOTf5Q_jafvXaWfq3czJp_-EHgDtF-ru90g

If you make an API call using the first way, you will get a result like below.

*   Trying ::1...
* TCP_NODELAY set
* Connected to localhost (::1) port 5001 (#0)
> GET /api/time HTTP/1.1
> Host: localhost:5001
> User-Agent: curl/7.54.0
> Accept: */*
> Authorization: Bearer pZ-NP9xuMOTf5Q_jafvXaWfq3czJp_-EHgDtF-ru90g
>
< HTTP/1.1 200 OK
< Date: Mon, 08 Jan 2018 13:27:56 GMT
< Content-Type: application/json; charset=utf-8
< Server: Kestrel
< Content-Length: 151
< Cache-Control: no-store
< Pragma: no-cache
<
{
"year": 2018,
"month": 1,
"day": 8,
"hour": 13,
"minute": 27,
"second": 56,
"millisecond": 341
}
* Connection #0 to host localhost left intact

You got a response of 200 OK with JSON.

Summary

There are not so many choices of C# libraries for OAuth 2.0 and OpenID Connect, but one choice has been added by authlete-csharp library (Authlete.Authlete NuGet package).

Being different from other libraries, authlete-csharp library uses Authlete as a backend service. Thanks to Authlete, implementations of frontend authorization servers can be fairly simple because:

  1. The logic of OAuth 2.0 and OpenID Connect is implemented inside Authlete.
  2. Authorization data such as access tokens are stored in Authlete’s database.
  3. Configuration of servers and client applications can be changed through GUI (Service Owner Console & Developer Console). (You don’t have to do programming for configuration of servers and client applications as you would be required by other libraries.)

For example, in csharp-oauth-server, the configuration endpoint defined in OpenID Connect Discovery 1.0 is implemented as simply as shown below (ConfigurationController.cs).

using System.Net.Http;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Authlete.Api;
using Authlete.Handler;

namespace AuthorizationServer.Controllers
{
[Route(".well-known/openid-configuration")]
public class ConfigurationController : BaseController
{
public ConfigurationController(IAuthleteApi api) : base(api)
{
}

[HttpGet]
public async Task<HttpResponseMessage> Get()
{
// Call Authlete's /api/service/configuration API.
return await
new ConfigurationRequestHandler(API).Handle();
}
}
}

The advantages described above are not limited to C#. For example, even if you implement a frontend authorization server in Java (e.g. java-oauth-server), you can benefit from the same advantages. So, regardless of what programming language you are planning to use, please consider Authlete when you implement an authorization and OpenID provider!

(The author of this article is a co-founder of Authlete, Inc.)