In the previous part of this tutorial series, we learned how to create a serverless Apollo Server in GraphQL. Now, we will enhance the server by adding a JSON Web Token (JWT) authentication layer.
This will allow us to authenticate and authorize requests made to our server.
We will explore how to create an authentication middleware plugin for Apollo GraphQL. This middleware will help us protect certain operations or fields in our GraphQL schema and ensure that only authenticated users can access them.
Defining the Authentication Middleware
Now, let’s define our authentication middleware. The middleware will check if the user is authenticated before allowing access to protected operations or fields.
import { GraphQLError, GraphQLResolveInfo } from 'graphql';
import jwt from "jsonwebtoken";
// Error handler for authentication failures
const authError = () => {
throw new GraphQLError('User is not authenticated', {
extensions: {
code: 'UNAUTHENTICATED',
http: { status: 401 },
},
});
};
const authenticationMiddleware = (publicOperations: ReadonlyArray<string>) => {
return {
async requestDidStart() {
return {
async executionDidStart() {
return {
willResolveField({ contextValue, info }: { contextValue: ApolloContext, info: GraphQLResolveInfo }) {
const currentOperation = info.operation.name?.value;
if (publicOperations.includes(currentOperation as string)) return;
else {
try {
const authorization = contextValue.authorization;
const token = authorization?.substring(7, authorization.length);
if (!token || !process?.env?.JWT_SECRET) return authError();
jwt.verify(token, process?.env?.JWT_SECRET);
} catch (error) {
authError();
}
}
},
};
},
};
},
};
};
The authenticationMiddleware
function takes an array of public operation names that should not require authentication.
In the willResolveField
method, we check if the current operation is included in the public operations array. If it is, we allow access without authentication. Otherwise, we verify the JWT token extracted from the authorization header using the provided secret. If the token is invalid or missing, we throw an authentication error.
Integrating the Middleware with Apollo Server
Now that we have our authentication middleware, let’s integrate it with our Apollo Server instance.
const server = new ApolloServer({
plugins: [authenticationMiddleware(["LoginMutation", "Login"])],
});
Handling the Context and Authorization
To complete the authentication flow, we need to handle the context and authorization within our GraphQL resolvers.
export default startServerAndCreateNextHandler(server, {
context: async (req, res) => ({
req,
res,
authorization: req?.headers?.authorization,
}),
});
In the context function, we extract the authorization header from the request and decode the JWT token using the provided secret. We include the decoded token and other necessary data in the context object, such as the request and response objects and any data sources needed by the resolvers.
Conclusion
This middleware ensures that only authenticated users can access protected operations or fields in your GraphQL schema.
Remember to always handle authentication and authorization carefully to protect sensitive data and ensure the security of your application.
Happy Hacking !