I have authClient.js file as below:
export default (type, params) => {
if (type === AUTH_LOGIN) {
return UserLoginService.signIn(params)
}
if (type === AUTH_LOGOUT) {
// ...
}
if (type === AUTH_ERROR) {
// ...
}
if (type === AUTH_CHECK) {
return localStorage.getItem('token') ? Promise.resolve() : Promise.reject({ redirectTo: '/no-access' });
}
return Promise.reject('Unkown method');};
In authentication services, after user signed up, I create temporary password and send it to user via email. I require user to change their password in the first login.
In UserLoginService.signIn(), if it's the first login, I do this reject({ code: 'NewPasswordChallenge' });
Is there any way to redirect to /change-password page if type is AUTH_LOGIN? like when type is AUTH_CHECK?
Not easily for now. You might be able to address this by implementing a custom saga which would be triggered on the USER_LOGIN_FAILURE action. It won't prevent the notification display though.
However, this could be a nice addition I think. You're welcome to make a PR about it :)
Related
I want to build an email verification. After the user registers, the user gets an email and clicks on it for verification purposes. The email-link invokes a netlify lambda function (api end point). Inside the link is a jwt token, which I decode on the backend. I used
window.location.href
for it and sliced the part I needed and decoded it. On localhost, it works fine, however, if I deploy it to netlify, I get an
window is undefined
error. I read that you have to check for
typeof window !== 'undefined'
However, if I add that to my lambda function I don't get any console.log statements.
exports.handler = async (event, context, callback) => {
if (typeof window !== 'undefined') {
let url = window.location.href
let index = url.indexOf("=");
let token = url.slice(index+1)
console.log(token, 'token here')
const decoded = jwt.verify(token, process.env.SECRET);
console.log('confirm registration route triggered',decoded)
if (decoded) {
const { email } = decoded;
console.log(decoded, 'decoded here')
User.findOneAndUpdate({email: email}, {verified: true },(...e)=>{
console.log(e)
});
} else {
console.log('could not update user')
//redirect user to page with message about email confirmation link expiration
//and proposal to register again
}
console.log('confirm registration got invoked')
}
return {
statusCode: 400,
body: "Oops"
}
};
I read that the function first runs on the server when deployed and afterwards on the client. Seems like it does not run on my client, as I invoke the api-endpoint directly? I'm quite a beginner when it comes to API-Endpoints, thanks for reading!
In case you have the same issue when deploying to netlify, you have to run
event.queryStringParameters
which gives you access to the query parts of your url.
I need to send a specific user ID from the bot emulator (https://github.com/microsoft/BotFramework-Emulator). I use this textbox (see on a picture below)
But nothing sent. There is absolutely another guid in activity.From.Id.
Is it possible to sent message from emulator with a specific user ID?
The short answer is, if you are using Direct Line to generate a token from a secret and you specify user Id there (see attached code), then Emulator should favor that value over any value you pass in thru the Emulator settings.
In my personal testing, the User ID setting seems to be overriding any other pre-existing value.
It should be noted, however, that if you specify a User ID value in settings, you will need to close the tabbed conversation and start it anew by re-entering the messaging endpoint and AppId/AppPassword (or reconnecting to your .bot file, if used). Simply pressing "Restart conversation" will not cause Emulator to pickup the User ID setting.
Hope of help!
// Listen for incoming requests.
server.post('/directline/token', (req, res) => {
// userId must start with `dl_` for Direct Line enhanced authentication
const userId = (req.body && req.body.id) ? req.body.id : `dl_${ Date.now() + Math.random().toString(36) }`;
const options = {
method: 'POST',
uri: 'https://directline.botframework.com/v3/directline/tokens/generate',
headers: {
'Authorization': `Bearer ${ process.env.directLineSecret }`
},
json: {
user: {
Id: `${ userId }`
}
}
};
request.post(options, (error, response, body) => {
if (!error && response.statusCode < 300) {
res.send(body);
console.log('Someone requested a token...');
} else {
res.status(500).send('Call to retrieve token from DirectLine failed');
}
});
});
I am building a react-redux app, using custom redux middleware.
In the definition of my project, action only provides an object to define action type and necessary parameters for middleware and reducer. All the ajax request will be handle by middleware. This is the life cycle would look like:
action -> middleware(if action is intercepted) -> reducer -> store
When the user tries to log in, the operation on the react component will fire an action, which would look like this:
export function login(username, password) {
return {
type: 'LOGIN',
username: username,
password: password
}
}
export function authSucceed(username, isAdmin) {
return {
type: 'AUTHSUCCEED',
username: username,
isAdmin: isAdmin
}
}
export function authFail(text) {
return {
type: 'AUTHFAIL',
errorMessage: text
}
}
Then middleware will use the parameters passed in action to send ajax request, which would be like this.
export function customedMiddleware(store) {
return next => action => {
if (action.type === 'LOGIN') {
axios.post(url + '/api/login', {
username: action.username,
password: action.password
})
.then(res => {
if (res.status === 200) {
store.dispatch(actions.authSucceed(res.data.username, res.data.isAdmin));
} else {
store.dispatch(actions.authFail(res.data));
}
})
.catch(error => console.log(error));
}
return next(action);
};
}
After the middleware sends login request to server, depending on whether the authentication succeeds or not, the middleware will dispatch some action in reducer correspondingly. Since authSucceed and authFail would not be intercepted by middleware, reducer will process accordingly.
export default function(state = false, action) {
switch(action.type) {
case 'AUTHSUCCEED':
return true;
case 'AUTHFAIL':
return false;
case 'LOGOUT':
return false;
}
return state;
}
What has been done here in reducer is to change the system state. If the state is true, the front-end will render the information page. If the state is false, the front-end will remain in the login page.
I like system definition this way. Every MVC part is well isolated. However, it's very difficult to test the middleware. Currently, I am testing this way:
it('should dispatch authSucceed if signup with correct info', () => {
nock('http://localhost:8080')
.post('/api/signup', {
username: 'bruce',
password: 'Gx1234'
})
.reply(200, {
username: 'bruce',
isAdmin: false
});
const createStoreWithMiddleware = applyMiddleware(customedMiddleware)(createStore);
const store = createStoreWithMiddleware(reducers);
const dispatch = sinon.spy(store, 'dispatch');
store.dispatch(actions.login('bruce', 'Gx1234'));
setTimeout(() => {
expect(dispatch.calledWith({
type: 'AUTHSUCCEED',
username: 'bruce',
isAdmin: false
})).to.be.true;
}, 100);
});
I dispatch login action. Then spy the whether authSucceed action and authFail action will be called correctly within 100ms. This method works if there is only one test to be run. If there are more then one test running in sequence, they might affect each other. I have to adjust the time delay of the setTimeout to make it work for all cases, which is 10ms.
I don't feel comfortable this way. I can't make sure whether it just work for me or for everybody too, since absolute time is related to hardware.
I would really appreciate if anybody can give me some advice on how to test this custom middleware.
Your code works fine, but you shouldn't need a setTimeout with such a long time since using nock makes the remote resquest response instantly. The problem is promises enqueue microtasks and they only run after a macrotask is finished (in your case, it()), in the same event loop.
That's why you need setTimeout to enqueue another macrotask, the time doesn't make a difference. I believe setImmediate should work as well.
I am trying to login to single user with multi OAuth (facebook, google) login service. Here is what I try.
In Client:
'click #signInByFacebook': function (e) {
e.preventDefault();
Meteor.loginWithFacebook({requestPermissions: ['public_profile', 'email', 'user_about_me', 'user_photos']}, function (err, res) {
if (err) {
showError($('.alert'), err, 'login');
return;
}
showSuccess($('.alert'), 'login');
Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
Router.go('/');
});
},
'click #signInByGoogle': function (e) {
e.preventDefault();
Meteor.loginWithGoogle({requestPermissions: ['profile', 'email', 'openid']}, function (err, res) {
if (err) {
showError($('.alert'), err, 'login');
return;
}
showSuccess($('.alert'), 'login');
Session.set('notAdmin', !Roles.userIsInRole(Meteor.user(), ["admin"]));
Router.go('/');
});
}
In Server:
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
var sameuser = Meteor.users.findOne({$or: [{'emails.address': getEmail(user)}, {'services.facebook.email': getEmail(user)}, {'services.google.email': getEmail(user)}]});
console.log(sameuser);
if (sameuser) {
if (user.services.facebook) {
console.log("facebook");
Meteor.users.update({_id: sameuser._id}, {$set: {'services.facebook': user.services.facebook}});
}
if (user.services.google) {
console.log("google");
Meteor.users.update({_id: sameuser._id}, {$set: {'services.google': user.services.google}});
}
return;
}
console.log('register success');
return user;
});
This code will check if any user logined with facebook/google has the
same email or not with current sign in. If they are the same, just
update information to old account. If not, create new user.
This works great, but there is a problem with the 'return ;' in server code. I dont know what should I return to stop create user and auto login to the user that has same email. Anybody can help this issue ? Thank you.
The only way to stop creation of the new user is to throw an exception, but that will also prevent logging in as the existing user.
However, your general approach is insecure. Consider a user who has a Google account with a strong password and a Facebook account with a weak one. When he uses the Google account to authenticate with your app, he doesn't (and shouldn't) expect that someone who gains access to his Facebook account will be able access your app as him.
A better approach is to require that the user be logged into both services simultaneously before merging the services. The good news is that this also means that you don't need to worry about logging in after preventing the creation of the new user, because the user will already be logged in. Something like this might work:
Accounts.onCreateUser(function (options, user) {
if (options.profile) {
user.profile = options.profile;
}
var currentUser = Meteor.user();
console.log(currentUser);
if (currentUser) {
if (user.services.facebook) {
console.log("facebook");
Meteor.users.update({_id: currentUser._id}, {$set: {'services.facebook': user.services.facebook}});
}
if (user.services.google) {
console.log("google");
Meteor.users.update({_id: currentUser._id}, {$set: {'services.google': user.services.google}});
}
throw new Meteor.Error(Accounts.LoginCancelledError.numericError, "Service added to existing user (or something similar)");;
}
console.log('register success');
return user;
});
There are still a couple loose ends. First, I think Meteor expects OAuth credentials to be "pinned" to the user that they are associated with, so you probably need to repin the credentials you are copying.
Second, the above approach bypasses the validateLoginAttempt() callbacks. If you, or any package you are using, has registered any such callbacks, they won't be called when logging in using the second service, so they won't be able to prevent any such logins that they might consider invalid.
You can address both of these issues and skip the onCreateUser() callback as well, by just adding my brettle:accounts-add-service package to your app.
I'm trying to use a authenticateUser() middleware before loading all of my pages. Instead of including it in each call (as in app.get('/', authenticateUser, function()...)), I tried setting it with app.use(authenticateUser) right before calling app.use(app.router).
This didn't work, however. authenticateUser is basically:
if (req.session.loginFailed) {
next()
else {
if (req.session.user_id) {
...
if (userAuthenticated) {
next();
} else {
req.session.loginFailed = true;
console.log('setting loginFailed to true');
res.redirect('/login');
}
}
}
And then in app.get('/login') I set req.session.loginFailed to be false;
This should work, but I only want to call it on an app.get() or app.post() etc. for one of my actual pages. I think its getting called lots of times for many different requests (because upon loading one page, 'setting loginFailed to true' is called many times)
Is there a better way to do this? Or should I simply be calling it before every page on my site?
You are doing way too many checks out there in my opinion. Only one route should handle user login (check for user & pass, and store the username in the session if succeeded) and you should assign the auth middleware only on the routes that require auth (not all).
I've put up a simplified example so you can understand my point:
The login route
app.post('/login', function (req, res) {
var variables_set = (req.body.user && req.body.pass);
if (variables_set && (req.body.user === 'username') && (req.body.pass === 'password')) {
req.session.username = req.body.user;
} else {
res.redirect('/login?failed=true');
}
});
The auth middleware
if (!req.session.username) {
res.redirect('/login');
} else {
next();
}
You can see a more complete example in action in Alex Young's Nodepad application: https://github.com/alexyoung/nodepad (tutorials for that app here: http://dailyjs.com/tags.html#lmawa )