mirror of
https://github.com/Alfresco/alfresco-ng2-components.git
synced 2025-07-31 17:38:48 +00:00
[AAE-12521] move navigation to the guard to fix infinite loop issue with code flow auth
This commit is contained in:
@@ -16,57 +16,90 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { TestBed } from '@angular/core/testing';
|
import { TestBed } from '@angular/core/testing';
|
||||||
import { ActivatedRouteSnapshot, RouterStateSnapshot } from '@angular/router';
|
|
||||||
import { RouterTestingModule } from '@angular/router/testing';
|
import { RouterTestingModule } from '@angular/router/testing';
|
||||||
import { AuthService } from './auth.service';
|
import { Router } from '@angular/router';
|
||||||
import { OidcAuthGuard } from './oidc-auth.guard';
|
import { OidcAuthGuard } from './oidc-auth.guard';
|
||||||
|
import { AuthService } from './auth.service';
|
||||||
const state: RouterStateSnapshot = {
|
|
||||||
root: new ActivatedRouteSnapshot(),
|
|
||||||
url: 'http://example.com'
|
|
||||||
};
|
|
||||||
const routeSnapshot = new ActivatedRouteSnapshot();
|
|
||||||
|
|
||||||
describe('OidcAuthGuard', () => {
|
describe('OidcAuthGuard', () => {
|
||||||
|
let oidcAuthGuard: OidcAuthGuard;
|
||||||
|
let authServiceSpy: jasmine.SpyObj<AuthService>;
|
||||||
|
let routerSpy: jasmine.SpyObj<Router>;
|
||||||
|
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
|
const routerSpyObj = jasmine.createSpyObj('Router', ['navigateByUrl']);
|
||||||
|
const authSpy = jasmine.createSpyObj('AuthService', ['loginCallback']);
|
||||||
|
|
||||||
TestBed.configureTestingModule({
|
TestBed.configureTestingModule({
|
||||||
imports: [RouterTestingModule],
|
providers: [
|
||||||
providers: [OidcAuthGuard]
|
OidcAuthGuard,
|
||||||
|
{ provide: AuthService, useValue: authSpy },
|
||||||
|
{ provide: Router, useValue: routerSpyObj }
|
||||||
|
],
|
||||||
|
imports: [RouterTestingModule]
|
||||||
|
});
|
||||||
|
|
||||||
|
routerSpy = TestBed.inject(Router) as jasmine.SpyObj<Router>;
|
||||||
|
oidcAuthGuard = TestBed.inject(OidcAuthGuard);
|
||||||
|
authServiceSpy = TestBed.inject(AuthService) as jasmine.SpyObj<AuthService>;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('canActivate', () => {
|
||||||
|
it('should return true if is authenticated', () => {
|
||||||
|
authServiceSpy.authenticated = true;
|
||||||
|
|
||||||
|
const result = oidcAuthGuard.canActivate();
|
||||||
|
|
||||||
|
expect(result).toBe(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should call isAuthenticated and return the result', () => {
|
||||||
|
const isAuthenticatedSpy = spyOn<any>(oidcAuthGuard, '_isAuthenticated').and.returnValue(true);
|
||||||
|
|
||||||
|
const result = oidcAuthGuard.canActivate();
|
||||||
|
|
||||||
|
expect(isAuthenticatedSpy).toHaveBeenCalled();
|
||||||
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#canActivate', () => {
|
describe('canActivateChild', () => {
|
||||||
it('should return false if the user is not authenticated, and call login method', () => {
|
it('should call isAuthenticated and return its result', () => {
|
||||||
const authService = { authenticated: false, login: jasmine.createSpy() } as unknown as AuthService;
|
const isAuthenticatedSpy = spyOn<any>(oidcAuthGuard, '_isAuthenticated').and.returnValue(true);
|
||||||
const authGuard = new OidcAuthGuard(authService);
|
|
||||||
|
|
||||||
expect(authGuard.canActivate(routeSnapshot, state)).toEqual(false);
|
const result = oidcAuthGuard.canActivateChild();
|
||||||
expect(authService.login).toHaveBeenCalled();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should return true if the user is authenticated', () => {
|
expect(isAuthenticatedSpy).toHaveBeenCalled();
|
||||||
const authService = { authenticated: true } as unknown as AuthService;
|
expect(result).toBe(true);
|
||||||
const authGuard = new OidcAuthGuard(authService);
|
|
||||||
|
|
||||||
expect(authGuard.canActivate(routeSnapshot, state)).toEqual(true);
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('#canActivateChild', () => {
|
describe('isAuthenticated', () => {
|
||||||
it('should return false if the user is not authenticated, and call login method', () => {
|
it('should return true if is authenticated', () => {
|
||||||
const authService = { authenticated: false, login: jasmine.createSpy() } as unknown as AuthService;
|
authServiceSpy.authenticated = true;
|
||||||
const authGuard = new OidcAuthGuard(authService);
|
|
||||||
|
|
||||||
expect(authGuard.canActivateChild(routeSnapshot, state)).toEqual(false);
|
const result = oidcAuthGuard['_isAuthenticated']();
|
||||||
expect(authService.login).toHaveBeenCalled();
|
|
||||||
|
expect(result).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return true if the user is authenticated', () => {
|
it('should call loginCallback and navigateByUrl if not authenticated', async () => {
|
||||||
const authService = { authenticated: true } as unknown as AuthService;
|
authServiceSpy.authenticated = false;
|
||||||
const authGuard = new OidcAuthGuard(authService);
|
authServiceSpy.loginCallback.and.returnValue(Promise.resolve('/fake-route'));
|
||||||
|
|
||||||
expect(authGuard.canActivateChild(routeSnapshot, state)).toEqual(true);
|
await oidcAuthGuard.canActivate();
|
||||||
});
|
|
||||||
|
expect(authServiceSpy.loginCallback).toHaveBeenCalled();
|
||||||
|
expect(routerSpy.navigateByUrl).toHaveBeenCalledWith('/fake-route', { replaceUrl: true });
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should navigate to default route if loginCallback fails', async () => {
|
||||||
|
authServiceSpy.authenticated = false;
|
||||||
|
authServiceSpy.loginCallback.and.returnValue(Promise.reject(new Error()));
|
||||||
|
|
||||||
|
await oidcAuthGuard.canActivate();
|
||||||
|
|
||||||
|
expect(routerSpy.navigateByUrl).toHaveBeenCalledWith('/', { replaceUrl: true });
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
@@ -16,15 +16,17 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { Injectable } from '@angular/core';
|
import { Injectable } from '@angular/core';
|
||||||
import { CanActivate, UrlTree } from '@angular/router';
|
import { CanActivate, Router, UrlTree } from '@angular/router';
|
||||||
import { Observable } from 'rxjs';
|
import { Observable } from 'rxjs';
|
||||||
import { AuthService } from './auth.service';
|
import { AuthService } from './auth.service';
|
||||||
|
|
||||||
|
const ROUTE_DEFAULT = '/';
|
||||||
|
|
||||||
@Injectable({
|
@Injectable({
|
||||||
providedIn: 'root'
|
providedIn: 'root'
|
||||||
})
|
})
|
||||||
export class OidcAuthGuard implements CanActivate {
|
export class OidcAuthGuard implements CanActivate {
|
||||||
constructor(private auth: AuthService) {}
|
constructor(private auth: AuthService, private _router: Router) { }
|
||||||
|
|
||||||
canActivate(
|
canActivate(
|
||||||
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree {
|
||||||
@@ -40,13 +42,10 @@ export class OidcAuthGuard implements CanActivate {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const loginResult = this.auth.loginCallback({customHashFragment: window.location.search});
|
return this.auth.loginCallback({ customHashFragment: window.location.search })
|
||||||
|
.then(route => this._router.navigateByUrl(route, { replaceUrl: true }))
|
||||||
if (loginResult instanceof Promise) {
|
.catch(() => this._router.navigateByUrl(ROUTE_DEFAULT, { replaceUrl: true }));
|
||||||
return loginResult.then(() => true).catch(() => false);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -16,26 +16,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
import { ChangeDetectionStrategy, Component } from '@angular/core';
|
||||||
import { Router } from '@angular/router';
|
|
||||||
import { from, of } from 'rxjs';
|
|
||||||
import { catchError, first, map } from 'rxjs/operators';
|
|
||||||
import { AuthService } from '../../auth.service';
|
|
||||||
|
|
||||||
const ROUTE_DEFAULT = '/';
|
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
template: '<div data-automation-id="auth-confirmation"></div>',
|
template: '<div data-automation-id="auth-confirmation"></div>',
|
||||||
changeDetection: ChangeDetectionStrategy.OnPush
|
changeDetection: ChangeDetectionStrategy.OnPush
|
||||||
})
|
})
|
||||||
export class AuthenticationConfirmationComponent {
|
export class AuthenticationConfirmationComponent {
|
||||||
constructor(private auth: AuthService, private _router: Router) {
|
constructor(){}
|
||||||
const routeStored$ = from(this.auth.loginCallback()).pipe(
|
|
||||||
map((route) => route || ROUTE_DEFAULT),
|
|
||||||
catchError(() => of(ROUTE_DEFAULT))
|
|
||||||
);
|
|
||||||
|
|
||||||
routeStored$.pipe(first()).subscribe((route) => {
|
|
||||||
this._router.navigateByUrl(route, { replaceUrl: true });
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user