JWT Authentication with Apollo
Jiayi
Posted on December 22, 2020
One of the features for this app is to have an authentication system without using OAuth. This implementation would be slightly new to me as I've never used GraphQL API.
The authentication flow is the same as how one would use in a REST API.
- Server (backend) will create the routes or mutations as they call it in GraphQL to perform authentication logic.
- The client will call the mutations, passing the necessary parameters such as username and password to the server.
- The server would handle the logic via jsonwebtoken to sign or verify the user and of course encrypt the password to store in the database.
- After a successful signature, server will return a valid token and client will store it.
I have a few routes that I want to protect and they must be logged in as the app's user to be able to make those requests. This is how I made it work.
// pages/api/graphql/index.js
import { ApolloServer } from 'apollo-server-micro';
import { makeExecutableSchema } from 'graphql-tools';
import resolvers from './resolvers';
import typeDefs from './TypeDef';
import jwt from 'jsonwebtoken';
const schema = makeExecutableSchema({
typeDefs,
resolvers,
});
let db;
const apolloServer = new ApolloServer({
schema,
context: async ({ req, res }) => {
// AUTHORIZATION
let loggedUser;
const token = req.headers.cookie ? req.headers.cookie.split('token=')[1] : '';
if (token) {
const user = jwt.verify(token, process.env.JWT_SECRET_KEY);
if (!user) throw new AuthenticationError('You must be logged in');
loggedUser = user;
}
// DATABASE
if (!db) {
// connect to db
}
return { db, loggedUser };
},
});
export const config = {
api: {
bodyParser: false,
},
};
export default apolloServer.createHandler({ path: '/api/graphql' });
In the apollo server entry point, I'm making use of the context object to achieve this. There I'm verifying the token via jsonwebtoken
. If there is a user signed with that token, I'm setting the loggedUser
as the user that's verified and returning it in the context object. Now I have access to loggedUser
in the resolvers as well.
// pages/api/graphql/resolvers.js
const resolvers = {
Query: {
posts: async (_parent, _args, { db, loggedUser }, _info) => {
if (!loggedUser) throw new AuthenticationError('you must be logged in');
return await db
.collection('posts')
.find()
.toArray();
},
}
}
I want to protect this posts
query from not-logged-users so I'm simply putting a check before returning desired response. The client can now handle it however they want with that error :)
So far, this is working rather nicely 😂. I'll continue posting on the development process in the next posts. Happy hacking!
Posted on December 22, 2020
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.