3
\$\begingroup\$

I have created service to communicate with my backend for user registration and login. I use the JS fetch API and send all data through HTTPS. I use JWT tokens to authenticate queries once I have logged, and this is stored in the window local storage.

The API details are stored in a config.json file.

Two things that I'm not sure if I can improve are:

  1. Should I use local storage for storing the JWT token or is there a better alternative?
  2. Is it ok to send the password without hashing it first on the client side? On the server side it is hashed and salted before storage so no plaintext passwords are stored.
import config from "../config.json"

let jwtToken = null;

const login = async (email, pass) => {
    const loginData = {
        "email": email,
        "password": pass
    }
    const loginResponse =  await fetch(config.host + config.loginUrl, {
        body: JSON.stringify(loginData),
        method: "POST",
        headers: {
          'Content-Type': 'application/json'
        }
    })
    if(loginResponse.ok)
    {
        jwtToken = await loginResponse.text();
        window.localStorage.setItem("jwtToken", jwtToken);
        return true;
    }
    return false;
}

const register = async ({ firstName,
    lastName,
    email,
    password
}) => {
    const registrationData = { 
        "firstName":firstName,
        "lastName":lastName,
        "email": email,
        "password":password
    };
    const registerResponse = await fetch(config.host + config.usersUrl, {
        body: JSON.stringify(registrationData),
        method: "POST",
        headers: {
            'Content-Type': 'application/json'
        }
    })

    return registerResponse.ok;
}

const logout = () =>
{
    window.localStorage.removeItem("jwtToken", null);
}

const isLoggedIn = () => !(window.localStorage.getItem("jwtToken") === null);

const userSerivce = {
    login,
    logout,
    register,
    isLoggedIn
}
export default userSerivce;
```
\$\endgroup\$

1 Answer 1

3
\$\begingroup\$

Nice work, the code looks good!

Couple of comments-

With ES6 syntax, if you're returning an object with a property that is coming from a variable of the same name:

    const registrationData = { 
        "firstName":firstName,
        "lastName":lastName,
        "email": email,
        "password":password
    };

You can completely omit the property name, and shorten the syntax like so:

    const registrationData = { 
        firstName,
        lastName,
        email,
        password
    };

If you're repeating the same block of code, it makes sense to extrapolate that into a function. For example, your fetch functions are more or less identical, aside from a few parameters..

    const registerResponse = await fetch(config.host + config.usersUrl, {
        body: JSON.stringify(registrationData),
        method: "POST",
        headers: {
            'Content-Type': 'application/json'
        }
    })

could be turned into:

const sendPost = async(url, body) => {
    const res = await fetch(url, {
        body,
        method: "POST",
        headers: {
            "Content-Type": "application/json"
        }
    })
    return res
}

And then you can call it whenever you want to send a post... reducing repetition and leaving less room for human error.

In response to your questions:

  1. There is a more secure alternative to local storage, which involves using HTTPOnly cookies. HTTPOnly cookies aren't stored in the user's browser, and exist only on the HTTPS connection itself. It is more involved than using local storage, but it's much more secure then storing session secrets in the user's browser. You can also set this cookie to only be used on a secure connection.

  2. If everything is sent over HTTPS, you won't need to hash the password clientside, as the TLS encryption mangles the passwords in transit.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.