【Rails】初めてのBasic認証

- RailsへのBasic認証の導入についての備忘録。
- unicorn、Nginx使用。





# Basic認証とは
- HTTP通信の規格に備え付けられてるユーザー認証の仕組み。
- サーバーと通信可能なユーザーとPWを設定し、ユーザーを制限する。
- Railsには、Basic認証導入のメソッドがあるので、簡単に実装できる。

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

```rb:例)ユーザー名:'admin'、パスワード:'password'で、Basic認証を設定する場合
authenticate_or_request_with_http_basic do |username, password|
  username == 'ユーザー名' && password == 'パスワード'
end
```

# RailsアプリへのBasic認証導入
- Basic認証によるログイン要求は、全コントローラで行いたいので、Basic認証の処理は、 application_controller.rb で、private下にメソッドとして定義し、before_actionで呼び出す形にする。

```rb: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 == 'admin' && password == 'pass'
    end
  end
end
```
- これで、全ページで、Basic認証が要求されるハズ。ポップアップウインドウが表示される。







## Basic認証の設定変更
1. 上の方法では、コード中にユーザー名・PWが記述されてるので、誰でも取得できてしまうので、環境変数で定義するべき。
1. 全環境でBasic認証を要求してしまう状態。アクセス制限は本番環境のみにしたい。



### 1. ユーザー名、PWを環境変数にする
- basic_authメソッド内のユーザー名とPWを環境変数に格納。
- **環境変数は、環境ごとに設定する**必要がある。

```rb:ローカル環境の設定(ターミナル)
% vim ~/.bash_profile       /# .bash_profileを開き、「i」でインサートモードに移行

# .bash_profile内に、ユーザー名とPWの記述を追加
export BASIC_AUTH_USER='admin'
export BASIC_AUTH_PASSWORD='pass'
# 「escキー」でインサートモードを抜け、「:wq」 (保存して終了)

% source ~/.bash_profile   /# .bash_profileを再読み込みし、定義した環境変数を有効にする
```
```rb:本番環境の設定(ターミナル)
% ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]         # ダウンロードした鍵を用い、ec2-userとしてログイン
% sudo vim /etc/environment

# 「i」でインサートモードに移行し、下記を追記。
BASIC_AUTH_USER='admin'
BASIC_AUTH_PASSWORD='pass'
# 「escapeキー」で終了後、「:wq」 (保存して終了)

# 環境変数を適用するため、一旦ログアウト。
% exit
% ssh -i [ダウンロードした鍵の名前].pem ec2-user@[作成したEC2インスタンスと紐付けたElastic IP]
# 環境変数が適用されたか確認
% env | grep BASIC_AUTH_USER
% env | grep BASIC_AUTH_PASSWORD
```
- **BASIC_AUTH_USERとBASIC_AUTH_PASSWORD**という名前で、ユーザー名とPWを定義したので、この環境変数をアプリ側で読み込むようにする。


```rb: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
```
- 環境変数は、環境ごとに設定する必要がある。
  - 開発環境は、ローカルの ~/.bash_profile。
  - 本番環境は、ssh接続し、 ~/.bash_profile に記述。
- Capistranoで自動デプロイする場合、環境変数を明示的に指定する必要がある(config/deploy/production.rbへ追記が必要)。

### 2. 本番環境のみでBasic認証する

```rb: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にも追記。

```rb:app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
  before_action :basic_auth, if: :production?    # 本番環境だけで、basic_authメソッドを実行するように変更
  protect_from_forgery with: :exception

  private
  def production?   # 現在の環境が本番環境か？のメソッドを定義
    Rails.env.production?    # true/falseを返す
  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
```

### 参)ローカルと本番環境で処理を分ける書き方

```rb:例)環境で処理を分ける
# 本番環境
  if Rails.env == 'production'
    # 処理内容
  end

# 条件分岐
  if Rails.env.development?     # ローカル環境
    # 処理内容
  elsif Rails.env.production?   # 本番環境
    # 処理内容
  elsif Rails.env.test?         # テスト環境
    # 処理内容
  else                          # その他の環境
    # 処理内容
  end
```




## 動作確認
- マージ後、Capistranoでデプロイして、確認。
- 初めてBasic認証の動作確認をする時だけ、デプロイしてunicornを 停止 + 起動してから動作確認する。

```rb:ターミナル(開発端末上での操作)
% bundle exec cap production deploy unicorn:stop

# デプロイ完了後、↓を実行
% bundle exec cap production deploy unicorn:start
```
- 動かない時は、AWSのコンソールからEC2を再起動し、nginx、MySQL、Unicornを手動で順番に再起動する。

```rb:EC2上での操作
% cd /var/www/アプリ名/current
% bundle exec unicorn -c /var/www/アプリ名/current/config/unicorn.rb -E production -D
```




# Basic認証の問題点
- 安全性の観点から、完全に信頼できる認証方式ではない。
- Basic認証は、HTTPで定義されている認証方法。HTTP通信で定義されてる仕様上、ユーザー名とPWが通信経路上にそのまま送られるため、漏洩リスクがある。
- ログアウトの概念が定義されてないので、必要な場合は、自力で実装する必要がある。複数サーバーを跨いだ認証が難しいという特徴も問題。
- 必要最低限の認証機能を実装したい場合のみ、Basic認証を利用する。

## 参考リンク
- [Basic認証 Wikipedia](https://ja.wikipedia.org/wiki/Basic%E8%AA%8D%E8%A8%BC)  
- [Rails.env リファレンス(英語)](https://apidock.com/rails/Rails/env/class)