はじめに
OAuth 2.0 と OpenID Connect をサポートする認可サーバー兼 OpenID プロバイダーの C# 実装である『csharp-oauth-server』、及び、OpenID Connect Core 1.0 の「5.3. UserInfo Endpoint」で定義されているユーザー情報エンドポイントの実装を含むリソースサーバーの C# 実装である『csharp-resource-server』を紹介します。
English version: “OAuth 2.0 and OpenID Connect implementation in C# (Authlete)”
1. アーキテクチャー
csharp-oauth-server と csharp-resource-server は『authlete-csharp』ライブラリ(NuGet パッケージ 『Authlete.Authlete』)を用いて書かれています。authlete-csharp ライブラリは Authlete(オースリート)というクラウドサービスが提供する Web API と通信するためのライブラリです。
Authlete は OAuth 2.0 と OpenID Connect を実装するのに必要な機能を Web API として提供するクラウドサービスです。特長的なのは、Authlete 自身は認可サーバーでも OpenID プロバイダーでもなく、あくまで認可サーバーや OpenID プロバイダーの後ろで動くバックエンドサービスだということです。
このため、OAuth 2.0 と OpenID Connect のほとんどの部分は Authlete 内で実装されてはいるものの、それでもフロントエンドとして csharp-oauth-server のようなものが必要となります。 java-oauth-server や spring-oauth-server もそのようなフロントエンド認可サーバーの実装例です。
Authlete のアーキテクチャーの利点については『OAuth 2.0 / OIDC 実装の新アーキテクチャー』をご参照ください。
2. 前準備
2.1. API キーと API シークレット
csharp-oauth-server と csharp-resource-server を動かすには、Authlete サーバーと通信する際に要求される API キーと API シークレットが必要になります。Authlete のサインアップページでアカウント登録をすると、自動的に API キーと API シークレットの組みが一つ生成されるので、API キーと
API シークレットの取得に必要な作業はアカウント登録作業のみです。詳細な手順は『Spring + OAuth 2.0 + OpenID Connect』の「3.1. Authlete API にアクセスするための API キー」をご参照ください。
2.2. クライアント ID
認可サーバー csharp-oauth-server に対して認可リクエストを投げる際、OAuth 2.0 の仕様(RFC 6749)により、クライアント ID が必要になります。Authlete にアカウント登録をすると、自動的にクライアントアプリケーションが一つ生成されるので、クライアント ID 取得についても、必要な作業はアカウント登録作業のみです。詳細な手順は『Spring + OAuth 2.0 + OpenID Connect』の「3.2. クライアント ID」をご参照ください。
3. 実行
3.1. 認可サーバー起動
$ git clone https://github.com/authlete/csharp-oauth-server
$ cd csharp-oauth-server/AuthorizationServer
$ vi authlete.properties
$ dotnet run
上記の手順により、localhost:5000 で認可サーバーが起動します。
authlete.properties
は csharp-oauth-server
が参照する設定ファイルです。このファイルに「2.1. API キーと API シークレット」で取得した API キーと API シークレットを設定してください。
3.2. リソースサーバー起動
$ git clone https://github.com/authlete/csharp-resource-server
$ cd csharp-resource-server/ResourceServer
$ vi authlete.properties
$ dotnet run
上記の手順により、localhost:5001 でリソースサーバーが起動します。
authlete.properties
は csharp-resource-server
が参照する設定ファイルです。このファイルに「2.1. API キーと API シークレット」で取得した API キーと API シークレットを設定してください。
3.3. 認可リクエスト
csharp-oauth-server は /api/authorization
で「認可エンドポイント」を提供します。インプリシットフロー(response_type=token
)で認可リクエストをこのエンドポイントに投げてみましょう。下記の URL を Web ブラウザのアドレスバーに入力してください。{クライアントID}
の箇所は「2.2. クライアント ID」で取得したクライアント ID で置き換えてください。
http://localhost:5000/api/authorization?response_type=token&client_id={クライアントID}
次のような認可画面が表示されます。
認可画面 |
---|
Login ID フィールドと Password フィールド※に、それぞれ john
, john
と入力し、Authorize ボタンを押してください。
※: Login ID フィールドと Password フィールドが表示されない場合は認可リクエストの URL の末尾に &prompt=login
を追加してください。
すると、Web ブラウザは(クライアントアプリケーションの設定をあなたが変更していなければ) https://api.authlete.com/api/mock/redirection/{サービスAPIキー}
へとリダイレクトされ、認可リクエストの結果が表示されます。
認可リクエストの結果 |
---|
表示されている表の access_token
の隣にある値が、今回の認可リクエストの結果として発行されたアクセストークンです。
3.4. API コール
認可リクエストの結果、アクセストークンが取得できたので、リソースサーバーに対して API コールをしてみましょう。
csharp-resource-server
は /api/time
で簡単な API を提供しています。この API に、まず、アクセストークンをつけないでアクセスしてみます。リソースサーバーが返す HTTP ヘッダーの値を見るため、-v
オプションをつけて curl コマンドを実行します。
$ curl -v http://localhost:5001/api/time
すると、HTTP ステータス 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
RFC 6750(The OAuth 2.0 Authorization Framework: Bearer Token Usage)の仕様により、エラーメッセージはレスポンスボディーではなく、WWW-Authenticate
ヘッダーに書かれていることに注意してください。
次に、「3.3. 認可リクエスト」で取得したアクセストークンをつけて /api/time
API を呼んでみます。なお、/api/time
API は、RFC 6750 で定義されている 3 つの方法を全てサポートしているので、次のいずれの方法でもアクセストークンを受け付けることができます。
RFC 6750, 2.1. Authorization Request Header 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
さて、一番目の方法で API コールを実行すると、結果は次のようになります。
* 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
HTTP ステータス 200 OK
で JSON が返ってきます。
おわりに
認可サーバーや OpenID プロバイダーの実装に利用可能な C# ライブラリはそれほど多くありませんが、authlete-csharp ライブラリ(Authlete.Authlete NuGet パッケージ)の登場により、選択肢が一つ増えました。
authlete-csharp ライブラリは他のライブラリと異なり、バックエンドサービスとして Authlete を利用します。(1)OAuth 2.0 と OpenID Connect
のロジックは Authlete 側で実装され、(2)アクセストークン等の関連データも Authlete 側のデータベースに保存され、(3)サーバーやクライアントの設定も GUI(Service Owner Console & Developer Console)でおこなえるため(他のライブラリのように設定自体をいちいちコーディングしなくてもよい)、フロントエンドの認可サーバーの実装はかなり簡単になります。
例えば、OpenID Connect Discovery 1.0 で定義されている設定エンドポイントの実装(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();
}
}
}
上記の利点は C# に限った話ではありません。フロントサーバーを Java で実装しても(例:java-oauth-server)、同様の利点を得られます。ですので、どのプログラミング言語を使うかに関わらず、認可サーバー・OpenID プロバイダーの実装をおこなう際は Authlete の利用を是非ご検討ください!
注:この記事の筆者は Authlete 社の創業者です。