I've set up an angular/nodejs(express) application with google authentication but whenever I request to google oauth through angular app it throws a cors error but if I request directly from browser it works as it should.
Backend is running on port 3000 and angular is running on 4200.
I'm using cors package to allow all cors requests in server.js:
app.use(passport.initialize());
// Passport Config
require('./config/passport')(passport);
// Allowing all cors requests
app.use(cors());
app.use(express.static(path.join(__dirname, 'public')));
app.use('/users', usersRouter);
app.use('/auth', authRouter);
Passport config:
passport.use(
new GoogleStrategy(
keys,
(token, refreshToken, profile, done) => {
process.nextTick(() => {
User.findOne({ email: email })
.then(user => {
if (user) {
return done(null, user);
}
})
.catch(err => console.log(err));
});
}
)
);
This is the google authentication route:
router.get('/google', passport.authenticate('google', {
scope: ['profile', 'email'],
session: false
}),
(req, res) => {
// Create JWT payload
const payload = {
id: req.user.id,
email: req.user.email
};
jwt.sign(payload, secret, expires, (err, token) => {
res.json(token);
});
}
);
And here is angular request:
googleAuth() {
return this.http.get<any>('http://localhost:3000/auth/google').pipe(
map(user => {
if (user) {
localStorage.setItem(
'currentUser',
JSON.stringify(user)
);
}
return user;
})
);
}
Error in chrome console:
Failed to load "https://accounts.google.com/o/oauth2/v2/auth?response_type=code...": No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access
I've also authorized JavaScript origins in google developers console:
origins
I'm pretty new to angular and nodejs and I have read all the similar questions but couldn't find a workaround to this problem.
Try out this method of directly calling the URL: ( Pardon if I missed something syntactically, but basically this is the idea )
googleAuth(){
window.open('/google',"mywindow","location=1,status=1,scrollbars=1, width=800,height=800");
let listener = window.addEventListener('message', (message) => {
//message will contain google user and details
});
}
and in server you have passport strategy set I assume, Now
router.get('/google', passport.authenticate('google', {
scope: ['profile', 'email'],
session: false } ))
router.get('/google/callback',
passport.authenticate('google', { failureRedirect: '/auth/fail' }),
function(req, res) {
var responseHTML = '<html><head><title>Main</title></head><body></body><script>res = %value%; window.opener.postMessage(res, "*");window.close();</script></html>'
responseHTML = responseHTML.replace('%value%', JSON.stringify({
user: req.user
}));
res.status(200).send(responseHTML);
});
Related
I have implemented local as well as google login using passport.js in a mern web application. The local authentication is working fine with the frontend but I am getting errors when using the Google strategy.
Error:
Access to XMLHttpRequest at 'https://accounts.google.com/o/oauth2/v2/auth?.........' (redirected from 'http://localhost:5000/auth/google') from origin 'http://localhost:3000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
Error: Network Error
at createError (createError.js:16)
at XMLHttpRequest.handleError (xhr.js:84)
Issue shown by the console:
Indicate whether a cookie is intended to be set in a cross-site context by specifying its SameSite attribute.
Indicate whether to send a cookie in a cross-site request by specifying its SameSite attribute
I tested the google strategy using POSTMAN and it was working fine but when requesting from my frontend there seems to be some issue.
server.js
require("dotenv").config();
const express=require("express");
const cors=require("cors");
const mongoose=require("mongoose");
const session=require("express-session");
const passport=require("passport");
const GoogleStrategy = require('passport-google-oauth20').Strategy;
const errorController=require("./controllers/errorController")
const app=express();
const port = process.env.PORT || 5000;
app.use(
cors({
origin: "http://localhost:3000", // <-- location of the react app were connecting to
credentials: true,
})
);
app.use(express.static("public"));
app.use(express.urlencoded({extended: true}));
app.use(express.json());
app.use(session({
secret:process.env.SECRET,
resave:false,
saveUninitialized:false,
}));
app.use(passport.initialize());
app.use(passport.session());
const User=require("./models/user.model");
passport.use(User.createStrategy());
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
passport.use(new GoogleStrategy({
clientID: process.env.CLIENT_ID,
clientSecret: process.env.CLIENT_SECRET,
callbackURL: "http://localhost:5000/auth/google/keeper"
},
function(accessToken, refreshToken, profile, cb) {
console.log(profile);
User.findOrCreate({ googleId: profile.id }, function (err, user) {
return cb(err, user);
});
}
));
mongoose.connect(process.env.MONGO_URI, {useNewUrlParser: true, useUnifiedTopology: true, useFindAndModify:false, useCreateIndex:true});
const connection = mongoose.connection;
connection.once('open', () => {
console.log("MongoDB database connection established successfully");
});
const loginRouter=require("./routes/login");
const registerRouter=require("./routes/register");
const logoutRouter=require("./routes/logout");
const authRouter=require("./routes/auth");
app.use("/login", loginRouter);
app.use("/register", registerRouter);
app.use("/logout", logoutRouter);
app.use("/auth/google", authRouter);
app.use(errorController);
app.listen(port, function(){
console.log("server started on port 5000");
});
auth.js (handles the google login related routes)
const router=require("express").Router();
const passport=require("passport");
router.get("/", passport.authenticate("google", { scope: ["profile"] }));
router.get("/keeper",
passport.authenticate("google", { failureRedirect: "/login" }),
function(req, res) {
// Successful authentication, redirect home.
res.redirect("/");
});
module.exports=router;
Axios request from the frontend
function googleLogin(event){
Axios({
method: "GET",
withCredentials: true,
url: "http://localhost:5000/auth/google",
})
.then(function(res){
console.log(res);
})
.catch(function(err){
console.log(err);
})
event.preventDefault();
}
button that triggers the request
<button className="btn btn-danger" onClick={googleLogin}>Sign in with Google</button>
You can't make an axios call to the /auth/google URL!!
Here's my solution...
// step 1:
// onClick handler function of the button should use window.open instead
// of axios or fetch
const googleLogin = () => window.open("http://[server:port]/auth/google", "_self")
//step 2:
// on the server's redirect route add this successRedirect object with correct url.
// Remember! it's your clients root url!!!
router.get(
'/google/redirect',
passport.authenticate('google',{
successRedirect: "[your CLIENT root url/ example: http://localhost:3000]"
})
)
// step 3:
// create a new server route that will send back the user info when called after the authentication
// is completed. you can use a custom authenticate middleware to make sure that user has indeed
// been authenticated
router.get('/getUser',authenticated, (req, res)=> res.send(req.user))
// here is an example of a custom authenticate express middleware
const authenticated = (req,res,next)=>{
const customError = new Error('you are not logged in');
customError.statusCode = 401;
(!req.user) ? next(customError) : next()
}
// step 4:
// on your client's app.js component make the axios or fetch call to get the user from the
// route that you have just created. This bit could be done many different ways... your call.
const [user, setUser] = useState()
useEffect(() => {
axios.get('http://[server:port]/getUser',{withCredentials : true})
.then(response => response.data && setUser(response.data) )
},[])
Explanation....
step 1 will load your servers auth url on your browser and make the auth request.
step 2 then reload the client url on the browser when the authentication is
complete.
step 3 makes an api endpoint available to collect user info to update the react state
step 4 makes a call to the endpoint, fetches data and updates the users state.
Note : It could be duplicate of some SO questions, but going through those answers didn't resolve this issue.
Am trying to integrate google-oauth using passport-google-oauth as per passport js doc and github example.
Facing this error ( It refer to some link, but link doesn't work )
GooglePlusAPIError: Legacy People API has not been used in project xxxxxxx before or it is disabled. Enable it by visiting https://console.developers.google.com/apis/api/legacypeople.googleapis.com/overview?project=xxxxxxxxxx then retry. If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
at D:\openSource\passport\express-passport-google-oauth-exmple\node_modules\passport-google-oauth20\lib\strategy.js:95:21
at passBackControl (D:\openSource\passport\express-passport-google-oauth-exmple\node_modules\oauth\lib\oauth2.js:132:9)
at IncomingMessage.<anonymous> (D:\openSource\passport\express-passport-google-oauth-exmple\node_modules\oauth\lib\oauth2.js:157:7)
at IncomingMessage.emit (events.js:215:7)
at endReadableNT (_stream_readable.js:1184:12)
at processTicksAndRejections (internal/process/task_queues.js:80:21)
As per some other answers on SO, enabled Google+ API, Google People API and Contact API, but error remain same. I know Google+ and Google People API has been decommissioned as of now.
Let me know what Google API exactly need to be enabled to function Google Auth. I need only to validate user and get basic profile during authentication.
In case it is not related with API enable, help me to find the error.
Code is posted below, and same is available at github example
auth.js
const GoogleStrategy = require('passport-google-oauth')
.OAuth2Strategy;
module.exports = function (passport) {
passport.serializeUser((user, done) => {
done(null, user);
});
passport.deserializeUser((user, done) => {
done(null, user);
});
passport.use(new GoogleStrategy({
clientID: "xxxxxxxxxxxx",
clientSecret: "xxxxxxxxxxxx",
callbackURL: '/auth/google/callback'
}, (token, refreshToken, profile, done) => {
return done(null, {
profile: profile,
token: token
});
}));
};
server.js
const express = require('express'),
app = express(),
passport = require('passport'),
auth = require('./auth'),
cookieParser = require('cookie-parser'),
cookieSession = require('cookie-session');
auth(passport);
app.use(passport.initialize());
app.use(cookieSession({
name: 'session',
keys: ['SECRECT KEY'],
maxAge: 24 * 60 * 60 * 1000
}));
app.use(cookieParser());
app.get('/', (req, res) => {
if (req.session.token) {
res.cookie('token', req.session.token);
res.json({
status: 'session cookie set'
});
} else {
res.cookie('token', '')
res.json({
status: 'session cookie not set'
});
}
});
app.get('/logout', (req, res) => {
req.logout();
req.session = null;
res.redirect('/');
});
app.get('/auth/google', passport.authenticate('google', {
scope: ['https://www.googleapis.com/auth/userinfo.profile']
}));
app.get('/auth/google/callback',
passport.authenticate('google', {
failureRedirect: '/'
}),
(req, res) => {
console.log(req.user.token);
req.session.token = req.user.token;
res.redirect('/');
}
);
app.listen(3000, () => {
console.log('Server is running on port 3000');
});
Finally issue resolved, there are multiple libraries with similar name.. i.e. passport-google-oauth , passport-google-oauth2 and passport-google-oauth20. After trying all 3, passport-google-oauth20 did work for me.
passport-google-oauth2 is github url but its about passport-google-oauth20.
I'm working on integrating Auth0 into a MERN Stack app. The flow should look like this:
User clicks the login button which triggers Auth0Lock.show()
User fills in their credentials and clicks the submit button
The callback URL of the API is hit which logs the user in and redirects them back to the front-end app
(everything looks like it's working fine up to this point)
The front-end requests user information from the API
The front-end receives the information and redirects
This seems to be a fairly standard authentication flow. The problem is that when the front-end asks the back-end for user information, there's an error:
UnauthorizedError: No authorization token was found
My setup looks essentially like this:
// client-side config
const lock = new Auth0Lock(clientID, domain, {
auth: {
responseType: 'token',
audience: 'https://${domain}/userinfo',
redirectUrl: API_URL + '/api/users/callback',
params: {
scope: 'openid profile email' // no change
}
}
})
// server.js
app.use(bodyParser.urlencoded({ extended: false }));
app.use(bodyParser.json());
// [DB setup]
var sessConfig = {
secret: "[random string]",
cookie: {
sameSite: false
},
resave: false,
saveUninitialized: true
};
if(app.get('env') === 'production') sessConfig.cookie.secure = true;
app.use(session(sessConfig));
const {domain, clientID, clientSecret, callbackURL} = require('./config/auth0');
const passportStrategy = new Auth0Strategy(
{domain, clientID, clientSecret, callbackURL},
(accessToken, refreshToken, extraParams, profile, done) => done(null, profile)
)
passport.use(passportStrategy);
passport.serializeUser((user, done) => done(null, user));
passport.deserializeUser((user, done) => done(null, user));
app.use(passport.initialize());
app.use(passport.session());
// [routing]
// routes/users.js
router.get('/callback', (req, res, next) => {
passport.authenticate('auth0', (err, user, info) => {
if(err) return next(err);
if(!user) return next(info);
req.logIn(user, err => {
if(err) return next(err);
const returnTo = req.session.returnTo;
delete req.session.returnTo;
res.redirect(returnTo || clientRootURL + '/callback');
})
})(req, res, next);
})
router.get(
'/current',
require('cors')(),
authenticate,
(req, res) => {
res.json({
id: req.user.id,
name: req.user.name,
email: req.user.email
});
}
);
// authenticate.js
module.exports = jwt({
secret: jwksRsa.expressJwtSecret({
cache: true,
rateLimit: true,
jwksRequestsPerMinute: 5,
jwksUri: `https://${domain}/.well-known/jwks.json`
}),
audience: clientID,
issuer: `https://${domain}/`,
algorithms: ['RS256']
});
The vast majority of comes straight out of the Auth0 documentation.
I'm trying to get the user info from the /users/current endpoint after logging in and it says it can't find authorization. Does anyone have any idea how to get this to work?
You should be calling the /userinfo endpoint to get the user profile, or getting the info from the id_token. Take a look at this doc
https://auth0.com/docs/api/authentication#get-user-info
Every authenticated frontend call should contain:
headers: {
Authorization: `Bearer ${token}`,
},
where token should be:
const token = await getAccessTokenSilently();
getAccessTokenSilently is a public function of auth0 lib.
See: getAccessTokenSilently doc
I'm writing one of my first applications in NodeJS so please bear with me. I've managed to successfully authenticate a user to our Active directory and I can see the connect.sid cookie being set and used on the subsequent requests.
Upon debugging the application by dumping the req object I can also see that the user variable has been set successfully. From the documentation I've read that seems to be a criteria for a successful session match?
However, the request is still getting a 401 Unauthorized.
To summarize:
The user is successfully authenticated after posting credentials /login.
Upon successful authentication the user is redirected to "/".
The "/" path replies with 401 Unauthorized.
Any ideas much appreciated. Code below.
const express = require('express');
var bodyParser = require('body-parser');
var session = require('express-session');
var passport = require('passport')
var ActiveDirectoryStrategy = require('passport-activedirectory')
// Setup the authentication strategy
passport.use(new ActiveDirectoryStrategy({
integrated: false,
ldap: {
url: 'ldap://myad.company.com',
baseDN: 'DC=domain,DC=company,DC=com',
username: 'user',
password: 'password'
}
}, function (profile, ad, done) {
ad.isUserMemberOf(profile._json.dn, 'Group', function (err, isMember) {
if (err) return done(err)
return done(null, profile)
})
}));
passport.serializeUser(function(user, done) {
done(null, JSON.stringify(user));
});
passport.deserializeUser(function(user, done) {
done(null, JSON.parse(user));
});
const app = express();
app.use(bodyParser.urlencoded({extended: true}));
app.use(session(
{ secret: "password" }
));
app.use(passport.initialize());
app.use(passport.session());
// For debugging purposes
app.use(function (req, res, next) {
console.log(req)
next()
})
// The login page posts a form containing user and password
app.get("/login", (req, res) => {
res.sendFile(__dirname + '/public/index.html');
})
// Handler for the login page. Receives user and password and redirects the user to /
app.post('/login',
passport.authenticate('ActiveDirectory', {
failWithError: true,
successRedirect: "/",
failureRedirect: "/login"
}
), function(req, res) {
res.json(req.user)
}, function (err) {
res.status(401).send('Not Authenticated')
}
)
// This is where the issue happens. The page returns "Unauthorized".
// Using console.log(req) shows that the user property has been set to the req object.
// However, for some reason it still fails.
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Found what I did wrong!
The .authenticate method is only used to validate credentials, not to validate a session.
So this:
app.get('/',
passport.authenticate('ActiveDirectory', {
failWithError: true,
}
), function(req, res) {
res.send("test")
}, function (err) {
res.status(401).send('Not Authenticated')
})
Should become:
app.get('/', function(req, res, next) {
// This is verifying that the user part has been populated,
// which means that the user has been authenticated.
if (req.user) {
res.send('Returning with some text');
} else {
// If the user property does no exist, redirect to /login
res.redirect('/login');
}
});
Another thing that I changed was the serialize/deserialize functions:
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(user, done) {
done(null, user);
});
This removes redundant serializing/deserializing.
These articles really helped me understand the flow:
http://toon.io/understanding-passportjs-authentication-flow/
https://www.airpair.com/express/posts/expressjs-and-passportjs-sessions-deep-dive
Hope it helps someone else!
/Patrik
I am new to using Passport authentication with express between my
server and client (on two different ports), and am having great
trouble understanding both the concept of maintaining session state
with Passport and also trying to retrieve req.session.passport.user
in order to determine if a request is sent from a user that has
successfully logged on.
Checking Persistent Session State with Passport
In my code, I have a route to my profile page that uses my own
authenticated function. My login route uses
passport.authenticate('local'), however. Should I be applying this
to ALL subsequent routes (like for my profile), or is my separate
authentication expected?
req.session.passport undefined
For some reason, the cookie that is sent with the /profile GET request is missing the passport object after a successful login (by
passport.use('local-login')) It looks like the following if I
console.log(req.session) in authenticated. Why is this?
Session {
cookie:
{ path: '/',
_expires: 2017-07-11T01:38:30.087Z,
originalMaxAge: 14400000,
httpOnly: true,
secure: false } }
Server Code
// server.js
let app = express();
app.use(cors());
import passportConfig from '../config/passport';
passportConfig(passport); //apply passport configuration
var jsonParser = bodyParser.json();
app.use(jsonParser);
app.use(cookieParser('mySecret'));
app.use(session({
secret: 'mySecret',
resave: false,
saveUninitialized: true,
cookie: {secure: false, maxAge: 4*60*60*1000 }
}));
app.use(passport.initialize());
app.use(passport.session());
function authenticated (req, res, next) {
// req.session.passport is undefined
if (req.session.passport.user) {
next();
}
else {
res.redirect('/signup');
}
}
app.get('/profile', authenticated, (req, res) => {
// server code handling route
}
app.post('/login', passport.authenticate('local-login', {
session: true
}), (req, res) => {
res.json({
email: req.user.local.email
});
});
// passport.js
import User from '../models/User';
export default function passportConfig(passport) {
passport.serializeUser((user, done) => {
console.log('serializing user');
done(null, user.id);
});
// used to deserialize the user
passport.deserializeUser((id, done) => {
console.log('deserializing user');
User.findById(id, (err, user) => {
done(err, user);
});
});
passport.use('local-login', new LocalStrategy({
usernameField : 'email',
passwordField : 'password',
passReqToCallback : true
}, (req, email, password, done) => {
// if verification is successful
var newUser = new User();
// set credentials of newUser...
return done(null, newUser);
}));
}
Client Code
// profile.js (client action code with react-redux)
export function profile(terms, location) {
return (dispatch) => {
return fetch('profile',
{credentials: 'include'})
.then(response => response.json())
.then(json => { dispatch(profileSuccess(json)); })
.catch(error => dispatch(profileError(error)));
}
}
I am aware there are actually quite a lot of articles/posts available
of similar issues, but applying many of the different solutions have
all failed for me. Any help to my problem specifically is greatly
appreciated.