Skip to content

Commit e98e574

Browse files
authored
Merge pull request #2 from framer/feature/cors-configuration
CORS configuration for plugins
2 parents c151a92 + a056827 commit e98e574

File tree

5 files changed

+69
-43
lines changed

5 files changed

+69
-43
lines changed

.dev.vars.example

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
CLIENT_ID=XXXXX
22
CLIENT_SECRET=XXXX
33

4-
PLUGIN_URI=https://localhost:5173
4+
# Set the Plugin ID when your Plugin is submitted to the Marketplace.
5+
PLUGIN_ID = ""
56
REDIRECT_URI=https://localhost:8787/redirect
67

78
AUTHORIZE_ENDPOINT=https://accounts.google.com/o/oauth2/v2/auth

README.md

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -10,15 +10,15 @@ See our [Implementing OAuth guide](https://developers.framer.wiki/plugins/docs/o
1010

1111
The following environment variables need to be added via the CloudFlare console or CLI.
1212

13-
| Name | Details |
14-
| ------------------ | ----------------------------------------------------------------------------------------------------------------------- |
15-
| CLIENT_ID | App ID created in the providers developer console |
16-
| CLIENT_SECRET | App secret key created in the providers developer console. **Do not expose** in source code or send back to the client! |
17-
| PLUGIN_URI | Root path of where your plugin is hosted |
18-
| REDIRECT_URI | Callback path that provider will redirect to after logging in |
19-
| AUTHORIZE_ENDPOINT | Provider endpoint path for showing the log in screen |
20-
| TOKEN_ENDPOINT | Provider endpoint path for fetching and refreshing access tokens |
21-
| SCOPE | Provider permissions separated by a space |
13+
| Name | Details |
14+
| ------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
15+
| CLIENT_ID | App ID created in the providers developer console |
16+
| CLIENT_SECRET | App secret key created in the providers developer console. **Do not expose** in source code or send back to the client! |
17+
| PLUGIN_ID | The Plugin ID environment variable should be set when the Plugin is submitted to the marketplace to ensure correct CORS headers. You can find the Plugin ID in the URL of your marketplace submission. |
18+
| REDIRECT_URI | Callback path that provider will redirect to after logging in |
19+
| AUTHORIZE_ENDPOINT | Provider endpoint path for showing the log in screen |
20+
| TOKEN_ENDPOINT | Provider endpoint path for fetching and refreshing access tokens |
21+
| SCOPE | Provider permissions separated by a space |
2222

2323
To test locally, create a `.dev.vars` file with your own `CLIENT_ID` and `CLIENT_SECRET`.
2424

src/index.ts

Lines changed: 45 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ async function handleRequest(request: Request, env: Env) {
4545
return new Response(response, {
4646
headers: {
4747
"Content-Type": "application/json",
48-
"Access-Control-Allow-Origin": env.PLUGIN_URI,
4948
},
5049
});
5150
}
@@ -125,9 +124,6 @@ async function handleRequest(request: Request, env: Env) {
125124
if (!readKey) {
126125
return new Response("Missing read key URL param", {
127126
status: 400,
128-
headers: {
129-
"Access-Control-Allow-Origin": env.PLUGIN_URI,
130-
},
131127
});
132128
}
133129

@@ -136,7 +132,6 @@ async function handleRequest(request: Request, env: Env) {
136132
if (!tokens) {
137133
return new Response(null, {
138134
status: 404,
139-
headers: { "Access-Control-Allow-Origin": env.PLUGIN_URI },
140135
});
141136
}
142137

@@ -147,7 +142,6 @@ async function handleRequest(request: Request, env: Env) {
147142
return new Response(tokens, {
148143
headers: {
149144
"Content-Type": "application/json",
150-
"Access-Control-Allow-Origin": env.PLUGIN_URI,
151145
},
152146
});
153147
}
@@ -158,9 +152,6 @@ async function handleRequest(request: Request, env: Env) {
158152
if (!refreshToken) {
159153
return new Response("Missing refresh token URL param", {
160154
status: 400,
161-
headers: {
162-
"Access-Control-Allow-Origin": env.PLUGIN_URI,
163-
},
164155
});
165156
}
166157

@@ -178,18 +169,12 @@ async function handleRequest(request: Request, env: Env) {
178169
});
179170

180171
if (refreshResponse.status !== 200) {
181-
return new Response(refreshResponse.statusText, {
182-
status: refreshResponse.status,
183-
});
172+
return new Response(refreshResponse.statusText);
184173
}
185174

186175
const tokens = await refreshResponse.json();
187176

188-
return new Response(JSON.stringify(tokens), {
189-
headers: {
190-
"Access-Control-Allow-Origin": env.PLUGIN_URI,
191-
},
192-
});
177+
return new Response(JSON.stringify(tokens));
193178
}
194179

195180
if (request.method === "GET" && requestUrl.pathname === "/") {
@@ -198,23 +183,59 @@ async function handleRequest(request: Request, env: Env) {
198183

199184
return new Response("Page not found", {
200185
status: 404,
201-
headers: {
202-
"Access-Control-Allow-Origin": env.PLUGIN_URI,
203-
},
186+
});
187+
}
188+
189+
function getCORSAllowOriginHeader(request: Request, env: Env) {
190+
const origin = request.headers.get("Origin");
191+
192+
const defaultCORSHeader = `https://${env.PLUGIN_ID}.${env.PLUGIN_PARENT_DOMAIN}`;
193+
194+
if (!origin) return defaultCORSHeader;
195+
196+
const originURL = new URL(origin);
197+
if (originURL.hostname === "localhost") {
198+
return originURL.origin;
199+
}
200+
201+
// Support for versioned plugins
202+
const [hostLabel, ...parentDomainLabels] = originURL.hostname.split(".");
203+
if (
204+
parentDomainLabels.join(".") === env.PLUGIN_PARENT_DOMAIN &&
205+
hostLabel.startsWith(env.PLUGIN_ID)
206+
) {
207+
return originURL.origin;
208+
}
209+
210+
// Otherwise set the CORS header to non versioned plugin URI always
211+
return defaultCORSHeader;
212+
}
213+
214+
function addCorsHeaders(request: Request, response: Response, env: Env) {
215+
const headers = new Headers(response.headers);
216+
217+
headers.set(
218+
"Access-Control-Allow-Origin",
219+
getCORSAllowOriginHeader(request, env)
220+
);
221+
222+
return new Response(response.body, {
223+
headers: headers,
224+
status: response.status,
225+
statusText: response.statusText,
204226
});
205227
}
206228

207229
export default {
208230
async fetch(request: Request, env: Env): Promise<Response> {
209-
return handleRequest(request, env).catch((error) => {
231+
const response = await handleRequest(request, env).catch((error) => {
210232
const message = error instanceof Error ? error.message : "Unknown";
211233

212234
return new Response(`😔 Internal error: ${message}`, {
213235
status: 500,
214-
headers: {
215-
"Access-Control-Allow-Origin": env.PLUGIN_URI,
216-
},
217236
});
218237
});
238+
239+
return addCorsHeaders(request, response, env);
219240
},
220241
};

worker-configuration.d.ts

Lines changed: 10 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,14 @@
1-
// Generated by Wrangler on Wed May 29 2024 11:22:07 GMT+0100 (British Summer Time)
1+
// Generated by Wrangler on Thu Oct 10 2024 14:20:35 GMT+0200 (Central European Summer Time)
22
// by running `wrangler types`
33

44
interface Env {
5-
keyValueStore: KVNamespace;
6-
CLIENT_ID: string;
7-
CLIENT_SECRET: string;
8-
PLUGIN_URI: string;
9-
REDIRECT_URI: string;
10-
AUTHORIZE_ENDPOINT: string;
11-
TOKEN_ENDPOINT: string;
12-
SCOPE: string;
5+
keyValueStore: KVNamespace
6+
PLUGIN_PARENT_DOMAIN: "plugins.framercdn.com"
7+
CLIENT_ID: string
8+
CLIENT_SECRET: string
9+
PLUGIN_ID: string
10+
REDIRECT_URI: string
11+
AUTHORIZE_ENDPOINT: string
12+
TOKEN_ENDPOINT: string
13+
SCOPE: string
1314
}

wrangler.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,3 +6,6 @@ compatibility_flags = ["nodejs_compat"]
66
kv_namespaces = [
77
{ binding = "keyValueStore", id = "5f1cbf9a64964ae5be8464cc2882e58c" },
88
]
9+
10+
[vars]
11+
PLUGIN_PARENT_DOMAIN = "plugins.framercdn.com"

0 commit comments

Comments
 (0)