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
}
]
}
以上です。