0

I have an application with decoupled front-end and back-end and I'm trying to figure out 2 things:

  1. What response should I return when the user tries to log in with wrong credentials
  2. After returning the response, how to indicate to the user that the credentials he has used are wrong

Right now I have the following function used for login a user. If a user with that email does not exist, I send a 401 response. If a user exists, I check if his password is correct, and if it is I return a 200 response with all the data. If the password is incorrect, I send 401 response.

module.exports.loginUser = async (req, res, next) => {
    let email = req.body.email
    let password = req.body.password

    let userExists = await User.findOne({
        where: {
            email: email
        }
    })

    if (userExists) {
        bcrypt.compare(password, userExists.password, (error, result) => {
            if (result) {
                let token = jwt.sign({ id: userExists.id, email: userExists.email }, 'secretkey')
                res.status(200).json({
                    message: 'Authentication succeeded',
                    username: userExists.username,
                    userId: userExists.id,
                    token: token
                })
            } else {
                res.status(401).json({
                    message: 'Authentication failed'
                })
            }
        })
    } else {
        res.status(401).json({
            message: 'Authentication failed'
        })
    }

}

On the front-end, I have a Login view with the following code:

<template lang="html">
    <div class="register">
        <div class="register-container">
            <p>Welcome back!</p>
            <form @submit.prevent="onSubmit" novalidate>
                <div class="margin-bottom-20">
                    <p>Email</p>
                    <input v-model='email' type='email'>
                </div>
                <div class="margin-bottom-20">
                    <p>Password</p>
                    <input v-model='password' type="password">
                </div>
                <button type="submit" name="button">Continue</button>
            </form>
        </div>
    </div>
</template>

<script>
import axios from 'axios'

export default {
    data(){
        return {
            email: '',
            password: '',
        }
    },
    methods: {
        onSubmit(){
            let userInfo = {
                password: this.password,
                email: this.email
            }
            this.$store.dispatch('login', userInfo)
        }
    }
}
</script>

And my store:

const store = new Vuex.Store({
    state: {
        token: null,
        username: null,
        userId: null,
    },
    mutations: {
        authUser(state, userData){
            state.token = userData.token
            state.userId = userData.userId
            state.username = userData.username
            localStorage.setItem('token', userData.token);
            localStorage.setItem('userId', userData.userId);
            localStorage.setItem('username', userData.username);
        }
    },
    actions: {
        login({commit, dispatch}, userInfo){
            axios.post('/login', userInfo)
            .then(response => {
                commit('authUser', {
                    token: response.data.token,
                    userId: response.data.userId,
                    username: response.data.username
                })
                router.replace('/')
            }).catch((error) => console.log(error));
        },
    }
}

Now I'm not quite sure how to correctly and cleanly add functionality to the front-end that will render error if the user has added incorrect credentials. In order to render the error on the front-end, I'd have to send something in the response from the back-end to the front-end that will indicate that an error has occurred. Sadly, I'm not sure what exactly should I send.

halfer
  • 19,824
  • 17
  • 99
  • 186
Onyx
  • 5,186
  • 8
  • 39
  • 86
  • You can send an HTTP error code 401 and the frontend can show the message "Sorry, you username or password is incorrect." – Weedoze Aug 11 '20 at 13:30
  • But do I render the error message based on the message I sent from the back-end, or do I render it based on the fact that I've received a 401 code on the front-end? – Onyx Aug 11 '20 at 13:38

1 Answers1

3
  1. From this answer:

The correct HTTP code would actually be 401. From the RFC:

The 401 (Unauthorized) status code indicates that the request has not been applied because it lacks valid authentication credentials for the target resource. The server generating a 401 response MUST send a WWW-Authenticate header field (Section 4.1) containing at least one challenge applicable to the target resource. If the request included authentication credentials, then the 401 response indicates that authorization has been refused for those credentials. The user agent MAY repeat the request with a new or replaced Authorization header field (Section 4.2).

  1. By checking the response code on the client-side from the axios, for example:
axios.post('/login', userInfo)
            .then(response => {
                if (response.code === 401) {
                    // Incorrect credential
                    showErrorMessage(); // implement this
                } else {
                    // Correct credential
                    commit('authUser', {
                        token: response.data.token,
                        userId: response.data.userId,
                        username: response.data.username
                    })
                    router.replace('/')
                }
            }).catch((error) => console.log(error));

Keep in mind that by default, Axios will throw an error if the response code isn't status >= 200 && status < 300, if you want to prevent this, you need to change axios default:

axios.defaults.validateStatus = function() {
  return true; // this wont throw error whatever the response code is
  // the default is `return status >= 200 && status < 300;`
};
Owl
  • 6,337
  • 3
  • 16
  • 30
  • I was wondering why I was seeing an error and couldn't access the JSON I had returned from the back-end whenever I returned a response code different than 200. Is there any reason why this is the default? Should I change the validateStatus to return true like you've done in the code above? – Onyx Aug 11 '20 at 14:21
  • Yes by default it will throw an error if the response code is lower than 200 or higher than 300, I don't know the reason why this is the default, but seems like [some people don't like it too](https://github.com/axios/axios/issues/41). Yes you should change the validateStatus if you don't want it to throw an error if the response code is 401 – Owl Aug 11 '20 at 14:25