I am upgrading the express 4 and my passport is failing every time now. It is not even logging to the console in passport.use(new LocalStrategy.
It redirects the /failure every time without hitting any breakpoints
// Use the LocalStrategy within Passport.
// Strategies in passport require a `verify` function, which accept
// credentials (in this case, a username and password), and invoke a callback
// with a user object. In the real world, this would query a database;
// however, in this example we are using a baked-in set of users.
passport.use(new LocalStrategy(
function(username, password, done) {
console.log("LocalStrategy working...");
// asynchronous verification, for effect...
process.nextTick(function() {
// Find the user by username. If there is no user with the given
// username, or the password is not correct, set the user to `false` to
// indicate failure and set a flash message. Otherwise, return the
// authenticated `user`.
findByUsername(username, password, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, {
message: 'Unknown user ' + username
});
} else {
return done(null, user);
}
})
});
}
));
app.use(cookieParser('keyboard cat'));
app.use(session({
secret: 'keyboard cat',
saveUninitialized: true,
resave: true
}));
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.post('/login', passport.authenticate('local', {
failureRedirect: '/failure',
failureFlash: false
}),
function(req, res) {
res.cookie('userdata', req.user);
switch (req.user.role) {
case 'candidate':
res.redirect('/app/candidates');
break;
case 'employer':
res.redirect('/app/employers');
break;
case 'provider':
res.redirect('/app/providers');
break;
case 'admin':
res.redirect('/app/admin');
break;
default:
break;
}
});
Assuming you have all relevant code included the reason for the failure is likely missing body parser. The authentication strategy will try to find the username and password fields from req.body and req.query, and if there is no body parser used req.body will be empty. The strategy will then fail straight away as it would have nothing to pass to your verify callback.
You need to make the Express application use relevant body parser, for example:
var bodyParser = require('body-parser');
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: true}));
Have you included LocalStrategy?
var LocalStrategy = require('passport-local').Strategy;
app.use(express.cookieParser()); // read cookies
app.use(express.bodyParser()); // get information from html forms
Related
The issue is when I log in, passport will run its deserialize function a good number of times. While this isn't having any effect on things, that I know of. Having it do this could be problematic later on down the road.
Here is the logs:
Bloodmorphed has been Serialized
Bloodmorphed has been deserialized
Bloodmorphed has been deserialized
Bloodmorphed has been deserialized
Bloodmorphed has been deserialized
Bloodmorphed has been deserialized
Bloodmorphed has been deserialized
Here is the passport:
/*jshint esversion: 6 */
const LocalStrategy = require('passport-local').Strategy;
const db = require('../config/db');
const bcrypt = require('bcryptjs');
let io = require('./io');
module.exports = (passport) => {
// =========================================================================
// passport session setup ==================================================
// =========================================================================
// used to serialize the user for the session
passport.serializeUser((user, done) => {
console.log(user.username + ' has been Serialized');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
let sql = 'SELECT * FROM users, users_meta WHERE users.id = ? AND users_meta.id =?';
db.query(sql, [id, id]).then(results => {
var userdata = results[0];
console.log(userdata.username + ' has been deserialized');
done(null, userdata);
});
});
// Local Strategy login
passport.use('local-login', new LocalStrategy({
passReqToCallback: true,
}, (req, username, password, done) => {
// Match Username
let sql = 'SELECT * FROM users WHERE username = ?';
db.query(sql, [username]).then(results => {
if (!results.length) {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
// Match Password
bcrypt.compare(password, results[0].password, (err, isMatch) => {
if (isMatch) {
var userData = results[0];
sql = 'SELECT * FROM users_meta WHERE id = ?';
db.query(sql, userData.id).then(results => {
Object.assign(userData, results[0]);
return done(null, userData);
});
} else {
return done(null, false, {
type: 'loginMessage',
message: 'Wrong Login',
});
}
});
});
}));
};
While this is not a high priority issue as of now, I would like to get it fixed, or if it is indeed normal for the to happen.
Please refer to https://github.com/jaredhanson/passport/issues/14#issuecomment-4863459
The serving of static files should be done before passport.session.
For instance, according to the refereed source:
app.configure(function() {
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
// passport session is triggered, causing deserializeUser to be invoked
app.use(passport.session());
// but request was for a static asset, for which authentication is not
// necessary
app.use(express.static(__dirname + '/../../public'));
});
Should be changed to:
app.configure(function() {
app.use(express.logger())
// requests for static assets will be handled immediately and will not continue
// down the middleware stack
app.use(express.static(__dirname + '/../../public'));
// any request that gets here is a dynamic page, and benefits from session
// support
app.use(express.session({ secret: 'keyboard cat' }));
app.use(passport.initialize());
app.use(passport.session());
});
If memory serves correct, Passport deserializes on every request. Because the session key is stored in a cookie on the user's browser.
From the PassportJS documentation:
In a typical web application, the credentials used to authenticate a
user will only be transmitted during the login request. If
authentication succeeds, a session will be established and maintained
via a cookie set in the user's browser.
Each subsequent request will not contain credentials, but rather the
unique cookie that identifies the session. In order to support login
sessions, Passport will serialize and deserialize user instances to
and from the session.
I have read everything on the argument but still cannot understand it. The documentation on the Passport Js web site is very vague.
I am using Passport JS with the passport-ldapauth Strategy. I do not have a Database. I obviously don't want to hit the LDAP server on each request. I would like to authenticate the user the first time on the POST /login route using the passport strategy with LDAP, store the user in the session and on each subsequent requests I just want to check if the user is already logged in.
I am trying to use the session but I cannot understand how to use Passport + session with the serialize/deserialize flow. Every example I checked use a User.findOne in the deserializeUser function.
As of now I disabled the use of the session for Passport and I am using a custom middleware where I check if req.session.user != null. If that's the case the user is already logged in and I hit next(). Otherwise redirect to login.
Here is some code (for sake of simplicity I deleted the code not related to the question):
Passport configuration:
var express = require('express'),
session = require('express-session'),
passport = require('passport'),
LdapStrategy = require('passport-ldapauth');
var app = express();
var LdapStrategyOptions = {
server: {
url: '<url>',
bindDN: '<dn>',
bindCredentials: "<pwd>",
searchBase: '<searchBase>',
searchFilter: '<filter>'
}
};
passport.use(new LdapStrategy(LdapStrategyOptions));
app.use(cookieParser());
app.use(session({
store: new LokiStore({autosave: false}),
resave: false,
saveUninitialized: false
secret: env.get("SESSION_SECRET")
}));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
Routes:
// LOGIN ROUTE
app.get('/login',
function(req, res) {
res.render('login');
});
// LOGIN HANDLER ROUTE
app.post('/login',
passport.authenticate('ldapauth', { session: false }),
function(req, res) {
req.session.userId = req.user.cn;
req.session.user = {
"userId": req.user.cn,
"displayName": req.user.displayName
};
res.redirect('/');
});
// LOGOUT ROUTE
app.get('/logout',
function(req, res) {
req.session.destroy(function(err) {
req.logout();
res.redirect('/');
});
});
// HOME ROUTE
app.get('/', isLoggedIn, function(req, res) {
res.render('home');
});
IsLoggedIn Middleware:
var isLoggedIn = function(req, res, next) {
if (req.session.user != null){
console.log("is auth ok '" + req.session.user.userId +"'");
return next();
}
console.log("redirect to auth/login");
res.redirect('/auth/login');
}
What am I missing? Is there any security fault in my setup?
Any help is appreciated.
From passportjs docs:
In a typical web application, the credentials used to authenticate a user will only be transmitted during the login request. If authentication succeeds, a session will be established and maintained via a cookie set in the user's browser.
Each subsequent request will not contain credentials, but rather the unique cookie that identifies the session. In order to support login sessions, Passport will serialize and deserialize user instances to and from the session.
Basically serializeUser is supposed to return a unique user identifier so you can deserializeUser back into JSON later.
So for your implementation you should probably do something along these lines:
DISCLAIMER: I have no experience with LDAP.
passport.serializeUser(function(user, done) {
//We can identify the user uniquely by the CN,
//so we only serialize this into the session token.
done(null, user.cn);
});
passport.deserializeUser(function(cn, done) {
//Directly query LDAP.
//I'm not sure passport caches the result (only calls deserializeUser for new sessions)
//but worst case you can cache the result yourself.
somehowLoadUserFromLDAPByCN(cn, function(err, user) {
done(err, {
userId: user.cn,
displayName: user.displayName
});
});
});
If you only need an id and a display name, it's totally fine to keep them in session. You should only load the full user profile when you need more fields.
I am trying to implement sign-in with google and passport but I am running into a bit of a problem. I successfully authenticate with google, but my data isn't being passed to the front end. I Haven't changed anything from the original code except for the URI and necessary client id and secret. Can anyone tell me what I am missing?
var express = require( 'express' )
, app = express()
, server = require( 'http' ).createServer( app )
, passport = require( 'passport' )
, util = require( 'util' )
, bodyParser = require( 'body-parser' )
, cookieParser = require( 'cookie-parser' )
, session = require( 'express-session' )
, RedisStore = require( 'connect-redis' )( session )
, GoogleStrategy = require( 'passport-google-oauth2' ).Strategy;
// API Access link for creating client ID and secret:
// https://code.google.com/apis/console/
var GOOGLE_CLIENT_ID = "307841191614-1shiak514mrjugtbon3dm2if8hbhnvdv.apps.googleusercontent.com"
, GOOGLE_CLIENT_SECRET = "fgViegEgHWuoc1X-p63iPmpF";
// Passport session setup.
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing. However, since this example does not
// have a database of user records, the complete Google profile is
// serialized and deserialized.
passport.serializeUser(function(user, done) {
done(null, user);
console.log("User: "+ user.displayName); // If there is a persistent session, the console logs out the displayName
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
// Use the GoogleStrategy within Passport.
// Strategies in Passport require a `verify` function, which accept
// credentials (in this case, an accessToken, refreshToken, and Google
// profile), and invoke a callback with a user object.
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
//NOTE :
//Carefull ! and avoid usage of Private IP, otherwise you will get the device_id device_name issue for Private IP during authentication
//The workaround is to set up thru the google cloud console a fully qualified domain name such as http://mydomain:3000/
//then edit your /etc/hosts local file to point on your private IP.
//Also both sign-in button + callbackURL has to be share the same url, otherwise two cookies will be created and lead to lost your session
//if you use it.
callbackURL: "http://127.0.0.1:3000/oauth2callback",
passReqToCallback : true
},
function(request, accessToken, refreshToken, profile, done) {
// asynchronous verification, for effect...
process.nextTick(function () {
// To keep the example simple, the user's Google profile is returned to
// represent the logged-in user. In a typical application, you would want
// to associate the Google account with a user record in your database,
// and return that user instead.
console.log(profile); //logs google profile successfully
return done(null, profile);
});
}
));
// configure Express
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use( express.static(__dirname + '/public'));
app.use( cookieParser());
app.use( bodyParser.json());
app.use( bodyParser.urlencoded({
extended: true
}));
app.use( session({
secret: 'cookie_secret',
name: 'kaas',
store: new RedisStore({
host: '127.0.0.1',
port: 6379
}),
proxy: true,
resave: true,
saveUninitialized: true
}));
app.use( passport.initialize());
app.use( passport.session());
/*
===
===
===
Here is where the data is not being read.
*/
app.get('/', function(req, res){
res.render('index', { user: req.user });
console.log(req.user); //Output: undefined
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login', function(req, res){
res.render('login', { user: req.user });
});
// GET /auth/google
// Use passport.authenticate() as route middleware to authenticate the
// request. The first step in Google authentication will involve
// redirecting the user to google.com. After authorization, Google
// will redirect the user back to this application at /auth/google/callback
app.get('/auth/google', passport.authenticate('google', { scope: [
'https://www.googleapis.com/auth/plus.login',
'https://www.googleapis.com/auth/plus.profile.emails.read']
}));
// GET /auth/google/callback
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
app.get( '/oauth2callback',
passport.authenticate( 'google', {
successRedirect: '/',
failureRedirect: '/login'
}));
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
server.listen( 3000 );
// Simple route middleware to ensure user is authenticated.
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login');
}
`
Here is the simple layout that doesn't seem to be receiving any data.
<% if (!user) { %>
<h2>Welcome! Please log in.</h2>
<% } else { %>
<h2>Hello, <%= user.displayName %>.</h2>
<% } %>
Your code works, I just used same example on my app.
I had the same problem and I realized that I'm not using a valid account in my tests.
This API retrieves data from Google+ profile. Are you using a valid Google account with linked Google+ profile to authenticate?
I'm using Passport.js to login a user with username and password. I'm essentially using the sample code from the Passport site. Here are the relevant parts (I think) of my code:
app.use(passport.initialize());
app.use(passport.session());
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
passport.use(new LocalStrategy(function(username, password, done) {
User.findOne({ username: username }, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
app.post('/login',
passport.authenticate('local', { failureRedirect: '/login/fail', failureFlash: false }),
function(req, res) {
// Successful login
//console.log("Login successful.");
// I CAN ACCESS req.user here
});
This seems to login correctly. However, I would like to be able to access the login user's information in other parts of the code, such as:
app.get('/test', function(req, res){
// How can I get the user's login info here?
console.log(req.user); // <------ this outputs undefined
});
I have checked other questions on SO, but I'm not sure what I'm doing wrong here. Thank you!
Late to the party but found this unanswered after googling the answer myself.
Inside the request will be a req.user object that you can work withr.
Routes like so:
app.get('/api/portfolio', passport.authenticate('jwt', { session: false }), stocks.buy);
Controller like this:
buy: function(req, res) {
console.log(req.body);
//res.json({lel: req.user._id});
res.json({lel: req.user});
}
In reference to the Passport documentation, the user object is contained in req.user. See below.
app.post('/login',
passport.authenticate('local'),function(req, res) {
// If this function gets called, authentication was successful.
// `req.user` contains the authenticated user.
res.redirect('/users/' + req.user.username);
});
That way, you can access your user object from the page you redirect to.
In case you get stuck, you can refer to my Github project where I implemented it clearly.
I'm pretty new to javascript but as I understand it from the tutorials you have to implement some session middleware first as indicated by 250R.
const session = require('express-session')
const app = express()
app.use(bodyParser.urlencoded({ extended: false }))
app.use(bodyParser.json())
let sess = {
genid: (req) => {
console.log('Inside the session middleware')
console.log(req.sessionID)
return uuid()
},
store: new FileStore(),
secret: 'keyboard cat', // password from environment
resave: false,
rolling: true,
saveUninitialized: true,
cookie: {
HttpOnly: true,
maxAge: 30 * 60 * 1000 // 30 minutes
}
}
app.use(session(sess))
// call passport after configuring the session with express-session
// as it rides on top of it
app.use(passport.initialize())
app.use(passport.session())
// then you will be able to use the 'user' property on the `req` object
// containing all your session details
app.get('/test', function (req, res) {
console.log(req.user)
})
res.render accepts an optional parameter that is an object containing local variables for the view.
If you use passport and already authenticated the user then req.user contains the authenticated user.
// app.js
app.get('/dashboard', (req, res) => {
res.render('./dashboard', { user: req.user })
})
// index.ejs
<%= user.name %>
You can define your route this way as follows.
router.post('/login',
passport.authenticate('local' , {failureRedirect:'/login', failureFlash: true}),
function(req, res) {
res.redirect('/home?' + req.user.username);
});
In the above code snippet, you can access and pass any field of the user object as "req.user.field_name" to the page you want to redirect. One thing to note here is that the base url of the page you want to redirect to should be followed by a question mark.
late to party but this worked for me
use this in your app.js
app.use(function(req,res,next){
res.locals.currentUser = req.user;
next();
})
get current user details in client side like ejs
<%= locals.currentUser.[parameter like name || email] %>
Solution for those using Next.js:
Oddly, —> the solution <— comes from a recently removed part of the README of next-connect, but works just as it should. You can ignore the typescript parts if you're using plain JS.
The key part is the getServerSideProps function in ./src/pages/index (or whichever file you want to get the user object for).
// —> ./src/authMiddleware.ts
// You'll need your session, initialised passport and passport with the session,
// so here's an example of how we've got ours setup, yours may be different
//
// Create the Passport middleware for SAML auth.
//
export const ppinit = passport.initialize();
//
// Set up Passport to work with expressjs sessions.
//
export const ppsession = passport.session();
//
// Set up expressjs session handling middleware
//
export const sess = session({
secret: process.env.sessionSecret as string,
resave: true,
saveUninitialized: true,
store: sessionStore,
});
// —> ./src/pages/index.ts
// update your user interface to match yours
export interface User {
id: string;
name: string;
}
interface ExtendedReq extends NextApiRequest {
user: User;
}
interface ServerProps {
req: ExtendedReq;
res: NextApiResponse;
}
interface ServerPropsReturn {
user?: User;
}
export async function getServerSideProps({ req, res }: ServerProps) {
const middleware = nc()
.use(sess, ppinit, ppsession)
.get((req: Express.Request, res: NextApiResponse, next) => {
next();
});
try {
await middleware.run(req, res);
} catch (e) {
// handle the error
}
const props: ServerPropsReturn = {};
if (req.user) props.user = req.user;
return { props };
}
interface Props {
user?: User;
}
//
// A trivial Home page - it should show minimal info if the user is not authenticated.
//
export default function Home({ user }: Props) {
return (
<>
<Head>
<title>My app</title>
<link rel="icon" href="/favicon.ico" />
</Head>
<main>
<h1>Welcome to My App {user?.name}</h1>
</main>
</>
);
}
You'll need to make sure that you register a middleware that populates req.session before registering the passport middlewares.
For example the following uses express cookieSession middleware
app.configure(function() {
// some code ...
app.use(express.cookieParser());
app.use(express.bodyParser());
app.use(express.cookieSession()); // Express cookie session middleware
app.use(passport.initialize()); // passport initialize middleware
app.use(passport.session()); // passport session middleware
// more code ...
});
I've set up a working login test as follows:
var express = require('express');
var fs = require('fs');
var http = require('http');
var path = require('path');
var routes = require('./routes/index.coffee');
var passport = require('passport');
var LocalStrategy = require('passport-local').Strategy;
var User = require('./userdb.coffee');
var app = express();
var RedisStore = require('connect-redis')(express);
passport.use(new LocalStrategy(function(username, password, done) {
return User.findOne({
username: username
}, function(err, user) {
if (err) {
return done(null, false, message: error);
}
if (!user) {
return done(null, false, {
message: 'Unknown user'
});
}
if (!user.validPassword(password)) {
return done(null, false, {
message: 'Invalid password'
});
} else {
return done(null, user);
}
});
}));
passport.serializeUser(function(user, done) {
return done(null, user);
});
passport.deserializeUser(function(user, done) {
return done(null, user);
});
app.configure(function() {
app.set('port', process.env.PORT || 5003);
app.set('views', __dirname + '/views');
app.set('view engine', 'jade');
app.enable('trust proxy');
app.use(express["static"](path.join(__dirname, 'public')));
app.use(express.bodyParser());
app.use(express.methodOverride());
app.use(express.cookieParser('SOMESECRET'));
app.use(express.session({
cookie: {
maxAge: 10 * 60 * 1000
},
key: 'express.sid',
secret: 'SOMESECRET',
store: new RedisStore({
port: 6379,
host: 'localhost'
})
}));
app.use(passport.initialize());
app.use(passport.session());
return app.use(app.router);
});
var check_auth = function(req, res, next) {
if (req.isAuthenticated()) {
console.log(req.session.cookie.expires);
return next();
}
return res.redirect('/login');
};
app.get('/', check_auth, routes.index);
app.get('/login', routes.login);
app.get('/logout', routes.logout);
app.post('/authenticate', passport.authenticate('local', {
successRedirect: '/',
failureRedirect: '/login'
}));
app.listen(app.get('port'), '0.0.0.0');
The routes and User logic is omitted, as I take it they're irrelevant to my issue, but I'll happily share them if needed (they're very small, this is just to get a small proof of concept up and running).
Login works, reading the session works, rendering templates based on session values work.
My problem is with maxAge/expires. I'm not sure where-in the problem lies, but I'll try to describe it:
When I log in, the session is saves with passport.js, stored correctly in my RedisStore, and a cookie pointing at the session is returned to the browser.
On subsequent requests the cookie is successfully found and points to the correct session from my SessionStore. req.session.cookie show an UPDATED expires and in my redis server, the TTL is reset to 10 minutes (600).
My problem is that the cookie remains at the same expirary in the browser (Chrome, windows and Mac).
So my question is: How can I debug this further?
As req.session is updated (by express, passport and connect-redis internally/automatically), I'm wondering where the problem lies, and what I should do to fix this: Cookie remains at the initial maxAges/expires.
Any tips, pointers or ideas are grately appreciated.
Express-session supports a rolling cookie expiration date. Unfortunately, it was only recently documented.
Use the "rolling" option for your session. This "forces a cookie set on every response" and "resets the expiration date." You want to set rolling to true.
Also pay attention to the "resave" option. That "forces session to be saved even when unmodified..." You'll likely want to set that option to true as well. Note that even though the default value is true for this option, you should set the value explicitly. Relying on the default for this option, rather than setting it explicitly, is now deprecated.
Try something like:
app.use( session( { secret: 'keyboard cat',
cookie: { maxAge: 60000 },
rolling: true,
resave: true,
saveUninitialized: false
}
)
);
Here's the documentation. Look under "Options" and "options.resave": https://github.com/expressjs/session .
After some digging it turns out Express does not support this sort of rolling, and is left as an exercise for the programmer to implement.
It would help if the browsers expirary was reliably readable to express, so you could bump the session only when it's close to expirary, but I use this as a workaround (inefficient) until I figure something smarter out:
check_auth = function(req, res, next) {
console.log(req.isAuthenticated());
if (req.isAuthenticated()) {
if (req.session.roll) {
req.session.roll = 0;
} else {
req.session.roll = 1;
}
return next();
}
return res.redirect('/login');
};
Where roll could be anything, the point being the session is changed (on every auth-checked request*).
*) which also means it's wildly inefficient, but it will do for now.
One alternative could be to lookup the TTL of the session id. This would have to be checked in a way like:
if ttl < 10% * maxAge (as defined by the app), as the TTL is actually correctly updated on every request, it's just that Set-Cookie isn't sent. As such, say the user stays within the 90% of maxAge, his browser-cookie will eventually expire, so even that approach is not sufficient. It could be a good middleground though.
I'll leave the question unaccepted, to encourage others to weigh in with better solutions.
just in case someone is facing this issue in Google Chrome, the solution is very easy:
app.use(cors({
allowedHeaders: ['Content-Type','Authorization'],
origin: '.dev.loc', <- your domain here, but it requires to have a dot infront
methods:['GET','POST','PUT','DELETE'],
preflightContinue: true
}));