This tutorial is out of date and no longer maintained.
Warning: This tutorial is out of date and no longer maintained. It is based on Prisma 1 which is not actively developed any more. To learn everything about the latest version of Prisma, visit the official Prisma documentation.
If you want to learn how to build server-side applications with Prisma, you can follow these new tutorials:
In part 1, we set up our project to require authentication to interact with the GraphQL API. Now, we’re going to look at logging in users and generating JSON Web Tokens (JWT) for our users to lock them out from data we don’t want them to access.
We’re going to be using bcrypt to encrypt our passwords, since storing passwords as just normal strings is very bad for security, and jsonwebtoken to generate tokens we can use to verify if a user should have access to something.
$ npm install bcryptjs jsonwebtoken
Let’s take a moment to go over the two libraries we’ll be using. If you’re already familiar with them, then you can skip this as well as the next section.
bcrypt
allows us to encrypt our passwords and compare that hashed version with another string to see if they’re the same. The point of this is that you can compare the hashed and un-hashed versions, but you can never decode the original from it. This way, even if someone were to query for a user’s password, although they would get something, it wouldn’t be usable for breaking into anyone’s account.
It takes the string we would like to hash and the number of salts. A salt is a cycle that runs the password through its hashing algorithm; the higher the number of salts, the more secure it’ll be, but the longer it’ll take to generate.
import bcrypt from 'bcryptjs';
const password = 'pleaseDontHackMe3248';
console.log('Raw Password: ', password);
bcrypt.hash(password, 8)
.then(hashed => {
console.log('Secure Password: ', hashed);
// Must take the string version then the hashed version
const doesMatch = bcrypt.compare(password, hashed);
return doesMatch;
}
).then(doesMatch => console.log('Password Matches: ', doesMatch));
jsonwebtoken
is an extremely popular library for generating unique access tokens that we can use to verify for authenticity and even have expire over some period of time.
jwt.sign
takes in something to link the token to a particular user and a secret that we’ll use to verify if the token is valid since anyone could generate their own outside of our app. We can set the expiration with the expiresIn
property and a string describing the time span, such as 5000
(milliseconds), '8d'
, or '9 months'
.
const token = jwt.sign({ name: 'Someone' }, 'anotherSecret', { expiresIn: '1 day' });
console.log(token);
Now we can use jwt.verify
to use our secret to decrypt our user’s data that was stored. You’d normally be storing this secret as an environment variable.
const decrypted = jwt.verify(token, 'anotherSecret');
console.log(decrypted);
Creating a login system is a very simple process. All we need to do is give every user a token when they run the login
or createUser
mutations, which would normally be saved and sent back from the client-side, but we’ll stick with GraphQL Playground’s header for now.
Let’s start by adding some mutations and generating a token for our user. Since a user will only sometimes need a token, we can leave it nullable.
type Mutation {
createUser(data: CreateUserInput): User!
loginUser(email: String!, password: String!): User!
}
type User {
id: ID!
name: String!
email: String!
password: String!
token: String
}
input CreateUserInput {
name: String!
email: String!
password: String!
}
With our schema in place, let’s try to create a new user with a valid token.
const expiresIn = '1 day'; // We'll be using this value repeatedly.
const Mutation = {
async createUser(parent, { data }, { prisma }, info) {
const password = await bcrypt.hash(data.password, 10);
const user = await prisma.mutation.createUser({
data: {
...data,
password
}
});
// Since id is added by prisma it is unavailable when creating a user.
const userWithToken = {
...user,
token: jwt.sign({ userId: user.id }, process.env.TOKEN_SECRET, { expiresIn })
};
return userWithToken;
}
}
Finally, just add our secret to our environment variables, which by now, should look like this.
API_SECRET=SuperSecretSecret
TOKEN_SECRET=EvenSecreterSecret
The last step is to use the user’s email to get their account and compare their encoded password with the one they entered. If it’s correct, then we’ll generate and return a new token for them.
const expiresIn = '1 day';
const Mutation = {
async createUser(parent, { data }, { prisma }, info) {...},
async loginUser(parent, { email, password }, { prisma }, info) {
const user = await prisma.query.user({ where: { email } });
if (!user) throw new Error('No User Found');
const isValid = await bcrypt.compare(password, user.password);
if (!isValid) throw new Error('Wrong Password');
const userWithToken = {
...user,
token: jwt.sign({ userId: user.id }, process.env.TOKEN_SECRET, { expiresIn })
}
return userWithToken
}
}
In this tutorial, you explored how to log in users and generate JWTs. In part 3 of this series, we will cover validation for queries, mutations, and subscriptions.
Warning: This tutorial is out of date and no longer maintained. It is based on Prisma 1 which is not actively developed any more. To learn everything about the latest version of Prisma, visit the official Prisma documentation.
If you want to learn how to build server-side applications with Prisma, you can follow these new tutorials:
Thanks for learning with the DigitalOcean Community. Check out our offerings for compute, storage, networking, and managed databases.
This textbox defaults to using Markdown to format your answer.
You can type !ref in this text area to quickly search our full set of tutorials, documentation & marketplace offerings and insert the link!
Sign up for Infrastructure as a Newsletter.
Working on improving health and education, reducing inequality, and spurring economic growth? We'd like to help.
Get paid to write technical tutorials and select a tech-focused charity to receive a matching donation.