RailsアプリケーションにおけるBasic認証導入方法

# はじめに
RailsアプリケーションでBasic認証を導入する方法について書いていきます。
自動デプロイを実行すると、デフォルトでは世界中の誰でもアクセスできる状態にあります。
まだ未完成のページにアクセスして欲しくない、ある一部の人のみに見てもらいたいなど
そういうときにBasic認証を導入して閲覧を制限していきます。

# Basic認証

## Basic認証とは
HTTP通信の企画に備え付けられているユーザー認証機能の仕組みです。
サーバーと通信可能なユーザーとパスワードをあらかじめ設定しておき、それを知っているユーザーのみがWebアプリケーションを利用できるようにすることができる。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/414101/7f3a9c12-c0ec-5d46-c7db-5fac6a440d6b.png)
Ruby on Railsには、Basic認証を導入するためのメソッドが用意されており、簡単に実装できます。

## Basic認証の問題点
Basic認証は少ない手間で認証を実現できるので、便利ですが、安全性という観点から、完全に信頼できる認証方式ではありません、HTTP通信で定義されている使用上、**ユーザー名とパスワードが通信経路上にそのまま送られるため、漏洩のリスクがあります。**また、ログアウトの概念が定義されていないため、もし必要になる場合は自力で実装する必要があるほか、複数のサーバーを跨いだ認証が難しいといった特徴も問題になり得ます。
あくまで、**必要最低限の認証機能を作成中のwebアプリケーションに実装したい場合のみ、Basic認証を利用するようにしましょう。**

## authenticate_or_request_with_http_basic
Ruby on RailsでBasic認証を実装するためのメソッドです。
ブロックを開き、ブロック内部で**username**と**password**を設定することでBasic認証を利用できます。

```ruby:sample.rb
# 'test'というユーザー名と、'password'というパスワードBasic認証できるように設定
authenticate_or_request_with_http_basic do |username, password|
  username == 'test' && password == 'password'
end
```

## Basic認証のRailsアプリケーションへの導入
`authenticate_or_request_with_http_basic`メソッドをを利用して、開発中のRailsアプリケーションにBasic認証を導入していきましょう。

Basic認証によるログインの要求は、全てのコントローラーで行うのがいいでしょう。
そこで、Basic認証の処理を**application_controller.rb**にて、private以下にメソッドとして定義し、**before_action**で呼び出すよう実装しましょう。

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :basic_auth
  
  private
  
  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == 'test' && password == 'password'
    end
  end
end
```

## Basic認証の動作を確認する
application_controller.rbの記述が完了したら、実際にアプリケーションを起動し、Basic認証の動作が期待したものになっているか確認してみましょう。
**'rails s'**をした上で、適当なページにアクセスすると、ユーザー名とパスワードを求めるポップアップウィンドウが表示されます。間違ったユーザー名とパスワードではBasic認証できないことを確かめるために、あえて適当な文字を入力してみましょう。

正しく実装されていれば、もう一度**Basic認証用のポップアップウィンドウが表示されます。**間違った情報ではログインできないことを確認できたので、今度は正しい情報を入力してログインできるかどうかを確かめてみましょう。
ユーザー名に**test**、パスワードに**password**と入力して、もう一度ログインを試みてください。
ルーティングに対応したページが表示されれば、Basic認証に成功しています。

## Basic認証のコードを改良する
ここまでの実装で、Basic認証をRailsアプリケーションに導入することができました。
しかしながら、現状のコードには次の2つの問題点があります。

### (1)ユーザー名・パスワードがコードに記述されている
Basic認証するための、**test**というユーザー名と、そのパスワード**password**がコードに記述されています。もしもGithub嬢の公開リポジトリでソースコードを管理している場合、コードを読める何者かに不正にBasic認証を突破されてしまいます。
対策として、コードに直接ユーザー名とパスワードを記述するのではなく、**環境変数を離党する実装に切り替える**ことが考えられます。

### (2)全ての環境でBasic認証を要求してしまう
Basic認証を使ってアクセスしたいのは、**アプリケーションがデプロイされている本番環境だけのはずです**。ですが、現在のコードでは**basic_auth**メソッドを使いたい環境を特に指定していないため、本番環境・テスト環境・開発環境の全てでBasic環境が動いてしまいます。開発中にいちいちBasic認証をするのは煩わしいので、**本番環境のみでBasic認証をするようにコードを書き換えましょう。**

## ユーザー名・パスワードを環境変数にする
**basic_auth**メソッド内で直接記述しているユーザー名とパズワードを環境変数に格納しましょう
:warning:**環境変数は、環境毎に設定する必要があります。開発環境でBasic認証をしたいときはローカルの~/.bash_profileを、本番環境でBasic認証をしたい場合は本番サーバーにssh接続して、~/.bash_profileを編集しましょう！**

```terminal:ターミナル
$ vim ~/.bash_profile

# .bash_profileを開いたら「i」とタイプしてインサートモードに移行

# .bash_profileの内部に次の記述を追加
export BASIC_AUTH_USER='test'
export BASIC_AUTH_PASSWORD='password'
# 記述を追加したら、escキーを押してインサートモードを抜け、「:wq」と入力して保存して終了する

# .bash_profileを再読み込みし、定義した環境変数を有効にする
$ source ~/.bash_profile
```
`BASIC_AUTH_USER`と`BASIC_AUTH_PASSWORD`という名前で、それぞれユーザー名とパスワードを定義することができました。続いてこの環境変数をRailsアプリケーション側で読み込むように記述を変更しましょう。

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :basic_auth
  protect_from_forgery with: :exception
  
  private
  
  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end
end
```
これで環境変数を使って、Basic認証を行えるユーザー名とパスワードを定義することができました。もう一度Basic認証を試みて、環境変数として設定したユーザー名とパスワードでログインができれば、正しく実装できています。

# Capistranoを利用して自動デプロイを行う場合
今回はCapistranoを利用して自動デプロイを行っていきます。
この時、環境変数を明示的に指定する必要があります。
これを行わないと、導入した後にユーザーとパスワードを入力してもログインすることができません。
導入方法は以下の二つがあります。
**(1) config/deploy.rbに追記する方法**
**(2) /etc/environmentに環境変数を入力する**

今回は(2)を用いて環境変数を指定します。

## /etc/enviromentに環境変数を入力する
**/.bash profile**に入力した環境変数を入力していきましょう
:warning: **/etc/enviromentは読み取り専用ファイルなので、```sudo```をつけて入力する必要があります。**

```terminal:ターミナル
[ec2-user@[IPアドレス] ~]$ sudo vim /etc/environment

# /etc/environmentを開いたら「i」とタイプしてインサートモードに移行
BASIC_AUTH_USER='設定したユーザー名'
BASIC_AUTH_PASSWORD='設定したパスワード'
# 記述を追加したら、escキーを押してインサートモードを抜け、「:wq」と入力して保存して終了する

# /etc/environmentを再読み込みし、定義した環境変数を有効にする
[ec2-user@[IPアドレス] ~]$ source /etc/environment 
```

# 本番環境のみでBasic認証をするようにする
続いて、本番環境のみでBasic認証をするようにします。
まずはconfig/deploy配下のproduction.rbに追記します（すでに追加済みなら作業不要です）

```ruby:config/deploy/production.rb
# server "db.example.com", user: "deploy", roles: %w{db}
server "(EC2のIPアドレス)", user: "ec2-user", roles: %w{app db web}

set :rails_env, "production"
set :unicorn_rack_env, "production"

# role-based syntax
# ==================
```
これでunicornが現在の環境を本番環境として認識するようになりました。
さらにapplication_controllerの記述を少し変更します。

```ruby:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :basic_auth, if: :production?
  protect_from_forgery with: :exception

  private

  def production?
    Rails.env.production?
  end

  def basic_auth
    authenticate_or_request_with_http_basic do |username, password|
      username == ENV["BASIC_AUTH_USER"] && password == ENV["BASIC_AUTH_PASSWORD"]
    end
  end
end
```
``production?``というメソッドを定義し、現在の環境が本番環境ならtrue、そうでないならfalseを返すように、**Rails.env.production?**と記述しています。そして、**before_action :basic_auth**の後に、 **if: :production?**と記述することによって、本番環境のみで**basic_auth**メソッドが実行されるようになります。

# Basic認証の動作確認を行う
続いて、Basic認証の動作確認を行います。
実装したブランチをマージしたらCapistranoを用いたデプロイを行いましょう。
初めてBasic認証の動作確認をするときだけ、以下のようにデプロイをしてunicornの停止と起動を行ってから動作確認します。

```terminal:開発端末上での操作
$ bundle exec cap production deploy unicorn:stop
(デプロイ完了まで待ち、完了後に以下を実行)

$ bundle exec cap production deploy unicorn:start
```
もしうまく動かない場合は、一度AWSのコンソールからEC2を再起動し、nginx、MySQL、Unicornを手動で順番に再起動してください。
なおUnicornは以下のように起動しましょう

```terminal:EC2上での操作
$ cd /var/www/アプリケーション名/current
$ bundle exec unicorn -c /var/www/アプリケーション名/current/config/unicorn.rb -E production -D
```

これでページにアクセスし、Basic認証のポップアップが出現します。
設定したユーザーとパスワードを入力し、ページにアクセスできたら成功です！

#参考サイト
・Basic認証 Wikipedia
https://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC

Basic認証はHTTPで定義されている認証方法です。どのようなリクエスト・レスポンスを送っているのか気になる方はご確認ください。

・Rails.env リファレンス(英語)
https://apidock.com/rails/Rails/env/class

今回の実装で使用したRails.envについて、具体的な説明はありませんが、コード例が載っています。