Skip to content

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.

Before you begin, ensure you have:

  1. Node.js 18+: Download from nodejs.org
  2. AuthAction Account: You’ll need your AuthAction tenant domain and API identifier
Terminal window
npm install @apollo/server graphql jwks-rsa jsonwebtoken dotenv

Create a .env file in your project root:

Terminal window
AUTHACTION_DOMAIN=your-authaction-tenant-domain
AUTHACTION_AUDIENCE=your-authaction-api-identifier

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 };
}
}

Create src/schema.js:

export const typeDefs = `#graphql
type PublicMessage {
message: String!
}
type ProtectedMessage {
message: String!
sub: String!
}
type Query {
publicMessage: PublicMessage!
protectedMessage: ProtectedMessage!
}
`;

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,
};
},
},
};

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}`);
Terminal window
node src/index.js

The GraphQL server will be available at http://localhost:4000.

Terminal window
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"
}'
Terminal window
curl --request POST \
--url http://localhost:4000/ \
--header 'content-type: application/json' \
--data '{"query": "{ publicMessage { message } }"}'
Terminal window
curl --request POST \
--url http://localhost:4000/ \
--header 'content-type: application/json' \
--header 'Authorization: Bearer YOUR_ACCESS_TOKEN' \
--data '{"query": "{ protectedMessage { message sub } }"}'
  • Verify AUTHACTION_DOMAIN and AUTHACTION_AUDIENCE match your AuthAction dashboard exactly
  • Ensure the token is signed with the RS256 algorithm
  • Verify your application can reach https://your-authaction-tenant-domain/.well-known/jwks.json
  • Ensure the Authorization: Bearer <token> header is present and the token is not expired
  • Confirm the token’s audience matches your API identifier