Created
September 25, 2020 10:27
-
-
Save dmitry-stepanenko/945c3e4d98b76272f8bef635fdb1dbed to your computer and use it in GitHub Desktop.
Typescript utility class to run OAuth process in popup
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { fromEvent, Subject, Observable, OperatorFunction, merge } from 'rxjs'; | |
import { take, takeUntil, filter, map } from 'rxjs/operators'; | |
interface OAuthData { | |
source: string; | |
success: boolean; | |
listingSourceId?: string; | |
} | |
type OAuthEvent = Event & { data?: OAuthData; origin: string }; | |
export class OAuthProcess { | |
private windowObjectReference: Window | null = null; | |
private previousUrl?: string; | |
private readonly onMessage$ = fromEvent<OAuthEvent>(window, 'message').pipe(validateMessage()); | |
private readonly onOpen$ = new Subject<void>(); | |
constructor(private onDestroy$: Observable<void>) {} | |
/** | |
* method to initiate auth process | |
* @param url url of OAuth provider | |
*/ | |
open(url: string): Observable<OAuthData> { | |
// complete the previous subscription | |
this.onOpen$.next(); | |
if (!this.windowObjectReference || this.windowObjectReference.closed || this.previousUrl !== url) { | |
// if the pointer to the window object in memory does not exist | |
// or if such pointer exists but the window was closed or if the resource to load is different | |
const windowArea = { | |
width: Math.max(Math.floor(window.outerWidth * 0.8), 1000), | |
height: Math.max(Math.floor(window.outerHeight * 0.5), 630), | |
left: 0, | |
top: 0, | |
}; | |
windowArea.left = Math.floor(window.screenX + (window.outerWidth - windowArea.width) / 2); | |
windowArea.top = Math.floor(window.screenY + (window.outerHeight - windowArea.height) / 8); | |
const sep = url.indexOf('?') !== -1 ? '&' : '?'; | |
const targetUrl = `${url}${sep}`; | |
const windowOpts = `toolbar=0,scrollbars=1,status=1,resizable=1,location=1,menuBar=0, | |
width=${windowArea.width},height=${windowArea.height}, | |
left=${windowArea.left},top=${windowArea.top}`; | |
this.windowObjectReference = window.open(targetUrl, 'OAuth Login', windowOpts); | |
this.windowObjectReference?.focus(); | |
} else { | |
// else the window reference must exist and the window | |
// is not closed; therefore, we can bring it back on top of any other | |
// window with the focus() method. There would be no need to re-create | |
// the window or to reload the referenced resource. | |
this.windowObjectReference.focus(); | |
} | |
this.previousUrl = url; | |
return this.onMessage$.pipe(take(1), takeUntil(merge(this.onOpen$, this.onDestroy$))); | |
} | |
} | |
function validateMessage(): OperatorFunction<OAuthEvent, OAuthData> { | |
return (src$) => | |
src$.pipe( | |
filter((event) => { | |
// Do we trust the sender of this message? | |
// might be different from what we originally opened, for example. | |
// also check validness of source | |
if (event?.origin !== window.origin || event.data?.source !== 'scorpion-oauth') { | |
return false; | |
} | |
return true; | |
}), | |
map((event) => event.data as OAuthData) | |
); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment