Help us understand the problem. What is going on with this article?

[node] sailsでPassportを使わずにBasic認証する方法

More than 5 years have passed since last update.

passportを使ってBasic認証する方法はここに書いてありました。

とても参考になりました。

Sails.js + PassportでID/Password認証

ただ、passportを使うということはブラウザからアクセスするということです。

クッキーにセッションを保存しておいて、2度目以降はそれで認証する方法です。

しかしAPIを作って公開したい場合には、ブラウザ以外からサーバーにアクセスすることになります。

ここでは毎回IDとpasswordをサーバーに送って認証するやりかたを書いておきたいと思います。

まず上記の記事の前半部分でUserモデルを作って[user:hoge,password:fuga]のアカウントを作る所までを省略します。

Passport 承認処理設定の前まで、つまりその他hook処理までを終えている前提で始めます。

この記事はCoffee-Scriptで記述されています

RestController.coffee

まずRestController.coffeeを作ります。

sails generate controller rest test --coffee

test部分を以下のように書き換えます。

  test:(req,res)->
    name = req.user.username
    res.json hello: name

認証がOKならreqオブジェクトからusernameを取り出して、hello:"(name)"というJSONを返すだけです。

認証が失敗した場合はここまで処理されずにエラーオブジェクトが返ります。

config/policies.js

最初からある、config/policies.jsファイルを書き換えます。

  RestController: {
    '*': "basicAuth"
  }

これで、RestControllerが呼ばれたらその前にbasicAuth関数が呼ばれるようになります。

api/policies/basicAuth.coffee

api/policies以下にbasicAuth.coffeeを作ります。

上記のconfig/policies.jsの設定により、RestControllerの前に呼ばれます。
このファイルのポイントは以下の部分です。

module.exports = (req, res, next)->

つまりこの中でIDとpassをチェックして、OKならnext()を呼びます。

next()が呼ばれなければRestControllerは呼ばれません。

その場合は必ずres.json()でエラーオブジェクトを返さないと、クライアントはレスポンス待ちの状態になってしまいます。

以下、コードです。

bcrypt = require('bcrypt')
findByUsername = (u, fn)->
  User.findOne(username: u).exec (err, user)->
    if (err)
      return fn(null, null)
    else
      return fn(null, user)
isBasicAuth = (username, password, done)->
  process.nextTick ()->
    findByUsername username, (err, user)->
      return done(null, err) if err
      unless user
        return done(null, false, {
          message: 'Unknown user ' + username
          })
      bcrypt.compare password, user.password, (err, res)->
        unless res
          return done(null, false, message: 'Invalid Password' )
        returnUser =
          username: user.username,
          id: user.id
        return done(null, returnUser,message: 'Logged In Successfully')
module.exports = (req, res, next)->
  try
    # エラー定義
    e = errors:[message: 'Bad Authentication data',code: 215]
    if s = req.get('authorization')
      authStr = s.replace("Basic ",'')
      auth = new Buffer(authStr, 'base64').toString().split(":")
      # auth = [ 'username11', 'password11' ]
      isBasicAuth auth[0],auth[1],(err,user,msg)->
        console.log msg if msg
        if err
          return res.json(err)
        else if user
          req.user = user
          return next()
        else
          return res.json(e)
    else
      return res.json(e)
  catch err
    return res.json(err)

ここに書いてあるpassportの設定方法を流用していますが、ポイントがいくつかあるので解説します。

sails liftした場合に、ブラウザで呼び出すにはhttp://localhost:1337/rest/testにアクセスします。

これをcurlコマンドで呼び出す場合は

curl -u "hoge:fuga" http://localhost:1337/rest/test

となります。

-uオプションの部分でidとパスワードを設定できます。

クライアントから送られてきたidとパスワードは、サーバー側(sails側)ではreq.header.authorizationにあります。

req.getメソッドで取り出せます。

console.log req.get('authorization')
# 'Basic aG9nZTpmdWdh'

しかしこのままではbase64でエンコードされていますのでデコードします。

authStr = s.replace("Basic ",'')
auth = new Buffer(authStr, 'base64').toString().split(":")
# auth = [ 'username11', 'password11' ]

これでクライアントから送られてきたidとパスワードが取得出来ました。

あとはDBからhogeユーザーの情報を取り出してbcrypt.compare()でpasswordが合っているか照合するだけです。

isBasicAuthメソッドでuser情報が返ってきますので、reqオブジェクトに追加しておきます。

req.user = user
return next()

これで認証がOKとなり、RestControllerへ処理が流れます。

RestControllerからはreq.userでユーザーの情報が取り出せます。

検証

sails liftして別のターミナルから呼んでみます。

curl -u "hoge:fuga" http://localhost:1337/rest/test
{
  "hello": "hoge"
}

わざと認証失敗させてみます。

curl -u "hogeo:fugao" http://localhost:1337/rest/test
{
  "errors": [
    {
      "message": "Bad Authentication data",
      "code": 215
    }
  ]
}

以上です。

__mick
nodejs,coffee-script等で遊んでいます。 https://github.com/mick-whats
https://twitter.com/mick_x_
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away