Skip to content

Commit b0c3768

Browse files
committed
feat(binding-http): Make Access-Control-Allow-Origin header configurable
Add allowedOrigins option to HttpConfig interface to let users configure the Access-Control-Allow-Origin header value. Defaults to '*' for backward compatibility. Secured things still echo the request origin with credentials regardless of this setting. Fixes #941 Signed-off-by: jona42-ui <jonathanthembo123@gmail.com>
1 parent 20c336a commit b0c3768

File tree

11 files changed

+219
-17
lines changed

11 files changed

+219
-17
lines changed

packages/binding-http/src/http-server.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ export default class HttpServer implements ProtocolServer {
6565
private readonly baseUri?: string;
6666
private readonly urlRewrite?: Record<string, string>;
6767
private readonly devFriendlyUri: boolean;
68+
private readonly allowedOrigins: string;
6869
private readonly supportedSecuritySchemes: string[] = ["nosec"];
6970
private readonly validOAuthClients: RegExp = /.*/g;
7071
private readonly server: http.Server | https.Server;
@@ -85,6 +86,7 @@ export default class HttpServer implements ProtocolServer {
8586
this.urlRewrite = config.urlRewrite;
8687
this.middleware = config.middleware;
8788
this.devFriendlyUri = config.devFriendlyUri ?? true;
89+
this.allowedOrigins = config.allowedOrigins ?? "*";
8890

8991
const router = Router({
9092
ignoreTrailingSlash: true,
@@ -251,6 +253,10 @@ export default class HttpServer implements ProtocolServer {
251253
return this.things;
252254
}
253255

256+
public getAllowedOrigins(): string {
257+
return this.allowedOrigins;
258+
}
259+
254260
/** returns server port number and indicates that server is running when larger than -1 */
255261
public getPort(): number {
256262
const address = this.server?.address();

packages/binding-http/src/http.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ export interface HttpConfig {
4747
security?: SecurityScheme[];
4848
devFriendlyUri?: boolean;
4949
middleware?: MiddlewareRequestHandler;
50+
/**
51+
* Configures the Access-Control-Allow-Origin header.
52+
* Default is "*" (any origin allowed).
53+
*/
54+
allowedOrigins?: string;
5055
}
5156

5257
export interface OAuth2ServerConfig extends SecurityScheme {

packages/binding-http/src/routes/action.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,7 +67,7 @@ export default async function actionRoute(
6767
return;
6868
}
6969
// TODO: refactor this part to move into a common place
70-
setCorsForThing(req, res, thing);
70+
setCorsForThing(req, res, thing, this.getAllowedOrigins());
7171
let corsPreflightWithCredentials = false;
7272
const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme;
7373

@@ -110,6 +110,6 @@ export default async function actionRoute(
110110
} else {
111111
// may have been OPTIONS that failed the credentials check
112112
// as a result, we pass corsPreflightWithCredentials
113-
respondUnallowedMethod(req, res, "POST", corsPreflightWithCredentials);
113+
respondUnallowedMethod(req, res, "POST", corsPreflightWithCredentials, this.getAllowedOrigins());
114114
}
115115
}

packages/binding-http/src/routes/common.ts

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@ export function respondUnallowedMethod(
2121
req: IncomingMessage,
2222
res: ServerResponse,
2323
allowed: string,
24-
corsPreflightWithCredentials = false
24+
corsPreflightWithCredentials = false,
25+
allowedOrigins = "*"
2526
): void {
2627
// Always allow OPTIONS to handle CORS pre-flight requests
2728
if (!allowed.includes("OPTIONS")) {
@@ -40,7 +41,7 @@ export function respondUnallowedMethod(
4041
res.setHeader("Access-Control-Allow-Origin", origin);
4142
res.setHeader("Access-Control-Allow-Credentials", "true");
4243
} else {
43-
res.setHeader("Access-Control-Allow-Origin", "*");
44+
res.setHeader("Access-Control-Allow-Origin", allowedOrigins);
4445
}
4546
res.setHeader("Access-Control-Allow-Methods", allowed);
4647
res.setHeader("Access-Control-Allow-Headers", "content-type, authorization, *");
@@ -91,7 +92,12 @@ export function securitySchemeToHttpHeader(scheme: string): string {
9192
return first.toUpperCase() + rest.join("").toLowerCase();
9293
}
9394

94-
export function setCorsForThing(req: IncomingMessage, res: ServerResponse, thing: ExposedThing): void {
95+
export function setCorsForThing(
96+
req: IncomingMessage,
97+
res: ServerResponse,
98+
thing: ExposedThing,
99+
allowedOrigins = "*"
100+
): void {
95101
const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme;
96102
// Set CORS headers
97103

@@ -100,6 +106,6 @@ export function setCorsForThing(req: IncomingMessage, res: ServerResponse, thing
100106
res.setHeader("Access-Control-Allow-Origin", origin);
101107
res.setHeader("Access-Control-Allow-Credentials", "true");
102108
} else {
103-
res.setHeader("Access-Control-Allow-Origin", "*");
109+
res.setHeader("Access-Control-Allow-Origin", allowedOrigins);
104110
}
105111
}

packages/binding-http/src/routes/event.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ export default async function eventRoute(
4747
return;
4848
}
4949
// TODO: refactor this part to move into a common place
50-
setCorsForThing(req, res, thing);
50+
setCorsForThing(req, res, thing, this.getAllowedOrigins());
5151
let corsPreflightWithCredentials = false;
5252
const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme;
5353

@@ -109,7 +109,7 @@ export default async function eventRoute(
109109
} else {
110110
// may have been OPTIONS that failed the credentials check
111111
// as a result, we pass corsPreflightWithCredentials
112-
respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials);
112+
respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials, this.getAllowedOrigins());
113113
}
114114
// resource found and response sent
115115
}

packages/binding-http/src/routes/properties.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default async function propertiesRoute(
3939
}
4040

4141
// TODO: refactor this part to move into a common place
42-
setCorsForThing(req, res, thing);
42+
setCorsForThing(req, res, thing, this.getAllowedOrigins());
4343
let corsPreflightWithCredentials = false;
4444
const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme;
4545

@@ -86,6 +86,6 @@ export default async function propertiesRoute(
8686
} else {
8787
// may have been OPTIONS that failed the credentials check
8888
// as a result, we pass corsPreflightWithCredentials
89-
respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials);
89+
respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials, this.getAllowedOrigins());
9090
}
9191
}

packages/binding-http/src/routes/property-observe.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,7 @@ export default async function propertyObserveRoute(
5757
}
5858

5959
// TODO: refactor this part to move into a common place
60-
setCorsForThing(req, res, thing);
60+
setCorsForThing(req, res, thing, this.getAllowedOrigins());
6161
let corsPreflightWithCredentials = false;
6262
const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme;
6363

@@ -113,6 +113,6 @@ export default async function propertyObserveRoute(
113113
res.writeHead(202);
114114
res.end();
115115
} else {
116-
respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials);
116+
respondUnallowedMethod(req, res, "GET", corsPreflightWithCredentials, this.getAllowedOrigins());
117117
}
118118
}

packages/binding-http/src/routes/property.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export default async function propertyRoute(
7777
}
7878

7979
// TODO: refactor this part to move into a common place
80-
setCorsForThing(req, res, thing);
80+
setCorsForThing(req, res, thing, this.getAllowedOrigins());
8181
let corsPreflightWithCredentials = false;
8282
const securityScheme = thing.securityDefinitions[Helpers.toStringArray(thing.security)[0]].scheme;
8383

@@ -108,7 +108,7 @@ export default async function propertyRoute(
108108
} else if (req.method === "PUT") {
109109
const readOnly: boolean = property.readOnly ?? false;
110110
if (readOnly) {
111-
respondUnallowedMethod(req, res, "GET, PUT");
111+
respondUnallowedMethod(req, res, "GET, PUT", false, this.getAllowedOrigins());
112112
return;
113113
}
114114

@@ -128,6 +128,6 @@ export default async function propertyRoute(
128128
} else {
129129
// may have been OPTIONS that failed the credentials check
130130
// as a result, we pass corsPreflightWithCredentials
131-
respondUnallowedMethod(req, res, "GET, PUT", corsPreflightWithCredentials);
131+
respondUnallowedMethod(req, res, "GET, PUT", corsPreflightWithCredentials, this.getAllowedOrigins());
132132
} // Property exists?
133133
}

packages/binding-http/src/routes/thing-description.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -169,7 +169,7 @@ export default async function thingDescriptionRoute(
169169
const payload = await content.toBuffer();
170170

171171
negotiateLanguage(td, thing, req);
172-
res.setHeader("Access-Control-Allow-Origin", "*");
172+
res.setHeader("Access-Control-Allow-Origin", this.getAllowedOrigins());
173173
res.setHeader("Content-Type", contentType);
174174
res.writeHead(200);
175175
debug(`Sending HTTP response for TD with Content-Type ${contentType}.`);

packages/binding-http/src/routes/things.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ export default function thingsRoute(
2222
res: ServerResponse,
2323
_params: unknown
2424
): void {
25-
res.setHeader("Access-Control-Allow-Origin", "*");
25+
res.setHeader("Access-Control-Allow-Origin", this.getAllowedOrigins());
2626
res.setHeader("Content-Type", ContentSerdes.DEFAULT);
2727
res.writeHead(200);
2828
const list = [];

0 commit comments

Comments
 (0)