Integrating with Apollo GraphQL
Using @authaction/node-sdk (Recommended)
Section titled “Using @authaction/node-sdk (Recommended)”npm install @authaction/node-sdkimport { 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 resolversconst 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
Manual Setup using jwks-rsa
Section titled “Manual Setup using jwks-rsa”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.
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