Open In App

Authentication and Authorization with JWT in a GraphQL

Last Updated : 29 Apr, 2024
Improve
Improve
Like Article
Like
Save
Share
Report

Authentication and authorization are important aspects of building secure web applications by including those powered by GraphQL. JSON Web Tokens (JWT) provide a popular mechanism for implementing authentication and authorization in GraphQL applications.

In this article, we’ll explore the concepts of authentication and authorization with JWT in a GraphQL application by covering their implementation, and benefits.

Understanding Authentication and Authorization

Authentication

  • Authentication is verifying who a user or client is.
  • It ensures that the user is who they claim to be by validating credentials such as username and password.
  • Authentication protocols like OAuth and OpenID Connect are commonly used to authenticate users in web applications.
  • Best practices for authentication include using strong, unique passwords, implementing MFA, and regularly updating authentication methods.

Authorization

  • Authorization controls what actions users can do in the app.
  • It involves checking permissions and enforcing access control rules based on the user’s identity and role.
  • Authorization mechanisms include role-based access control (RBAC) and attribute-based access control (ABAC).
  • RBAC assigns permissions based on user roles, while ABAC evaluates attributes such as user attributes, resource attributes, and environmental attributes.

Using JWT for Authentication and Authorization

JSON Web Tokens (JWT) are compact, URL-safe tokens that contain JSON data and are digitally signed. They can securely transmit information between parties and are commonly used for authentication and authorization in web applications.

Implementation Steps

  • User Authentication: When a user logs in, the server generates a JWT containing the user’s identity and signs it with a secret key. This token is then sent back to the client, which stores it locally (e.g., in localStorage or sessionStorage).
  • Authorization Middleware: In GraphQL resolvers or middleware, the server verifies the JWT sent by the client. If the token is valid and contains the necessary permissions, the server allows the requested operation to proceed. Otherwise, it returns an error or denies access.

Example: Implementing Authentication and Authorization with JWT in GraphQL

Let’s consider a simple GraphQL schema for managing user authentication and authorization:

type Query {
currentUser: User
}

type Mutation {
login(username: String!, password: String!): AuthPayload
}

type User {
id: ID!
username: String!
email: String!
}

type AuthPayload {
token: String!
user: User!
}

Explanation:

  • The Query type includes a single field currentUser that returns a User object representing the currently authenticated user, or null if not authenticated.
  • The Mutation type includes a login mutation that takes a username and password, and returns an AuthPayload object containing a JWT (token) and the authenticated User object.
  • The User type represents a user with an id, username, and email.
  • The AuthPayload type contains a JWT (token) for authentication purposes and the authenticated User object.

Server-side Implementation

Generate JWT on Login

This code provides a basic implementation for a login resolver in a GraphQL server. However, it has a critical security flaw: it stores user passwords in plain text in the users array. Storing passwords in plain text is highly insecure and can lead to security breaches if the data is compromised.

const jwt = require('jsonwebtoken');

// Mock user data
const users = [
{ id: '1', username: 'john_doe', password: 'password', email: 'john@example.com' }
];

// Login resolver
const login = async (_, { username, password }) => {
const user = users.find(u => u.username === username && u.password === password);
if (!user) throw new Error('Invalid username or password');

const token = jwt.sign({ userId: user.id }, 'secretKey', { expiresIn: '1h' });
return { token, user };
};

Explanation: This JavaScript code defines a simple login resolver for a GraphQL schema. It uses a hardcoded array of user objects to find a user with the provided username and password. If the user is found, it generates a JWT (token) using the jsonwebtoken library, signing it with a secret key and setting an expiration time of 1 hour. Finally, it returns an object containing the token and the authenticated user. If the user is not found or the password is incorrect, it throws an error.

Authorization Middleware

This code defines a context function for Apollo Server that extracts and verifies a JWT from the request headers. It throws an AuthenticationError if the token is missing, invalid, or expired. The context function is then passed to the Apollo Server constructor to be used for authentication in GraphQL resolvers.

const { AuthenticationError } = require('apollo-server');
const jwt = require('jsonwebtoken');

const context = ({ req }) => {
const token = req.headers.authorization || '';
if (!token) throw new AuthenticationError('Authentication token missing');

try {
const decoded = jwt.verify(token, 'secretKey');
return { userId: decoded.userId };
} catch (error) {
throw new AuthenticationError('Invalid or expired token');
}
};

const server = new ApolloServer({
typeDefs,
resolvers,
context
});

Explanation: This code sets up a context function for Apollo Server that extracts the JWT from the request headers, verifies it using the ‘jsonwebtoken’ library, and returns the decoded user ID if the token is valid. If the token is missing, invalid, or expired, it throws an AuthenticationError. The context function is then passed to the Apollo Server constructor, where it will be used to provide context to all GraphQL resolvers

Client-side Implementation

Login Mutation

This code defines a GraphQL mutation called Login using the gql tag from Apollo Client. It takes username and password as arguments and returns a token and user details upon successful login.

const LOGIN_MUTATION = gql`
mutation Login($username: String!, $password: String!) {
login(username: $username, password: $password) {
token
user {
id
username
email
}
}
}
`;

const { data } = useMutation(LOGIN_MUTATION, {
variables: { username, password },
onCompleted: ({ login }) => {
localStorage.setItem('token', login.token);
// Redirect or perform other actions
}
});

Explanation: This code defines a GraphQL mutation using the gql tag from Apollo Client, which represents a login operation. It takes username and password as arguments and returns a token and user details upon successful login.

The useMutation hook from Apollo Client is then used to execute the mutation. When the mutation completes successfully, the onCompleted callback is called with the login data, which contains the token. The token is then stored in the browser’s localStorage for future use, such as for making authenticated requests to the server

Include JWT Token in Requests

This code creates an HTTP link using createHttpLink from Apollo Client. The link is configured to send requests to the ‘/graphql’ endpoint on the same domain. It also includes an authorization header with a JWT token retrieved from localStorage.getItem('token'), or an empty string if the token is not found. This allows the client to send the JWT token along with each request to authenticate with the server

const httpLink = createHttpLink({
uri: '/graphql',
headers: {
authorization: localStorage.getItem('token') || ''
}
});

Explanation: This code creates an HTTP link using createHttpLink from Apollo Client. The link is configured to send requests to the ‘/graphql’ endpoint on the same domain. It also includes an authorization header with a JWT token retrieved from localStorage.getItem('token'), or an empty string if the token is not found. This allows the client to send the JWT token along with each request to authenticate with the server.

Conclusion

Overall, Implementing authentication and authorization with JWT in a GraphQL application enhances security and ensures that only authorized users can access protected resources. By following the steps outlined in this article and using JWT for token-based authentication, you can build secure and scalable GraphQL APIs with confidence.



Like Article
Suggest improvement
Previous
Next
Share your thoughts in the comments

Similar Reads