createUserWithEmailAndPassword redirect causes an error - authentication

I want to redirect a user to a part of my site after signup, not signin
So at the end of the createUserWithEmailAndPassword function I change the window.location and it all works fine, I do get an error since the function was interrupted
How do I avoid this? There's signInWithRedirect and getRedirectResult but the Firebase documentation doesn't include email as a provider, only facebook
I can try-catch the error but perhaps there's a more elegant way to tackle this?
function handleSignUp() {
var email = document.getElementById('email').value;
var password = document.getElementById('password').value;
if (email.length < 4) {
alert('Please enter an email address.');
return;
}
if (password.length < 4) {
alert('Please enter a password.');
return;
}
firebase.auth().createUserWithEmailAndPassword(email, password).catch(function(error) {
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode == 'auth/weak-password') {
alert('The password is too weak.');
} else {
alert(errorMessage);
}
console.log(error);
});
window.location = "/add-location";
}
// console error
A network error (such as timeout, interrupted connection or unreachable host) has occured.

Simply redirect after successful sign-up:
firebase.auth().createUserWithEmailAndPassword(email, password)
.then(function(user) {
window.location = "/add-location";
})
.catch(function(error) {
...
});
You were redirecting before the promise resolved, hence the interrupted operation error.

What about redirecting after login callback?
firebase.auth().onAuthStateChanged(function(user) {
if (user) {
// User is signed in. So redirect:
window.location = "/add-location";
} else {
// No user is signed in.
}
});
As mentioned in the docs, after creation, auth status changes to logged in automatically so that will trigger the redirection after getting a successful login.

Related

cognito custom auth (CUSTOM_CHALLENGE) ignore retry because session expiration

my goal is to implement otp by sending a sms to user mobile. im able to achieve this using cognito custom auth flow, but, only works if the user success in the firts attemp, if the user enter a bad code, the session is gonna expire and a new code is required to be sent again, bad ux. i do need at least 3 attemps, which in theory are 3 sessions across this cognito auth flow.
im gonna share the four cognito lambdas (cognito triggers) i used for this: preSignUp, defineAuthChallenge, createAuthChallenge and verifyChanllenge
// preSignUp lambda
exports.handler = async (event) => {
event.response.autoConfirmUser = true;
event.response.autoVerifyPhone = true;
return event;
};
// defineAuthChallenge
exports.handler = async (event, context, callback) => {
if (event.request.session.length >= 3 && event.request.session.slice(-1)[0].challengeResult === false) {
// wrong OTP even After 3 sessions? FINISH auth, dont send token
event.response.issueToken = false;
event.response.failAuthentication = true;
} else if (event.request.session.length > 0 && event.request.session.slice(-1)[0].challengeResult === true) {
// Last answer was Correct! send token and FINISH auth
event.response.issueTokens = true;
event.response.failAuthentication = false;
} else {
// INIT flow - OR - not yet received correct OTP
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
}
return event;
};
// createAuthChallenge
exports.handler = async (event, context) => {
if (!event.request.session || event.request.session.length === 0) {
// create only once the otp, send over sms only once
var otp = generateOtp();
const phone = event.request.userAttributes.phone_number;
sendSMS(phone, otp);
} else {
// get previous challenge answer
const previousChallenge = event.request.session.slice(-1)[0];
otp = previousChallenge.challengeMetadata;
}
event.response = {
...event.response,
privateChallengeParameters: {
answer: otp
},
challengeMetadata: otp // save it here to use across sessions
};
return event
}
// verifyChanllenge
exports.handler = async (event, context) => {
event.response.answerCorrect = event.request.privateChallengeParameters.answer === event.request.challengeAnswer;
return event
}
for the client, which is a RN app, im using amplify, this is the flow in the app:
// SignIn form screen
import { Auth } from "aws-amplify";
const signUp = (phone) => {
Auth.signUp({
username: phone,
/** dummy pass since its required but unused for OTP */
password: "12345678"
}).then(() => {
// after signup, go an automatically login (which trigger sms to be sent)
otpSignIn(phone);
}).catch(({code}) => {
// signup fail because user already exists, ok, just try login it
if (code === SignUpErrCode.USER_EXISTS) {
otpSignIn(phone)
} else {
...
}
})
}
const otpSignIn = async (phoneNumber) => {
const cognitoUser = await Auth.signIn(phoneNumber)
setCognitoUser(cognitoUser);
navigate("ConfirmNumber", {phoneNumber});
}
import { Auth } from "aws-amplify";
let cognitoUser;
export function setCognitoUser(user) {
console.log('setCognitoUser', user)
cognitoUser = user;
}
export function sendChallenge(challengeResponse) {
return Auth.sendCustomChallengeAnswer(cognitoUser, challengeResponse)
}
// Confirm number screen
const onChangeText = (value) => {
if (value.length === 4) {
try {
const user = await sendChallenge(value)
// WEIRD THING NUMBER 1
// when the user send the second attempt, no error is raised, this promise is resolve!
// even when the trigger *verifyChanllenge* is returning false.
} catch (err) {
// WEIRD THING NUMBER 2
// from the trigger *createAuthChallenge* if i define the anser in the if block,
// and not store such answer for future use (i do that in else block), then,
// for the second..third attempt the error raised here is that *Invalid session for user* which mean session has expired,
// what i need is to persist session until third attempt
}
}
}
// this is amplify config: try 1
const awsExports = {
Auth: {
region: ...,
userPoolId: ...,
userPoolWebClientId: ...,
authenticationFlowType: 'CUSTOM_AUTH',
},
...
}
Amplify.configure(awsExports);
// this is amplify config: try 2
import {Auth} from "aws-amplify"
Auth.configure({
authenticationFlowType: 'CUSTOM_AUTH'
});
everything is correct in the code above, and the config for amplify authenticationFlowType: 'CUSTOM_AUTH' is not necessary.
the problem is that Auth.sendCustomChallengeAnswer(cognitoUser, challengeResponse) is not raising an error when the trigger defineAuthChallenge set this combination:
event.response.issueTokens = false;
event.response.failAuthentication = false;
event.response.challengeName = 'CUSTOM_CHALLENGE';
which presents the next attempt.
so i found a way to check the error when the user fail the otp:
const sendCode = async (value) => {
try {
// send the answer to the User Pool
// this will throw an error if it's the 3rd wrong answer
const user = await sendChallenge(value);
// the answer was sent successfully, but it doesnt mean it is the right one
// so we should test if the user is authenticated now
// this will throw an error if the user is not yet authenticated:
await Auth.currentSession();
} catch (err) {
setError(true);
}
}

How to store a property in a session with express-session?

I have the following code:
app.post("/login", (req, res) => {
const { username, password } = req.body;
// dummy local database with custome helper functions to look up a user:
db.users.findByUsername(username, (err, user) => {
if (!user) return res.status(403).json({ msg: "No user found!" });
if (user.password === password) {
// Adding properties to session
req.session.authenticated = true;
req.session.user = {
username,
password,
};
console.log(req.session);
// Session is printed in terminal with the above properties. Works fine up to here.
res.redirect("/shop");
} else {
res.status(403).json({ msg: "Bad Credentials" });
}
});
});
I used express-session to create a session and i'm storing it in memory. I created a middleware that would allow a user to access a /shop page only if they're authenticated and have the req.session.authenticated property set to true. For some reason, after they log in, and they're redirected to the /shop page, the properties created in the session are no longer there. Here's the rest of the code:
Authentication middleware:
function ensureAuthentication(req, res, next) {
if (req.session.authenticated) {
// Properties that were added upon logging in are not attached.
return next();
} else {
res.status(403).json({ msg: "You're not authorized to view this page" });
}
}
Shop page
app.get("/shop", ensureAuthentication, (req, res) => {
// Send the user object to the view page:
res.render("shop", { user: req.session.user });
});
Any opinions? Am I missing something here? Does the order of how I have the endpoints written matter?

Auth0 re-login after token expire does not display login window

I'm working with Auth0, I have a problem where after user token expire and user try to relogin, it doesn't redirect user to login window at all instead it just automatically logged in when user click on login link.
They are fine if I manually log out then re-login, then it will ask for authentication again.
I tried removing all the localstorage memory regarding the user but it still doesn't fix it.
export const expiredAtKey = 'expired_at';
export const uidKey = 'uid';
export const urlStateKey = 'urlState';
#Injectable()
export class Auth {
auth0 = new auth0.WebAuth({
clientID: environment.auth0ClientId,
domain: environment.auth0Domain,
responseType: 'token id_token',
redirectUri: `${constants.ORIGIN_URL}/auth`,
scope: 'openid email'
});
constructor(private router: Router,
public dialog: MatDialog,
private http: HttpClient) {
}
public handleAuthentication(): void {
this.auth0.parseHash(this.handleAuthResult);
}
public login() {
//I have tried to clear local storage everytime user call login to prevent this to happen, but it still skip the login window
this.clearLocalStorage();
localStorage.setItem(urlStateKey, location.pathname);
this.auth0.authorize();
};
public signUp(email, password, cb) {
this.auth0.signupAndAuthorize({
email: email,
password: password,
connection: environment.auth0Connection
}, cb);
}
public authenticated() {
const exp = localStorage.getItem(expiredAtKey);
if (!exp) {
return false;
}
const expiresAt = JSON.parse(localStorage.getItem(expiredAtKey));
return new Date().getTime() < expiresAt;
};
public logout() {
this.clearLocalStorage();
window.location.href = `https://${ environment.auth0Domain }/v2/logout?returnTo=${ constants.ORIGIN_URL }`;
};
public setSession(authResult): void {
const idToken = jwtDecode(authResult.idToken);
localStorage.setItem('idToken', authResult.idToken);
localStorage.setItem(uidKey, idToken.email);
localStorage.setItem('userId', idToken.sub);
const expiresAt = JSON.stringify(idToken.exp * 1000);
localStorage.setItem(expiredAtKey, expiresAt);
}
private handleAuthResult = (err, authResult) => {
if (err) {
if (!environment.production) {
console.log(err);
}
if(err.errorDescription === "Please verify your email before logging in."){
this.dialog.open(
ErrorDialogComponent,
{ data: "Please verify your email before logging in."}
);
this.router.navigate(['/initiatives'])
}else{
this.dialog.open(
ErrorDialogComponent,
{ data: "An error occurred while trying to authenticate. Please ensure private browsing is disabled and try again."}
);
this.router.navigate(['/initiatives'])
}
} else if (authResult && authResult.idToken && authResult.idToken !== 'undefined') {
this.setSession(authResult);
const path = localStorage.getItem(urlStateKey);
this.router.navigateByUrl(path);
}
};
clearLocalStorage() {
localStorage.removeItem(expiredAtKey);
localStorage.removeItem(uidKey);
localStorage.removeItem(urlStateKey);
localStorage.removeItem('userId')
}
}
I want user to do the authentication again after the token is expired.
This is happening due to SSO cookie set in the server to maintain the session. To clear the server-side session, you need to redirect the user to /logout endpoint when token expires. The logout method does that.
https://auth0.com/docs/sso/current/single-page-apps

How can I have a seperate login page using Durandal that has a different layout then the shell?

I've read through Durandal login page redirect pattern wow, lots of code to do what I'd think would be pretty simple.
I've also read through https://groups.google.com/forum/#!topic/durandaljs/RdGpwIm1oOU as I'd like the login page to have a simple logo with a login form, but I'd also like routing for a registration and about page as well. The rest of my site will have a menu, header, etc which I don't want to show until the user is logged in. Also, I'm not sure how this approach would update when the user logs in.
Another code example that almost does what I want to do: https://github.com/Useful-Software-Solutions-Ltd/Durandal451/blob/master/Durandal451v2/App/global/session.js
So, what should I do? Is there an official way to do this? There seems to be a mish mash of things out there that people have tried. I would think this would be a really common occurrence but couldn't find anything on the main docs.
I'm not sure this is the simplest way, but this is what I got
you will need to add some extra function after app.start() is triggered.
main.js
var auth = require('authentication'); // Authentication module
app.start().then(function()
{
// This function will wait for the promise
auth.init().then(function(data)
{
// When successfully authenticate, set the root to shell
app.setRoot('views/shell');
}
});
authentication.js
define(function(require)
{
var app = require('durandal/app');
return {
init: function()
{
// Initialize authentication...
return system.defer(function(dfd)
{
// Check if user is authenticate or if there has stored token
var isAuthenticate = someOtherFunctiontoCheck();
if (isAuthenticate)
{
dfd.resolve(true); // return promise
}
else
{
// When not authenticate, set root to login page
app.setRoot('views/login');
}
}
}
};
});
good luck! :)
UPDATE
login.js
define(function(require)
{
var ko = require('knockout');
var auth = require('authentication');
var username = ko.observable();
var password = ko.observable();
return {
username: username,
password: password,
submitForm: function()
{
// Do a login, if success, auth module will take care of it
// and here will take of the error
auth.login(username(), password()).error(function()
{
// notify user about the error (e.g invalid credentials)
});
}
};
});
Authentication.js
define(function(require)
{
var app = require('durandal/app');
return {
init: function()
{
// Initialize authentication...
return system.defer(function(dfd)
{
// Check if user is authenticate or if there has stored token
var isAuthenticate = someOtherFunctiontoCheck();
if (isAuthenticate)
{
dfd.resolve(true); // return promise
}
else
{
// When not authenticate, set root to login page
app.setRoot('views/login');
}
}
},
login: function(username, password)
{
// do authenticate for login credentials (e.g for retrieve auth token)
return $.ajax({
url : 'api/login',
type : 'POST',
data : {
username: username,
password: password
}
}).then(function(token){
// on success, stored token and set root to shell
functionToStoreToken(token);
// Set root to shell
app.setRoot('views/shell');
});
}
};
});

firebase simple authentication authClient is null?

Below is the sample code I am playing around with:
var myRootRef = new Firebase('https://url.firebaseIO.com/');
var authClient = new FirebaseAuthClient(myRootRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
} else {
// user is logged out
console.log('logged out!');
login();
}
});
function login(){
var email = "something#gmail.com";
var password = "123";
authClient.login('password', {
email: email,
password: password,
rememberMe: true
});
}
The error I get back is : Cannot call method 'login' of undefined
authClient seems to be always null? What am I doing wrong?
Here authClient seems good. I think problem with scope of the login() function.Try this
var myRootRef = new Firebase('https://url.firebaseIO.com/');
var authClient = new FirebaseAuthClient(myRootRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
} else {
// user is logged out
console.log('logged out!');
var email = "something#gmail.com";
var password = "123";
this.login('password', {
email: email,
password: password,
rememberMe: true
});
}
});
When you initially call new FirebaseAuthClient, it's going to invoke the callback with the current login state (The user could already be logged in, for instance, when this is invoked). This callback occurs before new FirebaseAuthClient returns, which means that authClient has not been assigned yet.
You do not need to move the your authClient.login inside the callback, although this works fine. You just need to be aware of the fact that the first time this callback is issued might be before the assignment.
You could, for instance, use a setTimeout around your call to ensure the variable is set first:
var authClient = new FirebaseAuthClient(myRootRef, function(error, user) {
if (error) {
// an error occurred while attempting login
console.log(error);
} else if (user) {
// user authenticated with Firebase
console.log('User ID: ' + user.id + ', Provider: ' + user.provider);
} else {
// user is logged out
console.log('not logged in yet or logged out again!');
setTimeout(login, 1);
}
});