Has something changed recently, I had the auto login feature working
https://github.com/damienbod/angular-auth-oidc-client/issues/154
but testing it again and it seems to be going in an endless loop between trying to validate from the sts server and then being passed into auto login. Is this some kind of timing issue?
Looks like for some reason this.oidcSecurityService.moduleSetup in app.component.ts is always false
I followed the sample in https://github.com/damienbod/dotnet-template-angular/tree/master/dotnet-angular-google-oidc. Any ideas ?
@satkanth will do a auto login branch in this repo, hopefully Friday
Greetings Damien
Thanks @damienbod, appreciate it.
Here's an example:
https://github.com/damienbod/dotnet-template-angular/blob/auto-login/dotnet-angular-azure-ad-oidc/ClientApp/src/app/authorization.guard.ts
I just add a windows hash check. you can then use the guard where required. The auto login component init the login.
Greetin Damien
I am still not able to get past the endless loop. The getIsAuthorized() is never getting set to true and this.oidcSecurityService.moduleSetup is always false. I followed the example above and I am still seeing the same issue.
I cloned your sample with the changes mentioned above and applied my configuration changes (Sts etc) and it works fine but having almost identical changes is still causing issue with the endless loop between autologin and the redirects.
@satkanth strange. If you see the difference, I would be very interested.
@damienbod, I am going through the package.json to see if there are any discrepancies and the only ones missing from my app are
"@angular-devkit/core": "0.3.1",
"@nguniversal/module-map-ngfactory-loader": "^5.0.0-beta.5"
I have upgraded all my other dependencies to the same version as your sample file with no luck.
Are there any dependencies that could potentially cause the issue that I am having ? I am just not able to track the exact issue down.
The endless loop must be something to do with the code implementation, not with the dependencies. You need to compare the routes, module. Try turning off the auto login and active it per root
Greetings Damien
@damienbod - This is some of the code I have, do you see anything that I am missing ?
app.routes.ts
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
import { HomeComponent } from './home/home.component';
import { AutoLoginComponent } from './autologin/auto-login.component';
import { UnauthorizedComponent } from './core/unauthorized/unauthorized.component';
import { ChildComponent } from './tenants/create/tenant-create.component';
import { AuthGuard } from './auth.guard'
const routes: Routes = [
{path: '', component: HomeComponent, pathMatch: 'full', canActivate: [AuthGuard]},
{path: 'home', component: HomeComponent, children: [{path: 'create', component: TenantCreateComponent }], canActivate: [AuthGuard]},
{path: 'autologin', component: AutoLoginComponent},
{path: 'unauthorized', component: UnauthorizedComponent},
{path: 'tenants', component: TenantListComponent, canActivate: [AuthGuard]}
];
@NgModule({ imports: [RouterModule.forRoot(routes)], exports: [RouterModule]})
export class AppRoutingModule { }
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, APP_INITIALIZER } from '@angular/core';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { Router } from '@angular/router';
import { HttpModule } from '@angular/http';
import { HttpClientModule } from '@angular/common/http';
import { FlexLayoutModule } from '@angular/flex-layout';
import { AuthModule, OidcSecurityService, OpenIDImplicitFlowConfiguration, OidcConfigService, AuthWellKnownEndpoints } from 'angular-auth-oidc-client';
import { AppRoutingModule } from './app-routing.module';
import { MaterialModule } from './material.module';
import { AppComponent } from './app.component';
import { AutoLoginComponent } from './autologin/auto-login.component';
import { HomeComponent } from './home/home.component';
import { ClientService } from './client.service';
import { AppSettings } from './app.settings';
import { AuthGuard } from './auth.guard'
export function loadConfig(oidcConfigService: OidcConfigService, router: Router) {
return () => { oidcConfigService.load_using_stsServer('http://stsServer')};
}
@NgModule({
declarations: [
AppComponent,
AutoLoginComponent,
HomeComponent
],
imports: [
BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
BrowserAnimationsModule,
HttpModule,
HttpClientModule,
AuthModule.forRoot(),
FlexLayoutModule,
ApolloModule,
HttpLinkModule,
AppRoutingModule,
MaterialModule,
CoreModule,
SharedModule,
TenantsModule
],
providers: [
OidcSecurityService,
OidcConfigService,
{
provide: APP_INITIALIZER,
useFactory: loadConfig,
deps: [OidcConfigService],
multi: true
},
ClientService,
AuthGuard
],
bootstrap: [AppComponent]
})
export class AppModule {
constructor(private oidcSecurityService: OidcSecurityService, private oidcConfigService: OidcConfigService,
private clientService: ClientService) {
this.oidcConfigService.onConfigurationLoaded.subscribe(() => {
const openIDImplicitFlowConfiguration = new OpenIDImplicitFlowConfiguration();
openIDImplicitFlowConfiguration.stsServer = 'http://stsServer';
openIDImplicitFlowConfiguration.redirect_url = 'http://localhost:4200';
openIDImplicitFlowConfiguration.client_id = 'admin';
openIDImplicitFlowConfiguration.response_type = 'id_token token';
openIDImplicitFlowConfiguration.scope = 'openid profile admin_api';
openIDImplicitFlowConfiguration.post_logout_redirect_uri = 'http://localhost:4200/Unauthorized';
openIDImplicitFlowConfiguration.post_login_route = '/home';
openIDImplicitFlowConfiguration.forbidden_route = '/Forbidden';
openIDImplicitFlowConfiguration.unauthorized_route = '/Unauthorized';
openIDImplicitFlowConfiguration.trigger_authorization_result_event = true;
openIDImplicitFlowConfiguration.log_console_warning_active = true;
openIDImplicitFlowConfiguration.log_console_debug_active = true;
openIDImplicitFlowConfiguration.max_id_token_iat_offset_allowed_in_seconds = 20;
const authWellKnownEndpoints = new AuthWellKnownEndpoints();
authWellKnownEndpoints.setWellKnownEndpoints(this.oidcConfigService.wellKnownEndpoints);
this.oidcSecurityService.setupModule(openIDImplicitFlowConfiguration, authWellKnownEndpoints);
},
error => {
console.log("Auth Module: Error Occured in Auth Module Registration", error);
});
this.clientService.create();
console.log('APP STARTING');
}
}
app.component.ts
import { Component } from '@angular/core';
import { AuthorizationResult, OidcSecurityService } from 'angular-auth-oidc-client';
import { Router } from '@angular/router';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'app';
constructor(public oidcSecurityService: OidcSecurityService,
private router: Router
) {
if (this.oidcSecurityService.moduleSetup) {
this.onOidcModuleSetup();
} else {
this.oidcSecurityService.onModuleSetup.subscribe(() => {
this.onOidcModuleSetup();
});
}
this.oidcSecurityService.onAuthorizationResult.subscribe(
(authorizationResult: AuthorizationResult) => {
this.onAuthorizationResultComplete(authorizationResult);
});
}
ngOnInit() {
}
ngOnDestroy(): void {
this.oidcSecurityService.onModuleSetup.unsubscribe();
}
private onOidcModuleSetup() {
if (window.location.hash) {
this.oidcSecurityService.authorizedCallback();
} else {
if ('/autologin' !== window.location.pathname) {
this.write('redirect', window.location.pathname);
}
this.oidcSecurityService.getIsAuthorized().subscribe((authorized: boolean) => {
if (!authorized) {
this.router.navigate(['/autologin']);
}
});
console.log('AppComponent:onModuleSetup');
}
}
private onAuthorizationResultComplete(authorizationResult: AuthorizationResult) {
console.log('AppComponent:onAuthorizationResultComplete');
const path = this.read('redirect');
if (authorizationResult === AuthorizationResult.authorized) {
this.router.navigate([path]);
} else {
this.router.navigate(['/Unauthorized']);
}
}
private read(key: string): any {
const data = localStorage.getItem(key);
if (data != null) {
return JSON.parse(data);
}
return;
}
private write(key: string, value: any): void {
localStorage.setItem(key, JSON.stringify(value));
}
}
auto-login.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { OidcSecurityService } from 'angular-auth-oidc-client';
@Component({
selector: 'app-auto-component',
templateUrl: './auto-login.component.html'
})
export class AutoLoginComponent implements OnInit, OnDestroy {
lang: any;
constructor(public oidcSecurityService: OidcSecurityService
) {
this.oidcSecurityService.onModuleSetup.subscribe(() => { this.onModuleSetup(); });
}
ngOnInit() {
if (this.oidcSecurityService.moduleSetup) {
this.onModuleSetup();
}
}
ngOnDestroy(): void {
this.oidcSecurityService.onModuleSetup.unsubscribe();
}
private onModuleSetup() {
this.oidcSecurityService.authorize();
}
}
authorization.guard.ts
import { Injectable } from '@angular/core';
import { Router, CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { map } from 'rxjs/operators';
import { OidcSecurityService } from 'angular-auth-oidc-client';
@Injectable()
export class AuthorizationGuard implements CanActivate {
constructor(
private router: Router,
private oidcSecurityService: OidcSecurityService
) { }
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | boolean {
console.log(route + '' + state);
console.log('AuthorizationGuard, canActivate');
return this.oidcSecurityService.getIsAuthorized().pipe(
map((isAuthorized: boolean) => {
console.log('AuthorizationGuard, canActivate isAuthorized: ' + isAuthorized);
if (isAuthorized) {
return true;
}
this.router.navigate(['/unauthorized']);
return false;
})
);
}
}
You use the AuthGuard on the default root. Remove this from the following
{path: '', component: HomeComponent, pathMatch: 'full', canActivate: [AuthGuard]},
=>
{path: '', component: HomeComponent, pathMatch: 'full'},
OR
Add a check for a authorized callback in the auth guard so that it will not redirect.
Greetings Damien
@damienbod Thank you for your patience. If you see my sample code above I do have a check in the auth guard for an authorized call back and I want the default root, which sends you to the home component, to use the AuthGuard. This is what you have done in your sample code at
https://github.com/damienbod/dotnet-template-angular/tree/auto-login/dotnet-angular-azure-ad-oidc/ClientApp/src/app
The only difference in the example above is you do not have it automatically sending you to autologin in app.component.ts
private onOidcModuleSetup() {
if (window.location.hash) {
this.oidcSecurityService.authorizedCallback();
} else {
if ('/autologin' !== window.location.pathname) {
this.write('redirect', window.location.pathname);
}
console.log('AppComponent:onModuleSetup');
}
}
@satkanth You need to add this check to the auth guard as well then.
Something like this:
Greetings Damien
@damienbod - Creating a sample git app with the issue.
@damienbod - I have created a shell of the app at
https://github.com/satkanth/TestAngularAuthOidcClient
I have put in the google sample setting you had for setting up the openIDImplicitFlowConfiguration in app.module.ts. I would really appreciate if you could take a look at this.
@satkanth Thanks, I look at it tonight.
@damienbod I was wondering if you had a chance to look at it. This is a blocking issue for me if I can't resolve it and your feedback will be invaluable.
Any ideas on this one?
To get around this you need to have a route that doesn't do the authGuard check. Make that routethe redirectURL. This is happening because you are being redirected to a page and immediately checking if auth'd while your cookies or session have not been set yet.
@satkanth I have an idea. I have been struggling with the same problem for a few days and I noticed this line in my console:
validate_id_token_iat_max_offset: 9726 < 10000
It turns out that the time between my token server issuing the id token and my SPA validating it was more than the allowed 10 seconds. For me, just increasing the allowed offset to 20 seconds did the trick. Hope this helps!
@mcshiz, could I potentially go back to the autologin page which does not have auth guard on it ? But this is where I am doing
this.oidcSecurityService.authorize();
So does this mean that it has to be a completely new route and what would this page do?
@PeterRockstars, thank you I will try this and see how it goes.
What I did was send them to a /login/callback route which is just empty. Then I have trigger_authorization_result_event = true which waits for a successfull login result, when it receives that I redirect them into /secure/
@mcshiz
Could you post example of your flow described? It will be very helpfull
@satkanth @damienbod Still don鈥檛 know how to do without unauthorized & forbidden components - just redirecting to login form of sts server (identity server 4) as separate project. Will be appreciate for any examples. Still endless loop and unable to redirect to module /component after login. In Auth guard it鈥檚 always authorized = false.
@inpicksys see onAuthorizationResult and trigger_authorization_result_event
You should be able to regulate your flow.
Is this solved?
@Booster2ooo Thanks for advice about "should"
Implemented @mcshiz suggestion and was able to get this up and working. This can be closed.
Sorry to resurrect a closed thread, but I could use your experience. @PeterRockstars mentioned increasing the time from 10 seconds to 20 seconds. My question is how do you account for every user having a correctly synced system time? That seems impractical, so I am searching to learn more about this topic.
@wolfhoundjesse I'm also curious as to what the recommended value is. Another OIDC implementation I found uses 600 seconds as default. I haven't been able to find any other info, in the specs or online.
Most helpful comment
@satkanth I have an idea. I have been struggling with the same problem for a few days and I noticed this line in my console:
validate_id_token_iat_max_offset: 9726 < 10000It turns out that the time between my token server issuing the id token and my SPA validating it was more than the allowed 10 seconds. For me, just increasing the allowed offset to 20 seconds did the trick. Hope this helps!