ようへいの日々精進XP

よかろうもん

Auth0 QuickStarts の Ruby API Authorization で学ぶ認証基盤の入り口の入り口

Auth0 とは

Auth0

  • Auth0 とは認証, 認可機能を SaaS (IDaaS) で提供している会社 (サービス)で, Node.js ベースの FaaS にて運用されている
  • アプリケーションに SaaS で認証基盤を提供し, SDK を利用して新規登録やログイン等の API を呼び出すことが出来る

ざっくり言うと, Auth0 は FaaS で認証, 認可を SaaS で提供するサービスである.

機能

Auth0 は以下のような機能を提供している.

  • OAuth 2.0/OpenID Connect, SAMLActive Directory/LDAP に対応
  • Identity 管理やソーシャルログイン等の機能を提供
  • RESTful API (Authentication, Management API) が提供されており, SDK やサンプルが GitHub にて公開されている
  • 既存のアプリケーションに簡単に統合出来る

等など. 当然, FaaS にて運用されている為, 高いスケーラビリティと共に信頼性も高いと思われる.

OAuth2.0 や Open ID Connect とは

それぞれをざっくりと...

Auth0 をイジる前に, OAuth2.0 や Open ID Connect についてざっくりと

OAuth 2.0 というか, OAuth について

OAuth は以下のような特徴を持つ認可情報委譲の為の仕様 (第1回 OAuthとは?―OAuthの概念とOAuthでできること:ゼロから学ぶOAuth|gihyo.jp … 技術評論社 より引用.) である.

  • あらかじめ信頼関係を構築したサービス間で
  • ユーザの同意のもとに
  • セキュアにユーザの権限を受け渡しする

登場人物は以下の通り.

  • OAuth Service Providers (ユーザーの認可情報を Consumers に渡すサービス)
  • OAuth Consumers (ユーザーに代わって, Service Provider から認可情報を受け取って, 情報にアクセスするサービス)
  • Users (Service Provider が Consumer に認可情報を渡すことを許可したり, 無効にしたりする)

これ以上, 詳しく書こうとすると, 1000 本くらい記事が書けそうので, OAuth とは...

のような機能を提供する為, 裏側で暗躍している認可委譲機能の仕様という理解で進めたいと思う...すいません.

Open ID Connect

  • OAuth 2.0 を拡張したプロトコル
  • 認証の技術である Open ID と認可の技術である OAuth 2.0 に属性取得機能をガッチャンコしたもの

登場人物 (用語) は以下の通り. (第一回 認証基盤のこれからを支えるOpenID Connect | オブジェクトの広場 より引用.)

用語 説明
OpenID Provider (OP) ユーザー認証とユーザー認証時に Relying Party から要求されたアイデンティティ情報を供給することができるサーバー
Relying Party (RP) OP に ID Token とアイデンティティ情報を要求するサーバーで SSO 対象のアプリケーションを指す
ID Token 認証と認可の情報を含む JWT (JSON Web Token) 形式のトーク
Access Token UserInfo エンドポイントにアクセスするためのトーク
UserInfo Access Token を提示するクライアントに対して, アイデンティティ情報を提供する

JWT

OpenID Connect は OAuth 2.0 のフローにアイデンティティ情報のやり取りを行うレイヤーを追加したものであり, このやり取りは OpenID Provider と Relying Party で行われるが, ここでは JWT (JSON Web Token) という ID Token を用いてやり取りする.

この JWT (ID Token) は以下のような仕様 (RFC 7519 - JSON Web Token (JWT)) で定義されている. (第一回 認証基盤のこれからを支えるOpenID Connect | オブジェクトの広場 より引用.)

項目 値の例 説明
iss http://op.example.domain/URI レスポンスの発行者の識別子(URL)
sub sampleuser ログインユーザー名
aud client123456 ID Token の要求元(Relying Party)であるウェブアプリケーションやネイティブアプリケーションの識別子
exp 1436784324 トークンが無効になる時刻 (UTC で計測される 1970-01-01T0:0:0Z から秒数)
iat 1436784264 トークン作成時刻 (UTC で計測される 1970-01-01T0:0:0Z から秒数)
nonce 18b88a6ab3…d58a0 認可エンドポイントの1回だけ使われるパラメータ

JWT は, URL Safe(URLに含めることの出来ない文字列を含まない)で, デジタル署名で保護されたコンテンツを表現することが可能な JSON 型のトークンとなります.

Ruby Sample を動かす

まだまだ

OAuth 2.0 や OpenID Connect については勉強不足だけど, ひとまず, Auth0 の Ruby Sample を動かしてみる.

Quick Start

各言語のサンプルは Github で公開されていて, 簡単に試すことが出来る

auth0.com

今回は Ruby Sample を試してみたいと思う.

github.com

Ruby Sample (1) ~ このサンプルは ~

This sample demonstrates how to protect endpoints in an Ruby API by verifying an incoming JWT access token signed by Auth0. The token must be signed with the RS256 algorithm and must be verified against your Auth0 JSON Web Key Set.

README より引用. このサンプルは...ざっくり意訳すると, 以下のような感じ.

  • Auth0 によって署名された JWT アクセストークンを検証することによって, Ruby API のエンドポイントを保護している
  • トークンは RS256 アルゴリズムで署名する必要があり, Auth0 JSON Web Key Set に対して検証する必要がある

引続き, 以下のドキュメントを元に進めていく予定.

auth0.com

auth0.com

Ruby Sample (2) ~ Auth0 での API 作成 ~

f:id:inokara:20180326000627p:plain

上図のように, Name と Identifier (audience) 及び, Signing Algorithm を RS256 を指定して API を作成しておく.

Ruby Sample (3) ~ .env ファイルの編集 ~

以下のようにサンプルコードを clone してくる.

git clone https://github.com/auth0-community/auth0-ruby-api-samples.git
cd auth0-ruby-api-samples/01-Authorization-RS256/

サンプルコードの中から .env ファイルを以下のように作成する.

AUTH0_DOMAIN=xxxx.auth0.com
AUTH0_API_AUDIENCE=https://ruby.sample.com

AUTH0_DOMAIN は Auth0 のアカウントを作成した際のアカウント名 (xxxx) + auth0.com を指定し, AUTH0_API_AUDIENCE には, 前のステップで Auth0 API を作成した際に指定した Identifier を指定する.

Ruby Sample (4) ~ docker run ~

Dockerfile とアプリケーションコードの一部を以下のように, ちょっとだけ弄ってみた.

$ git diff Dockerfile
diff --git a/01-Authorization-RS256/Dockerfile b/01-Authorization-RS256/Dockerfile
index 6580218..e987ed0 100644
--- a/01-Authorization-RS256/Dockerfile
+++ b/01-Authorization-RS256/Dockerfile
@@ -7,7 +7,3 @@ WORKDIR /myapp
 ADD Gemfile /myapp/Gemfile
 ADD Gemfile.lock /myapp/Gemfile.lock
 RUN gem install bundler && bundle install --jobs 20 --retry 5
-ADD . /myapp
-
-CMD ["ruby", "/myapp/lib/server_rs256.rb"]
-EXPOSE 3010

$ git diff lib/server_rs256.rb
diff --git a/01-Authorization-RS256/lib/server_rs256.rb b/01-Authorization-RS256/lib/server_rs256.rb
index 6c96000..40a2e12 100644
--- a/01-Authorization-RS256/lib/server_rs256.rb
+++ b/01-Authorization-RS256/lib/server_rs256.rb
@@ -5,6 +5,7 @@ require 'sinatra/json'
 require 'jwt'
 require_relative 'jwt/json_web_token'
 require 'dotenv'
+require 'sinatra/reloader'

 Dotenv.load

コードを弄りながら挙動を確認したかったので, Dockerfile から CMD や EXPOSE を外しておいて, sinatra/reloader を require した上で,以下のように docker run してコンテナを起動する.

$ docker build -t auth0-ruby-sample .
$ docker run --name=auth0-ruby-sample \
           --env-file .env \
           -v $(pwd):/myapp \
           -p 3010:3010 -i -t -d \
           auth0-ruby-sample \
           ruby /myapp/lib/server_rs256.rb

起動後, 試しに以下のようにアクセスしてみる.

$ curl --request GET --url http://localhost:3010/api/public -w '\n' -i
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 91
X-Content-Type-Options: nosniff
Server: WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21)
Date: Sun, 25 Mar 2018 14:32:44 GMT
Connection: Keep-Alive

{"message":"Hello from a public endpoint! You don't need to be authenticated to see this."}

$ curl --request GET --url http://localhost:3010/api/private -w '\n' -i
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Content-Length: 59
X-Content-Type-Options: nosniff
Server: WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21)
Date: Sun, 25 Mar 2018 14:33:11 GMT
Connection: Keep-Alive

{"error":"JWT::DecodeError","message":"Nil JSON web token"}

上記のように private エンドポイントへのアクセスには JWT が必要となる.

Ruby Sample (5) ~ アクセストークンの取得 ~

以下のように curl を利用してアクセストークンを取得する.

curl --request POST \
  --url 'https://xxxx.auth0.com/oauth/token' \
  --header 'content-type: application/json' \
  --data '{"grant_type":"client_credentials","client_id": "xxxxxxxxxxxx","client_secret": "YOUR_CLIENT_SECRET","audience": "https://ruby.sample.com"}'

実行すると, 以下のようなレスポンスが返ってくる.

{
  "access_token": "xxxxxxxxxxxx.yyyyyyyyyyyyyyyyy.zzzzzzzzzzzzzzz",
  "expires_in": 86400,
  "token_type": "Bearer"
}

access_token はだいぶん端折っているが, . で区切られた 3 つのパートで構成されている点に注目する. この access_token を利用して, 再度, アプリケーションにアクセスしてみる.

Ruby Sample (6) ~ アクセストークンを利用して private エンドポイントにアクセス ~

以下のように Authorization ヘッダに Bearer と 先に取得したアクセストークンを付与して private エンドポイントにアクセスする.

$ curl --request GET --url http://localhost:3010/api/private --header 'Authorization: Bearer xxxxxxxxxxxx.yyyyyyyyyyyyyyyyy.zzzzzzzzzzzzzzz' -w '\n' -i
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 86
X-Content-Type-Options: nosniff
Server: WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21)
Date: Sun, 25 Mar 2018 14:52:01 GMT
Connection: Keep-Alive

{"message":"Hello from a private endpoint! You need to be authenticated to see this."}

正常にレスポンスが返ってきた!

ちなみに, アクセストークンには期限は設けられており, 期限切れのトークンを利用した場合には, 以下のような認証エラーレスポンスとなる.

$ curl --request GET --url http://localhost:3010/api/private --header 'Authorization: Bearer aaaaaaaaaaaaa.bbbbbbbbbbbbbbbb.ccccccccccccccc' -w '\n' -i
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Content-Length: 67
X-Content-Type-Options: nosniff
Server: WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21)
Date: Sun, 25 Mar 2018 14:55:15 GMT
Connection: Keep-Alive

{"error":"JWT::ExpiredSignature","message":"Signature has expired"}

トークンが改ざんされた場合にも, 以下のような認証エラーレスポンスが返ってくる.

$ curl --request GET --url http://localhost:3010/api/private --header 'Authorization: Bearer xxxxxxxxxxxx.yyyyyyyyyyyyyyyyy.zzzzzzzzzzzzzzq' -w '\n' -i
HTTP/1.1 401 Unauthorized
Content-Type: application/json
Content-Length: 76
X-Content-Type-Options: nosniff
Server: WEBrick/1.3.1 (Ruby/2.3.3/2016-11-21)
Date: Sun, 25 Mar 2018 14:57:17 GMT
Connection: Keep-Alive

{"error":"JWT::VerificationError","message":"Signature verification raised"}

フムフム.

ということで

今回は, Auth0 から OAuth 2.0 や Open ID Connect とはなんぞやをホントにかる~く触れた上で, Auth0 APIRuby サンプルを動かしてみた.

このサンプルの実行結果と Auth0 の機能や, OAuth や Open ID Connect がどのように関連しているのか, まったく伝わってこないかもしれないが, ひとまず, こんな認証, 認可基盤が SaaS として提供されていて, 簡単にアプリケーションに組み込むことが出来るという雰囲気が味わえたの良しとしたい. (気付いたら, 色々と追記していくつもり)

引続き, この分野についても勉強していきたいと考えている.

参考

以下の記事を参考, 引用させて頂きました.

有難うございました.