Struggling with connecting the asynchro request from authentication service with ActivationGuard - javascript

I'm struggling with connecting the ActivationGuard with an UserAuthenticationService, which is returning from server if the given user is logged or not. The main problem here imo is the fact, it's coming from an asynchro request.
I've got some code, made with help from one kind user on Stack. It's made with observables.
But the problem here is that I'm getting following error, when entering the protected components:
zone.js:388 Unhandled Promise rejection: Cannot read property 'do' of undefined;
My files:
ActivationGuard.ts
import {Injectable} from '#angular/core';
import {Router, CanActivate, RouterStateSnapshot, ActivatedRouteSnapshot} from '#angular/router';
import {Observable} from 'rxjs/Observable';
import {UserAuthenticationService} from './UserAuthenticationService';
#Injectable()
export class ActivationGuard implements CanActivate {
constructor(private router: Router, private userService: UserAuthenticationService) {
}
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.userService.isUserAuthenticated
.do(success => {
if (!success) {
this.router.navigate(['/']);
}
});
}
}
UserAuthenticationService.ts
import {Injectable, OnInit} from '#angular/core';
import {Http} from '#angular/http';
import {Observable} from "rxjs/Observable";
#Injectable()
export class UserAuthenticationService implements OnInit {
public isUserAuthenticated: Observable<boolean>;
username: string = 'admin';
constructor(private http: Http) {}
ngOnInit(){
this.isUserAuthenticated = this.http.get(`http://localhost/api/auth/isLogged/${this.username}`)
.map(res => res.json())
.share();
}
}
Looking forward for any kind of help. If you have any questions let me know. Thank you.

Related

Auth0 angular 7 login redirect loop

I am using auth0 to configure my angular 7 app with auth. I followed the quickstart guide to get up and running. The only thing I added in addition to the auth.service.ts file is:
getTokenSilently$(options?): Observable<string> {
return this.auth0Client$.pipe(
concatMap((client: Auth0Client) => from(client.getTokenSilently(options)))
);
}
to support the HTTP interceptor:
import { Injectable } from '#angular/core';
import {
HttpRequest,
HttpHandler,
HttpEvent,
HttpInterceptor
} from '#angular/common/http';
import { AuthService } from './auth.service';
import { Observable, throwError } from 'rxjs';
import { mergeMap, catchError } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class InterceptorService implements HttpInterceptor {
constructor(private auth: AuthService) { }
intercept(
req: HttpRequest<any>,
next: HttpHandler
): Observable<HttpEvent<any>> {
return this.auth.getTokenSilently$().pipe(
mergeMap(token => {
const tokenReq = req.clone({
setHeaders: { Authorization: `Bearer ${token}` }
});
return next.handle(tokenReq);
}),
catchError(err => throwError(err))
)
}
}
The other thing I did differently is that my app does not have a login button, when the app starts up it trys to go to the home page which has an auth guard:
import { Injectable } from '#angular/core';
import { ActivatedRouteSnapshot, RouterStateSnapshot, UrlTree, CanActivate } from '#angular/router';
import { Observable } from 'rxjs';
import { AuthService } from './auth.service';
import { tap } from 'rxjs/operators';
#Injectable({
providedIn: 'root'
})
export class AuthGuard implements CanActivate {
constructor(private auth: AuthService) {}
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean|UrlTree> | boolean {
return this.auth.isAuthenticated$.pipe(
tap(loggedIn => {
if (!loggedIn) {
this.auth.login(state.url);
}
})
);
}
}
so it automatically calls the login so the user gets redirected to auth0. Once they enter their creds it then redirects them back to the home page. Then all of a sudden the auth.service.ts service is loaded again and the auth guard has fired again and it fires the login function again. This seems to just keep on looping.
Any idea why it just keeps on looping?
Replace your Guard method by this one :
canActivate(
next: ActivatedRouteSnapshot,
state: RouterStateSnapshot
): Observable<boolean> | Promise<boolean | UrlTree> | boolean {
return this.auth.isAuthenticated$.pipe(
delay(1000),
tap(loggedIn => {
if (!loggedIn) {
this.auth.login(state.url);
}
})
);
}
The problem was that loggedIn is still at false, even after a successful login.
I am aware that this is not a clean or pretty solution.
I modestly think that the Auth0 Angular sample needs some fixes.
Note: This answer could be helpful to avoid the redirect loop issue for anyone building a Angular SPA
And using the auth0 angular SDK's inbuilt AuthGuard
Or/And using the auth0 provided login page with callback URL
Or facing auth.isAuthenticated$ is always false issue
Try setting the 'Application Type' to 'Single Page Application' in auth0 application properties page

expression has changed after it was checked in loading component

I have repetitive problem in angular but I had search a lot about this problem and use all of Technics that answer in stackoverflow and... .
my problem is in my loader component when I subscribe over than one.
this is my loader component
import { Component, ChangeDetectionStrategy, ChangeDetectorRef, DoCheck, OnChanges, AfterViewInit, OnInit } from '#angular/core';
import { Subject, BehaviorSubject } from 'rxjs';
import { LoaderService } from './loader.service';
#Component({
changeDetection: ChangeDetectionStrategy.OnPush,
selector: 'app-loader',
templateUrl: './loader.component.html',
styleUrls: ['./loader.component.scss']
})
export class LoaderComponent implements OnInit {
isLoading: BehaviorSubject<boolean>=this.loaderService.isLoading;
constructor(private loaderService: LoaderService, private changeDetector: ChangeDetectorRef) {
}
color = 'accent';
mode = 'indeterminate';
value = 50;
ngOnInit(): void {
}
}
and this is my service loader component
import { BehaviorSubject } from 'rxjs/internal/BehaviorSubject';
import { Injectable } from '#angular/core';
import { Subject } from 'rxjs';
#Injectable({
providedIn: 'root'
})
export class LoaderService {
constructor() { }
isLoading: BehaviorSubject<boolean> = new BehaviorSubject(false);
count=0;
show(): void {
debugger
console.log(`show`+this.count++)
this.isLoading.next(true);
}
hide(): void {
debugger
console.log(`hide`+this.count++)
this.isLoading.next(false);
}
}
and this is my interceptor loader
import { Injectable } from '#angular/core';
import { HttpEvent, HttpHandler, HttpInterceptor, HttpRequest } from '#angular/common/http';
import { Observable } from 'rxjs';
import { finalize } from 'rxjs/operators';
import { LoaderService } from './loader/loader.service';
#Injectable()
export class LoaderInterceptor implements HttpInterceptor {
constructor(public loaderService: LoaderService) { }
intercept(req: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
this.loaderService.show();
return next.handle(req).pipe(
finalize(() => {this.loaderService.hide(); })
);
}
}
my error message is
"
ExpressionChangedAfterItHasBeenCheckedError: Expression has changed after it was checked. Previous value: 'ngIf: [object Object]'. Current value: 'ngIf: true'.
at viewDebugError (core.js:17871)
at expressionChangedAfterItHasBeenCheckedError (core.js:17859)
at checkBindingNoChanges (core.js:18059)
at checkNoChangesNodeInline (core.js:27635)
at checkNoChangesNode (core.js:27624)
at debugCheckNoChangesNode (core.js:28228)
at debugCheckDirectivesFn (core.js:28156)
at Object.updateDirectives (loader.component.html:2)
at Object.debugUpdateDirectives [as updateDirectives] (core.js:28145)
at checkNoChangesView ("
please help me to solve it.it's my big problem :-(
I was changing "behavior subject" to observable. subscribe data in loading page and used angular change detector in life cycle.Now, the problem is solve and work correctly
I'm not exactly sure where the 'ngIf' statement is being used, but an alternative might be instead to use css to hide the loader when not in use. E.g.
<div #myLoader [style.display]="isLoading ? 'block' : 'none'>...
To avoid it put a default value for your isLoading property (false for example), and wait the ngOnInit or ngAfterViewInit to change the property in the component.
Tried to replicate your code in a standalone stackblitz instance https://stackblitz.com/edit/angular-multisub with multiple subscriptions for loaderService.
Works without any problem.
Could you fork the above instance and modify to reproduce the same.

Angular 2: Unhandled Promise rejection: No provider for AuthService! ; Zone: angular

I use an AuthService and an AuthGuard to log in/log out users and guard routes. The AuthService is used in the AuthGuard as well as in a LoginComponent. The AuthGuard is used to guard routes via CanActivate. When I try to run the app I get the following error:
zone.js:522 Unhandled Promise rejection: No provider for AuthService! ; Zone: angular ; Task: Promise.then ; Value: NoProviderError {__zone_symbol__error: Error: DI Error
at NoProviderError.ZoneAwareError
I have checked that the LoginComponent and AuthGuard both import the AuthService and inject it into the components via the constructor. I have also checked that the AuthService is imported into the AppModule file and added to the providers array so it can be used as a singleton service.
Edited to add code samples:
My App module contains the following:
#NgModule({
imports: [...],
providers: [..., AuthService, AuthGuard, ...],
declarations: [..., LoginComponent, EntryComponent ...],
bootstrap: [EntryComponent]
})
export class AppModule {
}
AuthGuard:
import { Injectable } from '#angular/core';
import { CanActivate, Router, ActivatedRouteSnapshot, RouterStateSnapshot } from '#angular/router';
import { ApiConfig } from '../Api';
import { AuthService } from './authservice';
#Injectable()
export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router,
private config: ApiConfig
) { }
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
console.log(this.isAuthenticated());
if (this.isAuthenticated()) {
if (this.config.defined) {
return true;
} else {
this.authService.setConfig();
return true;
}
} else {
this.router.navigate(['/Login']);
return false;
}
}
// Checks if user is logged in
isAuthenticated() {
return this.authService.userLoggedIn();
}
}
LoginComponent constructor:
constructor(
private router: Router,
private notifications: Notifications,
private authService: AuthService
) {}
AuthService:
import { Injectable } from '#angular/core';
import { Http, Headers, RequestOptions } from '#angular/http';
import { Router } from '#angular/router';
import { Observable } from 'rxjs/Observable';
import { ApiConfig } from '../Api';
#Injectable()
export class AuthService {
constructor(
private http: Http,
private router: Router,
private config: ApiConfig
) {
this.apiRoot = localStorage.getItem('apiRoot');
}
...
}
In your app.component class #Component({}) decoration add a line that states:
providers: [AuthService]
This creates a singleton service for that service at that level. If you wanted to provide it at a more granular level you would provide (instantiate) it at that lower level.
Please see the first couple paragraphs in this official angular 2 documentation
Turns out I didn't capitalize my AuthService import correctly in AuthGuard.....
import { AuthService } from './authservice';
should have been
import { AuthService } from './AuthService';
Note to self: imports are case sensitive

Cookie undefined in service | cookie value exist in component | angular

api.service.ts
import { Injectable, Inject } from '#angular/core';
import { Http, Headers } from '#angular/http';
import { Observable } from 'rxjs/Observable';
import 'rxjs/add/observable/throw';
import 'rxjs/add/operator/map';
import 'rxjs/add/operator/catch';
import 'rxjs/add/operator/timeout';
import 'rxjs/add/operator/retry';
import { CacheService } from './cache.service';
import { AuthService } from '../services/auth.service';
import { CookieService } from 'angular2-cookie/core';
#Injectable()
export class ApiService {
constructor(
public _http: Http,
private _auth: AuthService,
private _cookie: CookieService,
#Inject('isBrowser') public isBrowser: boolean
) {}
get(){
console.log(this._cookie.get('Token'));//undefined
}
}
controller.component.ts
import { Component, OnInit, ChangeDetectionStrategy } from '#angular/core';
import { ActivatedRoute } from '#angular/router';
import { ApiService } from './api.service';
import { ReviewComponent } from '../shared/+review/review.component';
import { CookieService } from 'angular2-cookie/core';
// import { ModelService } from '../shared/model/model.service';
#Component({
selector: 'mall',
templateUrl: './mall.component.html',
styleUrls:['./mall.component.css'],
providers: [ ApiService, CookieService ]
})
export class MallComponent implements OnInit {
constructor(private _api: ApiService, private route: ActivatedRoute, private _cookie: CookieService) {}
ngOnInit(){
this._cookie.get('Token');// Token => value
this._api.get(); //Token => undefined
}
}
I don't understand this behavior. The cookie exist when i access in controller directly but is undefined when i access through service.
Is there any way to access cookie through services?
using https://github.com/salemdar/angular2-cookie with angular universal.
Maybe this?
ngOnInit(){
this._cookie.put('Token', WHATEVER_TOKEN_IS);// Token => value
console.log(this._api.get('Token')); //Token => undefined
}
and then
api-service
export class ApiService {
constructor(
readonly _http: Http,
private _auth: AuthService,
private _cookie: CookieService,
#Inject('isBrowser') public isBrowser: boolean
) {}
get() {
const token = this._cookie.get('Token');
console.log(token);
return token;
}
}
This might be late, but I went through the same problem.
I was not defining the base path as "/". So what was happening is that the cookie was being set for the default path where I was.
Eg. I was at site.com/auth/
Cookie would get saved at path "/auth"
If I save a cookie like
this.cookieService.set('token', token, null, "/");
then problem is solved.
Hope this helps further devs.
It was my mistake to add CookieService in component providers which initiate a new instance of service which was causing the issue.
#Component({
selector: 'mall',
templateUrl: './mall.component.html',
styleUrls:['./mall.component.css'],
providers: [ ApiService, CookieService ] //<--- here
})
CookieService should only be imported into AppComponent(root) to make a single instance available to other components.

Connect the authentication service with the AuthGuard (simple issue)

I guess it's quite simple issue, but unfortunately I don't really know how to deal with it.
I'm trying to connect my UserAuthenticationService service with the ActivationGuard.
UserAuthenticationService.ts:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
#Injectable()
export class UserAuthenticationService {
isUserAuthenticated: boolean = false;
username: string;
constructor(private http: Http) {
}
authentication() {
this.http.get(`http://localhost/api/auth/isLogged/${this.username}`)
.subscribe(res => { //^^returns true or false, depending if the user is logged or not
this.isUserAuthenticated = res.json();
},
err => {
console.error('An error occured.' + err);
});
}
}
ActivationGuard.ts
import {Injectable} from '#angular/core';
import {Router, RouterStateSnapshot, ActivatedRouteSnapshot} from '#angular/router';
import {Observable} from 'rxjs/Observable';
import {UserAuthenticationService} from './UserAuthenticationService';
interface CanActivate {
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean>|Promise<boolean>|boolean
}
#Injectable()
export class WorksheetAccessGuard implements CanActivate {
constructor(private router: Router, private userService: UserAuthenticationService) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.userService) {
this.router.navigate(['/']);
return false;
}
return true;
}
}
Note
It works great, if I just use localStorage to store the information if the user is logged or not:
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (!localStorage.getItem('currentUser')) {
this.router.navigate(['/']);
return false;
}
return true;
}
But how can I connect the service with the guard? Looking forward for any kind of help. Thank you in advance.
If you need any more information, please let me know and I will edit my post.
Call authentication() method of UserAuthenticationService either in constructor or On ngOnit then it sets the isUserAuthenticated variable and use that in the ActivationGuard.ts
UserAuthenticationService.ts:
import {Injectable} from '#angular/core';
import {Http} from '#angular/http';
#Injectable()
export class UserAuthenticationService {
isUserAuthenticated: boolean = false;
username: string;
constructor(private http: Http) {
this.authentication();
}
authentication() {
this.http.get(`http://localhost/api/auth/isLogged/${this.username}`)
.subscribe(res => { //^^returns true or false, depending if the user is logged or not
this.isUserAuthenticated = res.json();
},
err => {
console.error('An error occured.' + err);
});
}
}
ActivationGuard.ts
#Injectable()
export class WorksheetAccessGuard implements CanActivate {
constructor(private router: Router, private userService: UserAuthenticationService) {
}
public canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean {
if (this.userService.isUserAuthenticated) {
this.router.navigate(['/']);
return false;
}
return true;
}
}
This is not the right approach for doing it. Every time you call the service , it initialize a new instance and hence you get a false.
You should create a singleton service instance ( via the main module in your app) - where it will contain your app state ( in memory / localstorage)
Then , when you'll call UserAuthenticationService - you won't update its owbn parameter but the main's one ( the singleton).
I suggest you to use a BehaviourSubject ( read about it , it's like a Subject but it also yields its last value without waiting to emit a value manually).
From that point your app can see from anywhere ig the user is logged in or not.

Categories

Resources