I have a Web Application using NodeJS + AngularJS, I want to integrate OKTA SAML with my application(Using Okta as IdP, using our Application as SP), right now I have some info after configuration on Okta End, it like this:
https://www.pastepic.xyz/image/image.abqL4
https://www.pastepic.xyz/image/image.ab3G3
I have Idp Single-Sign On URL, IdP Issuer, X.509 Certificate and also MetaData.
My question is how to integrate it with NodeJS oR AngularJS, How should I use the data from okta.
I have tried to use passport library in NodeJS, the app.js like this:
var express = require("express");
var path = require("path");
var bodyParser = require("body-parser");
var routes = require("./api/routes");
var http = require("http");
var morgan = require('morgan');
var cookieParser = require('cookie-parser');
var session = require('express-session');
var passport = require('passport');
require("./api/helper/global_function");
// Defining the port and env to run on
const { environment, port, samlUrl } = require('./appconfig.js');
const PORT = port || 8000;
const ENV = environment || 'dev'
const SAMLURL = samlUrl
const config = require('./api/auth/config.js')[ENV];
require('./api/auth/passport.js')(passport, config);
var app = express();
app.set("port", PORT);
app.set("env", ENV)
app.set("samlUrl", SAMLURL)
// To Allow CORS calls
app.use(function (req, res, next) {
res.header('Access-Control-Allow-Origin', '*')
res.header('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE');
res.header('Access-Control-Allow-Headers', 'Content-Type, Authorization');
res.header('Access-Control-Allow-Credentials', true);
return next();
});
app.use(morgan('combined'));
app.use(cookieParser());
app.use(session(
{
resave: true,
saveUninitialized: true,
secret: 'something'
}));
// Enable parsing of posted forms
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session());
// Set static directory before defining routes
app.use(express.static(path.join(__dirname, "dist")));
app.use("/node_modules", express.static(__dirname + "/node_modules"));
// Add some routing
app.use("/", function (req, res, next) {
if (req.isAuthenticated()) {
console.log(req.isAuthenticated())
return next();
}
req.session.returnTo = req.originalUrl;
res.redirect(samlUrl);
});
app.use("/api", routes);
app.use("*", (req, res) => {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
// To verify the listen is up and running
var server = app.listen(app.get("port"), function () {
var port = server.address().port;
console.log("Magic happens on port " + port);
});
It doesn't work because all request is unauthenticated, when I make a api call, the application keep directing me to single sign on page.
Maybe I am absolutely wrong about this, can somebody help me out? Give me a hint or tell me some basic logic
Can any people help with SAML? If any unclear, please add a comment or answer, I will check in a very short time.
Related
I am doing the Coursera course on Server-Side Development and I have followed the instructions precisely. I keep getting this error, however. There are no relevant posts on their Discussion platform, and I cannot seem to debug because (if you see below) the trace is solely referring to files in the node_modules folder that are established upon initialization of the project as a node project. Thus, I am stuck. Presumably there is "something" wrong with my code, but since the trace is not referring to anything I coded, I am lost. I also thought that perhaps I failed to install express, but I have tried reinstalling as per instructions in the course and that doesn't seem to solve the problem.
Has anyone encountered this specific error when creating a Node.js project and, if so, what did you do to solve?
Login sessions require session support. Did you forget to use express-session middleware?
This is the app.js code:
var createError = require('http-errors');
const express = require('express');
var path = require('path');
var cookieParser = require('cookie-parser');
var logger = require('morgan');
const session = require('express-session');
var FileStore = require('session-file-store')(session);
var passport = require('passport');
var authenticate = require('./authenticate');
var config = require('./config');
var indexRouter = require('./routes/index');
var usersRouter = require('./routes/users');
var dishRouter = require('./routes/dishRouter');
var leaderRouter = require('./routes/leaderRouter');
var promoRouter = require('./routes/promoRouter');
const uploadRouter = require('./routes/uploadRouter');
const mongoose = require('mongoose');
mongoose.Promise = require('bluebird');
const Dishes = require('./models/dishes');
const url = config.mongoUrl;
const connect = mongoose.connect(url);
connect.then((db) => {
console.log('Connected correctly to the server.');
}, (err) => {console.log(err); });
var app = express();
// Secure traffic only
app.all('*', (req, res, next) => {
if (req.secure) {
return next();
}
else {
res.redirect(307, 'https://' + req.hostname + ':' + app.get('secPort') + req.url);
}
});
// view engine setup
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'jade');
app.use(logger('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
app.use(passport.initialize());
app.use(passport.session()); // FOund this in the Forum not Given Code
// Note that these two mountings occur before authentication
app.use('/', indexRouter);
app.use('/users', usersRouter);
// Authentication is now completed
app.use(express.static(path.join(__dirname, 'public')));
// This is where the mounting occurs
app.use('/dishes', dishRouter);
app.use('/promotions', promoRouter);
app.use('/leaders', leaderRouter);
app.use('/imageUpload',uploadRouter);
// 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');
});
module.exports = app;
This is the index.js file:
var express = require('express');
var router = express.Router();
/* GET home page. */
router.get('/', function(req, res, next) {
res.render('index', { title: 'Express' });
});
module.exports = router;
Per the doc for session-file-store, the proper initialization is this:
const session = require('express-session');
const FileStore = require('session-file-store')(session);
const fileStoreOptions = {};
app.use(session({
store: new FileStore(fileStoreOptions),
secret: 'keyboard cat'
}));
You seem to be missing the app.use() part that actually initializes express-session.
I'm new to node and am having some probably very basic trouble with an error message that I cannot fix:
proj/node_modules/express/lib/application.js:210
throw new TypeError('app.use() requires a middleware function')
I've been trying to use the csrf token module but for some reason I'm getting the above error code - literally clueless as to finding a fix here. May be very obvious to some - does anyone have any ideas?
This is the offending line of code: app.use(csrfProtection); // ERROR HERE: REASON UNKNOWN
I have installed the csurf module exactly identically to the tutorial I'm following. Any help would be much appreciated - original code is below:
const express = require('express');
const path = require('path');
const app = express();
const bodyParser = require('body-parser'); // enables use of body parser - body parser helps get data entered from a form (via req.body)
const subRoutes = require('./routes/subs');
const adminRoutes = require('./routes/admin');
const authRoutes = require('./routes/auth');
const mongoConnect = require('./util/database').mongoConnect;
let mailActivator = require('./util/nodemailer'); // activates nodemailer.js
const session = require('express-session');
const MongoDBStore = require('connect-mongodb-session')(session); // session argument from express-session
const csrf = require('csrf');
const store = new MongoDBStore({
uri: 'mongodb+srv://_____:______#empool-3klmr.mongodb.net/______',
collection: 'sessions'
});
const csrfProtection = csrf();
app.set('view engine', 'ejs'); // use EJS templating engine
app.set('views', 'views'); // views located here
app.use(bodyParser.urlencoded({
extended: false
})); // parses text as URL encoded data (which is how browsers tend to send form data from regular forms set to POST) and exposes the resulting object (containing the keys and values) on req.body)
app.use(bodyParser.json()); // for JSON post API requests
app.use(express.static(path.join(__dirname, 'public')));
app.use(session({
secret: "my secret",
resave: false,
saveUninitialized: false,
store: store
}));
app.use((req, res, next) => {
console.log('This demo middleware will always run...');
console.log('req.get("host")', req.get("host"));
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, PUT');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, Authorization');
next();
});
app.use((req, res, next) => {
res.locals.isLoggedIn = req.session.isLoggedIn;
res.locals.csrfToken = req.csrfToken();
next();
});
app.use(csrfProtection); // ERROR HERE: REASON UNKNOWN
app.use(subRoutes);
app.use(authRoutes);
app.use(adminRoutes);
//app.listen(3000);
mongoConnect(() => {
app.listen(process.env.PORT || 3000);
});
console.log('App running successfully...');
Here
I am new to nodejs + Express but I'm trying to build a very quick proof of concept website which allows user to authenticate via a REST API.
I have come against a CORS problem and despite installing the cors module and using it as suggested in the documentation, I am still getting the following error:
Access to XMLHttpRequest at xxx has been blocked by CORS policy:
Response to preflight request doesn't pass access control check: The
'Access-Control-Allow-Origin' header has a value
'https://www.example.com' that is not equal to the supplied origin.
Here is my (simplified) code:
app.js
const express = require('express');
const expressLayouts = require('express-ejs-layouts');
const cors = require('cors');
compression = require('compression'),
shouldCompress = (req, res) => {
if (req.headers['x-no-compression']) {
// don't compress responses if this request header is present
return false;
}
// fallback to standard compression
return compression.filter(req, res);
};
const app = express();
// EJS
app.use(expressLayouts);
app.set('view engine', 'ejs');
// Parsing related
app.use(express.urlencoded( { extended: false })); //Parse URL-encoded bodies
app.use(express.json()); //Used to parse JSON bodies
app.use(compression({
filter:shouldCompress,
threshold: 3
}));
app.use(express.static('public'));
app.disable('x-powered-by');
// Using the flash middleware provided by connect-flash to store messages in session
// and displaying in templates
const flash = require('connect-flash');
app.use(flash());
// Sessions
const session = require('express-session');
app.use(session({
secret: 'fat cat 42',
resave: false,
saveUninitialized: true,
cookie: { secure: true }
}));
// Initialize Passport and restore authentication state, if any, from the session.
const passport = require('passport');
require ('./config/passport')(passport);
app.use(passport.initialize());
app.use(passport.session())
// Routes
app.use('/', require('./routes/index'));
app.use('/member', require('./routes/users'));
const PORT = process.env.PORT || 5000;
app.listen(PORT, console.log(`Server started on port: ${PORT}`));
users.js
const express = require('express');
const router = express.Router();
const passport = require('passport');
require ('../config/passport')(passport);
router.post('/signin', passport.authenticate('facebook', {
successRedirect : '/home',
failureRedirect : '/'
}));
module.exports = router;
Here is the script portion of the view that makes the AJAX POST
homepage.ejs
$(document).ready(function(){
$('#demo').click(function(e){
$.ajax({
method: "POST",
url: "/member/signin",
data: {
"source": $(this).attr('id')
},
dataType: "json",
timeout: 5000 // 5000ms
}).done(function(data) {
// is called if request is successful
console.log('Success:' + data);
}).fail(function(jqXHR, status) {
// is called if request fails or timeout is reached
alert('Request could not complete: ' + status);
});
});
});
How do I fix this so that the AJAX calls work?
essentially you need to permit cross-site origin requests. you do that by setting the access-control-headers normally with some proxy like nginx in front of your node server like the following (it is not recommended to have node directly exposed on port 80)
#nginx config
if ($request_method = 'OPTIONS') {
add_header 'Access-Control-Allow-Origin' '*';
add_header 'Access-Control-Allow-Credentials' 'true';
add_header 'Access-Control-Allow-Methods' 'GET, POST, PUT, HEAD, DELETE, OPTIONS';
add_header 'Access-Control-Allow-Headers' 'Accept,Authorization,Cache-Control,Content-Type,DNT,If-Modified-Since,Keep-Alive,Origin,User-Agent,X-Requested-With';
return 204;
}
if you have expressjs you could use this cors middleware
var express = require('express')
var cors = require('cors')
var app = express()
app.use(cors())
app.post('/signin/', function (req, res, next) {
// ....
})
I am building a full stack React application that accesses its own back end API with Axios. In my local environment, the following works as expected, with the server responding with JSON data, which is then rendered properly.
axios.get('/api/questions/categories')
I deployed to Heroku, and the app is launching normally and MongoDB is connected. Now, when the same GET request is made, it is not reaching the back end. When I log the response from Axios to the console, it contains the actual HTML of the page, instead of the JSON object expected.
For further clarification, if I manually type 'http://localhost:8080/api/questions/categories' in the address bar, the expected JSON data is displayed. If I do the same with the app on Heroku, I see that a '#' is appended to the url and the page display does not change, no error messages. This leads me to think that react-router is involved, but I have not been able to figure out how/why.
My stack: Node, Express, Mongo, React
Not using Redux
Using Axios to call my own API
// Dependencies
var express = require('express');
var path = require('path');
var webpack = require('webpack');
var webpackMiddleware = require('webpack-dev-middleware');
var webpackHotMiddleware = require('webpack-hot-middleware');
var config = require('./webpack.config.js');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var morgan = require('morgan');
var inDev = process.env.NODE_ENV !== 'production';
var port = inDev ? 8080 : process.env.PORT;
var app = express();
// MIDDLEWARE
if (inDev){
var compiler = webpack(config);
var middleware = webpackMiddleware(compiler, {
publicPath: config.output.publicPath,
contentBase: 'app',
stats: {
colors: true,
hash: false,
timings: true,
chunks: false,
chunkModules: false,
modules: false
}
});
app.use(morgan('dev'));
app.use(middleware);
app.use(webpackHotMiddleware(compiler));
app.get('/', function response(req, res) {
res.write(middleware.fileSystem.readFileSync(path.join(__dirname, 'dist/index.html')));
res.end();
});
} else {
app.use(express.static(__dirname + '/dist'));
app.get('*', function response(req, res) {
res.sendFile(path.join(__dirname, 'dist/index.html'));
});
}
app.use(bodyParser.urlencoded({extended: true}));
app.use(bodyParser.json());
app.use(function(req, res, next) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'GET,HEAD,OPTIONS,POST,PUT,DELETE');
res.setHeader('Access-Control-Allow-Headers', 'Access-Control-Allow-Headers, Origin,Accept, X-Requested-With, Content-Type, Access-Control-Request-Method, Access-Control-Request-Headers');
//and remove caching so we get the most recent comments
res.setHeader('Cache-Control', 'no-cache');
next();
});
// DATABASE
var dbPath = inDev ? 'mongodb://localhost/quizMe' : 'mongodb://heroku_pmjl5579:c28cf07fpf05uus13ipjeur5s7#ds143000.mlab.com:43000/heroku_pmjl5579';
mongoose.connect(dbPath);
// ROUTING / API
// var indexRoute = require('./routes/index');
var questionsRoute = require('./routes/api/questions');
// app.use('/', indexRoute);
app.use('/api/questions', questionsRoute);
app.listen(port, function(){
console.log('Express server up on ' + port);
});
Thanks for any help!
Most single page applications route all requests to the root path and let the front end router take over. I suspect that is what is happening to your app.
Do you have any form of requests redirection logic in your back end code or any server configuration code?
What you can do is to whitelist some paths that you don't want front end routing to take over, such as those that start with /api. Pasting your server side config here will be helpful.
In your server config, when inDev is false, you have a app.get('*', ...) that catches all requests and responds with the static single page app. Hence API requests will also give the same response. You will need to restructure your routes to match /api before the wildcard *. Some examples can be found here
So i managed to get passport-twitter working together with jsonwebtoken library, but in order for it to work properly I have to use express-session as the middleware. I don't want to add session because I'm using jsonwebtoken to return the token.
Here's the code
autheticate.js
router.get('/twitter', function(req, res, next){
passport.authenticate('twitter', {session: false}, function(err, user, info){
if(err){ return next(err); }
if(user){
var token = createToken(user);
console.log(token);
return res.json({token: token});
} else {
return res.status(401).json(info);
}
})(req, res, next);
});
I already added session: false as the argument, but on server.js it keeps spitting error, that i need to use express-session.
server.js
var express = require('express');
var path = require('path');
var logger = require('morgan');
var bodyParser = require('body-parser');
var mongoose = require('mongoose');
var passport = require('passport');
var session = require('express-session');
var config = require('./config');
mongoose.connect('mongodb://localhost', function() {
console.log("Connected to the database");
})
require('./passport')(passport);
var app = express();
var authenticate = require('./routes/authenticate')(app, express, passport);
var api = require('./routes/api') (app, express, passport);
// uncomment after placing your favicon in /public
//app.use(favicon(__dirname + '/public/favicon.ico'));
app.use(logger('dev'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(session({
secret: config.TOKEN_SECRET,
resave: true,
saveUninitialized: true,
}));
app.use(express.static(path.join(__dirname, 'public')));
app.use(passport.initialize());
app.use('/auth', authenticate);
app.use('/api', api);
app.get('*', function(req, res) {
res.sendFile(__dirname + '/public/app/views/index.html');
});
app.listen(3000, function(err) {
if(err) {
return res.send(err);
}
console.log("Listening on port 3000");
});
So whenever i delete app.use(session()) and try to authenticate with passport-twitter. I will get this error
error Oauth Strategy requires app.use(express-session));
I know that the obvious solution is to add that line, but I dont want to use session. Does Oauth 0.1 really need to use session?
Passports OAuth based strategies use the session middleware to keep track of the login process. You do not need to use the session middleware for anything else, just base your authentication on your token and ignore the session.