Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion indexer/.env.example
Original file line number Diff line number Diff line change
Expand Up @@ -11,5 +11,4 @@ DB_SSL_REJECT_UNAUTHORIZED=false

KADENA_GRAPHQL_API_PORT=3001 #optional
SENTRY_DSN="http://sentryurl" #optional
ALLOWED_ORIGINS=http://localhost:3001,http://localhost:3002,http://localhost:3003 #optional
PRICE_CACHE_TTL=300 #optional
1 change: 0 additions & 1 deletion indexer/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,6 @@ cp indexer/.env.template indexer/.env
| `DB_SSL_ENABLED` | Enable/disable SSL for database | `true` or `false` |
| `KADENA_GRAPHQL_API_PORT` | GraphQL API port | `3000` |
| `SENTRY_DSN` | Sentry url to monitor indexer usage | `https://123.ingest.us.sentry.io/123` |
| `ALLOWED_ORIGINS` | Allowed origins for CORS | `http://abcde:3001,http://abcde:3002` |
| `PRICE_CACHE_TTL` | Time-to-live for price cache in seconds | `300` |

**NOTE:** The example Kadena node API from chainweb will not work for the indexer purpose. You will need to run your own Kadena node and set the `NODE_API_URL` to your node's API URL.
Expand Down
86 changes: 0 additions & 86 deletions indexer/src/kadena-server/server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,12 +84,6 @@ const typeDefs = readFileSync(join(__dirname, './config/schema.graphql'), 'utf-8
*/
const KADENA_GRAPHQL_API_PORT = process.env.KADENA_GRAPHQL_API_PORT ?? '3001';

/**
* Array of domains allowed to access the GraphQL API
*/

const ALLOWED_ORIGINS = getArrayEnvString('ALLOWED_ORIGINS');

/**
* Apollo Server plugin that validates pagination parameters in GraphQL requests
*
Expand Down Expand Up @@ -214,36 +208,6 @@ const securitySanitizationPlugin: ApolloServerPlugin = {
}),
};

/**
* Checks if an origin is allowed to access the GraphQL API
*
* Implements CORS policy by validating origin domains against the allowed list.
* Permits localhost for development and allows both exact matches and subdomains
* of kadena.io.
*
* @param origin - The origin domain requesting access
* @returns Boolean indicating if the origin is allowed
*/
const isAllowedOrigin = (origin: string): boolean => {
try {
const originUrl = new URL(origin);
if (originUrl.hostname === 'localhost') return true;

return ALLOWED_ORIGINS.some(allowed => {
const allowedUrl = new URL(allowed);
// Check if it's an exact match
if (originUrl.origin === allowedUrl.origin) return true;
// Check if it's a subdomain (only for kadena.io)
if (allowedUrl.hostname === 'kadena.io' && originUrl.hostname.endsWith('.kadena.io')) {
return true;
}
return false;
});
} catch {
return false;
}
};

/**
* Initializes and starts the GraphQL server
*
Expand Down Expand Up @@ -515,20 +479,6 @@ export async function startGraphqlServer() {
app.use(
'/graphql',
cors<cors.CorsRequest>({
origin: (origin, callback) => {
if (!origin || origin === 'null') {
return callback(null, false);
}

try {
if (isAllowedOrigin(origin)) {
return callback(null, true);
}
return callback(new Error(`[ERROR][CORS][ORIGIN] Origin ${origin} not allowed by CORS`));
} catch (error) {
return callback(null, false);
}
},
methods: ['POST', 'OPTIONS'],
allowedHeaders: [
'Content-Type',
Expand All @@ -548,42 +498,6 @@ export async function startGraphqlServer() {
}),
);

/**
* Handle CORS preflight OPTIONS requests explicitly
*
* This endpoint manages the CORS preflight requests that browsers send before making
* actual API requests. It's a critical security component that:
*
* 1. Validates the origin against the allowed domains list
* 2. Sets appropriate CORS headers when origins are allowed:
* - Access-Control-Allow-Origin: Reflects the allowed origin
* - Access-Control-Allow-Credentials: Enables authenticated requests
* - Access-Control-Allow-Methods: Limits to POST and OPTIONS methods
* - Access-Control-Allow-Headers: Specifies allowed request headers
* - Access-Control-Max-Age: Caches preflight result for 24 hours (86400s)
* 3. Returns 204 No Content for allowed origins or 403 Forbidden for disallowed ones
*
* This explicit handling ensures precise control over cross-origin security,
* preventing unauthorized domains from accessing the API while allowing
* legitimate client applications to function properly.
*/
app.options('*', (req: Request, res: Response) => {
const origin = req.headers.origin;
if (origin && isAllowedOrigin(origin)) {
res.setHeader('Access-Control-Allow-Origin', origin);
res.setHeader('Access-Control-Allow-Credentials', 'true');
res.setHeader('Access-Control-Allow-Methods', 'POST, OPTIONS');
res.setHeader(
'Access-Control-Allow-Headers',
'Content-Type, Authorization, Accept, Origin, X-Requested-With, Cache-Control, Pragma',
);
res.setHeader('Access-Control-Max-Age', '86400');
res.status(204).end();
} else {
res.status(403).end();
}
});

/**
* Handle 404 Not Found errors for all other routes
*
Expand Down