MEAN Stack + passport local: using sessions on a single page app - authentication

I tried implementing this tutorial on how to add an authentication process to a MEAN app:
http://mherman.org/blog/2015/07/02/handling-user-authentication-with-the-mean-stack/#.VgUaT_mqpBc
The change i tried to implement is to make the app a single page app ... so the only routes i left were the post routes for login ... i am addressing the login because if i can make that work ... the rest will follow
So my app consists of a google map that fills the page and two buttons (login and register) that open a modal window ... inside i have login form ... here are the codes for the angular controller of the modal window and the jade template
Controller:
angular.module('myApp')
.controller('ModalInstanceCtrl', ['$scope', '$modalInstance', 'settings', '$location', 'AuthService', function ($scope, $modalInstance, settings, $location, AuthService) {
$scope.settings = settings;
$scope.texts = {
login: {
title: "Login details",
button: "Login"
},
register: {
title: "Registration form",
button: "Register"
}
};
$scope.register = function () {
$modalInstance.close();
};
$scope.login = function () {
// initial values
$scope.error = false;
// call login from service
AuthService.login($scope.loginForm.username, $scope.loginForm.password)
// handle success
.then(function () {
console.log(AuthService.getUserStatus());
$modalInstance.close();
})
// handle error
.catch(function () {
$scope.error = true;
$scope.errorMessage = "Invalid username and/or password";
});
//$modalInstance.close();
};
$scope.cancel = function () {
$modalInstance.close();
};
}])
Jade template for modal window:
div(class="modal-header")
h3(class="modal-title") {{texts[settings.action].title}}
div(class="modal-body")
div(ng-show="error", class="alert alert-danger")
form(class="form", ng-submit="login()")
div(class="form-group")
label Username
input(type="text", class="form-control", name="username", ng-model="loginForm.username")
div(class="form-group")
label Password
input(type="password", class="form-control", name="password", ng-model="loginForm.password")
div(class="modal-footer")
button(ng-show="settings.action=='login'", class="btn btn-primary", type="button", ng-click="login()") {{texts.login.button}}
button(ng-show="settings.action=='register'", class="btn btn-primary", type="button", ng-click="register()") {{texts.register.button}}
button(class="btn btn-warning", type="button", ng-click="cancel()") Cancel
So my pb is this: the passport authenticate gets executed correctly .. I get the login success message ... but on refresh ... if I run AuthService.isLoggedIn() .. I get false ...
Here is the service:
angular.module('myApp').factory('AuthService',
['$q', '$timeout', '$http',
function ($q, $timeout, $http) {
// create user variable
var user = null;
// return available functions for use in controllers
return ({
isLoggedIn: isLoggedIn,
getUserStatus: getUserStatus,
login: login,
logout: logout,
register: register
});
function isLoggedIn() {
if(user) {
return true;
} else {
return false;
}
}
function getUserStatus() {
return user;
}
function login(username, password) {
// create a new instance of deferred
var deferred = $q.defer();
// send a post request to the server
$http.post('/user/login', {username: username, password: password})
// handle success
.success(function (data, status) {
if(status === 200 && data.status){
user = true;
deferred.resolve();
} else {
user = false;
deferred.reject();
}
})
// handle error
.error(function (data) {
user = false;
deferred.reject();
});
// return promise object
return deferred.promise;
}
}]);
Here is the post route for the passport.authenticate
router.post('/login', function (req, res, next) {
passport.authenticate('local', function (err, user, info) {
if (err) {
return next(err)
}
if (!user) {
return res.status(401).json({err: info})
}
req.logIn(user, function (err) {
if (err) {
return res.status(500).json({err: 'Could not log in user'})
}
res.status(200).json({status: 'Login successful!'})
});
})(req, res, next);
});

Related

How to show back-end message error, angular 10 only show statusText

I return in my front-end(Angular10) a back-end(ASP.NET) message using angular alertService, but only show statusText equals NotFound, when should be "Usuário não encontrado.", in network a message is appears, but in alert not.
login.component.ts
onSubmit() {
this.submitted = true;
// reset alerts on submit
this.alertService.clear();
// stop here if form is invalid
if (this.form.invalid) {
return;
}
error: HttpErrorResponse ;
this.loading = true;
this.accountService.login(this.f.user_nome.value, this.f.user_accessKey.value)
.pipe(first())
.subscribe({
next: () => {
// get return url from query parameters or default to home page
const returnUrl = this.route.snapshot.queryParams['returnUrl'] || '/';
this.router.navigateByUrl(returnUrl);
},
error: error => {
this.alertService.error(error);
this.loading = false;
}
});
}
account.service.ts
login(user_nome, user_accessKey) {
return this.http.post<User>(`${environment.apiUrl}/Conta/v1/login`, { user_nome,user_accessKey })
.pipe(map(user => {
// store user details and jwt token in local storage to keep user logged in between page refreshes
localStorage.setItem('user', JSON.stringify(user));
this.userSubject.next(user);
return user;
}));
}
Print Return
Message is comming in network
Console HttpErrorResponse return

Sequelize model property undefined Express.js controller after auth with passport-jwt

I am using passport-jwt to verify access to a given route in express.js, and then return a Sequelize model to the final controller. The code looks like:
The auth strategy:
const passportStrategy = passport => {
const options = {
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
secretOrKey: config.auth.ACCESS_TOKEN_SECRET
};
passport.use(
new Strategy(options, async (payload, done) => {
try {
const user = await User.findOne({ where: { email: payload.email }});
if (user) {
return done(null, {
user
});
}
return done(null, false);
}
catch (error) {
return done(error, false)
}
})
);
};
The route with the auth middleware
router.get('/:user_id/psy', passport.authenticate('jwt', { session: false }), patientsController.getPatientPsy);
The controller function
const getPatientPsy = async (req, res) => {
const authenticatedUser = req.user;
if (authenticatedUser.userType !== "patient") {
res.status(500).send("Big time error");
}
}
If I console.log(authenticatedUser) in the getPatientPsy() controller it successfully prints the Sequelize model with it's dataValues and so on, but when I try to access any property, be it userType or any other it consistently returns undefined.
In the passport-jwt authentication once a User has been found that matches the extracted JWT token, afaik it is returned synchronously and made it available in the req.user object, and I can print it with console.log, but why can't I access the model's properties?
I've tried to make the getPatientPsy() controller a sync function but it doesn't work either.
Thank you.
All right this is embarrassing, by default Passport.js returns the done(null, user) in the req.user property, and since I am returning { user }, I had to access through req.user.user.

How to execute code after action finished in vue js?

How to execute code after action finished in vue js? this is my login action
login: async ({commit},loginDTO)=>{
return commit('login',loginDTO);
}
My login mutations is this:
login:(state, loginDTO)=>{
axios.post(loginEndpoint.login, loginDTO)
.then(resp => {
if(resp.data.statusCode == 1) {
state.user.userId = resp.data.userId;
state.user.response = resp.data.responseText;
localStorage.setItem("token", "token")
state.isLogin = true;
router.push({name: 'Systems'});
}
else{
alert(66);
state.user.response = resp.data.responseText;
}
})
.catch(err => {
})
}
And I call it from component like this:
methods:{
...mapActions(['login']),
async login1(){
const loginDTO = {
Username : this.user.Username,
Password: this.user.Password
};
await this.$store.dispatch('login',loginDTO);
this.$toastr.s("Message", "");
}
}
Now I need toast message but after action is completed.
Updated.
Make use of async-await, and await for async action to complete and sync mutation to commit before you show the toast:
// action
login: async ({commit},loginDTO)=>{
try {
const { data } = await axios.post(loginEndpoint.login, loginDTO)
commit('login', data.userId, data.responseText, true);
} catch(error) {
commit('login', null, error.message, false);
}
}
// mutation
login: (state, userId, response, isLogin) {
state.user.userId = userId;
state.user.response = response;
state.isLogin = isLogin
}
methods:{
...mapActions(['login']),
async login1(){
const loginDTO = {
Username : this.user.Username,
Password: this.user.Password
};
await this.$store.dispatch('login',loginDTO);
this.$toastr.s("Message", "");
}
}
I think all you need to do is call the toast function after the action complete as usual, callback function after ajax returns 200, for example, I used
https://github.com/ankurk91/vue-toast-notification
then run it like so on the callback
this.$toast.open('You did it!');
(make sure the toast has been registered on your vue instance)

React-Native-FBSDK login doesn't return email

I'm trying to use the default <'LoginButton ... > for login in the app through Facebook login, but I can't manage to get the user's email.
This is my button:
<LoginButton
publishPermissions={["email"]}
onLoginFinished={
(error, result) => {
if (error) {
alert("Login failed with error: " + error.message);
} else if (result.isCancelled) {
alert("Login was cancelled");
} else {
alert("Login was successful with permissions: " + result.grantedPermissions)
}
}
}
onLogoutFinished={() => alert("User logged out")}
/>
And this is how i try to get the user's details:
async FBGraphRequest(fields, callback) {
const accessData = await AccessToken.getCurrentAccessToken();
console.log("token= ", accessData.accessToken )
// Create a graph request asking for user information
const infoRequest = new GraphRequest('/me', {
accessToken: accessData.accessToken,
parameters: {
fields: {
string: fields
}
}
}, this.FBLoginCallback.bind(this));
// Execute the graph request created above
new GraphRequestManager().addRequest(infoRequest).start();
}
async FBLoginCallback(error, result) {
if (error) {
this.setState({
showLoadingModal: false,
notificationMessage: "facebook error"
});
} else {
// Retrieve and save user details in state. In our case with
// Redux and custom action saveUser
this.setState({
id: result.id,
email: result.email,
name: result.name
});
console.log("facebook login",result)
}
}
The console.log("facebook login",result) line returns me only the account name and id, but there is no field for te email...
What am I doing wrong?
PS.: I've also tryed to use a "custom function", but it doesn't work too (for the email, the login worked and i get only the user details like name and id):
async facebookLogin() {
// native_only config will fail in the case that the user has
// not installed in his device the Facebook app. In this case we
// need to go for webview.
let result;
try {
this.setState({showLoadingModal: true});
LoginManager.setLoginBehavior('NATIVE_ONLY');
result = await LoginManager.logInWithReadPermissions(['public_profile', 'email']);
} catch (nativeError) {
try {
LoginManager.setLoginBehavior('WEB_ONLY');
result = await LoginManager.logInWithReadPermissions(['email']);
} catch (webError) {
// show error message to the user if none of the FB screens
// did not open
}
}
console.log("facebook result 1: ", result)
// handle the case that users clicks cancel button in Login view
if (result.isCancelled) {
this.setState({
showLoadingModal: false,
notificationMessage: I18n.t('welcome.FACEBOOK_CANCEL_LOGIN')
});
} else {
// Create a graph request asking for user information
this.FBGraphRequest('id, email, name', this.FBLoginCallback);
}
}
.
.
.
<LoginButton
publishPermissions={["email"]}
onPress={
this.facebookLogin()
}
onLogoutFinished={() => alert("User logged out")}
/>
this are the field request by the app. I need to insert also the user's Email:
!!!RESOLVED!!!
the <'LoginButton ...> props for the permission is "permissions", not "readPermission"...
so the button code is:
<LoginButton
permissions={['public_profile', 'email', 'user_birthday', ]}
onClick={this.facebookLogin}
/>
// imports
import {
Settings,
AccessToken,
LoginManager,
AuthenticationToken,
Profile,
GraphRequest,
GraphRequestManager,
} from 'react-native-fbsdk-next';
//put this lines in useEffect
Settings.setAppID('2920461228193006');
Settings.initializeSDK();
LoginManager.setLoginBehavior('web_only');
// put this method on button press
LoginManager.logInWithPermissions(['public_profile', 'email'])
.then(async data => {
if (!data.isCancelled) {
console.log(data, 'this is data');
if (Platform.OS === 'ios') {
let token =
await AuthenticationToken.getAuthenticationTokenIOS();
console.log(token, 'ios token');
} else {
let token = await AccessToken.getCurrentAccessToken();
console.log(token, 'android token');
}
const infoRequest = new GraphRequest(
'/me?fields=email,name,first_name,last_name',
null,
(err, res) => {
console.log({err, res}, 'this is');
if (Object.keys(res).length != 0) {
doSocialLogin({
registerBy: 2,
token: res.id,
user: {
firstName: res.first_name,
email: res.email,
lastName: res.last_name,
},
});
}
},
);
new GraphRequestManager().addRequest(infoRequest).start();
}
})
.catch(err => {
console.log(err, 'this is fb error');
});

stateless session api request

I am building a simple app that uses JWT for authentication. But I keeps on getting the error saying the route I GET to require a call back function.
What do I expect?
I should be getting the current user's data back.
What do I actually get?
Error: Route.get() requires a callback function but got a [object Object]
Route:
const authenticate = require("../middlewares/authenticate");
const usersController = require("../controllers").users;
app.get("/users/me", authenticate, usersController.getMe);
Model:
"use strict";
const jwt = require("jsonwebtoken");
module.exports = (sequelize, DataTypes) => {
var User = sequelize.define(
"User",
{
email: DataTypes.STRING,
password: DataTypes.STRING
},
{
classMethods: {
associate: function(models) {
// associations can be defined here
},
findByToken: function(token) {
const User = this;
let decoded;
try {
decoded = jwt.verify(token, "leogoesger");
} catch (e) {
console.log(e);
}
return User.find({ where: { email: decoded.email } });
}
}
}
);
return User;
};
Middleware:
const { User } = require("../models/user");
const authenticate = (req, res, next) => {
console.log("called here");
const token = req.header("x-auth");
User.findByToken(token)
.then(user => {
if (!user) {
}
req.user = user;
req.token = token;
next();
})
.catch(e => {
res.status(401).send(e);
});
};
module.exports = { authenticate };
Controller:
module.exports = {
getMe(req, res) {
res.status(200).send({ message: "hello" });
}
};
Your authenticate module exports an object, yet you do this:
const authenticate = require("../middlewares/authenticate");
which means your const authenticate is an object, not your function. Change that to this:
const authenticate = require("../middlewares/authenticate").authenticate;
Or, change the module to export the function directly instead of exporting an object with the function in it.