Googleの様々なAPIを叩く際、認証にOAuth 2.0を用いる。
Using OAuth 2.0 to Access Google APIs | Google Identity Platform | Google Developers
使用する場面やパターンによって以下のような6つのシナリオが想定されている。
- Login
- Web Server Applications
- Client-side Applications
- Installed Applications
- Devices
- Service Accounts
多くの場合は「ユーザごとに認証させて個別のtokenを発行しリクエストに利用する」という流れなのだけど、中にはたとえばURL短縮APIとか、必ずしもユーザ個別にtokenを発行させる必要がないこともある。
そういう場合には「サービス固有のtoken」だけあれば良い。ということで使えるのが「Service Accounts」という方式。
Using OAuth 2.0 for Server to Server Applications | Google Identity Platform | Google Developers
サービスアカウントに発行されたprivate keyを使って署名したJWTリクエストを生成してAPIを叩く、という仕組みのようだ。
rubyでgoogle-api-clientを使う例
GoogleのAPIを叩くためのライブラリとして、Ruby Gemsではgoogle-api-client
というのがある。
google-api-client | RubyGems.org | your community gem host
JWTを使ったリクエストや認証なども対応してくれている。
これを使って実際にやってみる。
準備
Google API Console
にて、Application typeを"Service account"と選択してClient IDを生成する。
すると、"Email address"が発行され、private keyがダウンロードできるようになるので、これを使う。
書く
Gemfileに
source :rubygems gem 'google-api-client', '0.6.0'
と書いてbundle install
して、以下のようなスクリプトを書く。
require 'google/api_client' client = Google::APIClient.new(:application_name => '') key = Google::APIClient::PKCS12.load_key('/Users/sugyan/Downloads/...-privatekey.p12', 'notasecret') client.authorization = Signet::OAuth2::Client.new( :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', :audience => 'https://accounts.google.com/o/oauth2/token', :scope => 'https://www.googleapis.com/auth/urlshortener', :issuer => <発行されたemail address> :signing_key => key, ) client.authorization.fetch_access_token! shortener = client.discovered_api('urlshortener') result = client.execute( :api_method => shortener.url.insert, :body_object => { :longUrl => 'http://d.hatena.ne.jp/sugyan/' }, ) puts result.data.id
ダウンロードしたprivate keyのファイルを読みこんでkeyを生成し、email addressやscopeを指定し、認証。
これで、GoogleのURL短縮APIを叩いて短縮URLを得ることができる。
$ bundle exec ruby shorten.rb http://goo.gl/zLVjD
Herokuに上げるために
で、こんなものをHeroku上で動かそうとすると、API Consoleからダウンロードしたprivate keyのファイルをgit repositoryに含める必要が出てしまう。それはイヤだ。
幸いにも、Google::APIClient::PKCS12
でloadしたkeyは文字列として得られる。
$ bundle exec ruby -r 'google/api_client' -e 'puts Google::APIClient::PKCS12.load_key("/Users/sugyan/Downloads/...-privatekey.p12", "notasecret")' -----BEGIN RSA PRIVATE KEY----- MIICXQ... ... -----END RSA PRIVATE KEY-----
なので、これを丸ごとheroku configで渡してしまえばいい。ついでに発行されたemail addressも。
$ heroku config:set GOOGLE_API_KEY="$(bundle exec ruby -r 'google/api_client' -e 'puts Google::APIClient::PKCS12.load_key("/Users/sugyan/Downloads/...-privatekey.p12", "notasecret")')" $ heroku config:set GOOGLE_API_EMAILADDRESS=...@developer.gserviceaccount.com
で、これを使用してKeyを生成するようにコードを変更する。
require 'google/api_client' client = Google::APIClient.new(:application_name => '') key = OpenSSL::PKey::RSA.new(ENV['GOOGLE_API_KEY']) client.authorization = Signet::OAuth2::Client.new( :token_credential_uri => 'https://accounts.google.com/o/oauth2/token', :audience => 'https://accounts.google.com/o/oauth2/token', :scope => 'https://www.googleapis.com/auth/urlshortener', :issuer => ENV['GOOGLE_API_EMAILADDRESS'], :signing_key => key, ) client.authorization.fetch_access_token! shortener = client.discovered_api('urlshortener') result = client.execute( :api_method => shortener.url.insert, :body_object => { :longUrl => 'http://d.hatena.ne.jp/sugyan/' }, ) puts result.data.id
これで、このへんをcommitしてherokuにpushすると、設定したconfigの文字列を使用してheroku上からService Accounts認証を使ってAPIを叩けるようになる。
$ git push heroku master Counting objects: 6, done. Delta compression using up to 8 threads. Compressing objects: 100% (5/5), done. Writing objects: 100% (6/6), 1.10 KiB, done. Total 6 (delta 0), reused 0 (delta 0) -----> Ruby app detected -----> Installing dependencies using Bundler version 1.3.0.pre.5 Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin --deployment Fetching gem metadata from http://rubygems.org/......... Fetching gem metadata from http://rubygems.org/.. Installing addressable (2.3.2) Installing extlib (0.9.16) Installing multi_json (1.5.0) Installing autoparse (0.3.2) Installing multipart-post (1.1.5) Installing faraday (0.8.4) Installing jwt (0.1.5) Installing launchy (2.1.2) Installing signet (0.4.4) Installing uuidtools (2.1.3) Installing google-api-client (0.6.0) Using bundler (1.3.0.pre.5) Your bundle is complete! It was installed into ./vendor/bundle Cleaning up the bundler cache. ... * [new branch] master -> master $ heroku run bundle exec ruby shorten.rb http://goo.gl/WnqP5
まとめ
…と、そういうようなことが、以下の記事に書いてあった。
http://ar.zu.my/how-to-store-private-key-files-in-heroku/