Skip to content

Commit

Permalink
first-class support for switching users
Browse files Browse the repository at this point in the history
  • Loading branch information
k-yle committed Jan 16, 2025
1 parent 0bd1a08 commit 7476b65
Show file tree
Hide file tree
Showing 3 changed files with 25 additions and 11 deletions.
8 changes: 5 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ var redirectPath = window.location.origin + window.location.pathname;
var auth = osmAuth.osmAuth({
client_id: "JWXSAzNp64sIRMStTnkhMRaMxSR964V4sFgn3KUZNTA",
redirect_uri: redirectPath,
scope: "read_prefs", // scopes should be separated by a space, e.g. "read_prefs write_prefs". See https://wiki.openstreetmap.org/wiki/OAuth#OAuth_2.0 for all scopes
scope: "read_prefs", // scopes should be separated by a space, e.g. "read_prefs write_prefs". See https://wiki.openstreetmap.org/wiki/OAuth#OAuth_2.0 for all scopes
auto: true // show a login form if the user is not authenticated and you try to do a call
singlepage: true,
});
Expand Down Expand Up @@ -182,20 +182,22 @@ Test whether the user is currently authenticated<br/>
Returns: `true` if authenticated, `false` if not<br/>


## `.authenticate(callback)`
## `.authenticate(callback, options?)`

First logs out, then runs the authentiation flow, finally calls the callback.<br/>
<br/>
Param: `callback` An "errback"-style callback (`err`, `result`), called when complete<br/>
Param: `options` Optional, an object which can contain `switchUser`. If `switchUser` is `true`, then the user will first be prompted to logout, then login, then go to the oauth flow.<br/>
Returns: none<br/>


## `.authenticateAsync()`
## `.authenticateAsync(options?)`

Promisified version of `.authenticate()`<br/>
First logs out, then runs the authentication flow and resolves if successful, or rejects if not.<br/>
<br/>
Param: `callback` An "errback"-style callback (`err`, `result`), called when complete<br/>
Param: `options` Optional, an object which can contain `switchUser`. If `switchUser` is `true`, then the user will first be prompted to logout, then login, then go to the oauth flow.<br/>
Returns: `Promise` settled with whatever authenticate did.<br/>


Expand Down
6 changes: 5 additions & 1 deletion src/osm-auth.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ declare namespace OSMAuth {
logout(): osmAuth;
fetch(path: string, options: OSMAuthFetchOptions): Promise<Response>;
authenticated(): boolean;
authenticate(callback: (err: null | any, result?: any) => any): any;
authenticate(callback: (err: null | any, result?: any) => any, options?: LoginOptions): any;
bringPopupWindowToFront(): boolean;
bootstrapToken(oauth_token: string, callback: (err: null | any, result?: any) => any): any;
xhr(options: OSMAuthXHROptions, callback: (err: null | any, result?: any) => any): XMLHttpRequest | null;
Expand All @@ -17,6 +17,10 @@ declare namespace OSMAuth {
options(options: OSMAuthOptions): osmAuth;
}

interface LoginOptions {
switchUser?: boolean;
}

interface OSMAuthOptions {
scope: string;
client_id: string;
Expand Down
22 changes: 15 additions & 7 deletions src/osm-auth.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -95,9 +95,10 @@ export function osmAuth(o) {
* TODO: detect lack of click event (probably can settimeout it)
*
* @param {function} callback Errback-style callback `(err, result)`, called when complete
* @param {LoginOptions} [options] Other options
* @return none
*/
oauth.authenticate = function(callback) {
oauth.authenticate = function(callback, options) {
if (oauth.authenticated()) {
callback(null, oauth);
return;
Expand All @@ -110,7 +111,7 @@ export function osmAuth(o) {
callback(error);
} else {
_generatePkceChallenge(function(pkce) {
_authenticate(pkce, popup, callback);
_authenticate(pkce, options, popup, callback);
});
}
});
Expand All @@ -120,9 +121,10 @@ export function osmAuth(o) {
/**
* authenticateAsync
* Promisified version of `authenticate`
* @param {LoginOptions} [options]
* @return {Promise} Promise settled with whatever `_authenticate` did
*/
oauth.authenticateAsync = function() {
oauth.authenticateAsync = function(options) {
if (oauth.authenticated()) {
return Promise.resolve(oauth);
}
Expand All @@ -142,7 +144,7 @@ export function osmAuth(o) {
if (error) {
errback(error);
} else {
_generatePkceChallenge(pkce => _authenticate(pkce, popup, errback));
_generatePkceChallenge(pkce => _authenticate(pkce, options, popup, errback));
}
});
});
Expand Down Expand Up @@ -186,17 +188,19 @@ export function osmAuth(o) {
* _authenticate
* internal authenticate
*
* @typedef {{ switchUser?: boolean }} LoginOptions
*
* @param {Object} pkce Object containing PKCE code challenge properties
* @param {LoginOptions=} options Other options
* @param {Window} popup Popup Window to use for the authentication page, should be undefined when using singlepage mode
* @param {function} callback Errback-style callback that accepts `(err, result)`
*/
function _authenticate(pkce, popup, callback) {
function _authenticate(pkce, options, popup, callback) {
var state = generateState();

// ## Request authorization to access resources from the user
// and receive authorization code
var url =
o.url +
const path =
'/oauth2/authorize?' +
utilQsString({
client_id: o.client_id,
Expand All @@ -209,6 +213,10 @@ export function osmAuth(o) {
locale: o.locale || '',
});

const url = options?.switchUser
? `${o.url}/logout?referer=${encodeURIComponent(`/login?referer=${encodeURIComponent(path)}`)}`
: o.url + path;

if (o.singlepage) {
if (_store.isMocked) {
// in singlepage mode, PKCE requires working non-volatile storage
Expand Down

0 comments on commit 7476b65

Please sign in to comment.