I have a nodejs/express app that runs passport js with local strategy for auth.
Everything works well so far, but i have one user who is unable to log in from his iphone 6s . it doesn't matter which browser (safari,chrome etc) he use. we tried allowing cookies, deleting history and changing different setting on his device but nothing helps.
he has no problem logging in using different devices or his desktop computer.
this is my set up for passport auth :
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);
});
}
));
passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(id, done) {
User.findById(id, function(err, user) {
done(err, user);
});
});
app.post('/home',
passport.authenticate('local', { successRedirect: 'main',
failureRedirect: '/',
failureFlash: true })
);
I get this flash message when he tries to log in : ["Incorrect username..Incorrect username"]
any ideas how to debug or solve this issue? thanks!
Related
I'm trying to implement OAuth2 authentication in Angular 2 ( Electron ) application.
I achieve that on the way with a popup that is called after user click on 'Sign In' button.
In popup user types their credentials and allows the access and on confirm code is returned and I'm able to catch redirect request which I can't do without popup.
Here is implementation that works:
return Observable.create((observer: Observer<any>) => {
let authWindow = new electron.remote.BrowserWindow({ show: false, webPreferences: {
nodeIntegration: false
} });
authWindow.maximize();
const authUrl = AUTHORIZATION_WITH_PROOF_KEY_URL
+ `?client_id=${CLIENT_ID}&response_type=code&scope=api_search&`
+ `redirect_uri=${REDIRECT_URL}&code_challenge=${challenge}&code_challenge_method=S256`;
if (this.clearStorage) {
authWindow.webContents.session.clearStorageData({}, () => {
this.clearStorage = false;
authWindow.loadURL(authUrl);
authWindow.show();
});
} else {
authWindow.loadURL(authUrl);
authWindow.show();
}
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
const code = this.getCode(newUrl, authWindow);
if (!code) {
this.clearStorage = true;
return;
}
this.requestToken({
grant_type: 'authorization_code',
code: code,
code_verifier: verifier,
redirect_uri: REDIRECT_URL
})
.subscribe((response: { access_token: string, refresh_token: string }) => {
observer.next(response);
});
});
// Reset the authWindow on close
authWindow.on('close', () => {
authWindow = null;
});
});
and as you can see in above code I'm creating new BrowserWindow with:
new electron.remote.BrowserWindow({ show: false, webPreferences: {
nodeIntegration: false
} });
and with that approach I'm able to catch up redirect request with a block of code that starts with:
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
....
}
but I'm not able to solve this without popup ( modal ).
Here is my attempt:
return Observable.create((observer: Observer<any>) => {
let authWindow = electron.remote.getCurrentWindow();
const authUrl = AUTHORIZATION_WITH_PROOF_KEY_URL
+ `?client_id=${CLIENT_ID}&response_type=code&scope=api_search&`
+ `redirect_uri=${REDIRECT_URL}&code_challenge=${challenge}&code_challenge_method=S256`;
if (this.clearStorage) {
authWindow.webContents.session.clearStorageData({}, () => {
this.clearStorage = false;
authWindow.loadURL(authUrl);
});
} else {
authWindow.loadURL(authUrl);
}
authWindow.webContents.on('did-get-redirect-request', (event, oldUrl, newUrl) => {
debugger;
// this is not called, I'm not able to catch up redirect request
});
// Reset the authWindow on close
authWindow.on('close', () => {
authWindow = null;
});
});
With my approach I get login screen from remote URL in a current window, but the problem is that I'm not able to catch redirect request with ('did-get-redirect-request') event.
I also tried with 'will-navigate' and many others.
Although I don't have a direct answer I thought I'd point you to Google's AppAuth-JS libraries, which cover OAuth based usage for Electron Apps.
My company have used AppAuth libraries for the mobile case and they worked very well for us, so that we wrote less security code ourselves and avoided vulnerabilities.
There is also an Electron Code Sample.
My app requires being connected to our server for any use. I wrote this function in the app component to prevent the user from using the app if the server is not available
app.component.ts
pingServer(){
this.api.pingServer().subscribe(result => {
if (result.success) {
return true;
}
else {
return false
}
},
error=>{
return false;
}
);
}
}
this function just makes the request to our server, which I verified is hitting the right address
initializeApp() {
console.log("initialize");
this.platform.ready().then(() => {
console.log("platform ready");
this.screenOrientation.lock('portrait');
console.log('Orientation is ' + this.screenOrientation.type);
// Okay, so the platform is ready and our plugins are available.
// Here you can do any higher level native things you might need.
let serverAlert = this.alertCtrl.create({
title: 'Error',
subTitle: 'We are unable to reach our servers at this time. Please try again later',
buttons: ['Dismiss']
});
setTimeout(() => {
if(this.pingServer){
console.log('pinged server successfully');
this.statusBar.styleDefault();
this.splashScreen.hide();
}
else{
console.log('unable to ping server');
return serverAlert.present();
}
}, 3000);
On the server side:
router.get( '/ping', ( req, res, next ) => {
return res.json( { success: true } );
} )
This worked fine when testing on the browser. However, when using iOS it says it pings successfully every time, meanwhile, the server isn't receiving the request.
On the simulator it does not crash and Alerts the error, but in production it is crashes as soon as fetch request suppose to be made and it is impossible to reopen the app until network connection is back (I turn on/off airplane mode for the testing)
here are the snippets of my code
componentWillMount: function(){
NetInfo.isConnected.addEventListener('change',this.handleConnectivityChange)
NetInfo.isConnected.fetch().done((data) => {
this.setState({
isConnected: data
})
console.log('this.state.isConnected: ', this.state.isConnected);
})
},
handleConnectivityChange: function(){
var connected = this.state.isConnected ? false : true
this.setState({isConnected: connected})
console.log('this.state.isConnected11: ', this.state.isConnected);
},
....
goToList: function(replace, listview){
console.log('this.state.isConnected: ', this.props.isConnected);
if (!this.props.isConnected){
AlertIOS.alert('Error', 'Please check your network connectivity')
this.props.removeFetching()
return
}
....
fetch(url)
.then((response) => response.json())
.then((responseData) => {
....
.catch((error) => {
StatusBarIOS.setNetworkActivityIndicatorVisible(false)
AlertIOS.alert('Error', 'Please check your network connectivity')
this.props.removeFetching()
})
.done()
I spent a lot of time trying to find a way to catch exceptions when using fetch() but I was unable to get it working (e.g. using .catch() or a try/catch blog didn't work). What did work was to use XMLHttpRequest with a try/catch blog instead of fetch(). Here's an example I based off of: https://facebook.github.io/react-native/docs/network.html#using-other-networking-libraries
var request = new XMLHttpRequest();
request.onreadystatechange = (e) => {
if (request.readyState !== 4) {
return;
}
if (request.status === 200) {
console.log('success', request.responseText);
var responseJson = JSON.parse(request.responseText);
// *use responseJson here*
} else {
console.warn('error');
}
};
try {
request.open('GET', 'https://www.example.org/api/something');
request.send();
} catch (error) {
console.error(error);
}
It seems as though the solution cited here:
Parse Facebook login fails with error code 251
No longer works. I have attempted both that and reconfiguring my Facebook app by resetting the secret, reconfiguring Parse with my new app credentials, and resetting xCode for a new simulator environment and all have failed. I continue to get old authentication data when the following function is called:
//sign up into parse db
Parse.FacebookUtils.logIn(authData, {
success: (user) => {
if (user.existed()) {
// login: nothing to do
console.log('User Already Logged In');
that.setState({loadingCurrentUser: false});
that.props.navigator.immediatelyResetRouteStack([{ name: 'home'}]);
} else {
// signup: update user data, e.g. email
console.log('getting user additional information');
var data = user.get('authData').facebook;
var api = 'https://graph.facebook.com/v2.3/'+data.id+'?fields=name,email&access_token='+data.access_token;
var fetchProfile = new FBSDKGraphRequest((error, result) => {
if (error) {
// TODO: error
this.setState({loadingCurrentUser: false});
} else {
console.log(result);
var name = responseData.name;
var email = responseData.email;
// FIXME: https://github.com/ParsePlatform/ParseReact/issues/45
var userId = {
className: '_User',
objectId: user.id
};
ParseReact.Mutation.Set(userId, {
username: email,
email: email,
name: name
}).dispatch();
that.setState({loadingCurrentUser: false});
that.props.navigator.immediatelyResetRouteStack([{ name: 'onboarding'}]);
}
}, '/me?fields=name,email');
// FIXME https://github.com/facebook/react-native-fbsdk/issues/20
// fetchProfile.start();
FBSDKGraphRequestManager.batchRequests([fetchProfile], function() {}, 10)
}
},
error: (user, error) => {
console.log('Error', error);
console.log(user.token);
switch (error.code) {
case Parse.Error.INVALID_SESSION_TOKEN:
Parse.User.logOut().then(() => {
this.onFacebookLogin(token);
});
break;
default:
// TODO: error
}
that.setState({loadingCurrentUser: false});
that.setState({errorMessage: error.message});
}
});
I am not sure how to proceed. Any help with error 251 and the most recent version of React Native (iOS)?
I'm currently working on integrating devise as an authentication backend with angular as its frontend.
I have faced a problem on when login and logout, the session data will be updated untill the page refresh.
What i will do get session data without page refresh..?
Thanks for your Answers...
AngularJs Controller :
function UsersCtrl($scope, Session) {"use strict";
$scope.CurrentUser = Session.requestCurrentUser();
$scope.login = function(user) {
$scope.authError = null;
Session.login(user.email, user.password)
.then(function(response) {
if (!response) {
$scope.authError = 'Credentials are not valid';
} else {
$scope.authError = 'Success!';
}
}, function(response) {
$scope.authError = 'Server offline, please try later';
});
};
$scope.logout = function() {
// alert("woow");
Session.logout();
};
$scope.register = function(user) {
$scope.authError = null;
console.log(user);
Session.register(user.email, user.password, user.confirm_password)
.then(function(response) {
}, function(response) {
var errors = '';
$.each(response.data.errors, function(index, value) {
errors += index.substr(0,1).toUpperCase()+index.substr(1) + ' ' + value + ''
});
$scope.authError = errors;
});
};
}
AngularJs Session Service:
angular.module('sessionService', ['ngResource'])
.factory('Session', function($location, $http, $q) {
// Redirect to the given url (defaults to '/')
function redirect(url) {
url = url || '/';
$location.path(url);
}
var service = {
login: function(email, password) {
return $http.post('/users/login', {user: {email: email, password: password} })
.then(function(response) {
service.currentUser = response.data.user;
if (service.isAuthenticated()) {
//$location.path(response.data.redirect);
$location.path('/store');
}
});
},
logout: function() {
$http.delete('/sessions').then(function(response) {
$http.defaults.headers.common['X-CSRF-Token'] = response.data.csrfToken;
service.currentUser = null;
redirect('/store');
});
},
register: function(email, password, confirm_password) {
return $http.post('/users', {user: {email: email, password: password, password_confirmation: confirm_password} })
.then(function(response) {
service.currentUser = response.data;
if (service.isAuthenticated()) {
console.log("authenticated");
$location.path('/');
}
});
},
requestCurrentUser: function() {
if (service.isAuthenticated()) {
return $q.when(service.currentUser);
} else {
return $http.get('/users').then(function(response) {
service.currentUser = response.data.user;
return service.currentUser;
});
}
},
currentUser: null,
isAuthenticated: function(){
return !!service.currentUser;
}
};
return service;
console.log(service);
});
One Thing about building applications like this (restful) is that understanding the the backend as an api and app as a front-end very well.
Then think about a story as such;
In the login screen of your app
Front-end: You Provided the credentials to your backend;
Back-end: Checked and authenticated then It will create a unique hash stored in db (JWT recommended to check expiration in frontend) to your Front-end.
Front-end:Save it in a cookie.
Also place it in your ajax setting header part as "Authorization: {token}"
Front-end: Then send each request with this header to your backend.
Back-end: Always check if the token is present and valid to provide resources.
http://www.thebuzzmedia.com/designing-a-secure-rest-api-without-oauth-authentication/ this link has helped me understand the whole thing and misconceptions in the past.
use $window.location.reload(); before page redirect.
One way to achieve this could be overriding the devise sessions_controller destroy action and afrer doing sign_out #current_user return the session as json