.\" Automatically generated by Pod::Man 4.14 (Pod::Simple 3.40) .\" .\" Standard preamble: .\" ======================================================================== .de Sp \" Vertical space (when we can't use .PP) .if t .sp .5v .if n .sp .. .de Vb \" Begin verbatim text .ft CW .nf .ne \\$1 .. .de Ve \" End verbatim text .ft R .fi .. .\" Set up some character translations and predefined strings. \*(-- will .\" give an unbreakable dash, \*(PI will give pi, \*(L" will give a left .\" double quote, and \*(R" will give a right double quote. \*(C+ will .\" give a nicer C++. Capital omega is used to do unbreakable dashes and .\" therefore won't be available. \*(C` and \*(C' expand to `' in nroff, .\" nothing in troff, for use with C<>. .tr \(*W- .ds C+ C\v'-.1v'\h'-1p'\s-2+\h'-1p'+\s0\v'.1v'\h'-1p' .ie n \{\ . ds -- \(*W- . ds PI pi . if (\n(.H=4u)&(1m=24u) .ds -- \(*W\h'-12u'\(*W\h'-12u'-\" diablo 10 pitch . if (\n(.H=4u)&(1m=20u) .ds -- \(*W\h'-12u'\(*W\h'-8u'-\" diablo 12 pitch . ds L" "" . ds R" "" . ds C` "" . ds C' "" 'br\} .el\{\ . ds -- \|\(em\| . ds PI \(*p . ds L" `` . ds R" '' . ds C` . ds C' 'br\} .\" .\" Escape single quotes in literal strings from groff's Unicode transform. .ie \n(.g .ds Aq \(aq .el .ds Aq ' .\" .\" If the F register is >0, we'll generate index entries on stderr for .\" titles (.TH), headers (.SH), subsections (.SS), items (.Ip), and index .\" entries marked with X<> in POD. Of course, you'll have to process the .\" output yourself in some meaningful fashion. .\" .\" Avoid warning from groff about undefined register 'F'. .de IX .. .nr rF 0 .if \n(.g .if rF .nr rF 1 .if (\n(rF:(\n(.g==0)) \{\ . if \nF \{\ . de IX . tm Index:\\$1\t\\n%\t"\\$2" .. . if !\nF==2 \{\ . nr % 0 . nr F 2 . \} . \} .\} .rr rF .\" ======================================================================== .\" .IX Title "Mojolicious::Plugin::OpenAPI::Cors 3pm" .TH Mojolicious::Plugin::OpenAPI::Cors 3pm "2021-01-30" "perl v5.32.0" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l .nh .SH "NAME" Mojolicious::Plugin::OpenAPI::Cors \- OpenAPI plugin for Cross\-Origin Resource Sharing .SH "SYNOPSIS" .IX Header "SYNOPSIS" .SS "Application" .IX Subsection "Application" Set \*(L"add_preflighted_routes\*(R" to 1, if you want \*(L"Preflighted\*(R" \s-1CORS\s0 requests to be sent to your already existing actions. .PP .Vb 1 \& $app\->plugin(OpenAPI => {add_preflighted_routes => 1, %openapi_parameters}); .Ve .PP See \*(L"register\*(R" in Mojolicious::Plugin::OpenAPI for what \&\f(CW%openapi_parameters\fR might contain. .SS "Simple exchange" .IX Subsection "Simple exchange" The following example will automatically set default \s-1CORS\s0 response headers after validating the request against \*(L"openapi_cors_allowed_origins\*(R": .PP .Vb 1 \& 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 => {}}); \& } .Ve .SS "Using the specification" .IX Subsection "Using the specification" It's possible to enable preflight and simple \s-1CORS\s0 support directly in the specification. Here is one example: .PP .Vb 10 \& "/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" } } \& } \& } \& } .Ve .PP The special part can be found in the \*(L"\s-1OPTIONS\*(R"\s0 request It has the \f(CW\*(C`x\-mojo\-to\*(C'\fR key set to \*(L"#openapi_plugin_cors_exchange\*(R". 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 \*(L"200 \s-1OK\*(R"\s0 and the default headers as listed under \&\*(L"openapi.cors_exchange\*(R" if the input is valid. The only extra part that needs to be done in the \f(CW\*(C`add_post()\*(C'\fR action is this: .PP .Vb 2 \& 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 => {}); \& } .Ve .SS "Custom exchange" .IX Subsection "Custom exchange" If you need full control, you must pass a callback to \&\*(L"openapi.cors_exchange\*(R": .PP .Vb 1 \& 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(\*(AqAccess\-Control\-Request\-Headers\*(Aq); \& return "Bad stuff." if $headers and $headers =~ /X\-No\-Can\-Do/; \& \& # Check the Access\-Control\-Request\-Method header \& my $method = $req_h\->header(\*(AqAccess\-Control\-Request\-Methods\*(Aq); \& 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; \& } .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" Mojolicious::Plugin::OpenAPI::Cors is a plugin for accepting Preflighted or Simple Cross-Origin Resource Sharing requests. See for more details. .PP This plugin is loaded by default by Mojolicious::Plugin::OpenAPI. .PP Note that this plugin currently \s-1EXPERIMENTAL\s0! Please comment on if you have any feedback or create a new issue. .SH "STASH VARIABLES" .IX Header "STASH VARIABLES" The following \*(L"stash variables\*(R" can be set in \*(L"defaults\*(R" in Mojolicious, \&\*(L"to\*(R" in Mojolicious::Routes::Route or \*(L"stash\*(R" in Mojolicious::Controller. .SS "openapi_cors_allowed_origins" .IX Subsection "openapi_cors_allowed_origins" This variable should hold an array-ref of regexes that will be matched against the \*(L"Origin\*(R" header in case the default \&\*(L"openapi_cors_default_exchange_callback\*(R" is used. Examples: .PP .Vb 2 \& $app\->defaults(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]); \& $c\->stash(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]); .Ve .SS "openapi_cors_default_exchange_callback" .IX Subsection "openapi_cors_default_exchange_callback" This value holds a default callback that will be used by \&\*(L"openapi.cors_exchange\*(R", unless you pass on a \f(CW$callback\fR. The default provided by this plugin will simply validate the \f(CW\*(C`Origin\*(C'\fR header against \&\*(L"openapi_cors_allowed_origins\*(R". .PP Here is an example to allow every \*(L"Origin\*(R" .PP .Vb 5 \& $app\->defaults(openapi_cors_default_exchange_callback => sub { \& my $c = shift; \& $c\->res\->headers\->header("Access\-Control\-Allow\-Origin" => "*"); \& return undef; \& }); .Ve .SS "openapi_cors_default_max_age" .IX Subsection "openapi_cors_default_max_age" Holds the default value for the \*(L"Access-Control-Max-Age\*(R" response header set by \*(L"openapi.cors_preflighted\*(R". Examples: .PP .Vb 2 \& $app\->defaults(openapi_cors_default_max_age => 86400); \& $c\->stash(openapi_cors_default_max_age => 86400); .Ve .PP Default value is 1800. .SS "openapi_cors_type" .IX Subsection "openapi_cors_type" This stash variable is available inside the callback passed on to \&\*(L"openapi.cors_exchange\*(R". It will be either \*(L"preflighted\*(R", \*(L"real\*(R" or \*(L"simple\*(R". \&\*(L"real\*(R" is the type that comes after \*(L"preflighted\*(R" when the actual request is sent to the server, but with \*(L"Origin\*(R" header set. .SH "HELPERS" .IX Header "HELPERS" .SS "openapi.cors_exchange" .IX Subsection "openapi.cors_exchange" .Vb 5 \& $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; .Ve .PP Used to validate either a simple \s-1CORS\s0 request, preflighted \s-1CORS\s0 request or a real request. It will be called as soon as the \*(L"Origin\*(R" request header is seen. .PP The \f(CW$callback\fR will be called with the current Mojolicious::Controller object and must return an error or \f(CW\*(C`undef()\*(C'\fR on success: .PP .Vb 1 \& my $error = $callback\->($c); .Ve .PP The \f(CW$error\fR must be in one of the following formats: .IP "\(bu" 2 \&\f(CW\*(C`undef()\*(C'\fR .Sp Returning \f(CW\*(C`undef()\*(C'\fR means that the \s-1CORS\s0 request is valid. .IP "\(bu" 2 A string starting with \*(L"/\*(R" .Sp Shortcut for generating a 400 Bad Request response with a header name. Example: .Sp .Vb 1 \& return "/Access\-Control\-Request\-Headers"; .Ve .IP "\(bu" 2 Any other string .Sp Used to generate a 400 Bad Request response with a completely custom message. .IP "\(bu" 2 An array-ref .Sp Used to generate a completely custom 400 Bad Request response. Example: .Sp .Vb 3 \& return [{message => "Some error!", path => "/Whatever"}]; \& return [{message => "Some error!"}]; \& return [JSON::Validator::Error\->new]; .Ve .PP On success, the following headers will be set, unless already set by \&\f(CW$callback\fR: .IP "\(bu" 2 Access-Control-Allow-Headers .Sp Set to the header of the incoming \*(L"Access-Control-Request-Headers\*(R" header. .IP "\(bu" 2 Access-Control-Allow-Methods .Sp Set to the list of \s-1HTTP\s0 methods defined in the OpenAPI spec for this path. .IP "\(bu" 2 Access-Control-Allow-Origin .Sp Set to the \*(L"Origin\*(R" header in the request. .IP "\(bu" 2 Access-Control-Max-Age .Sp Set to \*(L"openapi_cors_default_max_age\*(R". .SH "METHODS" .IX Header "METHODS" .SS "register" .IX Subsection "register" Called by Mojolicious::Plugin::OpenAPI. .SH "SEE ALSO" .IX Header "SEE ALSO" Mojolicious::Plugin::OpenAPI.