---
name: "Mojolicious::Plugin::OpenAPI::Cors"
section: "3pm"
platform: "linux"
package: "libmojolicious-plugin-openapi-perl"
version: "5.11-1"
release: "Debian 13 (Trixie)"
platforms: ["linux"]
url: "https://new.carta.tech/Mojolicious::Plugin::OpenAPI::Cors"
---

# Mojolicious::Plugin::OpenAPI::Cors(3pm)

OpenAPI plugin for Cross-Origin Resource Sharing

## NAME

Mojolicious::Plugin::OpenAPI::Cors - OpenAPI plugin for Cross-Origin Resource Sharing

## SYNOPSIS

### Application

Set "add\_preflighted\_routes" to 1, if you want "Preflighted" CORS requests to be sent to your already existing actions.

```
  $app->plugin(OpenAPI => {add_preflighted_routes => 1, %openapi_parameters});
```

See "register" in Mojolicious::Plugin::OpenAPI for what `%openapi_parameters` might contain.

### Simple exchange

The following example will automatically set default CORS response headers after validating the request against "openapi\_cors\_allowed\_origins":

```
  package MyApp::Controller::User;
  sub get_user {
    my $c = shift->openapi->cors_exchange->openapi->valid_input or return;
    # Will only run this part if both the cors_exchange and valid_input was successful.
    $c->render(openapi => {user => {}});
  }
```

### Using the specification

It's possible to enable preflight and simple CORS support directly in the specification. Here is one example:

```
  "/user/{id}/posts": {
    "parameters": [
      { "in": "header", "name": "Origin", "type": "string", "pattern": "https?://example.com" }
    ],
    "options": {
      "x-mojo-to": "#openapi_plugin_cors_exchange",
      "responses": {
        "200": { "description": "Cors exchange", "schema": { "type": "string" } }
      }
    },
    "put": {
      "x-mojo-to": "user#add_post",
      "responses": {
        "200": { "description": "Add a new post.", "schema": { "type": "object" } }
      }
    }
  }
```

The special part can be found in the "OPTIONS" request It has the `"x-mojo-to"` key set to "#openapi\_plugin\_cors\_exchange". This will enable Mojolicious::Plugin::OpenAPI::Cors to take over the route and add a custom callback to validate the input headers using regular OpenAPI rules and respond with a "200 OK" and the default headers as listed under "openapi.cors\_exchange" if the input is valid. The only extra part that needs to be done in the `add_post()` action is this:

```
  sub add_post {
    my $c = shift->openapi->valid_input or return;
    # Need to respond with a "Access-Control-Allow-Origin" header if
    # the input "Origin" header was validated
    $c->res->headers->access_control_allow_origin($c->req->headers->origin)
      if $c->req->headers->origin;
    # Do the rest of your custom logic
    $c->respond(openapi => {});
  }
```

### Custom exchange

If you need full control, you must pass a callback to "openapi.cors\_exchange":

```
  package MyApp::Controller::User;
  sub get_user {
    # Validate incoming CORS request with _validate_cors()
    my $c = shift->openapi->cors_exchange("_validate_cors")->openapi->valid_input or return;
    # Will only run this part if both the cors_exchange and valid_input was
    # successful.
    $c->render(openapi => {user => {}});
  }
  # This method must return undef on success. Any true value will be used as an error.
  sub _validate_cors {
    my $c     = shift;
    my $req_h = $c->req->headers;
    my $res_h = $c->res->headers;
    # The following "Origin" header check is the same for both simple and
    # preflighted.
    return "/Origin" unless $req_h->origin =~ m!^https?://whatever.example.com!;
    # The following checks are only valid if preflighted...
    # Check the Access-Control-Request-Headers header
    my $headers = $req_h->header('Access-Control-Request-Headers');
    return "Bad stuff." if $headers and $headers =~ /X-No-Can-Do/;
    # Check the Access-Control-Request-Method header
    my $method = $req_h->header('Access-Control-Request-Methods');
    return "Not cool." if $method and $method eq "DELETE";
    # Set the following header for both simple and preflighted on success
    # or just let the auto-renderer handle it.
    $c->res->headers->access_control_allow_origin($req_h->origin);
    # Set Preflighted response headers, instead of using the default
    if ($c->stash("openapi_cors_type") eq "preflighted") {
      $c->res->headers->header("Access-Control-Allow-Headers" => "X-Whatever, X-Something");
      $c->res->headers->header("Access-Control-Allow-Methods" => "POST, GET, OPTIONS");
      $c->res->headers->header("Access-Control-Max-Age" => 86400);
    }
    # Return undef on success.
    return undef;
  }
```

## DESCRIPTION

Mojolicious::Plugin::OpenAPI::Cors is a plugin for accepting Preflighted or Simple Cross-Origin Resource Sharing requests. See \<https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS\> for more details.

This plugin is loaded by default by Mojolicious::Plugin::OpenAPI.

Note that this plugin currently EXPERIMENTAL! Please comment on \<https://github.com/jhthorsen/mojolicious-plugin-openapi/pull/102\> if you have any feedback or create a new issue.

## STASH VARIABLES

The following "stash variables" can be set in "defaults" in Mojolicious, "to" in Mojolicious::Routes::Route or "stash" in Mojolicious::Controller.

### openapi\_cors\_allowed\_origins

This variable should hold an array-ref of regexes that will be matched against the "Origin" header in case the default "openapi\_cors\_default\_exchange\_callback" is used. Examples:

```
  $app->defaults(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]);
  $c->stash(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]);
```

### openapi\_cors\_default\_exchange\_callback

This value holds a default callback that will be used by "openapi.cors\_exchange", unless you pass on a `$callback`. The default provided by this plugin will simply validate the `"Origin"` header against "openapi\_cors\_allowed\_origins".

Here is an example to allow every "Origin"

```
  $app->defaults(openapi_cors_default_exchange_callback => sub {
    my $c = shift;
    $c->res->headers->header("Access-Control-Allow-Origin" => "*");
    return undef;
  });
```

### openapi\_cors\_default\_max\_age

Holds the default value for the "Access-Control-Max-Age" response header set by "openapi.cors\_preflighted". Examples:

```
  $app->defaults(openapi_cors_default_max_age => 86400);
  $c->stash(openapi_cors_default_max_age => 86400);
```

Default value is 1800.

### openapi\_cors\_type

This stash variable is available inside the callback passed on to "openapi.cors\_exchange". It will be either "preflighted", "real" or "simple". "real" is the type that comes after "preflighted" when the actual request is sent to the server, but with "Origin" header set.

## HELPERS

### openapi.cors\_exchange

```
  $c = $c->openapi->cors_exchange($callback);
  $c = $c->openapi->cors_exchange("MyApp::cors_validator");
  $c = $c->openapi->cors_exchange("_some_controller_method");
  $c = $c->openapi->cors_exchange(sub { ... });
  $c = $c->openapi->cors_exchange;
```

Used to validate either a simple CORS request, preflighted CORS request or a real request. It will be called as soon as the "Origin" request header is seen.

The `$callback` will be called with the current Mojolicious::Controller object and must return an error or `undef()` on success:

```
  my $error = $callback->($c);
```

The `$error` must be in one of the following formats:

- •

  `undef()`

  Returning `undef()` means that the CORS request is valid.

- •

  A string starting with "/"

  Shortcut for generating a 400 Bad Request response with a header name. Example:

  ```
    return "/Access-Control-Request-Headers";
  ```

- •

  Any other string

  Used to generate a 400 Bad Request response with a completely custom message.

- •

  An array-ref

  Used to generate a completely custom 400 Bad Request response. Example:

  ```
    return [{message => "Some error!", path => "/Whatever"}];
    return [{message => "Some error!"}];
    return [JSON::Validator::Error->new];
  ```

On success, the following headers will be set, unless already set by `$callback`:

- •

  Access-Control-Allow-Headers

  Set to the header of the incoming "Access-Control-Request-Headers" header.

- •

  Access-Control-Allow-Methods

  Set to the list of HTTP methods defined in the OpenAPI spec for this path.

- •

  Access-Control-Allow-Origin

  Set to the "Origin" header in the request.

- •

  Access-Control-Max-Age

  Set to "openapi\_cors\_default\_max\_age".

## METHODS

### register

Called by Mojolicious::Plugin::OpenAPI.

## SEE ALSO

Mojolicious::Plugin::OpenAPI.
