Davide De Santis
Davide De Santis

Reputation: 1035

Angular 4: Observable not working

The application I'm working implements Azure Active Directory authorization through an adal.js port for Angular 2+, which works perfectly.

We have an AuthenticationGuard that handles the calls to/from our own AdalService. The AdalService has a function ensureAuthenticatedAsync which checks whether the user is logged in and if not, redirects him to the AD login page.

We now have the new requirement to be able to alternatively sign in with a custom generated token without when no AD account is available. After some modifications, my code looks like this:

authentication.guard.ts

public canActivate(route: ActivatedRouteSnapshot, state RouterStateSnapshot) : boolean {
    ...
    let params = route.queryParams;
    let token = (params !== null && params['token'] !== undefined) ? params['token'] : null;

    this.adalService.ensureAuthenticatedAsync(token);
    ...
    return true;
}

adal.service.ts

private context: adal.AuthenticationContext;

public ensureAuthenticatedAsync(token: string | null = null) {
    console.log('ensureAuthenticatedAsync', token);
    this.isAuthenticated.subscribe(isAuthenticated => {
        if (!isAuthenticated) {
            console.log('not authenticated);
            if (token === null) {
                // forward to AAD login page -> this works perfectly
                this.context.login();
            } else {
                ...
                console.log('accessToken before', this.accessToken);
                this.customToken = token;
                console.log('accessToken after', this.accessToken);
                ...
            }
        }
    });
}

public get accessToken(): string {
    console.log('customToken in get accessToken', this.customToken);
    return (this.customToken === null) ? this.context.getCachedToken(this.adalClientId) : this.customToken;
}

public get isAuthenticated(): Observable<boolean> {
    console.log('accessToken in get isAuthenticated', this.accessToken);
    let isAuthenticated: boolean = (this.accessToken !== null);
    return Observable.of(isAuthenticated);
}

app.component.ts

public ngOnInit(): void {
    this.adalService.isAuthenticated.subscribe(isAuthenticated => {
        if (isAuthenticated) {
            // do some stuff here
        }
    });
}

app.routes.ts

export const ROUTES: Routes = [
    { path: 'Dashboard', component: DashboardComponent, canActivate: [AuthenticationGuard] },
    { path: 'Access-Denied', component: AccessDeniedComponent }
];

This is what's logged in the console:

authentication guard  
customToken in get accessToken null  
accessToken in get isAuthenticated null  
customToken in get accessToken null  
ensureAuthenticatedAsync 12345  
not authenticated  
customToken in get accessToken null  
accessToken before null  
customToken in get accessToken 12345  
accessToken after 12345  
customToken in get accessToken 12345

Accessing the page regularly (http://localhost:3000/Dashboard) correctly triggers a redirect to the AD login page, which then returns to my application after login. There, the cacheed AD token is changed and isAuthenticated is triggered wherever it is subscribed to (in my case in app.component.ts ).

However, accessing the page using a token (http://localhost:3000/Dashboard/?token=12345) does not work. Even though customToken is modified with the value in the param, changes don't seem to be propagated and isAuthenticated stays false in the subscription(s).

Did I miss something?

Upvotes: 2

Views: 1414

Answers (2)

Pierre Mallet
Pierre Mallet

Reputation: 7221

I think the problem is about the guard localization.

Your guard is on /Dashboard route. So if you load only http://localhost:3000/?token=12345 your AuthenticationGuard is not called, thus ensureAuthenticatedAsync is not called either. So you have no redirection and not update of customToken in adal.service.ts.

If you want to use the computed result of you guard in appComponent you must use the guard on the route "leading" to appComponent

EDIT : The problem is that you misunderstand the reactive behavior. the way isAuthenticated is implemented it will emit a boolean one time and then complete. So your subscription in app.component will trigger only once.

What you are looking for is behavior subject, where you could "push" new data when needed or when customToken change.

Upvotes: 2

anode7
anode7

Reputation: 396

Try:

in your adal.service.ts :

import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
...
public get isAuthenticated(): Observable<boolean> {
 let isAuthenticated: boolean = (this.accessToken !== null);
 return of(isAuthenticated);
}

Upvotes: 1

Related Questions