ハンズオン rails new から OAuth2 を使った機能を実装するまで

今年は OAuth2 の仕組みを使ってユーザーに認可を求める Web アプリをたくさん書きました。この振る舞いを実現するために、難しいことは必要ないのですが、いくつかの仕組みをまたいだ作業が必要です。ということで手順のまとめ記事です。

ところで、 ruby で google-apis-ruby-client を使うのが半端なく面倒くさいという記事があるようですが、たぶんどの言語、どのライブラリを使ってもこの手順になるんじゃないかと思っています。

ここでは、 hampton という名前のプロジェクトを生成していきます。以下のコマンドは、 Rails のプロジェクトを生成し、コードを一式そろえてくれます。Rails プロジェクトのフォルダ構造や、どこになにが書いてあるとかは、 https://guides.rubyonrails.org/getting_started.html を読むと良いと思いますが、いまは気にせずに作業していきましょう。

# プロジェクトを生成してエディタで開く

以下のコマンドを実行します。

```
rails new hampton
```

画面がこういう感じになると思います。

![スクリーンショット 2021-12-31 13.17.46.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/158ba0c9-c4d1-0a71-c709-dbc32a8b8e80.png)

生成したプロジェクトの中に `cd` で移動して、テキストエディタ (Visual Code Studio) を起動します。

```
cd hampton
code .
```

![スクリーンショット 2021-12-31 13.19.06.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/2d0f9f91-feef-35b0-0454-a2db5e27ae7d.png)

以下のコマンドを実行して、開発用サーバーを起動します。

```
rails server
```

![スクリーンショット 2021-12-31 13.27.24.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/601ec719-c26e-9d67-f1c2-11aebf69abd4.png)

このような画面になったら、サーバーが起動しています。ブラウザで http://localhost:3000 を開きましょう。

![スクリーンショット 2021-12-31 13.28.27.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/1e94eacc-1e10-d2cf-ff08-82093fc061ff.png)

この画面が表示されていれば、正しく動作しています。ここまでで準備ができたので、これからコードを書いていきます。

# まずユーザーを登録できるようにする

```
rails generate scaffold user email:string name:string
rake db:migrate
```

さきほどと同じくブラウザで開発用のサーバーを開くのですが、先ほどの URL と違い、 http://localhost:3000/users を開きます。すると下の画面が表示されます。

![スクリーンショット 2021-12-31 13.28.31.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/296c2e67-2d2f-9f97-9253-a990af65fdce.png)

ユーザーを登録しましょう。

![スクリーンショット 2021-12-31 13.31.49.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/db46afb9-608b-a639-ad37-dd96293a9eec.png)
![スクリーンショット 2021-12-31 13.45.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/c83f2864-5108-f8c7-f2ab-bf7f7e694d4e.png)

# google-api-client という gem を追加する


```Gemfile
gem 'google-api-client'
```


```
bundle install
```

# 飛ばす部分をつくる


```
rails generate controller oauth2_sessions 
```

これが

```
  resources :users

```

こうなる

```
  resources :users do
    get "oauth2_sessions" => "oauth2_sessions#new"
    get "oauth2_callback" => "oauth2_sessions#callback", on: :collection
  end

```


これが

```
class Oauth2SessionsController < ApplicationController

end


```

```
class Oauth2SessionsController < ApplicationController

  def new
  end

end
```

さっきの画面 

![スクリーンショット 2021-12-31 13.45.26.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/c83f2864-5108-f8c7-f2ab-bf7f7e694d4e.png)


この画面の URL の末尾に `/oauth2_sessions` を付け足します。

![スクリーンショット 2021-12-31 13.45.35.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/443c350a-512a-560f-5a3c-ae1d0be91e1b.png)

この画面が出ると思います。これはエラー画面ですが、正解です。

![スクリーンショット 2021-12-31 13.45.12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/10698/bde6684d-5aea-5ee5-3360-abcffab3322b.png)

# Token の保存場所をつくっておく

(この部分は以前に[ActiveRecord を googleauth の TokenStore として使う方法](https://qiita.com/ohataken/items/c4187df00119989c3d44)という記事に書いたものとほぼ同じ内容です)

```
rails generate model google_auth_token token_key:string client_id:string 
```


```google_auth_token.rb
class GoogleAuthToken < ApplicationRecord
  validates :token_key, presence: true, uniqueness: true
  validates :client_id, presence: true
end
```

```active_record_token_store.rb
class ActiveRecordTokenStore < Google::Auth::TokenStore
  def load id
    record = GoogleAuthTokenStore.find_by! token_key: id
    record.to_json
  end

  def store id, token
    json = JSON.parse(token)
    client_id = json["client_id"]
    access_token = json["access_token"]
    refresh_token = json["refresh_token"]
    expiration_time_millis = json["expiration_time_millis"]
    record = GoogleAuthToken.find_or_initialize_by token_key: id, client_id: client_id, access_token: access_token, refresh_token: refresh_token, expiration_time_millis: expiration_time_millis
    record.save
  end

  def delete id
    record = GoogleAuthToken.find_by! token_key: id
    record.destroy
  end
end


```



```oauth2_sessions

class Oauth2SessionsController < ApplicationController

  def new
    @user = User.find(params[:user_id])

    key = ENV.fetch 'GOOGLE_CREDENTIALS_KEY'
    secret = ENV.fetch 'GOOGLE_CREDENTIALS_SECRET'
    callback = ENV.fetch 'GOOGLE_CREDENTIALS_CALLBACK'
    token_store = ActiveRecordTokenStore.new

    client_id = Google::Auth::ClientId.new key, secret
    user_authorizer = Google::Auth::UserAuthorizer.new client_id, token_store, callback

    redirect_to user_authorizer.get_authorization_url
  end

end



```

# GCP で鍵を生成して、環境変数から読み込ませる

* ID
* Secret
* コールバック URL に http://localhost:3000/users/oauth2_callback を追加しておく

```
GOOGLE_CREDENTIALS_KEY=xxxxxxxxxxxxxxxxxxxxx GOOGLE_CREDENTIALS_SECRET=xxxxxxxxxxxxxxxxxxxxxxx GOOGLE_CREDENTIALS_CALLBACK=http://localhost:3000/users/oauth2_callback rails server
```


# コールバック側を実装する

```
  def callback
    key = ENV.fetch 'GOOGLE_CREDENTIALS_KEY'
    secret = ENV.fetch 'GOOGLE_CREDENTIALS_SECRET'
    callback = ENV.fetch 'GOOGLE_CREDENTIALS_CALLBACK'
    token_store = ActiveRecordTokenStore.new

    client_id = Google::Auth::ClientId.new key, secret
    user_authorizer = Google::Auth::UserAuthorizer.new client_id, token_store, callback
    user_authorizer.get_and_store_credentials_from_code code: params[:code]

    redirect_to root_path
  end

```