sourceapppassport-remember-me.js

// © 2013 Jared Hanson, MIT Licenced (♥)
// https://github.com/jaredhanson/passport-remember-me

import { Strategy as Strategy } from "passport-strategy";

class RememberMeStrategy extends Strategy {
	name = "remember-me";

	/**
	 * `Strategy` constructor.
	 *
	 * @param {Object} options
	 * @api public
	 */
	constructor(options, verify, issue) {
		super();
		if (typeof options == "function") {
			issue = verify;
			verify = options;
			options = {};
		}
		if (!verify)
			throw new Error(
				"remember_me cookie authentication strategy requires a verify function"
			);
		if (!issue)
			throw new Error(
				"remember_me cookie authentication strategy requires an issue function"
			);
		this._key = options.key ?? "remember_me";
		this._opts = {
			path: "/",
			httpOnly: true,
			maxAge: 604800000,
			...options.cookie
		}; // maxAge: 7 days by default
		this._verify = verify;
		this._issue = issue;
	}

	/**
	 * Authenticate request based on remember me cookie.
	 *
	 * @param {Express.Request} req
	 * @api protected
	 */
	authenticate(req, options) {
		let token;

		// The rememeber me cookie is only consumed if the request is not
		// authenticated.  This is in preference to the session, which is typically
		// established at the same time the remember me cookie is issued.
		if (req.isAuthenticated()) return this.pass();
		token = req.cookies[this._key];
		// Since the remember me cookie is primarily a convenience, the lack of one is
		// not a failure.  In this case, a response should be rendered indicating a
		// logged out state, rather than denying the request.
		if (!token) return this.pass();
		this._verify(token, (err, user, info) => {
			if (err) return this.error(err);
			if (!user) {
				// The remember me cookie was not valid.  However, because this
				// authentication method is primarily a convenience, we don't want to
				// deny the request.  Instead we'll clear the invalid cookie and proceed
				// to respond in a manner which indicates a logged out state.
				//
				// Note that a failure at this point may indicate a possible theft of the
				// cookie.  If handling this situation is a requirement, it is up to the
				// application to encode the value in such a way that this can be detected.
				// For a discussion on such matters, refer to:
				//   http://fishbowl.pastiche.org/2004/01/19/persistent_login_cookie_best_practice/
				//   http://jaspan.com/improved_persistent_login_cookie_best_practice
				//   http://web.archive.org/web/20130214051957/http://jaspan.com/improved_persistent_login_cookie_best_practice
				//   http://stackoverflow.com/questions/549/the-definitive-guide-to-forms-based-website-authentication
				req.res.clearCookie(this._key);
				return this.pass();
			}
			// The remember me cookie was valid and consumed.  For security reasons,
			// the just-used token should have been invalidated by the application.
			// A new token will be issued and set as the value of the remember me
			// cookie.
			this._issue(user, (err, val) => {
				if (err) return this.error(err);
				req.res.cookie(this._key, val, this._opts);
				return this.success(user, info);
			});
		});
	}
}

export default RememberMeStrategy;