I'm trying to use the angular-oauth2-oidc
Silent Refresh implementation in combination with the implicit flow configured in an IdentityServer4 server. I've got a proof of concept working inside an ng new ng-and-ids4 --minimal
application with this component:
import { Component } from '@angular/core';
import { AuthConfig, OAuthService, JwksValidationHandler, OAuthErrorEvent } from 'angular-oauth2-oidc';
@Component({
selector: 'app-root',
template: `<h1>Welcome!</h1>
<p>
<button (click)='login()'>login</button>
<button (click)='logout()'>logout</button>
<button (click)='refresh()'>refresh</button>
</p>
<h2>Your Claims:</h2><pre>{{claims | json}}</pre>
<h2>Your access token:</h2><p><code>{{accessToken}}</code></p>`,
styles: []
})
export class AppComponent {
constructor(public oauthService: OAuthService) {
this.oauthService.configure({
issuer: 'http://localhost:5000',
redirectUri: window.location.origin + '/',
silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
clientId: 'my-spa',
scope: 'openid profile',
silentRefreshTimeout: 5000, // For faster testing
sessionChecksEnabled: true,
});
this.oauthService.tokenValidationHandler = new JwksValidationHandler();
this.oauthService.loadDiscoveryDocumentAndLogin();
this.oauthService.setupAutomaticSilentRefresh();
this.oauthService.events.subscribe(e => {
if (e instanceof OAuthErrorEvent) { console.error(e); }
else { console.warn(e) }
});
}
public get claims() { return this.oauthService.getIdentityClaims(); }
public get accessToken() { return this.oauthService.getAccessToken(); }
login() { this.oauthService.initImplicitFlow(); }
logout() { this.oauthService.logOut(); }
refresh() { this.oauthService.silentRefresh(); }
}
On the IDServer4 side I'm configuring my client like this:
new Client
{
ClientId = "my-spa",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
AccessTokenLifetime = 30, // Seconds (for testing purposes)
RedirectUris = { "http://localhost:4200/", "http://localhost:4200/silent-refresh.html" },
PostLogoutRedirectUris = { "http://localhost:4200/" },
AllowedCorsOrigins = { "http://localhost:4200" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
}
}
As you can see, on the server I've whitelisted "http://localhost:4200/silent-refresh.html"
in the Client
's RedirectUris
property.
My question is whether you can configure angular-oauth2-oidc
in a way that it doesn't require me to whitelist the silent-refresh.html
page?
The reason I ask is because I want to use this Silent Refresh approach also in situations where it might not be so easy to change the IdentityServer side. Also, looking at Damien Bod's example of Silent Refresh in an Angular application I feel it should somehow be possible, because there such a whitelisting is not mentioned.
PS. If I don't include the extra option for the RedirectUris
, then I get Invalid redirect_uri: http://localhost:4200/silent-refresh.html
on the server (logs) and a silent_refresh_timeout
OAuthErrorEvent
in the client side library.
After working some more with the library, I think the answer is that you can't easily do it.
If you have to do it, you'd need to tweak the index.html
that serves the Angular app so that it bootstraps differently. Basically, the silent refresh would load that file instead of the silent-refresh.html
file, so the index will have to have 2 moduses: one for the regular app loading, and another one that effectively does this:
parent.postMessage(location.hash, location.origin);
Because that's all the silent-refresh.html
does. This can prove a bit difficult though, because it would require hooking into the generation of index.html
when the CLI creates a production build.
So even though it's technically possible, it's not very pragmatic to do so.