Integrating with Apollo GraphQL
This guide explains how to implement JWT authentication in your Apollo GraphQL server using AuthAction’s JWKS (JSON Web Key Set) endpoint. The decoded token payload is injected into the Apollo context and protected resolvers enforce authentication via a requireAuth helper.
Example Repository: For a complete working example, check out our example repository.
Prerequisites
Section titled “Prerequisites”Before you begin, ensure you have:
- Node.js 18+: Download from nodejs.org
- AuthAction Account: You’ll need your AuthAction tenant domain and API identifier
Configuration
Section titled “Configuration”1. Install Required Packages
Section titled “1. Install Required Packages”npm install @apollo/server graphql jwks-rsa jsonwebtoken dotenv2. Configure AuthAction Settings
Section titled “2. Configure AuthAction Settings”Create a .env file in your project root:
AUTHACTION_DOMAIN=your-authaction-tenant-domainAUTHACTION_AUDIENCE=your-authaction-api-identifier3. Implement JWT Validation
Section titled “3. Implement JWT Validation”Create src/auth.js:
import jwksClient from "jwks-rsa";import jwt from "jsonwebtoken";
const client = jwksClient({ jwksUri: `https://${process.env.AUTHACTION_DOMAIN}/.well-known/jwks.json`, cache: true, cacheMaxEntries: 5, cacheMaxAge: 3600000, // 1 hour});
function getKey(header, callback) { client.getSigningKey(header.kid, (err, key) => { if (err) return callback(err); callback(null, key.getPublicKey()); });}
export function verifyToken(token) { return new Promise((resolve, reject) => { jwt.verify( token, getKey, { audience: process.env.AUTHACTION_AUDIENCE, issuer: `https://${process.env.AUTHACTION_DOMAIN}`, algorithms: ["RS256"], }, (err, decoded) => { if (err) reject(err); else resolve(decoded); } ); });}
export async function buildContext({ req }) { const authHeader = req.headers.authorization || "";
if (!authHeader.startsWith("Bearer ")) { return { user: null }; }
const token = authHeader.split(" ")[1];
try { const payload = await verifyToken(token); return { user: payload }; } catch { return { user: null }; }}4. Define the Schema
Section titled “4. Define the Schema”Create src/schema.js:
export const typeDefs = `#graphql type PublicMessage { message: String! }
type ProtectedMessage { message: String! sub: String! }
type Query { publicMessage: PublicMessage! protectedMessage: ProtectedMessage! }`;5. Add Resolvers
Section titled “5. Add Resolvers”Create src/resolvers.js:
import { GraphQLError } from "graphql";
function requireAuth(context) { if (!context.user) { throw new GraphQLError("Unauthorized", { extensions: { code: "UNAUTHENTICATED", http: { status: 401 } }, }); }}
export const resolvers = { Query: { publicMessage: () => ({ message: "This is a public message!", }),
protectedMessage: (_parent, _args, context) => { requireAuth(context); return { message: "This is a protected message!", sub: context.user.sub, }; }, },};1. Start the Server
Section titled “1. Start the Server”Create src/index.js:
import "dotenv/config";import { ApolloServer } from "@apollo/server";import { startStandaloneServer } from "@apollo/server/standalone";import { typeDefs } from "./schema.js";import { resolvers } from "./resolvers.js";import { buildContext } from "./auth.js";
const server = new ApolloServer({ typeDefs, resolvers });
const { url } = await startStandaloneServer(server, { context: buildContext, listen: { port: 4000 },});
console.log(`Server running at ${url}`);node src/index.jsThe GraphQL server will be available at http://localhost:4000.
2. Obtain an Access Token
Section titled “2. Obtain an Access Token”curl --request POST \ --url https://your-authaction-tenant-domain/oauth2/m2m/token \ --header 'content-type: application/json' \ --data '{ "client_id": "your-authaction-m2m-app-clientid", "client_secret": "your-authaction-m2m-app-client-secret", "audience": "your-authaction-api-identifier", "grant_type": "client_credentials" }'3. Query the Public Resolver
Section titled “3. Query the Public Resolver”curl --request POST \ --url http://localhost:4000/ \ --header 'content-type: application/json' \ --data '{"query": "{ publicMessage { message } }"}'4. Query the Protected Resolver
Section titled “4. Query the Protected Resolver”curl --request POST \ --url http://localhost:4000/ \ --header 'content-type: application/json' \ --header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \ --data '{"query": "{ protectedMessage { message sub } }"}'Common Issues
Section titled “Common Issues”Invalid Token Errors
Section titled “Invalid Token Errors”- Verify
AUTHACTION_DOMAINandAUTHACTION_AUDIENCEmatch your AuthAction dashboard exactly - Ensure the token is signed with the RS256 algorithm
Public Key Fetching Errors
Section titled “Public Key Fetching Errors”- Verify your application can reach
https://your-authaction-tenant-domain/.well-known/jwks.json
Unauthorized Access
Section titled “Unauthorized Access”- Ensure the
Authorization: Bearer <token>header is present and the token is not expired - Confirm the token’s audience matches your API identifier