何しているのかわからなかったのでなんとなくドキュメントとかコメントとか読む。
読むのはパッケージの中身と公式のサンプルコード。
おことわり
思っていたより大作(笑)になってしまいました。
たぶんわかっている人からしたら基本的なことしか書いていません。
動かして確認しながら書いていますが、確認していない部分もあります。
自分用メモ的要素が強いです。
読みにくい部分があるかもしれません。というかあります。
ところどころ間違っていたりニュアンスが違っているところがあるかもしれません。
また、バージョンによっても異なる可能性もあるので読む場合は鵜呑みにしないでください。
文中で言っている「コメント」とは、サンプルコード内とPassport本体のパッケージ内に記述されているコメントのこと。
ストラテジとは、Passportで使用する認証方法です。たぶん。
Twitter、Google、JWTなどいっぱいあって自分が使いたいストラテジを選んで使う。
主な関数
-
initialize
初期化の関数。 -
use
ストラテジを取り込む。 -
authenticate
使用するストラテジを指定する。 -
session
セッション情報での認証を行う。 -
serializeUser
ユーザー情報をセッションに保存する(方法を指定する)。 -
deserializeUser
セッション情報からユーザーを復元する(方法を指定する)。
コールバック関数として渡されるdone
は基本的に実行しないと処理が止まってしまうので注意。
initialize
Passportを初期化する関数らしい。
でもサンプルコードでは使われていない。謎。
コメントと翻訳
Passport's primary initialization middleware.
This middleware must be in use by the Connect/Express application for Passport to operate.
Options:
userProperty
Property to set onreq
upon login, defaults to user
Passportの主要な初期化ミドルウェアです。
Passportが動作するためには、このミドルウェアがConnect/Expressアプリケーションによって使用されている必要があります。オプション:
userProperty
ログイン時に "req" に設定するプロパティ、デフォルトは user です。
Intializes Passport for incoming requests, allowing authentication strategies to be applied.
If sessions are being utilized, applications must set up Passport with functions to serialize a user into and out of a session. For example, a common pattern is to serialize just the user ID into the session (due to the fact that it is desirable to store the minimum amount of data in a session).
When a subsequent request arrives for the session, the full User object can be loaded from the database by ID.
Note that additional middleware is required to persist login state, so we must use theconnect.session()
middleware beforepassport.initialize()
.
If sessions are being used, this middleware must be in use by the Connect/Express application for Passport to operate. If the application is entirely stateless (not using sessions), this middleware is not necessary, but its use will not have any adverse impact.
受信したリクエストに対してPassportを初期化し、認証戦略を適用することができるようにする。
セッションを使用する場合、アプリケーションは、セッションへのユーザーの入出力をシリアライズする機能をPassportにセットアップする必要があります。 たとえば、一般的なパターンは、ユーザーIDだけをセッションにシリアライズすることです(セッションに最小限のデータを保存することが望ましいという事実があるため)。
そのセッションに対して後続のリクエストが来たとき、完全なUserオブジェクトをIDでデータベースからロードすることができます。
ログイン状態を保持するためには、追加のミドルウェアが必要であることに注意してください。
セッションを使用する場合、Passport が動作するためには、このミドルウェアが Connect/Express アプリケーションによって使用されている必要があります。 アプリケーションが完全にステートレスである場合(セッションを使用しない)、このミドルウェアは必要ありません。しかし、その使用による悪影響はありません。
// デフォルト
// req.userに情報が入る。
app.use(passport.initialize())
router.get('/', function(req, res, next) {
res.render('index', { user: req.user})
})
// プロパティ指定
// req.currentUserに情報が入る。
app.use(passport.initialize({userProperty: 'currentUser'}))
router.get('/', function(req, res, next) {
res.render('index', { user: req.currentUser})
})
デフォルトではreq.user
にログインユーザーの情報が入っているが、initialize
関数のuserProperty
オプションで名前を指定するとそこにユーザー情報が入れられる。
それ以外正直何をしているのかわかっていない。
使用するストラテジによっては以前のPassportのバージョンが必要になる場合があり、その互換性を保つために使う必要があるっぽい。
コメントと翻訳
passport@0.5.1
[removed][1] all internal use ofreq._passport
.
From the standpoint of this package, this should have been a non-breaking change. However, some strategies (such aspassport-azure-ad
) depend directly onpassport@0.4.x
or earlier.
require
-ing earlier versions ofpassport
has the effect of monkeypatchinghttp.IncomingMessage
withlogIn
,logOut
,isAuthenticated
andisUnauthenticated
functions that [expect][2] thereq._passport
property to exist.
Since pre-existing functions onreq
are given [preference][3], this results in [issues][4].
The changes here restore the expected properties needed when earlier versions ofpassport
arerequire
-ed. This compatibility mode is enabled by default, and can be disabld by simply notuse
-ingpassport.initialize()
middleware or settingcompat: false
as an option to the middleware.
An alternative approach to addressing this issue would be to not preferentially use pre-existing functions onreq
, but rather always overwritereq.logIn
, etc. with the versions of those functions shiped withauthenticate()
middleware. This option should be reconsidered in a future major version release.
"passport@0.5.1" [削除][1] "req._passport "のすべての内部使用を削除しました。
このパッケージの立場からすると、これは壊れることのない変更であったはずです。 しかし、いくつかの戦略("passport-azure-ad "など)は、passport@0.4.x
以前に直接 依存しています。 「passport」の以前のバージョンを "require "すると、「http.IncomingMessage」に「logIn」「logOut」「isAuthenticated」「isUnauthenticated」関数をモンキーパッチする効果があり、「req._passport」プロパティが存在すると [expectation][2] 期待されることになります。
req "上の既存の関数が[優先][3]されるため、[問題][4]が発生するのです。
今回の変更により、以前のバージョンの "passport "が "require "されたときに必要とされる期待されるプロパティが復元されました。 この互換モードはデフォルトで有効になっており、ミドルウェア「passport.initialize()」をuseしないか、ミドルウェアのオプションとして「compat: false」を設定するだけで解除することができる。
この問題を解決する方法として、「req」上で既存の関数を優先的に使用せず、「req.logIn」などを常に「authenticate()」ミドルウェアに同梱されているバージョンの関数で上書きすることが考えられます。 このオプションは、将来のメジャーバージョンリリースで再考されるべきものです。
compatはデフォルト有効だしfalse
にしている例もないのでややこしいことしないならとりあえず使っとけ感。
use
引数にストラテジを渡してPassportにストラテジを登録する。
名前の指定なしで実行するとストラテジのデフォルトの名前で登録されるが、第一引数に名前を指定することでストラテジの名前を上書きできる。
コメントと翻訳
ソースコード内The
LocalStrategy
authenticates users by verifying a username and password.
The strategy parses the username and password from the request and calls theverify
function.
Theverify
function queries the database for the user record and verifies the password by hashing the password supplied by the user and comparing it to the hashed password stored in the database. If the comparison succeeds, the user is authenticated; otherwise, not.
LocalStrategyは、ユーザ名とパスワードを検証することでユーザを認証する。
ストラテジーはリクエストからユーザー名とパスワードを解析し、verify関数を呼び出します。
verify関数はデータベースにユーザーレコードを問い合わせ、ユーザーから与えられたパスワードをハッシュ化し、データベースに保存されているハッシュ化されたパスワードと比較することでパスワードを検証する。 比較に成功した場合はユーザが認証され、そうでない場合は認証されません。
パッケージ内
Utilize the given
strategy
with optionalname
, overridding the strategy's default name.
オプションの "name" で指定された "strategy" を利用し、strategy のデフォルト名を上書きする。
// LocalStrategyデフォルトの'local'という名前で登録される。
passport.use(new LocalStrategy(...))
router.post('/login/password', passport.authenticate('local', {...}))
// 名前指定。'api'という名前で登録される。
passport.use('api', new LocalStrategy(...))
router.post('/login/password', passport.authenticate('api', {...}))
あくまで使う予定のストラテジの登録や設定をするだけで、use
を実行するだけでは実際に使われるようになるわけではない。
実際に使う時は後述のauthenticate
を実行する。
ストラテジの引数(new LocalStrategy(...)
の部分)にはストラテジごとにオプションがあり、Passport.jsのStrategiesで見ることができる。
それぞれのストラテジ(すべてかは不明)には認証情報からユーザーを照合するverify
関数を渡す。
verify
関数にはストラテジごとに決められた引数が渡されてくるので、アプリ側でユーザーの照合(のやり方の指定)を行う。
ユーザーの照合が完了したらその結果をverify
関数のdone
関数にdone(error, user, info)
の形で渡す。
エラーがない場合はerror
にはfalse
やnull
を渡す。
info
は成功、失敗時のメッセージの内容を、string
か{type: string, message: string}
の形で渡す。
authenticate
使用時にオプションを指定しないとこちらで設定してもアプリ側でメッセージを拾えないっぽいので注意。(authenticate
のオプションを参照)
公式サンプルコードではこの部分。(done
関数はcb
という名前で受け取っているので注意)
passport.use('str', new LocalStrategy(function verify(username, password, cb) {
db.get('SELECT rowid AS id, * FROM users WHERE username = ?', [ username ], function(err, row) {
if (err) {
return cb(err);
}
if (!row) {
return cb(null, false, { message: 'Incorrect username or password.' })
}
crypto.pbkdf2(password, row.salt, 310000, 32, 'sha256', function(err, hashedPassword) {
if (err) {
return cb(err);
}
if (!crypto.timingSafeEqual(row.hashed_password, hashedPassword)) {
return cb(null, false, { message: 'Incorrect username or password.' });
}
return cb(null, row);
});
});
}));
authenticate
ストラテジ名、オプション、コールバックを引数に取り、(req, res, next)
を引数に取るミドルウェア関数(?)を返す。
コメントと翻訳
Middleware that will authenticate a request using the given
strategy
name, with optionaloptions
andcallback
.
与えられた "strategy "名と、オプションの "options "および "callback "を使用してリクエストを認証するミドルウェアです。
Authenticates requests.
Applies thename
ed strategy (or strategies) to the incoming request, in order to authenticate the request. If authentication is successful, the user will be logged in and populated atreq.user
and a session will be established by default. If authentication fails, an unauthorized response will be sent.Options:
session
Save login state in session, defaults to truesuccessRedirect
After successful login, redirect to given URLsuccessMessage
True to store success message in req.session.messages, or a string to use as override message for success.successFlash
True to flash success messages or a string to use as a flash message for success (overrides any from the strategy itself).failureRedirect
After failed login, redirect to given URLfailureMessage
True to store failure message in req.session.messages, or a string to use as override message for failure.failureFlash
True to flash failure messages or a string to use as a flash message for failures (overrides any from the strategy itself).assignProperty
Assign the object provided by the verify callback to given propertyAn optional
callback
can be supplied to allow the application to override the default manner in which authentication attempts are handled. The callback has the following signature, whereuser
will be set to the authenticated user on a successful authentication attempt, orfalse
otherwise. An optionalinfo
argument will be passed, containing additional details provided by the strategy's verify callback - this could be information about a successful authentication or a challenge message for a failed authentication.
An optionalstatus
argument will be passed when authentication fails - this could be a HTTP response code for a remote authentication failure or similar.
リクエストを認証する。
name "付きのストラテジーを受信したリクエストに適用し、 リクエストを認証する。 認証に成功した場合、ユーザーはログインし、"req.user "に入力され、デフォルトでセッションが確立されます。 認証に失敗した場合は、未承認の応答が送信されます。オプション
session
ログイン状態をセッションに保存、デフォルトは true です。successRedirect
ログインに成功したら、指定された URL にリダイレクトする。successMessage
req.session.messages に成功メッセージを格納する場合は True、成功メッセージのオーバーライドとして使用する場合は文字列を指定する。successFlash
成功メッセージをフラッシュする場合はTrue、成功時のフラッシュメッセージとして使用する場合は文字列(ストラテジー自体からのオーバーライド)failureRedirect
ログインに失敗したら、指定したURLにリダイレクトする。failureMessage
req.session.messages に失敗時のメッセージを保存する場合は true、オーバーライドメッセージとして使用する場合は文字列を指定します。failureFlash
失敗時のメッセージをフラッシュする場合はTrue、失敗時のフラッシュメッセージとして使用する場合は文字列を指定します(ストラテジー自体のメッセージをオーバーライドします)。assignProperty
検証コールバックが提供するオブジェクトを、指定されたプロパティに割り当てます。オプションのcallbackを指定することで、認証の試行が処理される際のデフォルトの方法をアプリケーション側で上書きすることができます。このコールバックは次のようなシグネチャを持ちます。認証に成功すると「user」が認証されたユーザに設定され、そうでなければ「false」が設定されます。 オプションの "info" 引数が渡され、strategy の verify コールバックが提供する追加の詳細が格納されます。
認証に失敗した場合は、オプションの「status」引数が渡されます。これは、リモート認証に失敗した場合の HTTP レスポンスコードなどです。
router.post('/login/password', passport.authenticate('local', {
// オプション
}));
use
で登録したストラテジ名を指定して認証を行う。
-
ストラテジ名
第一引数name
で指定したストラテジを使用してユーザーを認証する。
ストラテジ名はストラテジごとに決められているのでそれを指定する。
use
実行時に名前を上書きして登録している場合はそっちの名前で。
ストラテジ名は複数指定できるらしく、配列で渡すと順番に認証が試され、最初に認証できた認証方法で成功が返されるっぽい。(未検証) -
オプション
オプションはストラテジごとに共通っぽい。
ストラテジごとに追加のオプションがあったりする。
例えばLocalStrategy
にはbadRequestMessage
がある。
主に成功、失敗時の処理(成功時リダイレクト先など)についてのオプションを指定する。
詳しくは上の「コメントと翻訳」内のオプションを参照。-
successMessage
,failureMessage
boolean
かstring
で指定する。
~~Message
はreq.session.messages
(配列)に追加されるメッセージを設定する。
どちらも指定しない(false
)とメッセージは追加されない。
true
を指定するとverify
関数のinfo
(use
参照)に渡したメッセージが使用され、string
を渡した場合はそのメッセージを上書きできる。 -
successFlash
,failureFlash
動作確認してません。
express-flash
をapp.use
で取り込んでおく必要があるっぽい。
string
かtrue
か{type: string, message: string}
のオブジェクト型で指定する。
flash('error')
かflash('指定したtype')
で取り出すことができるらしい。
上書きの処理は~~Message
と似ている。
true
を指定した場合、req.flash('error')
でverify
関数のinfo
に渡したメッセージが取り出すことができ、string
を渡すとそのメッセージを上書き。
オブジェクト型でtype
を指定した場合はflash('指定したtype')
で取り出す。
assignProperty
を指定した場合、認証のミドルウェアを通した以降はreq
の指定したプロパティ名でユーザー情報を参照できるようになるが、デフォルトのログイン処理が実行されなくなる。
ログイン処理がされないとは、認証自体は行われるが、セッションへの書き込みや認証後のリダイレクトなどが行われなくなるっぽい。
デフォルトのままでもreq.user
に入ってくるし、initialize
のuserProperty
オプションでも変えられるのであまり使うことはなさそう。 -
-
コールバック
コールバックはストラテジでデフォルトで定義されている認証後の処理を上書きできる。
成功時、失敗時の処理がすべて自分のアプリに任されることになる。(=自分側で作ることになる)
コールバックを使わなくてもオプションの指定でだいたいは事足りると思うのでこちらもあまり使うことはなさそう。
責任を負いたくないのでここでは詳しくは書かない。
コールバックについてのコメントと翻訳
An optional
callback
can be supplied to allow the application to override the default manner in which authentication attempts are handled.
The callback has the following signature, whereuser
will be set to the authenticated user on a successful authentication attempt, orfalse
otherwise.
An optionalinfo
argument will be passed, containing additional details provided by the strategy's verify callback - this could be information about a successful authentication or a challenge message for a failed authentication.
An optionalstatus
argument will be passed when authentication fails - this could be a HTTP response code for a remote authentication failure or similar.
オプションの「コールバック」を指定することで、認証の試行が処理される際のデフォルトの方法をアプリケーション側で上書きすることができます。
このコールバックは次のようなシグネチャを持ちます。認証に成功すると「user」が認証されたユーザに設定され、そうでなければ「false」が設定されます。
オプションの "info" 引数が渡され、strategy の verify コールバックが提供する追加の詳細が格納されます。
認証に失敗した場合は、オプションの「status」引数が渡されます。これは、リモート認証に失敗した場合の HTTP レスポンスコードなどです。
Note that if a callback is supplied, it becomes the application's responsibility to log-in the user, establish a session, and otherwise perform the desired operations.
コールバックが提供された場合、アプリケーションの責任になることに注意してください。ユーザーをログインさせ、セッションを確立し、その他必要な操作を行う責任があります。
- ログイン後の処理
authenticate
の後続の処理が実行されるかはauthenticate
の認証の結果(成功or失敗)と指定したオプションによって変わるので注意。
基本、リダイレクト系が設定されているかどうかによる。
ここで網羅して書くのは難しいので自分で調べてください。
調べたらあるかもだけど公式ドキュメントで見つけられませんでした。
コードはpassport\lib\middleware\authenticate.js
。
passport.authenticate('local', {
// ここのオプションで
}), function(req, res, next) {
// ここが実行されるかどうかが変わる
});
session
リクエストのセッション情報で認証を行う。
コメントと翻訳
Middleware that will restore login state from a session.
Web applications typically use sessions to maintain login state between requests. For example, a user will authenticate by entering credentials into a form which is submitted to the server. If the credentials are valid, a login session is established by setting a cookie containing a session identifier in the user's web browser. The web browser will send this cookie in subsequent requests to the server, allowing a session to be maintained.
If sessions are being utilized, and a login session has been established, this middleware will populatereq.user
with the current user.
Note that sessions are not strictly required for Passport to operate.
However, as a general rule, most web applications will make use of sessions.
An exception to this rule would be an API server, which expects each HTTP request to provide credentials in an Authorization header.
セッションからログイン状態を復元するミドルウェア。
ウェブアプリケーションは通常、リクエスト間のログイン状態を維持するためにセッションを使用します。 例えば、ユーザーはサーバーに送信されるフォームに認証情報を入力することで認証されます。 認証情報が有効な場合、ユーザーのブラウザにセッション識別子を含むクッキーを設定することにより、ログインセッションが確立されます。 Web ブラウザは、その後のサーバーへのリクエストでこのクッキーを送信し、セッションを維持することができます。
セッションが利用され、ログインセッションが確立された場合、このミドルウェアは「req.user」に現在のユーザーを入力します。
Passport が動作するためにセッションは厳密には必要ないことに注意してください。
しかし、一般的なルールとして、ほとんどのWebアプリケーションはセッションを使用します。
このルールの例外は、APIサーバーで、各HTTP リクエストは、Authorizationヘッダで認証情報を提供する必要があります。
app.use(passport.session())
Passport.js自体が内部にSessionStrategy
というストラテジを持っており、import
かrequire
された時点で内部でuse
され、取り込まれた状態になっている。
session
関数はそれを利用するためだけの特殊なauthenticate
関数とも言える。
SessionStrategy
はsession
という名前を持っているので
app.use(passport.authenticate('session'))
と処理は同じ。
というかsession
関数が内部でやっていることはこれ。
SessionStrategy
は認証時に後述のserializeUser
は実行されない。
おそらく「認証済みのユーザーがセッションに書き込まれている」という条件が前提なため。
serializeUser
認証情報からセッションに書き込む情報を決める。
コメントと翻訳
Registers a function used to serialize user objects into the session.
ユーザーオブジェクトをセッションにシリアライズするために使用する関数を登録します。
passport.serializeUser(function(user, done) {
process.nextTick(function() {
done(null, { id: user.id, username: user.username, test: 'aaaaa' });
});
});
authenticate
で認証が成功した時に、実行される。
第一引数user
にはストラテジごとに決められた認証情報が渡される。
第二引数done
にはコールバック関数が渡されており、done(error, user)
の形で実行する必要がある。
done
を実行しないと処理が止まってしまうので注意。
done
のuser
にはセッションに書き込みたい情報を渡し、エラーがある場合はerror
にエラー情報を渡す。
エラーがない場合は、第一引数error
にfalse
やnull
(false
となる値)を渡し、user
に書き込む値を渡せばいい。
error
とuser
を同時に渡して実行した場合はerror
が優先される。
authenticate
のオプションのassignProperty
かcallback
が指定されていると実行されない。
(内部の話のlogIn
参照)
serializeUser
は複数登録でき、認証が成功した時にserializeUser
で登録された順に実行される。
まず最初に実行したserializeUser
が実行され、done
関数がerror
とuser
両方false
で実行された場合に、次の順番のserializeUser
が実行される。
以降は同様に実行され、最初にerror
かuser
がdone
に渡された時点で終了。
deserializeUser
セッションに書き込まれている情報からユーザーを復元する。
コメントと翻訳
Registers a function used to deserialize user objects out of the session.
セッションからユーザーオブジェクトをデシリアライズするために使用する関数を登録します。
passport.deserializeUser(function(user, done) {
process.nextTick(function() {
return done(null, user);
});
});
サンプルではreturn
しているがしなくてもいいっぽい。わからない。
serializeUser
は書き込み版とするとこちらは復元版。
第一引数user
にセッション情報、第二引数done
にコールバック関数が渡される。
done
はserializeUser
同様にdone(error, user)
の形で実行する。
done
のuser
に渡した情報がreq
内のuser
またはinitialize
関数で指定したuserProperty
に入れられる。
deserializeUser
も複数登録でき、登録順に実行されていき最初にerror
かuser
で返した情報が反映される。
「リクエストのセッション情報から認証を行う」はたぶんSessionStrategy
しか行うことがないのでsession()
かauthenticate('session')
していないと実行されないと思う。
少なくともLocalStrategy
ではdeserializeUser
は実行されない。
Passport内部の話
-
strategy
passport.use
で取り込まれたストラテジはpassport/authenticator.js
の_strategies
オブジェクトに保持される。
passport.authenticate(strategy_name)
で指定されたオブジェクトを_strategies
から引き出して使用される。 -
logIn
authenticate
ミドルウェアを通るとreq
の中にlogIn
が定義される。
つまり、セッション認証を使用するためにpassport.session()
をアプリレベルでapp.use
しておくとすべてのルートでreq.logIn
が定義されることになる。
このreq.logIn
でserializeUser
が実行され、serializeUser
で返された値がセッションへ書き込まれる。
書き込む処理はpassport\lib\sessionmanager.js
にある。
assignProperty
かcallback
を指定するとこのreq.logIn
が実行されなくなる。 -
serializeUser
,deserializeUser
内部でそれぞれ_serializers
、_deserializers
という配列で保持されている。
serializeUser
、deserializeUser
を(第一引数に関数型を渡して)実行すると登録扱いでこれらの配列に追加されていく。
実行されるときはこの配列の0
から順番に実行される。