Port error when deploying express server with OIDC to Azure App Service - express

I am attempting to deploy a server to an azure app service. The server code can be found below.
The error I am getting from the log stream is:
2020-11-18T23:36:06.088Z ERROR - Container [container name] didn't respond to HTTP pings on port: 8080, failing site start. See container logs for debugging.
I have PORT set to 8080 and I know that config is picking up as I can see "Server listening on port 8080" in the logs. I have tried changing WEBSITES_PORT to 80 and 8080 as I saw that other posts, but I think my issue is different.
This site was working prior to my adding auth with OIDC libraries.
The app works locally with the server code below.
const https = require('https')
const express = require('express')
const path = require('path')
const app = express()
const fs = require('fs')
const key = fs.readFileSync('./key.pem')
const cert = fs.readFileSync('./cert.pem')
require('dotenv').config()
app.use(express.json())
app.use(express.urlencoded({
extended: true
}))
app.use(express.static('express'))
var cors = require('cors')
const OktaJwtVerifier = require('#okta/jwt-verifier')
const session = require('express-session')
const {
ExpressOIDC
} = require('#okta/oidc-middleware')
var getUserInfo = require('./getUserInfo')
// session support is required to use ExpressOIDC
app.use(
session({
secret: 'this should be secure',
resave: true,
saveUninitialized: false,
cookie: {
httpOnly: false,
secure: true,
},
})
)
const oidc = new ExpressOIDC({
issuer: process.env.ISSUER || 'https://[custom auth server domain].gov/oauth2/default',
client_id: process.env.CLIENT_ID || 'xxxxxxxxxxxxxxxxx',
client_secret: process.env.CLIENT_SECRET || 'xxxxxxxxxxxxxxxxxx',
redirect_uri: process.env.REDIRECT_URI ||
'https://localhost:3000/authorization-code/callback',
appBaseUrl: process.env.APP_BASE_URL || 'https://localhost:3000',
scope: 'openid profile',
})
// ExpressOIDC attaches handlers for the /login and /authorization-code/callback routes
app.use(oidc.router)
app.use(cors())
app.options('*', cors())
app.get('/userinfo', (req, res) => {
let domain = 'dev'
if (req.isAuthenticated()) {
getUserInfo.userRequest(res, req.userContext, domain)
}
})
app.get('/authStatus', (req, res) => {
if (req.isAuthenticated()) {
res.send(req.userContext.userinfo)
}
})
app.post('/forces-logout', oidc.forceLogoutAndRevoke(), (req, res) => {
// Nothing here will execute, after the redirects the user will end up wherever the `routes.logoutCallback.path` specifies (default `/`)
})
var linkObj = {not relevant links used hrefs on html based on env}
// default URL for website
app.get('/', function(req, res) {
res.sendFile(path.join(__dirname + '/express/index.html'))
//__dirname : It will resolve to your project folder.
})
// FAQ Path
app.get('/help', function(req, res) {
res.sendFile(path.join(__dirname + '/express/help.html'))
//__dirname : It will resolve to your project folder.
})
app.get('/links', (req, res) => {
res.json(linkObj)
})
app.post('/forces-logout', oidc.forceLogoutAndRevoke(), (req, res) => {
// Nothing here will execute, after the redirects the user will end up wherever the `routes.logoutCallback.path` specifies (default `/`)
})
// default URL for website
app.get('*', function(req, res) {
res.sendFile(path.join(__dirname + '/express/index.html'))
//__dirname : It will resolve to your project folder.
})
const port = normalizePort(process.env.PORT || '3000')
if (process.env.PORT) {
const server = https.createServer(app)
server.listen(port)
} else {
const server = https.createServer({
key: key,
cert: cert
}, app)
server.listen(port)
}
console.debug('Server listening on port ' + port)
function normalizePort(val) {
var port = parseInt(val, 10)
if (isNaN(port)) {
// named pipe
return val
}
if (port >= 0) {
// port number
return port
}
return false
}

I believe it's this line that could be giving you issues:
const port = normalizePort(process.env.PORT || '3000')
I'd try changing it to:
const port = normalizePort(process.env.PORT || '8080')
You'll also need to change these lines to have your public URL, not localhost:
redirect_uri: process.env.REDIRECT_URI ||
'https://localhost:3000/authorization-code/callback',
appBaseUrl: process.env.APP_BASE_URL || 'https://localhost:3000',
After you change these, you'll need to update your app on Okta to your production redirect URI.

Related

authenticate tp express server tunneled through ngrok

I have an express server serving react pages. I am trying to show the work to a client via a ngrok tunnel. But whenever I try to log in at the ngrok URL for my server it fails to connect back to my localhost where the express routes for auth are.
I feel like it's a simple error I'm making. The browser is trying to make a request to the express server at localhost:5000, but there is nothing at localhost on my client's network.
This is my app.js
const createError = require('http-errors');
const express = require('express');
const debug = require('debug')('app');
const cookieParser = require('cookie-parser');
const logger = require('morgan');
require('dotenv').config()
const ensureToken = require('./middleware/ensureToken');
const path = require('path');
const app = express();
const { init } = require('./utils/cron/createCron');
const cors = require('cors')
// Routers
const authRouter = require('./routes/auth')
const botRouter = require('./routes/bot')
const proxyRouter = require('./routes/proxy');
const winstonLogger = require('./utils/log/logger');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(cookieParser());
const whiteList = ['http://localhost:3000/', 'http://localhost:3000']
const corsOptions = {
origin: (origin, callback)=>{
if (whiteList.indexOf(origin) !== -1){
callback(null, true)
}else{
callback(new Error('Not Allowed by CORS'))
}
optionsSuccessStatus:200
}
}
// app.use(cors(corsOptions))
app.use(cors({ credentials: true, origin: true }))
app.use(express.static(path.join(__dirname, 'build')));
// Base Routes
app.use('/api/auth', authRouter);
app.use(ensureToken) //Add this before routes that need to be protected ny valid token
app.use('/api/bot', botRouter);
app.use('/api/proxy', proxyRouter);
app.get("*", (req, res) => {
res.sendFile(path.join(__dirname, "../client/build/index.html"));
});
// catch 404 and forward to error handler
app.use(function(req, res, next) {
next(createError(404));
});
// error handler
app.use(function(err, req, res, next) {
// set locals, only providing error in development
res.locals.message = err.message;
res.locals.error = req.app.get('env') === 'development' ? err : {};
// render the error page
res.status(err.status || 500);
res.render('error');
});
app.listen(process.env.PORT, ()=>{
winstonLogger.info(`Listening on ${process.env.PORT}`)
debug(`Listening on ${process.env.PORT}`);
console.log(`Listening on ${process.env.PORT}`);
init()
})
module.exports = app;
Any help is appreciated!!

Passport user object in request not available in getInitialProps when server is on a different port

I have my express server on a different port than my client-side nextjs project.
I know when you have a server on the same port you can use getRequestHandler with next that passes the user object to be accessible with getInitialProps in the client-side.
const express = require("express");
const next = require("next");
const app = next({ dev: true });
const handle = app.getRequestHandler();
app.prepare().then(() => {
const server = express();
// adds passport session
require("./middlewares").init(server);
const apolloServer = require("./graphql").createApolloServer();
apolloServer.applyMiddleware({ app: server });
server.all("*", (req, res) => {
return handle(req, res);
});
server.listen(port, (err) => {
if (err) throw err;
});
});
My passport implementation is as follows
const config = require("../config");
const session = require("express-session");
const passport = require("passport");
exports.init = (server, db) => {
require("./passport").init(passport);
const sess = {
name: "pid",
secret: config.SESSION_SECRET,
cookie: { maxAge: 2 * 60 * 60 * 1000 },
resave: false,
saveUninitialized: false,
store: db.initSessionStore(),
};
if (process.env.NODE_ENV === "production") {
server.set("trust proxy", 1);
sess.cookie.secure = true;
sess.cookie.httpOnly = true;
sess.cookie.sameSite = 'none';
sess.cookie.domain = process.env.DOMAIN;
}
server.use(session(sess));
server.use(passport.initialize());
server.use(passport.session());
};
And running the following on the express server, I can see req.user returning the user object.
app.use((req, res, next) => {
console.log(req.user);
next();
});
In a page in my nextjs app, in getInitialProps req.user is undefined
Home.getInitialProps = async (ctx) => {
const { req } = ctx;
const { user } = req;
console.log(user);
..........
};
Is there a way to either access the passport user object via SSR in nextjs or a different method to authorize and user on a page?
I do have a Github Repo with instructions on how to run the app in the README.md
Passport auth doesn't seems work across port. The solution is put a ngnix in front.
Local passport authorization on different ports

Next.js, Express.js app's apis running in development but status 404 in production

I have deployed nextjs, expressjs application on production but I am getting 404 for all apis.
I have pages that works fine
var session = require('express-session');
var passport = require('passport');
const KnexSessionStore = require('connect-session-knex')(session);
const knex = require('./db/knex.js');
const authMid = require('./config/utils')
const app = express();
app.use(compression())
app.use(cookieParser());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({extended: false}));
// sesstion
const store = new KnexSessionStore({
knex: knex,
tablename: 'sessions' // optional. Defaults to 'sessions'
});
app.use(session({
name: 'connect.sid',
store: store,
secret: 'somesupersecretkey',
resave: false,
saveUninitialized: false,
cookie: {
maxAge: 1000 * 60 * 60 * 24 * 7
},
// cookie: { secure: false }
}));
app.use(passport.initialize());
app.use(passport.session());
require("./config/passport");
require("./config/passport_fb");
require("./config/passport_google");
var routes = require('./routes/index');
routes.mp_routes(app);
// Server-side
const route = pathMatch();
// pages set up
// private & public
app.get('/', (req, res) => {
return nextApp.render(req, res, '/index', req.query)
})
app.get('/questions', (req, res) => {
return nextApp.render(req, res, '/questions', req.query)
})
And my routes/index.js file looks like
module.exports.mp_routes = (app) => {
app.use('/api/v1', questionsRoutes),
app.use('/api/v1', answersRoutes)
}
Everything nextjs pages and express apis do work fine in development. I don't have to do anything but on production pages do work but all apis say status 404. How do i get fixed

Can't set a cookie w/ Nuxt.js, Express-Session

I'm new to NUXT and SSR and I've been researching this for a few hours now and I can't seem to figure it out. I'm using JWT to authenticate users in my Nuxt app with a Bearer Token, which is working great until I hit refresh and lose my session.
Now I'm looking to persist sessions using express-session and connect-mongo. I can't get the cookie to set on the client to be included on future requests.
When a user is authenticated:
router.post('/login', function(req, res) {
User.findOne({
username: req.body.username
}, function(err, user) {
if (err) throw err;
if (!user) {
res.status(401).send({success: false, msg: 'Authentication failed. User not found.'});
} else {
// check if password matches
user.comparePassword(req.body.password, function (err, isMatch) {
if (isMatch && !err) {
// if user is found and password is right create a token
var token = jwt.sign(user.toJSON(), config.secret, { expiresIn: 604800 });
req.session.authUser = { 'user': 'Test User' }
return res.json({success: true, token: token, user: user});
} else {
res.status(401).send({success: false, msg: 'Authentication failed. Wrong password.'});
}
});
}
The console.log above shows the authUser in the session.
Session {
cookie:
{ path: '/',
_expires: 2018-04-03T18:13:53.209Z,
originalMaxAge: 60000,
httpOnly: true },
authUser: { user: 'Test User' } }
When I look at my chrome devtools application cookies a connect.ssid hasn't been set and when I console.log(req.session) on future requests the authUser is missing.
My server code is:
// Passport
var passport = require('passport');
var passportJWT = require("passport-jwt");
var ExtractJwt = passportJWT.ExtractJwt;
var JwtStrategy = passportJWT.Strategy;
// Config File
let config = require('./config/settings.js')
// Initialize Express
var app = express();
// CORS-ENABLE
app.use(function(req, res, next) {
res.header("Access-Control-Allow-Origin", "http://127.0.0.1:1337");
res.header("Access-Control-Allow-Credentials", 'true');
next();
});
app.use(cors())
const dbPath = 'mongodb://blogUser:blogUserPassword#localhost:27017/blog'
// Express Session
app.use(session({
secret: 'super-secret-key',
resave: false,
saveUninitialized: false,
store: new MongoStore({ url: dbPath }),
cookie: { maxAge: 60000 }
}))
// File Upload
app.use(fileUpload());
// view engine setup
// app.set('views', path.join(__dirname, 'views'));
// app.set('view engine', 'jade');
// uncomment after placing your favicon in /public
//app.use(favicon(path.join(__dirname, 'public', 'favicon.ico')));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(cookieParser());
app.use(express.static(path.join(__dirname, 'public')));
// Routes
var index = require('./routes/index');
var users = require('./routes/users');
app.use('/api', index);
app.use('/users', users);
// Passport Config
app.use(passport.initialize());
app.use(passport.session())
// mongoose
const options = {
autoIndex: true, // Don't build indexes
reconnectTries: Number.MAX_VALUE, // Never stop trying to reconnect
reconnectInterval: 500, // Reconnect every 500ms
poolSize: 10, // Maintain up to 10 socket connections
// If not connected, return errors immediately rather than waiting for reconnect
bufferMaxEntries: 0
};
console.log(options);
// Localhost Connect
mongoose.connect(dbPath, options).then(
() => { console.log("connected !!!"); },
err => { console.log(err); }
);
Any and all help is appreciated.
If you want to use the server you create the problem with the session is the express router, because change res and req vars so like recommend in nuxt use this.
const express = require('express');
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
const app = express()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
You are missing this step
// Create express router
const router = express.Router()
// Transform req & res to have the same API as express
// So we can use res.status() & res.json()
router.use((req, res, next) => {
Object.setPrototypeOf(req, app.request)
Object.setPrototypeOf(res, app.response)
req.res = res
res.req = req
next()
})
The req, res parameters need to be interchanged on the client side
Once you do router.post('/login') and logout
app.use('/api', router)
And that will work perfectly

Heroku App Login works for HTTP but not HTTPS

I have passport.js set up with strategies for Google, Facebook and Github. They work fine over HTTP but not so in HTTPS.
When I'm on my site over HTTPS, and I click for example, Login with Google, I see in my URL bar the site has sent me to the relative URL '/auth/google'. This is the route I've wired in my backend to initiate the OAuth process for logging in. But in HTTPS, I simply end up at the page, there are no error messages in dev console. I have a catch all block of code that serves up index.html when the URL doesn't match any of my routes and I'm pretty sure this is what's happening i.e. the backend routes doesn't seemed to be recognised.
So to summarise, on HTTP, the app stays in a non-logged in state and there are no errors in the dev console.
What's worth noting is that I can also get it to stay in logged in state with the same behaviour as well. If I log in over HTTP and then immediately go to the HTTPS site, I'm logged in. If I try to log out, I get sent to '/auth/logout'. Again no error in console and I get served index.html as if I hadn't written a route for '/auth/logout' and I continue to be logged in.
Because there are no error messages, I don't know what part of the code to show here, I'll just show what I think could be possibly relevant.
Here are my auth routes:
const passport = require('passport')
const express = require('express')
const auth = express.Router()
auth.get(
'/google',
passport.authenticate('google', {
scope: ['profile']
})
)
auth.get('/google/callback', passport.authenticate('google'), (req, res) => {
res.redirect('/')
})
auth.get('/facebook', passport.authenticate('facebook'))
auth.get(
'/facebook/callback',
passport.authenticate('facebook'),
(req, res) => {
res.redirect('/')
}
)
auth.get('/github', passport.authenticate('github'))
auth.get('/github/callback', passport.authenticate('github'), (req, res) => {
res.redirect('/')
})
auth.get('/current_user', (req, res) => {
res.send(req.user)
})
auth.get('/logout', (req, res) => {
req.logout()
res.redirect('/')
})
module.exports = auth
Here is my passport strategies
const passport = require('passport')
const GoogleStrategy = require('passport-google-oauth20').Strategy
const FacebookStrategy = require('passport-facebook').Strategy
const GithubStrategy = require('passport-github').Strategy
const mongoose = require('mongoose')
const keys = require('../config/keys')
const User = mongoose.model('user')
passport.serializeUser((user, done) => {
done(null, user.id)
})
passport.deserializeUser((id, done) => {
User.findById(id).then(user => {
done(null, user)
})
})
const login = (accessToken, refreshToken, profile, done) => {
User.findOne({ profileID: profile.id }).then(existingUser => {
if (existingUser) {
done(null, existingUser)
} else {
new User({
profileID: profile.id
})
.save()
.then(user => done(null, user))
}
})
}
passport.use(
new GoogleStrategy(
{
clientID: keys.googleClientID,
clientSecret: keys.googleSecretKey,
callbackURL: '/auth/google/callback',
proxy: true
},
login
)
)
passport.use(
new FacebookStrategy(
{
clientID: keys.facebookClientID,
clientSecret: keys.facebookSecretKey,
callbackURL: '/auth/facebook/callback',
profileFields: ['id', 'name'],
proxy: true
},
login
)
)
passport.use(
new GithubStrategy(
{
clientID: keys.githubClientID,
clientSecret: keys.githubSecretKey,
callbackURL: '/auth/github/callback',
proxy: true
},
login
)
)
And here is my server index.js
const express = require('express')
const mongoose = require('mongoose')
const passport = require('passport')
const session = require('express-session')
const bodyParser = require('body-parser')
const keys = require('./config/keys')
const auth = require('./routes/authRoutes')
const poll = require('./routes/pollRoutes')
require('./models/User')
require('./services/passport')
mongoose.connect(keys.mongoURI, { useMongoClient: true })
mongoose.Promise = global.Promise
const app = express()
app.use(bodyParser.json())
app.use(
session({
secret: keys.cookieKey,
saveUninitialized: true,
resave: true
})
)
app.use(passport.initialize())
app.use(passport.session())
app.use('/auth', auth)
app.use('/poll', poll)
if (process.env.NODE_ENV === 'production') {
app.use(express.static('client/build'))
const path = require('path')
app.get('*', (req, res) => {
res.sendFile(path.resolve(__dirname, 'client', 'build', 'index.html'))
})
}
const PORT = process.env.PORT || 5000
app.listen(PORT, () => console.log(`Server started on port ${PORT}`))
I'll also provide a link to my Heroku app so you guys can see the buggy behaviour over HTTPS and a link to my Github repo
I'm pretty sure this doesn't have anything to do with how I've set up my Oauth on developer consoles of Google, Facebook and Github because all three login strategies behave exactly the same way so I would have to set up the Google+ API credentials, the Facebook Login settings and the Github Developers Settings all in a way to produce the same error which seems really unlikely. Also everything works correctly on LocalHost. Can someone please help me with this issue?