Chore: update to OAuth 2.0, fix #1548

This commit is contained in:
Pieter Vander Vennet 2023-09-01 21:36:39 +02:00
parent 8a239503c5
commit 51f08c19a1
5 changed files with 73 additions and 104 deletions

View file

@ -2,9 +2,6 @@
<html> <html>
<head><title>MapComplete Auth</title></head> <head><title>MapComplete Auth</title></head>
<body> <body>
<script> <script type="module" src="./src/land.ts"></script>
opener.authComplete(window.location.href);
window.close();
</script>
</body> </body>
</html> </html>

71
package-lock.json generated
View file

@ -1,12 +1,12 @@
{ {
"name": "mapcomplete", "name": "mapcomplete",
"version": "0.31.2", "version": "0.31.4",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "mapcomplete", "name": "mapcomplete",
"version": "0.31.2", "version": "0.31.4",
"license": "GPL-3.0-or-later", "license": "GPL-3.0-or-later",
"dependencies": { "dependencies": {
"@rgossiaux/svelte-headlessui": "^1.0.2", "@rgossiaux/svelte-headlessui": "^1.0.2",
@ -40,7 +40,7 @@
"mangrove-reviews-typescript": "^1.1.0", "mangrove-reviews-typescript": "^1.1.0",
"maplibre-gl": "^3.2.0", "maplibre-gl": "^3.2.0",
"opening_hours": "^3.6.0", "opening_hours": "^3.6.0",
"osm-auth": "^1.0.2", "osm-auth": "^2.2.0",
"osmtogeojson": "^3.0.0-beta.5", "osmtogeojson": "^3.0.0-beta.5",
"papaparse": "^5.3.1", "papaparse": "^5.3.1",
"pic4carto": "^2.1.15", "pic4carto": "^2.1.15",
@ -7300,17 +7300,6 @@
"node": ">=4" "node": ">=4"
} }
}, },
"node_modules/jshashes": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/jshashes/-/jshashes-1.0.8.tgz",
"integrity": "sha512-btmQZ/w1rj8Lb6nEwvhjM7nBYoj54yaEFo2PWh3RkxZ8qNwuvOxvQYN/JxVuwoMmdIluL+XwYVJ+pEEZoSYybQ==",
"bin": {
"hashes": "bin/hashes"
},
"engines": {
"node": "*"
}
},
"node_modules/json-schema": { "node_modules/json-schema": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
@ -8189,18 +8178,6 @@
"node": ">= 0.4" "node": ">= 0.4"
} }
}, },
"node_modules/ohauth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ohauth/-/ohauth-1.0.1.tgz",
"integrity": "sha512-R9ZUN3+FVCwzeOOHCJpzA9jw/byRxp5O9X06mTL6Sp/LIQn/rLrMv6cwYctX+hoIKzRUsalGJXZ1kG5wBmSskQ==",
"deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.",
"dependencies": {
"jshashes": "~1.0.8"
},
"engines": {
"node": ">=10"
}
},
"node_modules/once": { "node_modules/once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -8372,16 +8349,14 @@
} }
}, },
"node_modules/osm-auth": { "node_modules/osm-auth": {
"version": "1.1.2", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-1.1.2.tgz", "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-2.2.0.tgz",
"integrity": "sha512-oLaU+c/TP7eKAZpBN4S1mv/N94IXp5A+wLpDfAVlpq/b6iikas8ZthXPqhM8QKg/qB8RaKvZPJgxqYS+5m8G8g==", "integrity": "sha512-x93jAMaYWqPgfVeOMydFLFpFC8ERnlIKXwiUOrYYWTDEWqq15K/BI5UAjzuYXvLg0WxVxM8YC4N1T30SZeKJBQ==",
"dependencies": { "dependencies": {
"ohauth": "~1.0.1",
"resolve-url": "~0.2.1",
"store": "~2.0.12" "store": "~2.0.12"
}, },
"engines": { "engines": {
"node": ">=14" "node": ">=16"
} }
}, },
"node_modules/osm-polygon-features": { "node_modules/osm-polygon-features": {
@ -9191,12 +9166,6 @@
"protocol-buffers-schema": "^3.3.1" "protocol-buffers-schema": "^3.3.1"
} }
}, },
"node_modules/resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
"integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==",
"deprecated": "https://github.com/lydell/resolve-url#deprecated"
},
"node_modules/restore-cursor": { "node_modules/restore-cursor": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",
@ -17830,11 +17799,6 @@
"integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==",
"dev": true "dev": true
}, },
"jshashes": {
"version": "1.0.8",
"resolved": "https://registry.npmjs.org/jshashes/-/jshashes-1.0.8.tgz",
"integrity": "sha512-btmQZ/w1rj8Lb6nEwvhjM7nBYoj54yaEFo2PWh3RkxZ8qNwuvOxvQYN/JxVuwoMmdIluL+XwYVJ+pEEZoSYybQ=="
},
"json-schema": { "json-schema": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz", "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.4.0.tgz",
@ -18513,14 +18477,6 @@
"resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz",
"integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA=="
}, },
"ohauth": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ohauth/-/ohauth-1.0.1.tgz",
"integrity": "sha512-R9ZUN3+FVCwzeOOHCJpzA9jw/byRxp5O9X06mTL6Sp/LIQn/rLrMv6cwYctX+hoIKzRUsalGJXZ1kG5wBmSskQ==",
"requires": {
"jshashes": "~1.0.8"
}
},
"once": { "once": {
"version": "1.4.0", "version": "1.4.0",
"resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
@ -18651,12 +18607,10 @@
"dev": true "dev": true
}, },
"osm-auth": { "osm-auth": {
"version": "1.1.2", "version": "2.2.0",
"resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-1.1.2.tgz", "resolved": "https://registry.npmjs.org/osm-auth/-/osm-auth-2.2.0.tgz",
"integrity": "sha512-oLaU+c/TP7eKAZpBN4S1mv/N94IXp5A+wLpDfAVlpq/b6iikas8ZthXPqhM8QKg/qB8RaKvZPJgxqYS+5m8G8g==", "integrity": "sha512-x93jAMaYWqPgfVeOMydFLFpFC8ERnlIKXwiUOrYYWTDEWqq15K/BI5UAjzuYXvLg0WxVxM8YC4N1T30SZeKJBQ==",
"requires": { "requires": {
"ohauth": "~1.0.1",
"resolve-url": "~0.2.1",
"store": "~2.0.12" "store": "~2.0.12"
} }
}, },
@ -19236,11 +19190,6 @@
"protocol-buffers-schema": "^3.3.1" "protocol-buffers-schema": "^3.3.1"
} }
}, },
"resolve-url": {
"version": "0.2.1",
"resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
"integrity": "sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg=="
},
"restore-cursor": { "restore-cursor": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-3.1.0.tgz",

View file

@ -1,6 +1,6 @@
{ {
"name": "mapcomplete", "name": "mapcomplete",
"version": "0.31.4", "version": "0.32.0",
"repository": "https://github.com/pietervdvn/MapComplete", "repository": "https://github.com/pietervdvn/MapComplete",
"description": "A small website to edit OSM easily", "description": "A small website to edit OSM easily",
"bugs": "https://github.com/pietervdvn/MapComplete/issues", "bugs": "https://github.com/pietervdvn/MapComplete/issues",
@ -93,7 +93,7 @@
"mangrove-reviews-typescript": "^1.1.0", "mangrove-reviews-typescript": "^1.1.0",
"maplibre-gl": "^3.2.0", "maplibre-gl": "^3.2.0",
"opening_hours": "^3.6.0", "opening_hours": "^3.6.0",
"osm-auth": "^1.0.2", "osm-auth": "^2.2.0",
"osmtogeojson": "^3.0.0-beta.5", "osmtogeojson": "^3.0.0-beta.5",
"papaparse": "^5.3.1", "papaparse": "^5.3.1",
"pic4carto": "^2.1.15", "pic4carto": "^2.1.15",

View file

@ -1,7 +1,9 @@
import osmAuth from "osm-auth" // @ts-ignore
import { Store, Stores, UIEventSource } from "../UIEventSource" import {osmAuth} from "osm-auth"
import { OsmPreferences } from "./OsmPreferences" import {Store, Stores, UIEventSource} from "../UIEventSource"
import { Utils } from "../../Utils" import {OsmPreferences} from "./OsmPreferences"
import {Utils} from "../../Utils"
import {LocalStorageSource} from "../Web/LocalStorageSource";
export default class UserDetails { export default class UserDetails {
public loggedIn = false public loggedIn = false
@ -22,22 +24,26 @@ export default class UserDetails {
} }
} }
export interface AuthConfig {
oauth_client_id: string
oauth_secret: string
url: string
}
export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable" export type OsmServiceState = "online" | "readonly" | "offline" | "unknown" | "unreachable"
export class OsmConnection { export class OsmConnection {
public static readonly oauth_configs = { public static readonly oauth_configs: Record<string, AuthConfig> = {
osm: { osm: {
oauth_consumer_key: "hivV7ec2o49Two8g9h8Is1VIiVOgxQ1iYexCbvem", oauth_client_id: 'sa1ngLJBJ8McmzHElN8NYtIDm5TZTYEYhq3-0snO4Qc',
oauth_secret: "wDBRTCem0vxD7txrg1y6p5r8nvmz8tAhET7zDASI", oauth_secret: 'XU_cD5Mvw9VKk9T0t_gO8V7cbRC4Hmw2Tb4Rv0Zmz-U',
url: "https://www.openstreetmap.org", url: "https://www.openstreetmap.org",
// OAUTH 1.0 application
// https://www.openstreetmap.org/user/Pieter%20Vander%20Vennet/oauth_clients/7404
}, },
"osm-test": { "osm-test": {
oauth_consumer_key: "Zgr7EoKb93uwPv2EOFkIlf3n9NLwj5wbyfjZMhz2", oauth_client_id: "HwUn6GPxGm1m9WwMarxTglhy6dBTM4YkaV1I9h6pDGU"
oauth_secret: "3am1i1sykHDMZ66SGq4wI2Z7cJMKgzneCHp3nctn", oauth_secret: "luFZtPJg7j96K6WM6RpcZ_3M-r6muuDq6fG1ygk0I_4",
url: "https://master.apis.dev.openstreetmap.org", url: "https://master.apis.dev.openstreetmap.org",
}, }
} }
public auth public auth
public userDetails: UIEventSource<UserDetails> public userDetails: UIEventSource<UserDetails>
@ -53,11 +59,7 @@ export class OsmConnection {
"not-attempted" "not-attempted"
) )
public preferencesHandler: OsmPreferences public preferencesHandler: OsmPreferences
public readonly _oauth_config: { public readonly _oauth_config: AuthConfig
oauth_consumer_key: string
oauth_secret: string
url: string
}
private readonly _dryRun: Store<boolean> private readonly _dryRun: Store<boolean>
private fakeUser: boolean private fakeUser: boolean
private _onLoggedIn: ((userDetails: UserDetails) => void)[] = [] private _onLoggedIn: ((userDetails: UserDetails) => void)[] = []
@ -190,6 +192,7 @@ export class OsmConnection {
const self = this const self = this
console.log("Trying to log in...") console.log("Trying to log in...")
this.updateAuthObject() this.updateAuthObject()
LocalStorageSource.Get("location_before_login").setData(window.location.href)
this.auth.xhr( this.auth.xhr(
{ {
method: "GET", method: "GET",
@ -202,13 +205,8 @@ export class OsmConnection {
if (err.status == 401) { if (err.status == 401) {
console.log("Clearing tokens...") console.log("Clearing tokens...")
// Not authorized - our token probably got revoked // Not authorized - our token probably got revoked
// Reset all the tokens self.auth.logout();
const tokens = [ self.LogOut()
"https://www.openstreetmap.orgoauth_request_token_secret",
"https://www.openstreetmap.orgoauth_token",
"https://www.openstreetmap.orgoauth_token_secret",
]
tokens.forEach((token) => localStorage.removeItem(token))
} }
return return
} }
@ -252,7 +250,7 @@ export class OsmConnection {
if (homeEl !== undefined && homeEl[0] !== undefined) { if (homeEl !== undefined && homeEl[0] !== undefined) {
const lat = parseFloat(homeEl[0].getAttribute("lat")) const lat = parseFloat(homeEl[0].getAttribute("lat"))
const lon = parseFloat(homeEl[0].getAttribute("lon")) const lon = parseFloat(homeEl[0].getAttribute("lon"))
data.home = { lat: lat, lon: lon } data.home = {lat: lat, lon: lon}
} }
self.loadingStatus.setData("logged-in") self.loadingStatus.setData("logged-in")
@ -310,6 +308,7 @@ export class OsmConnection {
): Promise<any> { ): Promise<any> {
return await this.interact(path, "POST", header, content) return await this.interact(path, "POST", header, content)
} }
public async put( public async put(
path: string, path: string,
content?: string, content?: string,
@ -355,13 +354,13 @@ export class OsmConnection {
console.warn("Dryrun enabled - not actually opening note with text ", text) console.warn("Dryrun enabled - not actually opening note with text ", text)
return new Promise<{ id: number }>((ok) => { return new Promise<{ id: number }>((ok) => {
window.setTimeout( window.setTimeout(
() => ok({ id: Math.floor(Math.random() * 1000) }), () => ok({id: Math.floor(Math.random() * 1000)}),
Math.random() * 5000 Math.random() * 5000
) )
}) })
} }
const auth = this.auth const auth = this.auth
const content = { lat, lon, text } const content = {lat, lon, text}
const response = await this.post("notes.json", JSON.stringify(content), { const response = await this.post("notes.json", JSON.stringify(content), {
"Content-Type": "application/json", "Content-Type": "application/json",
}) })
@ -389,7 +388,7 @@ export class OsmConnection {
console.warn("Dryrun enabled - not actually uploading GPX ", gpx) console.warn("Dryrun enabled - not actually uploading GPX ", gpx)
return new Promise<{ id: number }>((ok, error) => { return new Promise<{ id: number }>((ok, error) => {
window.setTimeout( window.setTimeout(
() => ok({ id: Math.floor(Math.random() * 1000) }), () => ok({id: Math.floor(Math.random() * 1000)}),
Math.random() * 5000 Math.random() * 5000
) )
}) })
@ -430,7 +429,7 @@ export class OsmConnection {
}) })
const parsed = JSON.parse(response) const parsed = JSON.parse(response)
console.log("Uploaded GPX track", parsed) console.log("Uploaded GPX track", parsed)
return { id: parsed } return {id: parsed}
} }
public addCommentToNote(id: number | string, text: string): Promise<void> { public addCommentToNote(id: number | string, text: string): Promise<void> {
@ -486,15 +485,27 @@ export class OsmConnection {
// Same for an iframe... // Same for an iframe...
this.auth = new osmAuth({ this.auth = new osmAuth({
oauth_consumer_key: this._oauth_config.oauth_consumer_key, client_id: this._oauth_config.oauth_client_id,
oauth_secret: this._oauth_config.oauth_secret,
url: this._oauth_config.url, url: this._oauth_config.url,
landing: standalone ? undefined : window.location.href, scope: "read_prefs write_prefs write_api write_gpx write_notes",
redirect_uri: window.location.protocol + "//" + window.location.host + "/land.html",
singlepage: !standalone, singlepage: !standalone,
auto: true, auto: true,
}) })
} }
/**
* To be called by land.html
*/
public finishLogin(callback: ((previousURL: string) => void)) {
this.auth.authenticate(function() {
// Fully authed at this point
console.log("Authentication successful!")
const previousLocation = LocalStorageSource.Get("location_before_login")
callback(previousLocation.data)
});
}
private CheckForMessagesContinuously() { private CheckForMessagesContinuously() {
const self = this const self = this
if (this.isChecking) { if (this.isChecking) {
@ -511,7 +522,7 @@ export class OsmConnection {
private UpdateCapabilities(): void { private UpdateCapabilities(): void {
const self = this const self = this
this.FetchCapabilities().then(({ api, gpx }) => { this.FetchCapabilities().then(({api, gpx}) => {
self.apiIsOnline.setData(api) self.apiIsOnline.setData(api)
self.gpxServiceIsOnline.setData(gpx) self.gpxServiceIsOnline.setData(gpx)
}) })
@ -519,18 +530,18 @@ export class OsmConnection {
private async FetchCapabilities(): Promise<{ api: OsmServiceState; gpx: OsmServiceState }> { private async FetchCapabilities(): Promise<{ api: OsmServiceState; gpx: OsmServiceState }> {
if (Utils.runningFromConsole) { if (Utils.runningFromConsole) {
return { api: "online", gpx: "online" } return {api: "online", gpx: "online"}
} }
const result = await Utils.downloadAdvanced(this.Backend() + "/api/0.6/capabilities") const result = await Utils.downloadAdvanced(this.Backend() + "/api/0.6/capabilities")
if (result["content"] === undefined) { if (result["content"] === undefined) {
console.log("Something went wrong:", result) console.log("Something went wrong:", result)
return { api: "unreachable", gpx: "unreachable" } return {api: "unreachable", gpx: "unreachable"}
} }
const xmlRaw = result["content"] const xmlRaw = result["content"]
const parsed = new DOMParser().parseFromString(xmlRaw, "text/xml") const parsed = new DOMParser().parseFromString(xmlRaw, "text/xml")
const statusEl = parsed.getElementsByTagName("status")[0] const statusEl = parsed.getElementsByTagName("status")[0]
const api = <OsmServiceState>statusEl.getAttribute("api") const api = <OsmServiceState>statusEl.getAttribute("api")
const gpx = <OsmServiceState>statusEl.getAttribute("gpx") const gpx = <OsmServiceState>statusEl.getAttribute("gpx")
return { api, gpx } return {api, gpx}
} }
} }

12
src/land.ts Normal file
View file

@ -0,0 +1,12 @@
import {OsmConnection} from "./Logic/Osm/OsmConnection";
console.log("Authorizing...");
new OsmConnection().finishLogin(previousURL => {
const fallback = window.location.protocol+"//"+window.location.host+"/index.html"
previousURL ??= fallback
if(previousURL.indexOf("/land") > 0){
previousURL = fallback
}
console.log("Redirecting to", previousURL)
window.location.href = previousURL
})