LoginSignup
46
41

OAuth 2.0のscopeを考える

Last updated at Posted at 2020-08-24

※ 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

ref: https://developer.github.com/apps/building-oauth-apps/understanding-scopes-for-oauth-apps/#available-scopes

特徴

  • コロン(:)区切りになっている。
  • write:discussionread:discussionのように、「操作(権限):リソース」の形をしているものが散見される。その視点で見ると、「操作(権限)」にあたるものはread, write, admin, deleteの4つ。
  • scopeなし(空欄)でパブリックな情報へのread only権限が与えられる。

「操作:リソース」のフォーマット良さそう。

YouTube Data API

scope一覧

ref: https://developers.google.com/youtube/v3/guides/auth/server-side-web-apps?hl=ja#Obtaining_Access_Tokens

特徴

  • 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なし(空欄)はサポートされていない。

Twitter

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

46
41
1

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
46
41