forked from MapComplete/MapComplete
		
	Merge pull request #1554 from pietervdvn/feature/env-variables
Allow use of environment variables for OAuth
This commit is contained in:
		
						commit
						d06c515ec1
					
				
					 5 changed files with 46 additions and 25 deletions
				
			
		
							
								
								
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							| 
						 | 
					@ -23,6 +23,7 @@ index_*.ts
 | 
				
			||||||
.~lock.*
 | 
					.~lock.*
 | 
				
			||||||
*.doctest.ts
 | 
					*.doctest.ts
 | 
				
			||||||
service-worker.js
 | 
					service-worker.js
 | 
				
			||||||
 | 
					.env
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.vscode/*
 | 
					.vscode/*
 | 
				
			||||||
!.vscode/settings.json
 | 
					!.vscode/settings.json
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
							
								
								
									
										2
									
								
								.nvmrc
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								.nvmrc
									
										
									
									
									
								
							| 
						 | 
					@ -1 +1 @@
 | 
				
			||||||
nodejs 16.9.1
 | 
					16.9.1
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,7 +13,8 @@
 | 
				
			||||||
      "`oauth_credentials` are the OAuth-2 credentials for the production-OSM server and the test-server.",
 | 
					      "`oauth_credentials` are the OAuth-2 credentials for the production-OSM server and the test-server.",
 | 
				
			||||||
      "Are you deploying your own instance? Register your application too.",
 | 
					      "Are you deploying your own instance? Register your application too.",
 | 
				
			||||||
      "See https://wiki.openstreetmap.org/wiki/OAuth#Registering_your_application_as_OAuth_2.0_consumer for instructions",
 | 
					      "See https://wiki.openstreetmap.org/wiki/OAuth#Registering_your_application_as_OAuth_2.0_consumer for instructions",
 | 
				
			||||||
      "Use `https://<your domain name>/<path to mapcomplete>/land.html` as redirect URIs. You can add `http://127.0.0.1:1234/land.html` too for local development."
 | 
					      "Use `https://<your domain name>/<path to mapcomplete>/land.html` as redirect URIs. You can add `http://127.0.0.1:1234/land.html` too for local development.",
 | 
				
			||||||
 | 
					      "Alternatively, you can override the `osm` credentials using the environment variables `VITE_OSM_OAUTH_CLIENT_ID` and `VITE_OSM_OAUTH_SECRET`"
 | 
				
			||||||
    ],
 | 
					    ],
 | 
				
			||||||
    "oauth_credentials": {
 | 
					    "oauth_credentials": {
 | 
				
			||||||
      "osm_pietervdvn": {
 | 
					      "osm_pietervdvn": {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1,9 +1,9 @@
 | 
				
			||||||
// @ts-ignore
 | 
					// @ts-ignore
 | 
				
			||||||
import {osmAuth} from "osm-auth"
 | 
					import { osmAuth } from "osm-auth"
 | 
				
			||||||
import {Store, Stores, UIEventSource} from "../UIEventSource"
 | 
					import { Store, Stores, UIEventSource } from "../UIEventSource"
 | 
				
			||||||
import {OsmPreferences} from "./OsmPreferences"
 | 
					import { OsmPreferences } from "./OsmPreferences"
 | 
				
			||||||
import {Utils} from "../../Utils"
 | 
					import { Utils } from "../../Utils"
 | 
				
			||||||
import {LocalStorageSource} from "../Web/LocalStorageSource";
 | 
					import { LocalStorageSource } from "../Web/LocalStorageSource"
 | 
				
			||||||
import * as config from "../../../package.json"
 | 
					import * as config from "../../../package.json"
 | 
				
			||||||
export default class UserDetails {
 | 
					export default class UserDetails {
 | 
				
			||||||
    public loggedIn = false
 | 
					    public loggedIn = false
 | 
				
			||||||
| 
						 | 
					@ -34,7 +34,8 @@ export interface AuthConfig {
 | 
				
			||||||
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: Record<string, AuthConfig>  = config.config.oauth_credentials
 | 
					    public static readonly oauth_configs: Record<string, AuthConfig> =
 | 
				
			||||||
 | 
					        config.config.oauth_credentials
 | 
				
			||||||
    public auth
 | 
					    public auth
 | 
				
			||||||
    public userDetails: UIEventSource<UserDetails>
 | 
					    public userDetails: UIEventSource<UserDetails>
 | 
				
			||||||
    public isLoggedIn: Store<boolean>
 | 
					    public isLoggedIn: Store<boolean>
 | 
				
			||||||
| 
						 | 
					@ -75,6 +76,19 @@ export class OsmConnection {
 | 
				
			||||||
        console.debug("Using backend", this._oauth_config.url)
 | 
					        console.debug("Using backend", this._oauth_config.url)
 | 
				
			||||||
        this._iframeMode = Utils.runningFromConsole ? false : window !== window.top
 | 
					        this._iframeMode = Utils.runningFromConsole ? false : window !== window.top
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // Check if there are settings available in environment variables, and if so, use those
 | 
				
			||||||
 | 
					        if (
 | 
				
			||||||
 | 
					            import.meta.env.VITE_OSM_OAUTH_CLIENT_ID !== undefined &&
 | 
				
			||||||
 | 
					            import.meta.env.VITE_OSM_OAUTH_SECRET !== undefined
 | 
				
			||||||
 | 
					        ) {
 | 
				
			||||||
 | 
					            console.debug("Using environment variables for oauth config")
 | 
				
			||||||
 | 
					            this._oauth_config = {
 | 
				
			||||||
 | 
					                oauth_client_id: import.meta.env.VITE_OSM_OAUTH_CLIENT_ID,
 | 
				
			||||||
 | 
					                oauth_secret: import.meta.env.VITE_OSM_OAUTH_SECRET,
 | 
				
			||||||
 | 
					                url: "https://www.openstreetmap.org",
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        this.userDetails = new UIEventSource<UserDetails>(
 | 
					        this.userDetails = new UIEventSource<UserDetails>(
 | 
				
			||||||
            new UserDetails(this._oauth_config.url),
 | 
					            new UserDetails(this._oauth_config.url),
 | 
				
			||||||
            "userDetails"
 | 
					            "userDetails"
 | 
				
			||||||
| 
						 | 
					@ -182,7 +196,9 @@ 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(Utils.runningFromConsole ? undefined : window.location.href)
 | 
					        LocalStorageSource.Get("location_before_login").setData(
 | 
				
			||||||
 | 
					            Utils.runningFromConsole ? undefined : window.location.href
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        this.auth.xhr(
 | 
					        this.auth.xhr(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
                method: "GET",
 | 
					                method: "GET",
 | 
				
			||||||
| 
						 | 
					@ -195,7 +211,7 @@ 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
 | 
				
			||||||
                        self.auth.logout();
 | 
					                        self.auth.logout()
 | 
				
			||||||
                        self.LogOut()
 | 
					                        self.LogOut()
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    return
 | 
					                    return
 | 
				
			||||||
| 
						 | 
					@ -240,7 +256,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")
 | 
				
			||||||
| 
						 | 
					@ -344,13 +360,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",
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -378,7 +394,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
 | 
				
			||||||
                )
 | 
					                )
 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
| 
						 | 
					@ -419,7 +435,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> {
 | 
				
			||||||
| 
						 | 
					@ -478,7 +494,9 @@ export class OsmConnection {
 | 
				
			||||||
            client_id: this._oauth_config.oauth_client_id,
 | 
					            client_id: this._oauth_config.oauth_client_id,
 | 
				
			||||||
            url: this._oauth_config.url,
 | 
					            url: this._oauth_config.url,
 | 
				
			||||||
            scope: "read_prefs write_prefs write_api write_gpx write_notes",
 | 
					            scope: "read_prefs write_prefs write_api write_gpx write_notes",
 | 
				
			||||||
            redirect_uri: Utils.runningFromConsole ? "https://mapcomplete.org/land.html" :  window.location.protocol + "//" + window.location.host + "/land.html",
 | 
					            redirect_uri: Utils.runningFromConsole
 | 
				
			||||||
 | 
					                ? "https://mapcomplete.org/land.html"
 | 
				
			||||||
 | 
					                : window.location.protocol + "//" + window.location.host + "/land.html",
 | 
				
			||||||
            singlepage: !standalone,
 | 
					            singlepage: !standalone,
 | 
				
			||||||
            auto: true,
 | 
					            auto: true,
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -487,13 +505,13 @@ export class OsmConnection {
 | 
				
			||||||
    /**
 | 
					    /**
 | 
				
			||||||
     * To be called by land.html
 | 
					     * To be called by land.html
 | 
				
			||||||
     */
 | 
					     */
 | 
				
			||||||
    public finishLogin(callback: ((previousURL: string) => void)) {
 | 
					    public finishLogin(callback: (previousURL: string) => void) {
 | 
				
			||||||
        this.auth.authenticate(function() {
 | 
					        this.auth.authenticate(function () {
 | 
				
			||||||
            // Fully authed at this point
 | 
					            // Fully authed at this point
 | 
				
			||||||
            console.log("Authentication successful!")
 | 
					            console.log("Authentication successful!")
 | 
				
			||||||
            const previousLocation = LocalStorageSource.Get("location_before_login")
 | 
					            const previousLocation = LocalStorageSource.Get("location_before_login")
 | 
				
			||||||
            callback(previousLocation.data)
 | 
					            callback(previousLocation.data)
 | 
				
			||||||
        });
 | 
					        })
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private CheckForMessagesContinuously() {
 | 
					    private CheckForMessagesContinuously() {
 | 
				
			||||||
| 
						 | 
					@ -512,7 +530,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)
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
| 
						 | 
					@ -520,18 +538,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 }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,7 +9,8 @@
 | 
				
			||||||
    "resolveJsonModule": true,
 | 
					    "resolveJsonModule": true,
 | 
				
			||||||
    "isolatedModules": true,
 | 
					    "isolatedModules": true,
 | 
				
			||||||
    "esModuleInterop": true,
 | 
					    "esModuleInterop": true,
 | 
				
			||||||
    "skipLibCheck": true
 | 
					    "skipLibCheck": true,
 | 
				
			||||||
 | 
					    "types": ["vite/client"]
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  "ts-node": {
 | 
					  "ts-node": {
 | 
				
			||||||
    "esm": true,
 | 
					    "esm": true,
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue