I'm upgrading/rewriting an existing angular app to use angular2. My problem is that I want to open a OAuth flow in a new pop up window and once the OAuth flow is pleted use window.postMessage to municate back to the angular 2 app that the OAuth flow was successful.
Currently what I have is in the angular 2 service is
export class ApiService {
constructor(private _loggedInService: LoggedInService) {
window.addEventListener('message', this.onPostMessage, false);
}
startOAuthFlow() {
var options = 'left=100,top=10,width=400,height=500';
window.open('http://site/connect-auth', , options);
}
onPostMessage(event) {
if(event.data.status === "200") {
// Use an EventEmitter to notify the other ponents that user logged in
this._loggedInService.Stream.emit(null);
}
}
}
This template that is loaded at the end of the OAuth flow
<html>
<head>
<title>OAuth callback</title>
<script>
var POST_ORIGIN_URI = 'localhost:8000';
var message = {"status": "200", "jwt":"2"};
window.opener.postMessage(message, POST_ORIGIN_URI);
window.close();
</script>
</head>
</html>
Using window.addEventListener
like this seems to pletely break the angular 2 app, dereferencing this
.
So my question is can I use window.addEventListener or should I not use postMessage to municate back to the angular2 app?
** Complete angular2 noob so any help is appreciated
I'm upgrading/rewriting an existing angular app to use angular2. My problem is that I want to open a OAuth flow in a new pop up window and once the OAuth flow is pleted use window.postMessage to municate back to the angular 2 app that the OAuth flow was successful.
Currently what I have is in the angular 2 service is
export class ApiService {
constructor(private _loggedInService: LoggedInService) {
window.addEventListener('message', this.onPostMessage, false);
}
startOAuthFlow() {
var options = 'left=100,top=10,width=400,height=500';
window.open('http://site/connect-auth', , options);
}
onPostMessage(event) {
if(event.data.status === "200") {
// Use an EventEmitter to notify the other ponents that user logged in
this._loggedInService.Stream.emit(null);
}
}
}
This template that is loaded at the end of the OAuth flow
<html>
<head>
<title>OAuth callback</title>
<script>
var POST_ORIGIN_URI = 'localhost:8000';
var message = {"status": "200", "jwt":"2"};
window.opener.postMessage(message, POST_ORIGIN_URI);
window.close();
</script>
</head>
</html>
Using window.addEventListener
like this seems to pletely break the angular 2 app, dereferencing this
.
So my question is can I use window.addEventListener or should I not use postMessage to municate back to the angular2 app?
** Complete angular2 noob so any help is appreciated
Share Improve this question asked Jan 5, 2016 at 16:14 roykaroyka 1,6102 gold badges13 silver badges10 bronze badges4 Answers
Reset to default 6I have a plete Angular2 OAuth2 skeleton application on Github that you can refer to.
It makes use of an Auth service for OAuth2 Implicit grants that in turn uses a Window service to create the popup window. It then monitors that window for the access token on the URL.
You can access the demo OAuth2 Angular code (with Webpack) here.
Here is the login routine from the Auth service, which will give you an idea of what's going on without having to look at the entire project. I've added a few extra ments in there for you.
public doLogin() {
var loopCount = this.loopCount;
this.windowHandle = this.windows.createWindow(this.oAuthTokenUrl, 'OAuth2 Login');
this.intervalId = setInterval(() => {
if (loopCount-- < 0) { // if we get below 0, it's a timeout and we close the window
clearInterval(this.intervalId);
this.emitAuthStatus(false);
this.windowHandle.close();
} else { // otherwise we check the URL of the window
var href:string;
try {
href = this.windowHandle.location.href;
} catch (e) {
//console.log('Error:', e);
}
if (href != null) { // if the URL is not null
var re = /access_token=(.*)/;
var found = href.match(re);
if (found) { // and if the URL has an access token then process the URL for access token and expiration time
console.log("Callback URL:", href);
clearInterval(this.intervalId);
var parsed = this.parse(href.substr(this.oAuthCallbackUrl.length + 1));
var expiresSeconds = Number(parsed.expires_in) || 1800;
this.token = parsed.access_token;
if (this.token) {
this.authenticated = true;
}
this.startExpiresTimer(expiresSeconds);
this.expires = new Date();
this.expires = this.expires.setSeconds(this.expires.getSeconds() + expiresSeconds);
this.windowHandle.close();
this.emitAuthStatus(true);
this.fetchUserInfo();
}
}
}
}, this.intervalLength);
}
Feel free to ask if you have any questions or problems getting the app up and running.
So with a bit of investigation found out the problem. I was de-referencing this
. This github wiki helped me understand it a bit more.
To solve it for my case needed to do a couple of things. Firstly I created a service that encapsulated the adding of an eventListener
import {BrowserDomAdapter} from 'angular2/platform/browser';
export class PostMessageService {
dom = new BrowserDomAdapter();
addPostMessageListener(fn: EventListener): void {
this.dom.getGlobalEventTarget('window').addEventListener('message', fn,false)
}
}
Then using this addPostMessageListener
I can attach a function in my other service to fire
constructor(public _postMessageService: PostMessageService,
public _router: Router) {
// Set up a Post Message Listener
this._postMessageService.addPostMessageListener((event) =>
this.onPostMessage(event)); // This is the important as it means I keep the reference to this
}
Then it works how I expected keeping the reference to this
I think this is the Angular2 way:
(Dart code but TS should be quite similar)
@Injectable()
class SomeService {
DomAdapter dom;
SomeService(this.dom) {
dom.getGlobalEventTarget('window').addEventListener("message", fn, false);
}
}
I fiddled around with this for ages but in the end, the most robust way for me was to redirect the user to the oath page
window.location.href = '/auth/logintwitter';
do the oath dance in the backend (I used express) and then redirect back to a receiving front end page...
res.redirect(`/#/account/twitterReturn?userName=${userName}&token=${token}`);
There are some idiosyncracies to my solution because e.g. I wanted to use only JsonWebToken on the client regardless of login type, but if you are interested, whole solution is here.
https://github./JavascriptMick/learntree