├── README.md └── README_RU.md /README.md: -------------------------------------------------------------------------------- 1 | # Passport: The Hidden Manual 2 | 3 | * [Russian Translation](https://github.com/jwalton/passport-api-docs/blob/master/README_RU.md) courtesy of [@kirillurgant](https://github.com/kirillurgant) 4 | 5 | ## Table of Contents 6 | 7 | * [Intro](#intro) 8 | * [Passport class](#passport-class) 9 | * [passport.initialize()](#passportinitialize) 10 | * [passport.session(options)](#passportsessionoptions) 11 | * [passport.authenticate(strategyName, options, callback)](#passportauthenticatestrategyname-options-callback) 12 | * [passport.authorize(strategyName, options, callback)](#passportauthorizestrategyname-options-callback) 13 | * [passport.use(strategyName, strategy)](#passportusestrategyname-strategy) 14 | * [passport.serializeUser(fn(user, done) | fn(req, user, done))](#passportserializeuserfnuser-done--fnreq-user-done) 15 | * [passport.deserializeUser(fn(serializedUser, done) | fn(req, serializedUser, done))](#passportdeserializeuserfnserializeduser-done--fnreq-serializeduser-done) 16 | * [Strategies](#strategies) 17 | * [Writing custom strategies](#writing-custom-strategies) 18 | * [Verify Callback](#verify-callback) 19 | * [Functions added to the Request](#functions-added-to-the-request) 20 | * [req.login(user, callback)](#reqloginuser-callback) 21 | * [req.logout()](#reqlogout) 22 | * [Passport and Sessions](#passport-and-sessions) 23 | 24 | ## Intro 25 | 26 | The official [passport documentation](http://www.passportjs.org/docs/) has a long, example driven style. Some people like that. Some people, on the other hand, want to know "when I call this function, what's it going to do?" This is for those people. 27 | 28 | If you find inaccuracies, please feel free to open an issue or a PR. 29 | 30 | ## Passport class 31 | 32 | When you `import 'passport'`, you get back an instance of the "Passport" class. You can create new passport instances: 33 | 34 | ```js 35 | import passport from 'passport'; 36 | 37 | const myPassport = new passport.Passport(); 38 | ``` 39 | 40 | You'd want to do this if you want to use Passport in a library, and you don't want to pollute the "global" passport with your authentication strategies. 41 | 42 | ### passport.initialize() 43 | 44 | Returns a middleware which must be called at the start of connect or express based apps. This sets `req._passport`, which passport uses all over the place. Calling `app.use(passport.initialize())` for more than one passport instance will cause problems. 45 | 46 | (Prior to v0.5.1, this would also set up `req.login()` and `req.logout()`, but this has been moved to `passport.authenticate()`.) 47 | 48 | ### passport.session(\[options]) 49 | 50 | "If your application uses persistent login sessions, `passport.session()` middleware must also be used." Should be after your session middleware. 51 | 52 | This returns a middleware which will try to read a user out of the session; if one is there, it will store the user in `req.user`, if not, it will do nothing. Behind the scenes, this: 53 | 54 | ```js 55 | app.use(passport.session()); 56 | ``` 57 | 58 | is actually the same as: 59 | 60 | ```js 61 | app.use(passport.authenticate('session')); 62 | ``` 63 | 64 | which is using the [built-in "session strategy"](https://github.com/jaredhanson/passport/blob/2327a36e7c005ccc7134ad157b2f258b57aa0912/lib/strategies/session.js). You can customize this behavior by registering your own session strategy. 65 | 66 | `session()` does take an 'options' object. You can set pass `{pauseStream: true}` to turn on a hacky work-around for problems with really old node.js versions (pre-v0.10). Never set this true. 67 | 68 | ### passport.authenticate(strategyName\[, options][, callback]) 69 | 70 | strategyName is the name of a strategy you've previously registered with `passport.use(name, ...)`. This can be an array, in which case the first strategy to succeed, redirect, or error will halt the chain. Auth failures will proceed through each strategy in series, failing if all fail. 71 | 72 | This function returns a middleware which runs the strategies. If one of the strategies succeeds, this will set `req.user`. If you pass no options or callback, and all strategies fail, this will write a 401 to the response. Note that some strategies may also cause a redirect (OAuth, for example). This middleware also adds helper functions to the `req` object: `req.login()`, `req.logout()`, and `req.isAuthenticated()`. 73 | 74 | Valid options: 75 | 76 | * successRedirect - path to redirect to on a success. 77 | * failureRedirect - path to redirect to on a failure (instead of 401). 78 | * failureFlash - True to flash failure messages or a string to use as a flash message for failures (overrides any from the strategy itself). 79 | * successFlash - True to flash success messages or a string to use as a flash message for success (overrides any from the strategy itself). 80 | * successMessage - True to store success message in req.session.messages, or a string to use as override message for success. 81 | * failureMessage - True to store failure message in req.session.messages, or a string to use as override message for failure. 82 | * session - boolean, enables session support (default true) 83 | * failWithError - On failure, call next() with an AuthenticationError instead of just writing a 401. 84 | 85 | Note that the entire `options` object will be passed on to the strategy as well, so there may be extra options you can pass here defined by the strategy. For example, you can pass a `callbackURL` along to an [oauth strategy](https://github.com/jaredhanson/passport-oauth1/blob/4f8e3404126ef56b3e564e1e1702f5ede1e9a661/lib/strategy.js#L232). 86 | 87 | callback is an (err, user, info) function. No req, res, or next, because you're supposed to get them from the closure. If authentication fails, user will be false. If authentication succeeds, your callback is called and *`req.user` is NOT set*. You need to set it yourself, via `req.login()`: 88 | 89 | ```js 90 | app.post('/login', function(req, res, next) { 91 | passport.authenticate('local', function(err, user, info) { 92 | if (err) { return next(err); } 93 | if (!user) { return res.redirect('/login'); } 94 | 95 | // NEED TO CALL req.login()!!! 96 | req.login(user, next); 97 | })(req, res, next); 98 | }); 99 | ``` 100 | 101 | Don't just set `req.user = user`, since this won't update your session. 102 | 103 | ### passport.authorize(strategyName\[, options], callback) 104 | 105 | This isn't really well named, as it has nothing to do with authorization. This function is exactly like `passport.authenticate()`, but instead of setting `req.user`, it sets `req.account`, and it doesn't make any changes to the session. 106 | 107 | This is here so if you want to do something like "Link my account to OtherService", you can have a user who is already logged in, and use Passport to retreive their OtherService credentials. Useful for linking to social media platforms and such. 108 | 109 | ### passport.use(\[strategyName,] strategy) 110 | 111 | Configure a strategy. Strategies have a "default name" assigned to them, so you don't have to give them a name. 112 | 113 | ### passport.serializeUser(fn(user, done) | fn(req, user, done)) 114 | 115 | Passport will call this to serialize the user to the session whenever you login a user with `req.login()`, or whenever a user is authenticated via `passport.authenticate()`. The function you pass in should call `done(null, serializedUser)`. 116 | 117 | What this is going to do is set `req.session.passport.user = serializedUser`. Traditionally you'd make `serializedUser` some sort of string, like a user ID which you can fetch from your DB. Assuming whatever sessions middleware you're using can store arbitrary objects, though, you can make `serializedUser` an arbitrary JSON object. If your session middleware is writing the session to a client side cookie (like a JWT session that's stored in a cookie, or [client-sessions](https://github.com/mozilla/node-client-sessions)), then don't store anything too huge in here - browsers will ignore your cookie if it's too big. 118 | 119 | Undocumented: The fn() you pass can be a `fn(req, user, done)`. If multiple serializers are registered, they are called in order. Can return 'pass' as err to skip to next serialize. 120 | 121 | ### passport.deserializeUser(fn(serializedUser, done) | fn(req, serializedUser, done)) 122 | 123 | Passport will call this to deserialize the user from the session. Should call done(null, user). The `serializedUser` is `req.session.passport.user`. 124 | 125 | It can happen that a user is stored in the session, but that user is no longer in your database (maybe the user was deleted, or did something to invalidate their session). In this case, the deserialize function should pass `null` or `false` for the user, not `undefined`. 126 | 127 | Undocumented: fn() can be a `fn(req, id, done)`. As with serializeUser, serializers are called in order. 128 | 129 | ## Strategies 130 | 131 | ### Writing custom strategies 132 | 133 | Write a custom strategy by extending the `SessionStrategy` class from [passport-strategy](https://github.com/jaredhanson/passport-strategy). You can unit test a strategy in isolation with [passport-strategy-runner](https://github.com/jwalton/passport-strategy-runner). 134 | 135 | ```js 136 | import Strategy from 'passport-strategy'; 137 | 138 | export default class SessionStrategy extends Strategy { 139 | constructor() { 140 | super(); 141 | 142 | // Set the default name of our strategy 143 | this.name = 'session'; 144 | } 145 | 146 | /** 147 | * Authenticate a request. 148 | * 149 | * This function should call exactly one of `this.success(user, info)`, `this.fail(challenge, status)`, 150 | * `this.redirect(url, status)`, `this.pass()`, or `this.error(err)`. 151 | * See https://github.com/jaredhanson/passport-strategy#augmented-methods. 152 | * 153 | * @param {Object} req - Request. 154 | * @param {Object} options - The options object passed to `passport.authenticate()`. 155 | * @return {void} 156 | */ 157 | authenticate(req, options) { 158 | if(req.cookie.apikey === '6398d011-d80f-4db1-a36a-5dcee2e259d0') { 159 | this.success({username: 'dave'}); 160 | } else { 161 | this.fail(); 162 | } 163 | } 164 | } 165 | ``` 166 | 167 | Note when calling `fail()`, the `challenge` should be either a challenge string as defined by [RFC 7235 S2.1](https://tools.ietf.org/html/rfc7235#section-2.1), suitable for including in a WWW-Authenticate header, or else a `{message, type}` object, were `message` is the message to use a a "flash message", and `type` is the flash type (defaults to 'error'). 168 | 169 | ### Verify Callback 170 | 171 | Passport strategies require a verify callback, which is generally a `(err, user, options?)` object. `options.message` can be used to give a flash message. `user` should be `false` if the user does not authenticate. `err` is meant to indicate a server error, like when your DB is unavailable; you shouldn't set `err` if a user fails to authenticate. 172 | 173 | ## Functions added to the Request 174 | 175 | ### req.login(user, callback) 176 | 177 | Log a user in (causes passport to serialize the user to the session). On completion, req.user will be set. 178 | 179 | ### req.logout() 180 | 181 | Removes req.user, and clears the `session.passport` value from the session. 182 | 183 | ## Passport and Sessions 184 | 185 | Passport creates a key in the session called `session.passport`. 186 | 187 | When a request comes in to the `passport.session()` middleware, passport runs the [built-in 'session' strategy](https://github.com/jaredhanson/passport/blob/2327a36e7c005ccc7134ad157b2f258b57aa0912/lib/strategies/session.js) - this calls `deserializeUser(session.passport.user, done)` to read the user out of the session, and stores it in req.user. 188 | 189 | You can override how passport deserializes a session by creating a new strategy called 'session' and registering it with `passport.use()`. 190 | 191 | When you call `req.login()`, or when a strategy successfully authenticates a user, passport uses the [session manager](https://github.com/jaredhanson/passport/blob/2327a36e7c005ccc7134ad157b2f258b57aa0912/lib/sessionmanager.js#L12-L28), and essentially does: 192 | 193 | ```js 194 | serializeUser(req.user, (err, user) => { 195 | if(err) {return done(err);} 196 | session.passport.user = user; 197 | }); 198 | ``` 199 | 200 | Although it's more verbose about it. You can override the session manager by creating your own implementation and setting `passport._sm`, but this is not documented or supported, so use at your own risk. 201 | -------------------------------------------------------------------------------- /README_RU.md: -------------------------------------------------------------------------------- 1 | # Passport: Скрытое руководство 2 | 3 | * [Вступление](#вступление) 4 | * [Класс Passport](#класс-passport) 5 | * [passport.initialize()](#passportinitialize) 6 | * [passport.session(options)](#passportsessionoptions) 7 | * [passport.authenticate(strategyName, options, callback)](#passportauthenticatestrategyname-options-callback) 8 | * [passport.authorize(strategyName, options, callback)](#passportauthorizestrategyname-options-callback) 9 | * [passport.use(strategyName, strategy)](#passportusestrategyname-strategy) 10 | * [passport.serializeUser(fn(user, done) | fn(req, user, done))](#passportserializeuserfnuser-done--fnreq-user-done) 11 | * [passport.deserializeUser(fn(serializedUser, done) | fn(req, serializedUser, done))](#passportdeserializeuserfnserializeduser-done--fnreq-serializeduser-done) 12 | * [Стратегии](#стратегии) 13 | * [Написание кастомных стратегий](#написание-кастомных-стратегий) 14 | * [Подтверждающий коллбек](#подтверждающий-коллбек) 15 | * [Функции добавленные в Request](#функции-добавленные-в-request) 16 | * [req.login(user, callback)](#reqloginuser-callback) 17 | * [req.logout()](#reqlogout) 18 | * [Passport и Сессии](#passport-и-сессии) 19 | 20 | ## Вступление 21 | 22 | Официальная [документация passport](http://www.passportjs.org/docs/) имеет длинный стиль, сопровождаемый примерами. Некоторым людям это нравится, а другие хотят узнать "когда я вызываю эту функцию, что произойдет?". Это руководство именно для таких людей. 23 | 24 | Если вы обнаружите неточности, пожалуйста не стеснятесь открыть ишью или пулл реквест. 25 | 26 | ## Класс Passport 27 | 28 | Когда вы пишите `import 'passport'`, вы получаете экземпляр класса "Passport". Вы можете создать новый экземпляр следующим образом: 29 | 30 | ```js 31 | import passport from 'passport'; 32 | 33 | const myPassport = new passport.Passport(); 34 | ``` 35 | 36 | Вы бы хотели так сделать, если вам нужно использовать Passport в библиотеке, и вы не хотите загрязнять "глобальный" passport своими стратегиями аутентификации. 37 | 38 | ### passport.initialize() 39 | 40 | Возвращает middleware которая должная быть вызвана при старте приложения основанного на connect или express. Она устанавливает `req._passport`, который используетcя passport повсюду. Вызов `app.use(passport.initialize())` для более чем одного экземпляра passport будет иметь проблемы. 41 | 42 | ### passport.session(\[options]) 43 | 44 | "Если ваше приложение использует сессии, `passport.session()` так же должно быть установлено." Оно должно следовать после вашего промежуточного ПО для сессий. 45 | 46 | Это возвращает middleware, которая будет пытаться прочитать пользователя из сессии; если он есть, то будет сохранен в `req.user`, иначе ничего не будет. Это выглядит так: 47 | 48 | ```js 49 | app.use(passport.session()); 50 | ``` 51 | 52 | фактически это то же самое что: 53 | 54 | ```js 55 | app.use(passport.authenticate('session')); 56 | ``` 57 | 58 | которое использует [встроенную "сессионную стратегию"](https://github.com/jaredhanson/passport/blob/2327a36e7c005ccc7134ad157b2f258b57aa0912/lib/strategies/session.js). Вы можете изменить данное поведение зарегистрировав собственную сессионную стратегию. 59 | 60 | `session()` принимает объект 'options'. Вы можете установить `{pauseStrem: true}`, чтобы включить обход проблем с действительно старыми версиями node.js (pre-v0.10). Никогда не устанавливайте это. 61 | 62 | ### passport.authenticate(strategyName\[, options][, callback]) 63 | 64 | strategyName - это имя стратегии, которую вы зарегистрировали ранее с помощью `passport.use(name, ...)`. Это может быть массив и в таком случае сперва идет стратегия успеха, затем редирект или обработчик ошибки. Неудачная аутентификация будет проходить через каждую стратегию в цепочке до последнего обработчика, если все они проваляться. 65 | 66 | Данная функция возвращает middleware которая запускает стратегии. Если одна из стратегий пройдет успешно, будет установлен `req.user`. Если вы не передали options или callback, и все стратегии провалились, то будет записан 401 статус в ответе. Заметим что некоторые стратегии могут так же вызывать редирект (например OAuth). Это так же установит `req.login()`, `req.logout()` и `req.isAuthenticated()`. 67 | 68 | Допустимые options: 69 | 70 | * successRedirect - путь редиректа в случае успеха. 71 | * failureRedirect - путь редиректа в случае неудачи (вместо 401). 72 | * failureFlash - `true` для flash сообщения с ошибкой или `string` для использования в качестве сообщения об ошибке (переопределяет любые сообщения из самой стратегии). 73 | * successFlash - `true` для flash сообщения успеха или `string` для использования в качестве сообщения успеха (переопределяет любые сообщения из самой стратегии). 74 | * successMessage - `true` для сохранения сообщения успеха в `req.session.messages`, или `string` для перезаписи сообщения успеха. 75 | * failureMessage - `true` для сохранения сообщения ошибки в `req.session.messages`, или `string` для перезаписи сообщения ошибки. 76 | * session - `boolean`, разрешает поддержку сессий (по умолчанию `true`) 77 | * failWithError - в случае ошибки вызывает `next()` с `AuthenticationError` вместо простой записи 401. 78 | 79 | Обратите внимание, что весь объект `options` также будет передан в стратегию, поэтому могут быть дополнительные опции определенные стратегией. Например, вы можете передать `callbackURL` вместе с [oauth стратегией](https://github.com/jaredhanson/passport-oauth1/blob/4f8e3404126ef56b3e564e1e1702f5ede1e9a661/lib/strategy.js#L232). 80 | 81 | callback - это функция с параметрами (err, user, info). Без req, res, or next, потому что вы должны получить их из замыкания. Если аутентификация провалилась, то `user` будет `false`. Если аутентификация завершилась успешно, ваш колбек будет вызван и *`req.user` не будет установлен*. Вам нужно установить его самому при помощи `req.login()`: 82 | 83 | ```js 84 | app.post('/login', function(req, res, next) { 85 | passport.authenticate('local', function(err, user, info) { 86 | if (err) { return next(err); } 87 | if (!user) { return res.redirect('/login'); } 88 | 89 | // НУЖНО ВЫЗВАТЬ req.login()!!! 90 | req.login(user, next); 91 | })(req, res, next); 92 | }); 93 | ``` 94 | 95 | Не устанавливайте просто `req.user = user`, так как это не обновит вашу сессию. 96 | 97 | ### passport.authorize(strategyName\[, options], callback) 98 | 99 | Этот метод не очень хорошо назван, потому что не имеет ничего общего с авторизацией. Эта функция точно такая же как `passport.authenticate()`, но вместо параметра `req.user`, она устанавливает `req.account` и не вносит никаких изменений в сессию. 100 | 101 | Если вы хотите сделать что-то вроде "Связать мою учетную запись с 'Каким-то Сервисом'", у вас может быть пользователь который уже залогинен и вы используете Passport чтобы извлечь его данные из 'Какого-то Сервиса'(Google, Twitter и т.п.). Полезно для связки с социальными меда платформами и т.п. 102 | 103 | ### passport.use(\[strategyName,] strategy) 104 | 105 | Настройка стратегии. Стратегии имеют назначенное имя, так что не нужно придумывать им имя. 106 | 107 | ### passport.serializeUser(fn(user, done) | fn(req, user, done)) 108 | 109 | Passport будет вызвывать это для сериализации пользователя в сессию. Нужно вызвать done(null, user.id). 110 | 111 | Незадокументировано: fn() может быть `fn(req, user, done)`. Если зарегистрировано несколько сериализаторов, они вызываются по порядку. Может вернуть ошибку, чтобы перейти к следующей сериализации. 112 | 113 | ### passport.deserializeUser(fn(serializedUser, done) | fn(req, serializedUser, done)) 114 | 115 | Passport будет вызывать это для десериализации пользователя из сессии. Нужно вызывать done(null, user). 116 | 117 | Может случиться так, что пользователь будет сохранен в сессии, но этот пользователь больше не находится в вашей базе данных (может он был удален или нужно сделать что-то чтобы аннулировать его сеанс). В данном случае функция десериализации должна передать `null` или `false` в качестве user, но не `undefined`. 118 | 119 | Незадокументировано: fn() может быть `fn(req, id, done)`. Так же как в случае с serializeUser, сериалайзеры вызываются по порядку. 120 | 121 | ## Стратегии 122 | 123 | ### Написание кастомных стратегий 124 | 125 | Кастомные стратегии пишутся путем расширения класса `SessionStrategy` из [passport-strategy](https://github.com/jaredhanson/passport-strategy). Вы можете изолированно модульно протестировать стратегию при помощи [passport-strategy-runner](https://github.com/jwalton/passport-strategy-runner). 126 | 127 | ```js 128 | import Strategy from 'passport-strategy'; 129 | 130 | export default class SessionStrategy extends Strategy { 131 | constructor() { 132 | super(); 133 | 134 | // Установка дефолтного имени для нашей стратегии 135 | this.name = 'session'; 136 | } 137 | 138 | /** 139 | * Аутентификация запроса. 140 | * 141 | * Данная функция должна вызвать ровдо один из методов `this.success(user, info)`, `this.fail(challenge, status)`, 142 | * `this.redirect(url, status)`, `this.pass()`, или `this.error(err)`. 143 | * Смотри https://github.com/jaredhanson/passport-strategy#augmented-methods. 144 | * 145 | * @param {Object} req - Request. 146 | * @param {Object} options - Объект опций передаваемый в `passport.authenticate()`. 147 | * @return {void} 148 | */ 149 | authenticate(req, options) { 150 | if(req.cookie.apikey === '6398d011-d80f-4db1-a36a-5dcee2e259d0') { 151 | this.success({username: 'dave'}); 152 | } else { 153 | this.fail(); 154 | } 155 | } 156 | } 157 | ``` 158 | 159 | Заметим что когда вызывается `fail()`, `challenge` должен быть либо строкой как определено в [RFC 7235 S2.1](https://tools.ietf.org/html/rfc7235#section-2.1), подходит для включения в заголовок WWW-Authenticate, либо объектом `{message, type}`, где `message` используется для флэш сообщений и `type` является типом флэш сообщения (по умолчанию `type`). 160 | 161 | ### Подтверждающий коллбек 162 | 163 | Паспорт стратегии требуют подтверждающего коллбэка, который обычно принимает параметры `(err, user, options?)`. `options.message` может использоваться для флэш сообщений. `user` должен быть `false` если пользователь не аутентифицирован. `err` означает ошибку сервера, например когда ваша база данных недоступна; вам не нужно устанавливать `err` если пользователь не прошел аутентификацию. 164 | 165 | ## Функции добавленные в Request 166 | 167 | ### req.login(user, callback) 168 | 169 | Авторизует пользователя (заставляет passport сериализовать пользователя в сессию). По завершению req.user будет установлен. 170 | 171 | ### req.logout() 172 | 173 | Удаляет req.user и очищает сессию. 174 | 175 | ## Passport и Сессии 176 | 177 | Passport создает ключ в сессии называемый `session.passport`. 178 | 179 | Когда запрос приходит в middleware `passport.session()`, passport запускает [встроеную стратегию 'session'](https://github.com/jaredhanson/passport/blob/2327a36e7c005ccc7134ad157b2f258b57aa0912/lib/strategies/session.js) - она вызывает `deserializeUser(session.passport.user, done)` чтобы прочитать пользователя из сессии и сохранить в req.user. 180 | 181 | Вы можете переписать то как passport десериализует сессию путем создания новой стратегии 'session' и зарегистрировав ее с помощью `passport.use()`. 182 | 183 | Когда вы вызываете `req.login()` или когда стратегия успешно аутентифицирует пользователя, passport использует [session manager](https://github.com/jaredhanson/passport/blob/2327a36e7c005ccc7134ad157b2f258b57aa0912/lib/sessionmanager.js#L12-L28) и по сути делает: 184 | 185 | ```js 186 | serializeUser(req.user, (err, user) => { 187 | if(err) {return done(err);} 188 | session.passport.user = user; 189 | }); 190 | ``` 191 | 192 | Вы можете перезаписать менеджер сессий путем создания своей собственной реализации и установки `passport._sm`, но это не задокументировано, поэтому используйте на свой страх и риск. 193 | --------------------------------------------------------------------------------