As I mentioned in my question, there is no problem to get the relative status of whether logged in or not. The problem is that iframe, because only that iframe knows what is going on (because it's the redirected url!). To let my main app responsive as well. I made some tweaks and 'hacks'.
Here is how I got it working. It will detect if I logged in at somewhere else and log me in automatically. It will also log me out in my app if I log out at another site.
* Detect if I logged in at somewhere else *
I created a method 'checkLoginState', and it's responsible for checking if there is a token in my session or check with the server is I already logged in.
The interval there is just to periodically check if the iframe got the token.
checkLoginState() {
const claims = this.oauthService.getIdentityClaims();
if (!claims) {
if (this.ssoInterval) {
// if we are waiting on response, return;
return;
}
// try to get a token if already logged in somewhere else
this.oauthService
.loadDiscoveryDocument()
.then(() => this.oauthService.tryLogin())
.then(() => {
if (!this.oauthService.hasValidAccessToken()) {
this.setupSSOInterval();
this.oauthService.silentRefresh().catch(err => {
// this will throws a time_out error as we don't have a
valid token to refresh
// console.error('refresh error', err);
this.clearSSOInterval();
});
}
})
.catch(e => {
// console.log(e);
// if not logged in anywhere, it will throw a token error.
this.clearSSOInterval();
});
return;
}
if (this.oauthService.getIdTokenExpiration() < new Date().getTime()) {
this.userService.removeToken();
return this.logout();
}
this.isLoggedIn = true;
this.userService.authenticateWithNID(claims['email']);
}
private setupSSOInterval() {
this.ssoInterval = setInterval(() => {
if (this.isLoggedIn) {
clearInterval(this.ssoInterval);
} else {
this.checkLoginState();
}
}, 1000);
}
private clearSSOInterval() {
if (this.ssoInterval) {
clearInterval(this.ssoInterval);
}
}
and call this method in ngOnInit();
* Detect if I logged out at somewhere else *
To detect if I'm logged out, first set the sessionChecksEnabled
to true (as @Jeroen said). Then listen to the session storage change. (because the iframe will update the session storage)
ngOnInit() {
window.addEventListener(
'storage',
this.storageEventListener.bind(this)
);
// this is for handle the normal redirect when we login from this app
this.oauthService.events.subscribe(({ type }: OAuthEvent) => {
switch (type) {
case 'token_received': {
this.checkLoginState();
}
}
});
this.checkLoginState();
}
private storageEventListener(event: StorageEvent) {
// if there is a session change and claims is missing, means I am no longer logged in
if (event.storageArea === sessionStorage) {
if (!sessionStorage.getItem('id_token_claims_obj')) {
this.isLoggedIn = false;
}
}
}
REMEMBER to delete the this.oauthService.loadDiscoveryDocumentAndTryLogin();
in your constructor method. It will throw some errors if you log out at other sites. (you can catch the error if you want, but the same method has been called inside checkloginState()
).
I just realised that I could use the session storage listener for login check as well (replace the interval). But I will leave it for now.