【Rails】Reactとaxiosを用いて非同期でdevise-auth-tokenを利用したのサインアウトを行う

## 実装方針

- devise-auth-tokenで使用する、uid, client, access-tokenの3つが全て存在する場合のみサインアウトする
- csrf-tokenのチェックを事前に行う
- client, tokenをdestroyアクション内で削除
- csrf-token,sessionをafter_actionで削除

## フロントエンド

###  リクエストの送信

```jsx
getCsrfToken() {
    if (!(axios.defaults.headers.common['X-CSRF-Token'])) {
      return (
        document.getElementsByName('csrf-token')[0].getAttribute('content')
      )
    } else {
      return (
        axios.defaults.headers.common['X-CSRF-Token']
      )
    }
  };


setAxiosDefaults() {
    axios.defaults.headers.common['X-CSRF-Token'] = this.getCsrfToken();
  };

userAuthentification() {
    if (axios.defaults.headers.common['uid'] && axios.defaults.headers.common['client'] && axios.defaults.headers.common['access-token']) {
      axios.defaults.headers.common['uid']
      axios.defaults.headers.common['client']
      axios.defaults.headers.common['access-token']
    } else {
      return null
    }
  }


if (this.props.content == 'SignOut') {
      this.setAxiosDefaults();
      this.userAuthentification()
      axios
      .delete('/api/v1/users/sign_out', {uid: axios.defaults.headers.common['uid']})
```

      this.setAxiosDefaults();
      this.userAuthentification()

この2行でそれぞれcsrf-tokenとuser情報をrequest.headersにセットする。後者は新規登録時に発行される情報で、devise-auth-tokenを用いると発行されるもの。

```ruby
def update_auth_header 
    @token = @user.create_token
    return unless @user && @token.client
    @token.client = nil unless @used_auth_by_token
    if @used_auth_by_token && !DeviseTokenAuth.change_headers_on_each_request
      auth_header = @user.build_auth_header(@token.token, @token.client)
      response.headers.merge!(auth_header)  
    else
      unless @user.reload.valid?
        @user = @user.class.find(@user.to_param) 
        unless @user.valid?
          raise DeviseTokenAuth::Errors::InvalidModel, "Cannot set auth token in invalid model. Errors: #{@resource.errors.full_messages}"
        end
      end
      refresh_headers
    end
  end
```

これを新規登録後に読み込むことで request.headerにclient、tokenをmerge。uidはデフォルトでresponse.headersに含まれている。

```jsx
      .delete('/api/v1/users/sign_out', {uid: axios.defaults.headers.common['uid']})
```

ここは普段どおりのaxiosのリクエスト。パラメータにはuserの識別子となるuidを渡す。

## サーバーサイド

```ruby
def destroy  
  @user = User.find_for_database_authentication(uid: request.headers['uid'])
  @token = request.headers['access-token']
  @client = request.headers['client']
  if @user && @client && @token
    @token.clear 
    @client = nil
    render_destroy_success
  else
    render_destroy_error
  end
end
```

リクエストヘッダーからuid(email)を取り出してインスタンスをDBから参照し、token、clientもそれぞれリクエストヘッダーから定義する。
user,token,client全てが存在しないとログアウトできないようにしている。
次にtokenとclientを削除することでクライアント側でuserが存在しない状態にし、ログアウト処理完了。

destroyアクション後にafter_actionで以下を読み込む

```ruby
after_action :set_csrf_token_header
after_action :reset_session, only: [:destroy]
```

これでcsrf-tokenとsessionを削除

## 挙動
![recommended-books-2-2.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/652172/48941fa0-fc33-c439-7ed6-429421235742.gif)


### 感想

結局deviseのコントローラー0から改造することになってしまった。
まだ処理内容わかっていない点も多いのでこの後コメントアウトをつける作業に移る。

### 参考

devise-auth-token公式
https://github.com/lynndylanhurley/devise_token_auth/blob/master/app/controllers/devise_token_auth/sessions_controller.rb

### 次やること

- 結合テスト
- コメントアウトつける
- react-routerの導入