6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

【前編】OAuth2.0の勉強でAuthorization Code Grantをクライアント側として体感してみた

Last updated at Posted at 2022-02-01

はじめに

認証と認可に関しては、OpenID Connectがデファクトな状況だと思われる。今回はOAuth2.0を拡張した仕様であるOpenID Connectで定義されている、認可のフローであるAuthorization Code Grantについて、認可をもらう側(クライアント側)としてフローを実行する事で、理解を深めてみたいと思う。

流れとしては、

  1. 認可エンドポイントへ認可リクエスト
  2. 認証(今回はGoogleのOpenID Connectを使うので、Googleの認証)と認可
  3. リダイレクトするので、それをserverで受け取りトークンエンドポイントへアクセストークンをリクエスト

の中の、1と3の部分を実際に実装する。
※今回はあえてCSRF対策やPKCE対策に必要になるパラメータを省略している。CSRF対策やPKCE対策については今後記事を執筆予定。

※今回、Google Cloud PlatformでGoogle APIsの設定をしてAuthorization Code Flowを体感する。その際料金はかからない想定で本記事は書いている(無料枠があるので)。詳細はGoogle Cloud の無料プログラムを参照。ただし、Authorization Code Flowで取得したアクセストークンを使ってAPIを呼び出すが、その際に選んだAPIによっては課金される場合もあるため注意。本記事の内容を実践して万が一課金されても責任は負えません。Google Cloud Platformの利用にかかる課金やその他の事由については自己責任でお願い致します。)

※本記事中で筆者の理解に誤りがあればご指摘頂けると幸いです。

※今回、文章量が多くなってしまったため、記事を前編と後編の2つに分割した。前編では、「OpenID Connectとは?という話からAuthorization Code Flowを体感するための事前準備まで」を書いている。後編では、「実際にクライアント側としてAuthorization Code Flowを実装し、アクセストークンを用いてAPIを実行するまで」を書いている。

ソースコード全体は以下。

今回利用するOpenID Provider

OAuth2.0?OpenID Connect?

早速だが、記事のタイトルではOAuth2.0の勉強と言いつつ、OpenID Connectという単語が出てきて??となるかもしれないので、ここでOAuth2.0とOpenID Connectについて私が理解している範囲で整理してみたいと思う。

※ただしOpenID Connectがなぜできたのか?という話については触れない。それについてはOAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る 認証と認可単なる OAuth 2.0 を認証に使うと、車が通れるほどのどでかいセキュリティー・ホールができるを参照。

前提として…認証・認可とは?

よく混乱しがちな認証と認可の概念ついて、OAuth2.0やOpenID Connectを理解する上では重要になるので、まずは認証と認可の概念について理解する所からスタートしたいと思う。

認証・認可について理解するには、OAuth 2.0 + OpenID Connect のフルスクラッチ実装者が知見を語る 認証と認可を参照頂くと具体的に概念としてどう違うのか?が分かりやすいと思う。端的に言ってしまうと、以下のようになるだろう。

  • 認証とは
    「誰であるか?」を確かめる仕組みで、本人確認をする時に用いられる
  • 認可とは
    「誰が誰に何の権限を与えるか?」に関する仕組みで、権限を委譲する時に用いられる(ここでいう委譲は、その相手が人とは限らずアプリケーションである事もある)

つまり、認証と認可はそもそも別の概念である。

OAuth2.0とは?

ここからはそれぞれ(OAuth2.0とOpenID Connect)について、何者なのか?を見ていく。

一番分かりやすい OAuth の説明の記事を参照するのが分かりやすい。記事を読むと分かるが、OAuth2.0とは、認可の仕組みである。具体的には、「誰が誰に何の権限を与えるか?」に関する仕組みで、権限を委譲する時に用いられる。ここでいう委譲は、その相手が人とは限らずアプリケーションである事もある。

実際の仕様としてはRFC6749にそれが定義されている。

※認可の仕組みであるというのはRFC6749(3.1. Authorization Endpoint)にもちゃんと明記されており、

The authorization server MUST first verify the identity of the resource owner. The way in which the authorization server authenticates the resource owner (e.g., username and password login, session cookies) is beyond the scope of this specification.(認可サーバーは、最初にリソース所有者の身元を確認しなければならない(MUST)。認可サーバーがリソース所有者を認証する方法(例: ユーザー名とパスワードによるログイン、セッションクッキー)は、この仕様の範囲外である。)

と書かれている。

OpenID Connectとは?

一番分かりやすい OpenID Connect の説明の記事を参照するのが分かりやすい。記事を読むと分かるが、OpenID Connectとは、OAuth2.0を拡張したもので、OAuth2.0の認可仕組み(フロー)を流用してIDトークンと呼ばれる認証情報(本人確認されたという事実とそのユーザーの情報)を発行するための仕様を定めたもの。

OpenID ConnectはOAuth2.0を拡張したものなので、

OpenID Connect 1.0 is a simple identity layer on top of the OAuth 2.0 protocol.

(Identity, Authentication) + OAuth 2.0 = OpenID Connect

というような表現をされ、認証+OAuth2.0=OpenID Connectである。そのためOpenID Connectには認可の仕組みも含むものになっている(認可の仕組みについては、OAuth2.0のRFC6749そのままの部分ももちろんある。具体的にはOpenID Connectのresponse_type=code かつ scopeにopenidを含まないのパターン)。

OpenID Connectの実際の仕様(主要部分)は、OpenID Connect Core 1.0に定義がされている(その他にもOpenID Connect Discovery 1.0などあり、全てがOpenID Connect Core 1.0に書かれているわけではない)。

今回OAuth2.0の勉強なのにOpenID Connectなの?に対する答え

上記で見てきたように、OpenID Connectの仕様で実装されたサーバ(OpenID Provider)を利用すると、そのサーバには認証・認可の両方の機能が備わっており、認可はOAuth2.0の仕様を満たすものなので、OpenID Connectを利用してOAuth2.0の勉強をしていく。

※OpenID Connectを利用する理由には、今後実際にOpenID ConnectでIDトークンの発行(Implicit Flow)などもやってみて理解を深めていく予定もあるので、単にOAuth2.0の認可サーバではなく、OpenID Connectの認証・認可サーバで勉強するという意図もある。

実際に実装するもののフロー

はじめにで少し触れたが、ここでもう一度実装しようとしている、Authorization Code Grant(Flow)のクライアント側の動き(
どんなフローなのか?)を図示して整理してみる。
image.png

今回は上記の図のフローの中で

  • ①・②:認可リクエストを送る部分
  • ⑦を受け取るserverのエンドポイント:redirect_uriのエンドポイント
  • ⑧・⑨:トークンエンドポイントへトークンのリクエストを送る部分

を実装する事になる。

※上記のGoogle認証・認可サーバの部分で、認可エンドポイントのサーバトークンエンドポイントのサーバという2つを描いているが、これはOpenID Discoveryのエンドポイント https://accounts.google.com/.well-known/openid-configuration を見ると認可エンドポイントとトークンエンドポイントのサーバのホストが違う事が分かったので(2022/01/27時点)、サーバを2つに分けて描いている。

...
"authorization_endpoint": "https://accounts.google.com/o/oauth2/v2/auth",
...
"token_endpoint": "https://oauth2.googleapis.com/token",
...

この辺りは、OpenID Providerによっては同じサーバに認可エンドポイントもトークンエンドポイントもある場合もあり、Providerによって違うものらしい。Yahoo Japan!のOpenID Providerの方(https://auth.login.yahoo.co.jp/yconnect/v2/.well-known/openid-configuration )はそれぞれ同じサーバである(2022/01/27時点では)。

"authorization_endpoint": "https://auth.login.yahoo.co.jp/yconnect/v2/authorization",
"token_endpoint": "https://auth.login.yahoo.co.jp/yconnect/v2/token",

事前準備

Authorization Code Flowについて理解を深めるために、今回はGoogle CalendarのデータをOAuth2.0の仕組みで取得するWebアプリを作成してみる。Google CalendarのAPIを実行できるようにするにはいくつか事前準備が必要なのでその準備を行う。以下で1つずつ見ていく。

※公式で言うと、Authorizing Requests to the Google Calendar APIに書かれている内容が準備の内容になる。

また、今回はGoogleの認可サーバを利用するが、Googleの認可サーバではクライアントを作成する時に指定する事になるredirect_uriを持つアプリ(今回はWebアプリ)はhttpsである必要がある。さらにIPでの指定はできずパブリックトップレベルドメイン(.com、.org など)にする必要がある。そのための実装・設定も取り上げる。

APIを有効化する

まずは使うAPI(CalendarList: list )を有効化する。

Google Cloud PlatformにアクセスしてAPIとサービスのページを開き、APIとサービスの有効化をクリックする。

1 2
image.png image.png

検索の部分でcalendarと入力してEnterを押下すると、以下の図のようにGoogle Calendar APIというのが表示されるので、これをクリックして有効にするから有効にする。

3 4
image.png image.png

OAuth同意画面を設定する

続いて、認可画面に表示される内容とユーザが認可するscopeの種類を登録する。上記のAPIを有効化するで開いたAPIとサービスOAuth同意画面のメニューをクリックして、設定を行っていく(ここで設定する内容はユーザが実際に認可を行う際に表示される画面に表示される内容になる)。

外部を選択し、作成を行う。
image.png

続けて各Stepで設定を行っていく。

※今回はAPIとしては、CalendarList: listを呼び出すが、このscopeとしてはCalendarList: list Authorizationに書かれているscopeが必要なのでそれをStep2のスコープで設定する。
※アプリケーションのホームページ・プライバシーポリシーのリンク・利用規約のリンクなどは設定しなくても問題ない。

1 2
image.pngimage.png image.pngimage.pngimage.png
3 4
image.png image.pngimage.png

設定できたらOK。

クライアントを作成する

続いてクライアントを登録する。実際にAuthorization Code Flowを実行する時に必要となる情報(client_idやclient_secret)はここで作成される。

上記のAPIを有効化するで開いたAPIとサービス認証情報のメニューをクリックして、設定を行っていく。
image.png

アプリケーションのHomeとなるURIとリダイレクトURIを設定して作成をすると、以下の図のようにclient_id・client_secretが作成されるのでメモしておく。このclient_id・client_secretは認可エンドポイントへ認可リクエストを行う時、トークンエンドポイントへのリクエストを行う時に使う。また、リダイレクトURIとは、実際に実装するもののフローの章で取り上げた⑥で返ってくる認可レスポンスの302のLocationに設定される。

1 2
image.png image.png

ここまで設定できれば準備としては完了になる。
次の章からは実際に実装をしていき、アクセストークンを取得後、そのアクセストークンでCalendarList: list を実行してみる所までみていく。

※ちなみに、OpenID Connect Dynamic Client Registration 1.0という仕様でクライアントを登録する際の仕様が定められている。

ExpressサーバのHTTPS化

Authorization Code Flowを体感するために、今回はWebアプリをクライアントとして実装するが、Node.jsのExpressを使う。

Googleの認可サーバの仕様に合わせてExpressサーバをhttps化する必要がある(理由は以下の背景についてを参照)のでそれを行う。

背景について

HTTPS化する理由については、以下の2点。

  • GoogleAPIの設定でリダイレクトURIには、httpsのみが設定できるようになっているため
    image.png
  • (今回は上記で言及したようにOpenID Connectの仕様で実装された認可サーバでAuthorization Code Flowを行うのでOpenID Connectの仕様に従う部分が出てくるが)OpenID Connect Core 1.0 3.1.2.1. Authentication Requestredirect_uriには、以下のように書かれており、httpも使えるが基本はhttpsを使うべきと書かれているため

When using this flow, the Redirection URI SHOULD use the https scheme; however, it MAY use the http scheme, provided that the Client Type is confidential(このフローを使用する場合、Redirection URIはhttpsスキームを使用すべきですが、Client Typeが機密であれば、httpスキームを使用しても構いません(MAY)。)

実際にHTTPS化する

HTTPS化するが、ちゃんと証明書を用意するのは手間なので、オレオレ証明書を使ってHTTPS化する。手順としては、Enabling HTTPS on express.jsに書かれている内容が参考になる。

まずはOpenSSLを使って、HTTPS化に必要になるkeycrtを生成する。以下のようなopensslコマンドを実行するとCLI上で対話しながらkey・crtを生成できる(質問に対する答えはダミーの内容で実在しないものも含まれている)。

[root@localhost openid-connect]# sudo openssl req -x509 -nodes -days 365 -newkey rsa:2048 -keyout ./server.key -out server.crt
Generating a 2048 bit RSA private key
..........................................+++
..........+++
writing new private key to './server.key'
-----
You are about to be asked to enter information that will be incorporated
into your certificate request.
What you are about to enter is what is called a Distinguished Name or a DN.
There are quite a few fields but you can leave some blank
For some fields there will be a default value,
If you enter '.', the field will be left blank.
-----
Country Name (2 letter code) [XX]:JP
State or Province Name (full name) []:Tokyo
Locality Name (eg, city) [Default City]:Tokyo City
Organization Name (eg, company) [Default Company Ltd]:Example, Inc. 
Organizational Unit Name (eg, section) []:Example Unit
Common Name (eg, your name or your server's hostname) []:www.example.com
Email Address []:dumy@example.com

あとは、ここで生成したkeyとcrtを用いて、以下のようにhttpsモジュールを使ってサーバを作成すればいい(expressの公式の内容も参照)。

import express from 'express';
import https from 'https';
import fs from 'fs';
...
const app = express();
const server =
	callback.protocol === 'https:'
		? https.createServer(
				{
					key: fs.readFileSync('./ssl/server.key'),
					cert: fs.readFileSync('./ssl/server.crt')
				},
				app
		  )
		: app;
...
server.listen(8080);

windowsのhostsファイルを修正して、リダイレクト時のホストをIPに変換する(ローカルのなんちゃってDNSの設定)

そもそもwindowsのhostsって何?という話は「分かりそう」で「分からない」でも「分かった」気になれるIT用語辞典 hostsファイルが分かりやすいと思う。

本題だが、今回はAuthorization Code Grantを体感するために、OpenID Connectの仕様で実装されたGoogle's OAuth 2.0を使っているが、クライアントを登録しているする際に設定するリダイレクトURIにはIPは指定できない仕様になっている。
image.png

そのため何らかののドメインを設定する必要があるが、ちゃんとドメインを取得するのも面倒なのでなんちゃってDNSで対応する。
筆者はWindowsなのでhostsファイルを以下のように設定するだけでドメインをローカルのサーバのIPに解決する事ができるようになる。

# Copyright (c) 1993-2009 Microsoft Corp.
#
# This is a sample HOSTS file used by Microsoft TCP/IP for Windows.
#
# This file contains the mappings of IP addresses to host names. Each
# entry should be kept on an individual line. The IP address should
# be placed in the first column followed by the corresponding host name.
# The IP address and the host name should be separated by at least one
# space.
#
# Additionally, comments (such as these) may be inserted on individual
# lines or following the machine name denoted by a '#' symbol.
# 省略

192.168.56.2       example.com

※ちなみに、192.168.56.2はWindows上に立てたVirtualBoxの仮想マシンのIP。仮想マシンの構築についてはWindows上にLinux(CentOS)のWebアプリ開発環境をvirtualboxで構築するを参照。

続きは「【後編】OAuth2.0の勉強でAuthorization Code Grantをクライアント側として体感してみた」で

6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?