[go: up one dir, main page]

CORS on Meteor

Meteor is a full-stack JavaScript framework built on Node.js. To add CORS authorization to a Meteor application, use the webapp package's WebApp.rawConnectHandlers to customize HTTP headers.

⚠️ Security Warning: Using Access-Control-Allow-Origin: * allows any website to access your resources. Always specify exact origins in production.

Understanding Meteor CORS

Meteor uses two different communication protocols:

The WebApp.rawConnectHandlers middleware affects HTTP requests but not DDP connections. If you're exposing REST APIs alongside your Meteor application, you'll need proper CORS configuration.

Secure CORS Implementation

Here's a production-ready CORS implementation with origin validation, preflight handling, and proper security headers:

// server/cors.js
import { WebApp } from 'meteor/webapp';

const allowedOrigins = [
  'https://example.com',
  'https://app.example.com',
  'https://admin.example.com'
];

WebApp.rawConnectHandlers.use(function(req, res, next) {
  const origin = req.headers.origin;

  // Validate origin against whitelist
  if (allowedOrigins.includes(origin)) {
    res.setHeader("Access-Control-Allow-Origin", origin);
    res.setHeader("Vary", "Origin");
    res.setHeader("Access-Control-Allow-Credentials", "true");
  }

  res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type, X-Requested-With");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");

  // Handle preflight requests
  if (req.method === "OPTIONS") {
    res.setHeader("Access-Control-Max-Age", "86400"); // 24 hours
    res.writeHead(204);
    res.end();
    return;
  }

  next();
});

Path-Specific CORS

You can configure different CORS policies for different API paths using the optional path argument:

Public API (Open Access)

// Public API - allows any origin
WebApp.rawConnectHandlers.use("/api/public", function(req, res, next) {
  res.setHeader("Access-Control-Allow-Origin", "*");
  res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");

  if (req.method === "OPTIONS") {
    res.setHeader("Access-Control-Max-Age", "86400");
    res.writeHead(204);
    res.end();
    return;
  }

  return next();
});

Private API (Restricted Access)

// Private API - restricted to specific origins
const allowedOrigins = [
  'https://example.com',
  'https://app.example.com'
];

WebApp.rawConnectHandlers.use("/api/private", function(req, res, next) {
  const origin = req.headers.origin;

  if (allowedOrigins.includes(origin)) {
    res.setHeader("Access-Control-Allow-Origin", origin);
    res.setHeader("Access-Control-Allow-Credentials", "true");
    res.setHeader("Vary", "Origin");
  }

  res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
  res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS");

  if (req.method === "OPTIONS") {
    res.setHeader("Access-Control-Max-Age", "86400");
    res.writeHead(204);
    res.end();
    return;
  }

  next();
});

Environment-Based Configuration

For better security and flexibility, load allowed origins from your Meteor settings:

settings.json:

{
  "cors": {
    "origins": [
      "https://example.com",
      "https://app.example.com"
    ]
  }
}

server/cors.js:

import { Meteor } from 'meteor/meteor';
import { WebApp } from 'meteor/webapp';

if (Meteor.isServer) {
  const allowedOrigins = Meteor.settings.cors?.origins || [];

  WebApp.rawConnectHandlers.use(function(req, res, next) {
    const origin = req.headers.origin;

    if (allowedOrigins.includes(origin)) {
      res.setHeader("Access-Control-Allow-Origin", origin);
      res.setHeader("Vary", "Origin");
    }

    if (req.method === "OPTIONS") {
      res.setHeader("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE");
      res.setHeader("Access-Control-Allow-Headers", "Authorization, Content-Type");
      res.setHeader("Access-Control-Max-Age", "86400");
      res.writeHead(204);
      res.end();
      return;
    }

    next();
  });
}

Run Meteor with settings:

meteor --settings settings.json

Using Meteor Packages

For more advanced use cases, consider using community packages:

meteor add simple:rest

Understanding Required Headers

When making authenticated requests to Meteor APIs, clients typically send:

Your server must allow these headers with Access-Control-Allow-Headers. The examples above include these by default.

Key Security Considerations

Testing Your CORS Configuration

For comprehensive testing instructions including curl commands, browser DevTools usage, and troubleshooting common CORS errors, see the CORS Testing Guide.

Additional Resources

Who’s behind this

Monsur Hossain and Michael Hausenblas

Contribute

The content on this site stays fresh thanks to help from users like you! If you have suggestions or would like to contribute, fork us on GitHub.

Buy the book

Save 39% on CORS in Action with promotional code hossainco at manning.com/hossain