1Mojolicious::Plugin::OpUesneArPIC:o:nCtorrisb(u3t)ed PerMlojDoolciucmieonutsa:t:iPolnugin::OpenAPI::Cors(3)
2
3
4
6 Mojolicious::Plugin::OpenAPI::Cors - OpenAPI plugin for Cross-Origin
7 Resource Sharing
8
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
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
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
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
245 register
246 Called by Mojolicious::Plugin::OpenAPI.
247
249 Mojolicious::Plugin::OpenAPI.
250
251
252
253perl v5.36.0 2023-01-M2o0jolicious::Plugin::OpenAPI::Cors(3)