※ OAuth 2.0の規程に則って「認可サーバーを作成する」側の話です。
OAuth 2.0では、 scope
というパラメーターでアクセス範囲を表します。
クライアントは scope リクエストパラメーターを用いて要求するアクセス範囲を明示することができる.
同様に, 認可サーバーは発行されたアクセストークンの範囲をクライアントに通知するために scope レスポンスパラメーターを使用する.
このscopeですが、どんな形式にするかは実装者に委ねられています。RFCで定められている制約といえば下記くらいのものです。
- 大文字と小文字は区別する
- スペースは使えない(複数のscopeをスペース区切りで表すため)
- scopeを省略しても良いことにしても良い(その場合デフォルト値を定義する)
ref: https://openid-foundation-japan.github.io/rfc6749.ja.html#scope
めっちゃ自由。 自由すぎてどんな形式にするのが良いかちょっと悩みます。
この記事では他社事例を見た後、採用するscopeの形式を考えます。
他社事例を見てみる
まずは先人の事例を見てみます。
GitHub OAuth Apps
scope一覧
- (no scope)
- repo
- repo:status
- repo_deployment
- public_repo
- repo:invite
- security_events
- admin:repo_hook
- write:repo_hook
- read:repo_hook
- admin:org
- write:org
- read:org
- admin:public_key
- write:public_key
- read:public_key
- admin:org_hook
- gist
- notifications
- user
- read:user
- user:email
- user:follow
- delete_repo
- write:discussion
- read:discussion
- write:packages
- read:packages
- delete:packages
- admin:gpg_key
- write:gpg_key
- read:gpg_key
- workflow
特徴
- コロン(
:
)区切りになっている。 -
write:discussion
やread:discussion
のように、「操作(権限):リソース」の形をしているものが散見される。その視点で見ると、「操作(権限)」にあたるものはread
,write
,admin
,delete
の4つ。 - scopeなし(空欄)でパブリックな情報へのread only権限が与えられる。
「操作:リソース」のフォーマット良さそう。
YouTube Data API
scope一覧
- https://www.googleapis.com/auth/youtube
- https://www.googleapis.com/auth/youtube.readonly
- https://www.googleapis.com/auth/youtube.upload
- https://www.googleapis.com/auth/youtubepartner-channel-audit
特徴
- URLの形をしている。
- URLを叩くと、
text/plain
でscope名が返ってくる。 - scopeなし(空欄)はサポートされていない。
URLをどのように使っているのか気になる。リソースサーバー側で管理しやすくするためなのかな。実際にURLであるうまみはあったんだろうか。
LINE Social API
scope一覧
- profile
- profile%20openid
- profile%20openid%20email
- openid
- openid%20email
ref: https://developers.line.biz/ja/docs/line-login/integrate-line-login/#scopes
特徴
- あらかじめ空白文字(
%20
)で区切られた状態で列挙されている。%20
を空白文字として捉えると、email
は単体でscopeにならないので、RFCに準拠してないと言っても良いかもしれない。 - scopeなし(空欄)はサポートされていない。
scopeはなし。
OAuth 1.0aでの認可がまだ主流のようで、OAuth 2.0ではClient Credentials Grantでのpublicな情報へのアクセスしかサポートしていないため、scopeでアクセス範囲を決める必要がないのだろう。
ref: https://developer.twitter.com/en/docs/authentication/api-reference/token
ぼくのかんがえたさいきょうのscope
先人たちのscopeを見てみましたが、三者三様でした。
どんなAPIを提供するのかに依ってscopeのフォーマットも変わると思いますが、ここではRESTfulなAPIへの認可を考えることにします。
さて、ぼくのかんがえたさいきょうのscopeは、これだっ!!
read:RESTのリソース名
write:RESTのリソース名
理由
このscopeを考えるにあたっての、基本的な指針は下記2つです。
- 最小権限の原則に則って必要最低限の権限のみを認可できるように、scopeのアクセス範囲はなるべく小さくしたい。
- scope名を見ただけでアクセス範囲が分かるような明快なものが良い。
この思想をもとに、下記のように考えました。
- RESTの「リソース」という概念をそのまま利用して、リソース単位で切ると、明快で小さくscopeを分けられるだろう。
- リソースに対しての操作を分けたいが、リソースの新規作成ができるclientには、作成したリソースの更新・削除する権限も与えたいという考え・要件で、readとwriteだけにした。
オマケ 〜Railsでの実装〜
「リソース名」は単数形にしても複数形にしても良いですが、Railsではcontroller_name
でコントローラー名(基本的にリソースの複数形)が取れ、私はこれを利用するために複数形にしました。
# こんなアクセスのとき... http://hoge.jp/articles
controller_name
# => “articles”
# こんなのが取れる!
これを利用すれば、scopeの検証は下記のような簡単なコードで実装できます。controller concernなどに書くと良いと思います。
def required_read_scope
scope_name = "read:#{controller_name}"
render_unauthorized unless current_access_token.scopes.include?(scope_name)
end
def required_write_scope
scope_name = "write:#{controller_name}"
render_unauthorized unless current_access_token.scopes.include?(scope_name)
end
あとはこのメソッドを各controllerのbefore_actionで、指定したアクションの前に呼び出すだけです。
class ArticlesController < ApplicationController
before_action :required_read_scope, only: %i[index show]
before_action :required_write_scope, only: %i[create update destroy]
...
end
OAuth 2.0の他の仕様で迷ったら
私がOAuth 2.0の自由な仕様で迷ったときに、各社の仕様をこちらにまとめました。他の仕様で迷っているときは参考になるかもしれません。
https://qiita.com/murs313/items/078025d671e937a09285