1Mojolicious::Plugin::OpUesneArPIC:o:nCtorrisb(u3t)ed PerMlojDoolciucmieonutsa:t:iPolnugin::OpenAPI::Cors(3)
2
3
4

NAME

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

SYNOPSIS

10   Application
11       Set "add_preflighted_routes" to 1, if you want "Preflighted" CORS
12       requests to be sent to your already existing actions.
13
14         $app->plugin(OpenAPI => {add_preflighted_routes => 1, %openapi_parameters});
15
16       See "register" in Mojolicious::Plugin::OpenAPI for what
17       %openapi_parameters might contain.
18
19   Simple exchange
20       The following example will automatically set default CORS response
21       headers after validating the request against
22       "openapi_cors_allowed_origins":
23
24         package MyApp::Controller::User;
25
26         sub get_user {
27           my $c = shift->openapi->cors_exchange->openapi->valid_input or return;
28
29           # Will only run this part if both the cors_exchange and valid_input was successful.
30           $c->render(openapi => {user => {}});
31         }
32
33   Using the specification
34       It's possible to enable preflight and simple CORS support directly in
35       the specification. Here is one example:
36
37         "/user/{id}/posts": {
38           "parameters": [
39             { "in": "header", "name": "Origin", "type": "string", "pattern": "https?://example.com" }
40           ],
41           "options": {
42             "x-mojo-to": "#openapi_plugin_cors_exchange",
43             "responses": {
44               "200": { "description": "Cors exchange", "schema": { "type": "string" } }
45             }
46           },
47           "put": {
48             "x-mojo-to": "user#add_post",
49             "responses": {
50               "200": { "description": "Add a new post.", "schema": { "type": "object" } }
51             }
52           }
53         }
54
55       The special part can be found in the "OPTIONS" request It has the
56       "x-mojo-to" key set to "#openapi_plugin_cors_exchange". This will
57       enable Mojolicious::Plugin::OpenAPI::Cors to take over the route and
58       add a custom callback to validate the input headers using regular
59       OpenAPI rules and respond with a "200 OK" and the default headers as
60       listed under "openapi.cors_exchange" if the input is valid. The only
61       extra part that needs to be done in the add_post() action is this:
62
63         sub add_post {
64           my $c = shift->openapi->valid_input or return;
65
66           # Need to respond with a "Access-Control-Allow-Origin" header if
67           # the input "Origin" header was validated
68           $c->res->headers->access_control_allow_origin($c->req->headers->origin)
69             if $c->req->headers->origin;
70
71           # Do the rest of your custom logic
72           $c->respond(openapi => {});
73         }
74
75   Custom exchange
76       If you need full control, you must pass a callback to
77       "openapi.cors_exchange":
78
79         package MyApp::Controller::User;
80
81         sub get_user {
82           # Validate incoming CORS request with _validate_cors()
83           my $c = shift->openapi->cors_exchange("_validate_cors")->openapi->valid_input or return;
84
85           # Will only run this part if both the cors_exchange and valid_input was
86           # successful.
87           $c->render(openapi => {user => {}});
88         }
89
90         # This method must return undef on success. Any true value will be used as an error.
91         sub _validate_cors {
92           my $c     = shift;
93           my $req_h = $c->req->headers;
94           my $res_h = $c->res->headers;
95
96           # The following "Origin" header check is the same for both simple and
97           # preflighted.
98           return "/Origin" unless $req_h->origin =~ m!^https?://whatever.example.com!;
99
100           # The following checks are only valid if preflighted...
101
102           # Check the Access-Control-Request-Headers header
103           my $headers = $req_h->header('Access-Control-Request-Headers');
104           return "Bad stuff." if $headers and $headers =~ /X-No-Can-Do/;
105
106           # Check the Access-Control-Request-Method header
107           my $method = $req_h->header('Access-Control-Request-Methods');
108           return "Not cool." if $method and $method eq "DELETE";
109
110           # Set the following header for both simple and preflighted on success
111           # or just let the auto-renderer handle it.
112           $c->res->headers->access_control_allow_origin($req_h->origin);
113
114           # Set Preflighted response headers, instead of using the default
115           if ($c->stash("openapi_cors_type") eq "preflighted") {
116             $c->res->headers->header("Access-Control-Allow-Headers" => "X-Whatever, X-Something");
117             $c->res->headers->header("Access-Control-Allow-Methods" => "POST, GET, OPTIONS");
118             $c->res->headers->header("Access-Control-Max-Age" => 86400);
119           }
120
121           # Return undef on success.
122           return undef;
123         }
124

DESCRIPTION

126       Mojolicious::Plugin::OpenAPI::Cors is a plugin for accepting
127       Preflighted or Simple Cross-Origin Resource Sharing requests. See
128       <https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS> for more
129       details.
130
131       This plugin is loaded by default by Mojolicious::Plugin::OpenAPI.
132
133       Note that this plugin currently EXPERIMENTAL! Please comment on
134       <https://github.com/jhthorsen/mojolicious-plugin-openapi/pull/102> if
135       you have any feedback or create a new issue.
136

STASH VARIABLES

138       The following "stash variables" can be set in "defaults" in
139       Mojolicious, "to" in Mojolicious::Routes::Route or "stash" in
140       Mojolicious::Controller.
141
142   openapi_cors_allowed_origins
143       This variable should hold an array-ref of regexes that will be matched
144       against the "Origin" header in case the default
145       "openapi_cors_default_exchange_callback" is used. Examples:
146
147         $app->defaults(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]);
148         $c->stash(openapi_cors_allowed_origins => [qr{^https?://whatever.example.com}]);
149
150   openapi_cors_default_exchange_callback
151       This value holds a default callback that will be used by
152       "openapi.cors_exchange", unless you pass on a $callback. The default
153       provided by this plugin will simply validate the "Origin" header
154       against "openapi_cors_allowed_origins".
155
156       Here is an example to allow every "Origin"
157
158         $app->defaults(openapi_cors_default_exchange_callback => sub {
159           my $c = shift;
160           $c->res->headers->header("Access-Control-Allow-Origin" => "*");
161           return undef;
162         });
163
164   openapi_cors_default_max_age
165       Holds the default value for the "Access-Control-Max-Age" response
166       header set by "openapi.cors_preflighted". Examples:
167
168         $app->defaults(openapi_cors_default_max_age => 86400);
169         $c->stash(openapi_cors_default_max_age => 86400);
170
171       Default value is 1800.
172
173   openapi_cors_type
174       This stash variable is available inside the callback passed on to
175       "openapi.cors_exchange". It will be either "preflighted", "real" or
176       "simple".  "real" is the type that comes after "preflighted" when the
177       actual request is sent to the server, but with "Origin" header set.
178

HELPERS

180   openapi.cors_exchange
181         $c = $c->openapi->cors_exchange($callback);
182         $c = $c->openapi->cors_exchange("MyApp::cors_validator");
183         $c = $c->openapi->cors_exchange("_some_controller_method");
184         $c = $c->openapi->cors_exchange(sub { ... });
185         $c = $c->openapi->cors_exchange;
186
187       Used to validate either a simple CORS request, preflighted CORS request
188       or a real request. It will be called as soon as the "Origin" request
189       header is seen.
190
191       The $callback will be called with the current Mojolicious::Controller
192       object and must return an error or undef() on success:
193
194         my $error = $callback->($c);
195
196       The $error must be in one of the following formats:
197
198       • undef()
199
200         Returning undef() means that the CORS request is valid.
201
202       • A string starting with "/"
203
204         Shortcut for generating a 400 Bad Request response with a header
205         name. Example:
206
207           return "/Access-Control-Request-Headers";
208
209       • Any other string
210
211         Used to generate a 400 Bad Request response with a completely custom
212         message.
213
214       • An array-ref
215
216         Used to generate a completely custom 400 Bad Request response.
217         Example:
218
219           return [{message => "Some error!", path => "/Whatever"}];
220           return [{message => "Some error!"}];
221           return [JSON::Validator::Error->new];
222
223       On success, the following headers will be set, unless already set by
224       $callback:
225
226       • Access-Control-Allow-Headers
227
228         Set to the header of the incoming "Access-Control-Request-Headers"
229         header.
230
231       • Access-Control-Allow-Methods
232
233         Set to the list of HTTP methods defined in the OpenAPI spec for this
234         path.
235
236       • Access-Control-Allow-Origin
237
238         Set to the "Origin" header in the request.
239
240       • Access-Control-Max-Age
241
242         Set to "openapi_cors_default_max_age".
243

METHODS

245   register
246       Called by Mojolicious::Plugin::OpenAPI.
247

SEE ALSO

249       Mojolicious::Plugin::OpenAPI.
250
251
252
253perl v5.36.0                      2023-01-M2o0jolicious::Plugin::OpenAPI::Cors(3)
Impressum