PicasaAPIを使ってOAuth2.0のフロー(Authorization Code)を確認した。

OAuth、書籍やネット上の記事などから大まかに仕組みは理解していたのですが、具体的にリクエスト/レスポンスをどう送り合ってやりとりを行っているのかがよくわかっていませんでした。 OAuthの一連の流れを理解するために、今回は実際にOAuth2.0から認可してもらう流れをライブラリを使わずに実装していきたいと思います。

OAuth1.0はsignetureを作るのが少しメンドクサそうだったので、今回はOAuth2.0だけやります。

参考

やること・ゴール

  • Rubyで簡易的な実装で流れを掴んで行きたいと思います。
  • Google PhotosにOAuth2.0で画像をアップロードできるよう認可してもらい、実際に画像をアップロードできるようにするところまでやっていきたいと思います。 (自宅のPCはUbuntuを使っているのですが、Google PhotosのクライアントはWindowsMacしかないので、今後自作でちゃんとしたクライアント作っていくために、今回Google Photosへアップロードする流れを確認しました。)
  • Google Photosは統合前のサービスPicasa、このAPIが使えます。(Google Photosというのは残念ながらないようです。)

OAuth2.0の認可フロー(Grant Type)

今回は、今後WEBアプリケーションで使っていけるよう、上記フローのうちAuthorization Codeで実装します。

googleからクライアント ID、クライアントシークレットをもらう

Google APIs Consoleへログインし、クライアント ID、クライアント シークレットをもらいます。 手順は検索すると多くの記事がでてくるので割愛します。

今回はAuthorization Codeで実装を行うので、アプリケーションの種類ウェブ アプリケーションを選択しました。 承認済みのリダイレクト URIは、ユーザが認可のためGoogleにログイン後にリダイレクトされるURIなのですが、ここにクエリとしてアクセストークンがくっついてきます。 その他にすると、リダイレクトされず、トークンが画面に表示されるようになります。 (AndroidChromeアプリ、iOSPlayStation4にするとどういう動きになるかがわかりませんが、気になる方は調べてみると良いでしょう。)

認可コード(Authorization Code)をもらう

以下のコードで、認可コード(Authorization Code)をもらうためのログイン画面を表示します。 ここでは、uriの組み立てだけなのですが、hashでいい感じにクエリ付のuriを生成してくれるので、net/httpを使って実装しています。 (リクエストは送っていません。)

パラメータの仕様は以下です。 https://developers.google.com/identity/protocols/OpenIDConnect#authenticationuriparameters

state も指定できるので、WEBアプリケーションを実装するときにはつけておいた方が良いでしょう。

# -*- coding: utf-8 -*-

require 'uri'
require 'net/http'

client_id = { クライアントID }
redirect_uri = { リダイレクトURI } # Google APIs Consoleで登録したリダイレクトURIを入力します。異なる場合を入れた場合はエラーになります。
scope = 'https://picasaweb.google.com/data/' # Google Photosはpicasaのapiを利用しているので、次のURLを入れます。

uri = URI('https://accounts.google.com/o/oauth2/v2/auth')
params = {
  response_type: 'code',
  client_id: client_id,
  redirect_uri: redirect_uri,
  scope: scope
}
uri.query = URI.encode_www_form(params)
puts uri.to_s

ログインが完了したら、redirect_uriで指定したuriにリダイレクトされます。 その際に、クエリにcode={Authorization Code}がついてくるので、それをコピーしておきます。 WEBアプリケーションを実装する場合は、コールバック用のuriを用意しておき、Authorization Codeを取得できるようにしておくと良さそうです。

アクセストークンを取得する

以下のリクエストで、アクセストークンを取得します。 WEBアプリケーションを選択した場合は、有効期限はないため、リフレッシュトークンは発行されません。

# -*- coding: utf-8 -*-

require 'uri'
require 'net/http'
require 'openssl'

authorization_code = { 先ほど取得したauthorization_code }
client_id = { クライアントID }
client_secret = { クライアントシークレット }
redirect_uri = { リダイレクトURI }

uri = URI.parse('https://www.googleapis.com/oauth2/v4/token')
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true

request = Net::HTTP::Post.new(uri.request_uri)
request.set_form_data({
  code: authorization_code,
  client_id: client_id,
  client_secret: client_secret,
  redirect_uri: redirect_uri,
  grant_type: 'authorization_code'
})

response = https.request(request)

puts response.code
puts response.body

アクセストークンを使って、画像アップロード

以下のコードで画像をアップロードしてみます。

# -*- coding: utf-8 -*-

require 'uri'
require 'net/http'
require 'openssl'

user_id = { アップロードするユーザのid }
access_token = { アクセストークン }
upload_file = 'sample.jpg'

uri = URI.parse("https://picasaweb.google.com/data/feed/api/user/#{user_id}/?access_token=#{access_token}")
https = Net::HTTP.new(uri.host, uri.port)
https.use_ssl = true

request_header = {
  'Content-Type': "image/jpeg",
  'Content-Length': File.size(upload_file).to_s,
  'Slug': upload_file
}
request = Net::HTTP::Post.new(uri.request_uri, request_header)
request.body = File.read(upload_file)

response = https.request(request)

puts response.code
puts response.body

アップロードされた画像を確認する

Google Photesアクセスし、画像がアップロードされていることを確認します。

OAuth2.0でGoogle PhotosへのアクセスをGoogleに認可してもらい、実際にアップロードできるところまで確認しました。 Authorization Code以外の認可フローについてはまた別の機会にやろうと思います。