Skip to content

Commit

Permalink
fix: change the type of allowOrigin to string (#5741) (#5825)
Browse files Browse the repository at this point in the history
Fixes #5741

The `Access-Control-Allow-Origin` header must contain only one origin. If we provide an array with more than one origin, then the origins are joined with commas. This causes the CORS preflight to fail because the browser expects only one origin. The proper solution to this problem would be to select one of the origins from the list. However, as a temporary fix, I changed the type of `allowOrigin` from `Array<string>` to simply `string`. The expectation is that the developer should only provide one origin. This prevents the CORS preflight from failing. However, it also disallows developers from having more than one origin.

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution
License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md).*

## Checklist

- [ ] Title matches [Winglang's style guide](https://www.winglang.io/contributing/start-here/pull_requests#how-are-pull-request-titles-formatted)
- [ ] Description explains motivation and solution
- [ ] Tests added (always)
- [ ] Docs updated (only required for features)
- [ ] Added `pr/e2e-full` label if this feature requires end-to-end testing

*By submitting this pull request, I confirm that my contribution is made under the terms of the [Wing Cloud Contribution License](https://github.com/winglang/wing/blob/main/CONTRIBUTION_LICENSE.md)*.
  • Loading branch information
aaditmshah authored Mar 6, 2024
1 parent d5c06a8 commit 4ac907a
Show file tree
Hide file tree
Showing 11 changed files with 28 additions and 28 deletions.
16 changes: 8 additions & 8 deletions docs/docs/04-standard-library/cloud/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -521,7 +521,7 @@ let ApiCorsOptions = cloud.ApiCorsOptions{ ... };
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.allowCredentials">allowCredentials</a></code> | <code>bool</code> | Whether to allow credentials. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.allowHeaders">allowHeaders</a></code> | <code>MutArray&lt;str&gt;</code> | The list of allowed headers. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.allowMethods">allowMethods</a></code> | <code>MutArray&lt;<a href="#@winglang/sdk.cloud.HttpMethod">HttpMethod</a>&gt;</code> | The list of allowed methods. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.allowOrigin">allowOrigin</a></code> | <code>MutArray&lt;str&gt;</code> | The list of allowed allowOrigin. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.allowOrigin">allowOrigin</a></code> | <code>str</code> | The allowed origin. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.exposeHeaders">exposeHeaders</a></code> | <code>MutArray&lt;str&gt;</code> | The list of exposed headers. |
| <code><a href="#@winglang/sdk.cloud.ApiCorsOptions.property.maxAge">maxAge</a></code> | <code><a href="#@winglang/sdk.std.Duration">duration</a></code> | How long the browser should cache preflight request results. |
Expand Down Expand Up @@ -583,20 +583,20 @@ The list of allowed methods.
##### `allowOrigin`<sup>Optional</sup> <a name="allowOrigin" id="@winglang/sdk.cloud.ApiCorsOptions.property.allowOrigin"></a>
```wing
allowOrigin: MutArray<str>;
allowOrigin: str;
```
- *Type:* MutArray&lt;str&gt;
- *Default:* ["*"]
- *Type:* str
- *Default:* "*"
The list of allowed allowOrigin.
The allowed origin.
---
*Example*
```wing
["https://example.com"]
"https://example.com"
```
Expand Down Expand Up @@ -763,7 +763,7 @@ corsOptions: ApiCorsOptions;
```
- *Type:* <a href="#@winglang/sdk.cloud.ApiCorsOptions">ApiCorsOptions</a>
- *Default:* Default CORS options are applied when `cors` is set to `true` allowOrigin: ["*"], allowMethods: [ HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS, ], allowHeaders: ["Content-Type", "Authorization"], exposeHeaders: [], allowCredentials: false,
- *Default:* Default CORS options are applied when `cors` is set to `true` allowOrigin: "*", allowMethods: [ HttpMethod.GET, HttpMethod.POST, HttpMethod.PUT, HttpMethod.DELETE, HttpMethod.HEAD, HttpMethod.OPTIONS, ], allowHeaders: ["Content-Type", "Authorization"], exposeHeaders: [], allowCredentials: false,
Options for configuring the API's CORS behavior across all routes.
Expand All @@ -774,7 +774,7 @@ Options can also be overridden on a per-route basis. (not yet implemented)
*Example*
```wing
{ allowOrigin: ["https://example.com"] }
{ allowOrigin: "https://example.com" }
```
Expand Down
4 changes: 2 additions & 2 deletions examples/tests/valid/api_cors_custom.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ bring expect;
let api = new cloud.Api(
cors: true,
corsOptions: {
allowOrigin: ["winglang.io"],
allowOrigin: "winglang.io",
allowMethods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],
allowHeaders: ["Content-Type", "Authorization", "X-Custom-Header"],
allowCredentials: true,
Expand Down Expand Up @@ -70,4 +70,4 @@ test "OPTIONS /users responds with proper headers for requested" {
expect.equal(headers.tryGet("access-control-allow-methods"), "GET,POST,OPTIONS");
expect.equal(headers.tryGet("access-control-allow-headers"), "Content-Type,Authorization,X-Custom-Header");
expect.equal(headers.tryGet("access-control-allow-origin"), "winglang.io");
}
}
2 changes: 1 addition & 1 deletion examples/tests/valid/website_with_api.test.w
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ bring expect;
let api = new cloud.Api(
cors: true,
corsOptions: cloud.ApiCorsOptions {
allowOrigin: ["*"],
allowOrigin: "*",
allowMethods: [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS],
allowHeaders: ["Content-Type"],
allowCredentials: false,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 22
documentation:
kind: markdown
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `Array<str>?` — The list of allowed allowOrigin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `str?` — The allowed origin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
sortText: hh|ApiCorsOptions
- label: ApiDeleteOptions
kind: 22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 22
documentation:
kind: markdown
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `Array<str>?` — The list of allowed allowOrigin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `str?` — The allowed origin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
sortText: hh|ApiCorsOptions
- label: ApiDeleteOptions
kind: 22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 22
documentation:
kind: markdown
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `Array<str>?` — The list of allowed allowOrigin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `str?` — The allowed origin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
sortText: hh|ApiCorsOptions
- label: ApiDeleteOptions
kind: 22
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ source: libs/wingc/src/lsp/completions.rs
kind: 22
documentation:
kind: markdown
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `Array<str>?` — The list of allowed allowOrigin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
value: "```wing\nstruct ApiCorsOptions\n```\n---\nCors Options for `Api`.\n### Fields\n- `allowCredentials?` — `bool?` — Whether to allow credentials.\n- `allowHeaders?` — `Array<str>?` — The list of allowed headers.\n- `allowMethods?` — `Array<HttpMethod>?` — The list of allowed methods.\n- `allowOrigin?` — `str?` — The allowed origin.\n- `exposeHeaders?` — `Array<str>?` — The list of exposed headers.\n- `maxAge?` — `duration?` — How long the browser should cache preflight request results."
sortText: hh|ApiCorsOptions
- label: ApiDeleteOptions
kind: 22
Expand Down
20 changes: 10 additions & 10 deletions libs/wingsdk/src/cloud/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,11 +16,11 @@ export const API_FQN = fqnForType("cloud.Api");
*/
export interface ApiCorsOptions {
/**
* The list of allowed allowOrigin.
* @example ["https://example.com"]
* @default - ["*"]
* The allowed origin.
* @example "https://example.com"
* @default - "*"
*/
readonly allowOrigin?: Array<string>;
readonly allowOrigin?: string;

/**
* The list of allowed methods.
Expand Down Expand Up @@ -75,9 +75,9 @@ export interface ApiProps {
* Options for configuring the API's CORS behavior across all routes.
* Options can also be overridden on a per-route basis. (not yet implemented)
*
* @example { allowOrigin: ["https://example.com"] }
* @example { allowOrigin: "https://example.com" }
* @default - Default CORS options are applied when `cors` is set to `true`
* allowOrigin: ["*"],
* allowOrigin: "*",
* allowMethods: [
* HttpMethod.GET,
* HttpMethod.POST,
Expand Down Expand Up @@ -209,7 +209,7 @@ export class Api extends Resource {
};

private corsDefaultValues: ApiCorsOptions = {
allowOrigin: ["*"],
allowOrigin: "*",
allowMethods: [
HttpMethod.GET,
HttpMethod.POST,
Expand Down Expand Up @@ -502,7 +502,7 @@ export class Api extends Resource {
}

const {
allowOrigin = [],
allowOrigin = "*",
allowHeaders = [],
allowMethods = [],
exposeHeaders = [],
Expand All @@ -511,13 +511,13 @@ export class Api extends Resource {
} = corsOptions;

const defaultHeaders: CorsDefaultResponseHeaders = {
"Access-Control-Allow-Origin": allowOrigin.join(",") || "",
"Access-Control-Allow-Origin": allowOrigin || "*",
"Access-Control-Expose-Headers": exposeHeaders.join(",") || "",
"Access-Control-Allow-Credentials": allowCredentials ? "true" : "false",
};

const optionsHeaders: CorsOptionsResponseHeaders = {
"Access-Control-Allow-Origin": allowOrigin.join(",") || "",
"Access-Control-Allow-Origin": allowOrigin || "*",
"Access-Control-Allow-Headers": allowHeaders.join(",") || "",
"Access-Control-Allow-Methods": allowMethods.join(",") || "",
"Access-Control-Max-Age": maxAge.seconds.toString(),
Expand Down
2 changes: 1 addition & 1 deletion libs/wingsdk/test/target-sim/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -669,7 +669,7 @@ test("api with custom CORS settings", async () => {
const api = new cloud.Api(app, "my_api", {
cors: true,
corsOptions: {
allowOrigin: ["https://example.com"],
allowOrigin: "https://example.com",
allowCredentials: true,
exposeHeaders: ["x-wingnuts"],
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -484,7 +484,7 @@ class $Root extends $stdlib.std.Resource {
});
}
}
const api = this.node.root.new("@winglang/sdk.cloud.Api", cloud.Api, this, "cloud.Api", { cors: true, corsOptions: ({"allowOrigin": ["winglang.io"], "allowMethods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], "allowHeaders": ["Content-Type", "Authorization", "X-Custom-Header"], "allowCredentials": true, "exposeHeaders": ["Content-Type"]}) });
const api = this.node.root.new("@winglang/sdk.cloud.Api", cloud.Api, this, "cloud.Api", { cors: true, corsOptions: ({"allowOrigin": "winglang.io", "allowMethods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], "allowHeaders": ["Content-Type", "Authorization", "X-Custom-Header"], "allowCredentials": true, "exposeHeaders": ["Content-Type"]}) });
(api.get("/users", new $Closure1(this, "$Closure1")));
this.node.root.new("@winglang/sdk.std.Test", std.Test, this, "test:GET /users has cors headers", new $Closure2(this, "$Closure2"));
this.node.root.new("@winglang/sdk.std.Test", std.Test, this, "test:OPTIONS /users has cors headers", new $Closure3(this, "$Closure3"));
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -783,7 +783,7 @@ class $Root extends $stdlib.std.Resource {
});
}
}
const api = this.node.root.new("@winglang/sdk.cloud.Api", cloud.Api, this, "cloud.Api", { cors: true, corsOptions: ({"allowOrigin": ["*"], "allowMethods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], "allowHeaders": ["Content-Type"], "allowCredentials": false, "exposeHeaders": ["Content-Type"], "maxAge": (std.Duration.fromSeconds(600))}) });
const api = this.node.root.new("@winglang/sdk.cloud.Api", cloud.Api, this, "cloud.Api", { cors: true, corsOptions: ({"allowOrigin": "*", "allowMethods": [cloud.HttpMethod.GET, cloud.HttpMethod.POST, cloud.HttpMethod.OPTIONS], "allowHeaders": ["Content-Type"], "allowCredentials": false, "exposeHeaders": ["Content-Type"], "maxAge": (std.Duration.fromSeconds(600))}) });
const website = this.node.root.new("@winglang/sdk.cloud.Website", cloud.Website, this, "cloud.Website", { path: "./website_with_api" });
const usersTable = this.node.root.new("@winglang/sdk.ex.Table", ex.Table, this, "ex.Table", { name: "users-table", primaryKey: "id", columns: ({["id"]: ex.ColumnType.STRING, ["name"]: ex.ColumnType.STRING, ["age"]: ex.ColumnType.NUMBER}) });
const getHandler = new $Closure1(this, "$Closure1");
Expand Down

0 comments on commit 4ac907a

Please sign in to comment.