How to Handle Authentication and Authorization in GraphQL
Authentication and authorization are critical aspects of securing any API, including GraphQL. While GraphQL does not provide built-in mechanisms for these processes, developers can implement them using middleware, context, and custom logic. This guide will explain how to handle authentication and authorization in a GraphQL API.
1. Authentication vs. Authorization
- Authentication: The process of verifying the identity of a user or client. This typically involves checking credentials such as usernames and passwords.
- Authorization: The process of determining whether an authenticated user has permission to perform a specific action or access certain resources.
2. Implementing Authentication
To implement authentication in a GraphQL API, you can use JSON Web Tokens (JWT) or session-based authentication. Below is an example using JWT:
Sample Authentication Flow
// Import necessary libraries
const jwt = require('jsonwebtoken');
const { ApolloServer, gql } = require('apollo-server');
// Sample user data
const users = [
{ id: '1', username: 'Alice', password: 'password123' },
{ id: '2', username: 'Bob', password: 'password456' },
];
// Define the schema
const typeDefs = gql`
type User {
id: ID!
username: String!
}
type Query {
me: User
}
type Mutation {
login(username: String!, password: String!): String
}
`;
// Define the resolvers
const resolvers = {
Query: {
me: (parent, args, context) => {
// Return the authenticated user
return context.user;
},
},
Mutation: {
login: (parent, { username, password }) => {
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
throw new Error('Invalid credentials');
}
// Generate a JWT token
return jwt.sign({ id: user.id }, 'your_secret_key', { expiresIn: '1h' });
},
},
};
// Middleware to authenticate the user
const getUser FromToken = (token) => {
if (token) {
try {
return jwt.verify(token, 'your_secret_key');
} catch (err) {
throw new Error('Invalid token');
}
}
return null;
};
// Create the Apollo Server
const server = new ApolloServer({
typeDefs,
resolvers,
context: ({ req }) => {
// Get the token from the headers
const token = req.headers.authorization || '';
const user = getUser FromToken(token.replace('Bearer ', ''));
return { user }; // Add the user to the context
},
});
// Start the server
server.listen().then(({ url }) => {
console.log(`🚀 Server ready at ${url}`);
});
3. Implementing Authorization
Authorization can be implemented by checking the user's role or permissions within the resolvers. Below is an example of how to restrict access to certain queries based on user roles:
Sample Authorization Logic
const resolvers = {
Query: {
me: (parent, args, context) => {
return context.user; // Return the authenticated user
},
adminData: (parent, args, context) => {
if (!context.user || context.user.role !== 'ADMIN') {
throw new Error('Not authorized');
}
return { secret: 'This is admin data' };
},
},
Mutation: {
login: (parent, { username, password }) => {
const user = users.find(u => u.username === username && u.password === password);
if (!user) {
throw new Error('Invalid credentials');
}
return jwt.sign({ id: user.id, role: user.role }, 'your_secret_key', { expiresIn: '1h' });
},
},
};
4. Sample Queries and Mutations
Here’s how you might use the login mutation and the me query:
Login Mutation
mutation {
login(username: "Alice", password: "password123")
}
Me Query
{
me {
id
username
}
}
Admin Data Query
{
adminData {
secret
}
}
Conclusion
Handling authentication and authorization in GraphQL requires implementing custom logic to verify user identities and permissions. By using JWT for authentication and role-based checks for authorization, you can secure your GraphQL API effectively. This approach not only protects sensitive data but also ensures that users can only access resources they are permitted to view.