Skip to content

Commit 30d558d

Browse files
authored
fix popup auth broken due to new COOP header (#24)
1 parent f3a287f commit 30d558d

File tree

9 files changed

+28
-30
lines changed

9 files changed

+28
-30
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## 3.0.0 (2025-07-08)
11+
12+
- 💥 BREAKING CHANGE: Fix authentication broken when using the `popup` mode due to [recent security changes](https://github.com/openstreetmap/openstreetmap-website/commit/2ff4d6)
13+
1014
## 2.7.0 (2025-06-02)
1115

1216
- Optionally support type-safe `Tags`. `Tags` is currently defined as `Record<string, string>`. If you want additional type-safety, you can specify the keys are the allowed. See the docs for more info.

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,12 @@
88

99
🗺️🌏 Javascript/Typescript wrapper around the OpenStreetMap API.
1010

11+
> [!IMPORTANT]
12+
> Due to [security changes on 8 July 2025](https://github.com/openstreetmap/openstreetmap-website/commit/2ff4d6), authentication using the `popup` mode will not work until you:
13+
>
14+
> 1. update this library to v3.0.0
15+
> 2. AND update the code snippet in your `land.html` file to the latest version (see [the popup documentation below](#1-popup))
16+
1117
Benefits:
1218

1319
- Lightweight (24 kB gzipped)
@@ -121,8 +127,8 @@ If using a popup, you should create a separate landing page, such as `land.html`
121127
122128
```html
123129
<script>
124-
if (window.opener) {
125-
window.opener.authComplete(location.href);
130+
if (new URLSearchParams(location.search).has("code")) {
131+
new BroadcastChannel("osm-api-auth-complete").postMessage(location.href);
126132
window.close();
127133
}
128134
</script>

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "osm-api",
3-
"version": "2.7.0",
3+
"version": "3.0.0",
44
"contributors": [
55
"Kyle Hensel (https://github.com/k-yle)"
66
],

src/__tests__/index.html

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@
33
Then open http://127.0.0.1:4167/src/__tests__
44
-->
55
<script>
6-
if (window.opener) {
7-
window.opener.authComplete(location.href);
6+
if (new URLSearchParams(location.search).has("code")) {
7+
new BroadcastChannel("osm-api-auth-complete").postMessage(location.href);
88
window.close();
99
}
1010
</script>

src/auth/createPopup.ts

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,13 @@
1+
const CHANNEL_ID = "osm-api-auth-complete";
2+
13
/**
24
* resolves once the login flow in the popup is sucessful.
35
* rejects if the popup is closed by the user or if there's an error.
46
* @internal
57
*/
68
export function createPopup(loginUrl: string): Promise<string> {
79
let resolved = false;
8-
return new Promise((resolve, reject) => {
10+
return new Promise((resolve) => {
911
const [width, height] = [600, 550];
1012
const settings = Object.entries({
1113
width,
@@ -20,17 +22,14 @@ export function createPopup(loginUrl: string): Promise<string> {
2022
if (!popup) throw new Error("Popup was blocked");
2123
popup.location = loginUrl;
2224

23-
window.authComplete = (fullUrl: string) => {
24-
resolve(fullUrl);
25+
const bc = new BroadcastChannel(CHANNEL_ID);
26+
const onMessage = (event: MessageEvent) => {
27+
if (resolved) return; // already got a response
28+
resolve(event.data);
2529
resolved = true;
30+
bc.removeEventListener("message", onMessage);
31+
bc.close();
2632
};
27-
28-
// check every 0.5seconds if the popup has been closed by the user.
29-
const intervalId = setInterval(() => {
30-
if (popup.closed) {
31-
if (!resolved) reject(new Error("Cancelled"));
32-
clearInterval(intervalId);
33-
}
34-
}, 500);
33+
bc.addEventListener("message", onMessage);
3534
});
3635
}

src/auth/exchangeCode.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,6 @@ export async function exchangeCode(
5656
localStorage.setItem("__osmAuth", JSON.stringify(loginData));
5757

5858
// At this point, we can consider the login sucessfull
59-
delete window.authComplete;
6059

6160
return loginData;
6261
}

src/auth/oauth2.ts

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -73,10 +73,7 @@ export const authReady: Promise<void> = (async () => {
7373
const loginState = localStorage.getItem("__osmAuthTemp");
7474

7575
if (new URL(fullUrl).searchParams.get("code")) {
76-
if (window.opener?.authComplete) {
77-
window.opener.authComplete(fullUrl);
78-
window.close();
79-
} else if (loginState) {
76+
if (loginState) {
8077
try {
8178
const transaction: Transaction = JSON.parse(loginState);
8279
await exchangeCode(fullUrl, transaction);

src/auth/types.ts

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -40,10 +40,3 @@ export type Transaction = {
4040
pkceVerifier: string;
4141
options: LoginOptions;
4242
};
43-
44-
/** @internal */
45-
declare global {
46-
interface Window {
47-
authComplete?(fullUrl: string): void;
48-
}
49-
}

0 commit comments

Comments
 (0)