Hone logo
Hone
Problems

Implementing Cross-Origin Resource Sharing (CORS) in a Python Web Application

Web applications often need to communicate with APIs hosted on different domains, protocols, or ports. By default, browsers enforce a security policy called the Same-Origin Policy (SOP), which prevents web pages from making requests to a different origin. Cross-Origin Resource Sharing (CORS) is a mechanism that allows servers to explicitly indicate which origins are permitted to access their resources. This challenge focuses on implementing robust CORS handling in a Python web application.

Problem Description

Your task is to build a simple Python web application using Flask that serves a JSON API endpoint. You need to correctly configure Cross-Origin Resource Sharing (CORS) for this endpoint so that it can be accessed by a frontend application hosted on a different origin (e.g., a different port or domain).

What needs to be achieved:

  • Create a basic Flask web server.
  • Implement a GET endpoint that returns a simple JSON response.
  • Configure CORS headers for this endpoint to allow requests from a specified origin.

Key requirements:

  1. Flask Application: Use the Flask web framework.
  2. JSON API Endpoint: Create a /api/data endpoint that returns a JSON object like {"message": "Hello from your API!"}.
  3. CORS Configuration:
    • Allow requests from a specific origin. For the purpose of this challenge, assume the frontend will be served from http://localhost:3000.
    • The endpoint should allow GET requests.
    • You should also consider allowing credentials (though for this basic example, it's not strictly necessary but good practice to think about).
  4. Response Headers: Ensure the server sends the appropriate CORS headers in its response.

Expected behavior:

  • When a request comes from http://localhost:3000 (or any origin you've configured to be allowed), the /api/data endpoint should respond with the JSON data and the correct CORS headers.
  • When a request comes from an unallowed origin, the browser should block the request, and the server might not even receive it if it's a preflight request that fails.

Edge cases to consider:

  • Preflight Requests (OPTIONS method): Browsers often send an OPTIONS request (a preflight request) before the actual GET request to check if the server permits the actual request. Your server must handle these OPTIONS requests correctly by returning the appropriate CORS headers.
  • Different HTTP Methods: While this challenge focuses on GET, in a real-world scenario, you'd need to consider other methods like POST, PUT, DELETE.

Examples

Example 1: Successful CORS Request

Scenario: A frontend application running on http://localhost:3000 makes a GET request to http://localhost:5000/api/data.

Input (Frontend Request - simplified):

GET /api/data HTTP/1.1
Host: localhost:5000
Origin: http://localhost:3000

Output (Server Response):

HTTP/1.0 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: http://localhost:3000
Vary: Origin
Content-Length: 34

{"message": "Hello from your API!"}

Explanation: The server correctly identifies the Origin header from the incoming request and includes it in the Access-Control-Allow-Origin header of the response. The Vary: Origin header is also important for caching proxies.

Example 2: Preflight Request Handling

Scenario: A frontend application running on http://localhost:3000 makes a request that requires a preflight (e.g., a POST with a custom header). For this example, we'll simulate a preflight for our GET request as browsers might do this.

Input (Frontend Preflight Request):

OPTIONS /api/data HTTP/1.1
Host: localhost:5000
Origin: http://localhost:3000
Access-Control-Request-Method: GET
Access-Control-Request-Headers: content-type

Output (Server Response):

HTTP/1.0 200 OK
Content-Type: text/html; charset=utf-8
Access-Control-Allow-Origin: http://localhost:3000
Access-Control-Allow-Methods: GET, OPTIONS
Access-Control-Allow-Credentials: true
Vary: Origin, Access-Control-Request-Method, Access-Control-Request-Headers

Explanation: The server responds to the OPTIONS request, confirming that GET requests from http://localhost:3000 are allowed. It also specifies Access-Control-Allow-Methods and potentially Access-Control-Allow-Credentials to inform the browser about the server's capabilities. The Vary header is crucial here to ensure proper caching of preflight responses.

Example 3: Denied CORS Request

Scenario: A frontend application running on an unallowed origin, say http://another-domain.com:8080, makes a GET request to http://localhost:5000/api/data.

Input (Frontend Request):

GET /api/data HTTP/1.1
Host: localhost:5000
Origin: http://another-domain.com:8080

Output (Server Response):

HTTP/1.0 200 OK
Content-Type: application/json
Access-Control-Allow-Origin: null  # Or no Access-Control-Allow-Origin header at all
Content-Length: 34

{"message": "Hello from your API!"}

(Browser will likely block this response to the frontend JavaScript.)

Explanation: If the server does not explicitly allow http://another-domain.com:8080, it should not include Access-Control-Allow-Origin: http://another-domain.com:8080 in its response. The browser will then enforce the SOP and prevent the frontend JavaScript from accessing the response.

Constraints

  • The Flask application should listen on http://localhost:5000.
  • The allowed origin for frontend requests is strictly http://localhost:3000.
  • The API endpoint is /api/data.
  • The response format for the endpoint must be JSON.
  • You should use a Python solution.

Notes

  • You can use the flask and flask-cors libraries to simplify CORS implementation.
  • Consider the differences between simple requests and preflight requests. Simple requests (GET, HEAD, POST with specific Content-Types and no custom headers) might not trigger an OPTIONS preflight, but it's good practice to configure for them.
  • The flask-cors library abstracts away much of the complexity of handling OPTIONS requests and setting headers.
  • For local development, you can simulate different origins by running your frontend on a different port (e.g., npm start for React often runs on 3000, while the Flask server runs on 5000).
  • Success is defined by the ability of a client running on http://localhost:3000 to successfully fetch data from http://localhost:5000/api/data without encountering CORS errors in the browser console.
Loading editor...
python