[sinatra].htpasswdを使ったbasic認証をやってみる
sinatraで作っているアプリにbasic認証をかけようと思って調べていたのですが、公式に載っているサンプルだと一組のid、パスワードしか扱えなかったので、.htpasswdを読み込んで使えるようにできないかやってみました。
# app.rb
require 'sinatra/base'
module BasicSample
class App < Sinatra::Base
HTPASSWD_PATH = '.htpasswd'
helpers do
def protect!
unless authorized?
response['WWW-Authenticate'] = %(Basic realm="Restricted Area")
throw(:halt, [401, "Not authorized\n"])
end
end
def authorized?
@auth ||= Rack::Auth::Basic::Request.new(request.env)
passwd = File.open(HTPASSWD_PATH).read.split("\n").map {|credential| credential.split(':')}
if @auth.provided? && @auth.basic? && @auth.credentials
user, pass = @auth.credentials
auth = passwd.assoc(user)
return false unless auth
# crypt済みパスワードの先頭2文字がsalt
[user, pass.crypt(auth[1][0..2])] == auth
end
end
end
get '/' do
protect!
"Hello #{@auth.username}!"
end
get '/logout' do
halt 401
end
end
end
# config.ru
require './app.rb'
run BasicSample::App
# Gemfile
source "https://rubygems.org"
gem 'sinatra'
これで、
bundle install --path=vendor/bundler
bundle exec rackup config.ru -p 4567
とすれば.htpasswdを読み込んでbasic認証をかけるアプリが起動します。
.htpasswdにfooユーザが存在している場合、認証が通ると「Hello foo!」と表示されます。
authorized?メソッドの中で、.htpasswdファイルの中身を解析して、ユーザが入力したid, パスワードと一致するかチェックしています。
basic認証の場合、一度認証するとブラウザを閉じるまでAuthorizationヘッダを送りつづけるので、/logoutにアクセスすると強制的に401レスポンスを送って、ログアウト状態にするようにしました。
とりあえずは動いたけど、こんな感じでいいんだろうか。