Skip to content

Integrating with Apollo GraphQL

Terminal window
npm install @authaction/node-sdk
import { ApolloServer } from "@apollo/server";
import { startStandaloneServer } from "@apollo/server/standalone";
import { createVerifier } from "@authaction/node-sdk";
import { apolloContext, requireUser } from "@authaction/node-sdk/apollo";
const verifier = createVerifier({ domain, audience });
const server = new ApolloServer({ typeDefs, resolvers });
await startStandaloneServer(server, {
context: apolloContext(verifier), // injects { user: TokenPayload | null }
});
// In resolvers
const resolvers = {
Query: {
protectedMessage(_parent, _args, context) {
const user = requireUser(context); // throws UNAUTHENTICATED if missing
return { message: "Protected", sub: user.sub };
},
},
};

Full SDK reference: github.com/authaction/authaction-node-sdk


This guide explains how to implement JWT authentication in your Apollo GraphQL server using AuthAction’s JWKS endpoint with jwks-rsa and jsonwebtoken.

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