背景
仕事でAPIの実装を行うことになりました。ログイン後に発行するtokenでユーザー認証が必要ということで、どういった方法があるかを調べてみました。Authorizationヘッダを用いることが多いのは知っていたので調べてみることにしました。
Authorizationヘッダについて
Authorizationの仕様についてはしっかりと理解していなかったので改めて調べてみました。
このサイトに記載されているように、Authorization: <type> <credentials>
が構文のようです。以前、 Authorization: <credentials>
という実装をしていましたが、こちらは仕様に準拠していないことになります。
typeについて
Typeにはいろんな種類があるようです。個人的によく見るのは Bearer
です。OAuth認証で利用したりしました。
先ほどのMDNの中にtypeの一覧へのリンクがありました。
Hypertext Transfer Protocol (HTTP) Authentication Scheme Registry
TypeがBasicだった場合、credentialsをBase64でデコードする必要があったりして、少し手間がかかりそうな印象です。
Bearerを試してみる
まずは簡単にできそうなBearerを試してみます。
API側では、authenticate_or_request_with_http_token
メソッドを用います。
1 | def authenticate |
クライアント側はcurlを使います
1 | $ curl -H 'Authorization: Bearer xxx_credentials' https://api_endpoint |
認証が失敗してしまいました…
ブロック内でbinding.pry
を呼び出してもスルーされることから、ブロックに到達する前に認証に失敗しているようです。
いろいろ調べてみると、以下のサイトが見つかりました。
BearerだけではなくTokenでも良いようなので、Tokenを試してみます
1 | $ curl -H 'Authorization: Token xxx_credentials' https://api_endpoint |
これだとよくわからないので、レスポンスヘッダを表示してみます
1 | $ curl -D - -H 'Authorization: Token xxx_credentials' https://api_endpoint |
認証成功しました!
ということはTokenではよくてBearerはダメということですね。原因を調査するためにauthenticate_or_request_with_http_token
を調べてみます。
該当のバージョンでgithubを探すと該当箇所が見つかりました。
rails/http_authentication.rb at 4-2-stable · rails/rails · GitHub
なんと、Bearer
では引っかからないようになっています…
Tokenだと認証可能ですが、Tokenは
Basicを試す
こうなったら一般的なBasicを試してみます。credentialsの部分はユーザー名:パスワード
になりますが、今回ユーザー名がないので:パスワード
となり、パスワードの部分にcredentialsを指定します。
API側ではauthenticate_or_request_with_http_basic
を利用します
1 | def authenticate |
クライアント側はBase64.encode64(‘:xxx_credentials’)
でBase64エンコードした文字列を準備します。(先頭の:
を忘れない)
1 | $ curl -H 'Authorization: Basic base64_encode_credentials' https://api_endpoint |
うまく認証されました!
まとめ
認証はセキュリティ的に重要な箇所になるので、慎重に実装しましょう。
AuthorizationヘッダなどHTTPの仕様をしっかり理解してからどの方法を使うかを検討しましょう。
おまけ
今回クライアント側としてcurlを使いました。有用なオプションがいっぱいありますね。以下にまとめたいと思います。
-D (–dump-header)
HTTPレスポンスヘッダを表示します。標準出力に出力するには
-
を指定します。例:
1
2
3
4
5
6
7
8
9
10$ curl -D - https://www.google.com
HTTP/2 200
date: Wed, 16 Sep 2020 17:16:16 GMT
expires: -1
cache-control: private, max-age=0
content-type: text/html; charset=ISO-8859-1
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
server: gws
x-xss-protection: 0
(省略)-o
レスポンスボディの出力先を指定します。不要な場合に
/dev/null
を指定したりします。例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19$ curl -D - -o /dev/null https://www.google.com
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0HTTP/2 200
date: Wed, 16 Sep 2020 17:19:47 GMT
expires: -1
cache-control: private, max-age=0
content-type: text/html; charset=ISO-8859-1
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
server: gws
x-xss-protection: 0
x-frame-options: SAMEORIGIN
set-cookie: 1P_JAR=2020-09-16-17; expires=Fri, 16-Oct-2020 17:19:47 GMT; path=/; domain=.google.com; Secure
set-cookie: NID=204=rwcy-xyJUKkXMHIec1I-ETPuB_hzV-mbxnl5ot0DhU2q2B-I-aRIVHKkHFyKvsqESZqjRXQFxn7YjzjmvTkflbrfzePsc87d7F7GFELqNbXvPvATIo2v6EaeaHzIXrsoAL4LRKd20uniGfFR6dN8pq3gB4O4SPDzjYd7EJvWLaA; expires=Thu, 18-Mar-2021 17:19:47 GMT; path=/; domain=.google.com; HttpOnly
alt-svc: h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
accept-ranges: none
vary: Accept-Encoding
100 13141 0 13141 0 0 62279 0 --:--:-- --:--:-- --:--:-- 62279-s
進捗を表示しないようにします。
例:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15$ curl -D - -o /dev/null -s https://www.google.com
HTTP/2 200
date: Wed, 16 Sep 2020 17:21:07 GMT
expires: -1
cache-control: private, max-age=0
content-type: text/html; charset=ISO-8859-1
p3p: CP="This is not a P3P policy! See g.co/p3phelp for more info."
server: gws
x-xss-protection: 0
x-frame-options: SAMEORIGIN
set-cookie: 1P_JAR=2020-09-16-17; expires=Fri, 16-Oct-2020 17:21:07 GMT; path=/; domain=.google.com; Secure
set-cookie: NID=204=Mdp8loY0lxxV_Kf-YoGRq3db-HvCsX81ffRzePmxfFeT6gb2A72Ijy8AoG5PHsjGj962rqODoZ0VXn9ZMen5MPFGnUww7qnmuexWFLC0JGlyR07K1iIO3iAub_CFUzdjZogChUYvm1Qo9yxyzlm2pb0A2i44va50JOIBi-8qrDE; expires=Thu, 18-Mar-2021 17:21:07 GMT; path=/; domain=.google.com; HttpOnly
alt-svc: h3-29=":443"; ma=2592000,h3-27=":443"; ma=2592000,h3-T051=":443"; ma=2592000,h3-T050=":443"; ma=2592000,h3-Q050=":443"; ma=2592000,h3-Q046=":443"; ma=2592000,h3-Q043=":443"; ma=2592000,quic=":443"; ma=2592000; v="46,43"
accept-ranges: none
vary: Accept-Encodingヘッダだけ欲しい場合はこれで良いですね。
-X
HTTPメソッドを指定します。指定しない場合はGETになります。
例:
1
2
3
4
5
6
7$ curl -X POST https://www.google.com
<!DOCTYPE html>
<html lang=en>
<meta charset=utf-8>
<meta name=viewport content="initial-scale=1, minimum-scale=1, width=device-width">
<title>Error 405 (Method Not Allowed)!!1</title>
(省略)405が返ってきてますね…POSTには対応してないようです
-F(–form)
フォームの値を指定します。key=valueという形式で指定します
例:
1
$ curl -X POST -F 'username=user' -F 'password=password' https://somewhere_login_url
他にもいろいろあるようですが、また何かわかったら追記します。