Skip to content

Instantly share code, notes, and snippets.

@dmitry-stepanenko
Created September 25, 2020 10:27
Show Gist options
  • Save dmitry-stepanenko/945c3e4d98b76272f8bef635fdb1dbed to your computer and use it in GitHub Desktop.
Save dmitry-stepanenko/945c3e4d98b76272f8bef635fdb1dbed to your computer and use it in GitHub Desktop.
Typescript utility class to run OAuth process in popup
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