Graphql Introspection in Feathersjs with Jwt Tokens
Graphql Introspection in Feathersjs with Jwt Tokens — how this specific combination creates or exposes the vulnerability
GraphQL introspection is a built-in feature that allows clients to query the schema structure, types, and operations of a GraphQL API. In FeathersJS, GraphQL support is commonly added through integrations such as feathers-graphql or adapters that expose a GraphQL endpoint. When authentication is handled via JWT tokens but introspection is left enabled and unprotected, the combination creates a significant information disclosure risk.
In this setup, unauthenticated or partially authenticated requests can still reach the GraphQL endpoint because JWT validation may be applied only to specific resolvers or services, not to the introspection query itself. An attacker can send an introspection query over HTTPS to the FeathersJS GraphQL endpoint and retrieve the full schema, including queries, mutations, subscriptions, and the shape of underlying data models. This reveals sensitive implementation details such as field names, relationships, and potentially deprecated operations that can be leveraged for further attacks.
Because JWT tokens are often used to convey user roles and permissions, developers may assume that introspection is safe behind authentication. However, if the GraphQL server does not explicitly disable introspection for non-authenticated requests, or if a valid JWT token is obtained through other means (e.g., leaked credentials or insecure storage), the attacker gains insight into the API surface without needing valid user credentials. This is particularly dangerous in environments where the GraphQL endpoint is exposed broadly, such as in public-facing services or misconfigured staging environments.
Moreover, FeathersJS applications that use JWT tokens typically rely on hooks for authentication and validation. If introspection is not explicitly blocked in the GraphQL layer before hook execution, the server still processes the query, consuming resources and logging activity. This can aid in reconnaissance for more targeted attacks, such as BOLA/IDOR or privilege escalation, by mapping which operations are available and what data shapes they return.
Real-world examples include services where the GraphQL endpoint follows a predictable path like /graphql. An attacker can use a simple curl command to perform introspection even when JWT protection is present but not uniformly enforced. The returned schema may include sensitive types such as User, Token, or internal relations that should never be exposed. This information directly supports further exploitation, aligning with OWASP API Top 10 categories such as Broken Object Level Authorization and Excessive Data Exposure.
Jwt Tokens-Specific Remediation in Feathersjs — concrete code fixes
To secure a FeathersJS GraphQL endpoint that uses JWT tokens, you must explicitly disable introspection for requests that lack valid authentication or are not explicitly authorized. Below are concrete remediation steps with code examples.
First, ensure that your GraphQL HTTP handler validates JWT tokens before allowing any query execution. In FeathersJS, this is typically done in a hook. The following example shows a JWT verification hook that attaches the decoded payload to the context:
// src/hooks/authentication.js
const { AuthenticationError } = require('@feathersjs/errors');
const jwt = require('jsonwebtoken');
module.exports = function authentication(options = {}) {
return async context => {
const { headers } = context.params;
const token = headers && headers.authorization && headers.authorization.split(' ')[1];
if (!token) {
throw new AuthenticationError('Invalid access token');
}
try {
const payload = jwt.verify(token, process.env.JWT_SECRET);
context.params.user = payload;
} catch (error) {
throw new AuthenticationError('Invalid access token');
}
return context;
};
};
Second, configure your GraphQL server to disable introspection when a valid JWT is not present. With feathers-graphql, you can customize the GraphQL HTTP handler options. The following example shows how to enable introspection only for requests that include a verified user in the context:
// src/app.js
const path = require('path');
const { express } = require('@feathersjs/express');
const { GraphQLServer } = require('feathers-graphql');
const authentication = require('./hooks/authentication');
const app = express();
const graphqlServer = new GraphQLServer({
path: '/graphql',
graphqlOptions: {
schema: require('./schema'),
graphiql: false,
introspection: false, // disable by default
context: async ({ req }) => {
// The authentication hook will have already set req.user if valid
return {
user: req.user || null
};
}
}
});
// Override the handler to allow introspection only for authenticated requests
const originalHandler = graphqlServer.app;
graphqlServer.app = (req, res, next) =>
```Related CWEs: dataExposure
| CWE ID | Name | Severity |
|---|---|---|
| CWE-200 | Exposure of Sensitive Information | HIGH |
| CWE-209 | Error Information Disclosure | MEDIUM |
| CWE-213 | Exposure of Sensitive Information Due to Incompatible Policies | HIGH |
| CWE-215 | Insertion of Sensitive Information Into Debugging Code | MEDIUM |
| CWE-312 | Cleartext Storage of Sensitive Information | HIGH |
| CWE-359 | Exposure of Private Personal Information (PII) | HIGH |
| CWE-522 | Insufficiently Protected Credentials | CRITICAL |
| CWE-532 | Insertion of Sensitive Information into Log File | MEDIUM |
| CWE-538 | Insertion of Sensitive Information into Externally-Accessible File | HIGH |
| CWE-540 | Inclusion of Sensitive Information in Source Code | HIGH |