In my project I have two guards. AuthGuard and PermissionGuard. I need to first AuthGuard runs and when it resolved and if true the permissionGuard begins but now this guards are running parallel and permissionGuard not working well. the way I used for this issue is that I called the AuthGuard CanActivate method in Permission guard but I think there is a quite better way for doing this.
The best way I've seen it done is to expose the router guards on child routes. Here is a working example.
{
path:'', canActivate:[AuthorizationService1],
children: [
{
path:'', canActivate:[AuthorizationService2],component: HomeComponent
}
]
}
guards can't really depend on each other unfortunately. you can do as another person suggested and make one on the parent route and one on a child route, or My preference is just to make a third guard that runs both checks on it's own sequentially so I don't need to muddy up my routes, assuming they're implemented as observables:
#Injectable({ providedIn: 'root' })
export class AuthAndPermissionGuard implements CanActivate {
constructor(private authGuard: AuthGuard, permGuard: PermGuard) { }
canActivate(next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> {
return this.authGuard.canActivate(next, state).pipe(
switchMap(isAuthed => isAuthed ? this.permGuard.canActivate(next, state) : of(false))
);
}
}
you can esaly pass two gurd to routes like this :
{
path:'', canActivate:[AuthorizationGuards1,AuthorizationGuards2]
}
after AuthorizationGuards1 success AuthorizationGuards2 activated, the relation between these two guard is st like AuthorizationGuards1 && AuthorizationGuards2 conditions.
Related
I have console.log in constructor and ngOnInit() of Resolver but which are not logged.
resolve:{serverResolver:ServerResolverDynamicDataService}},
console.log("ServerResolverDynamicDataService constructor");
console.log('ServerResolverDynamicDataService resolve:'
const appRoutes : Routes =[
{path:"",component:HomeComponent},
{path:"servers",canActivateChild:[AuthGuardService],component:ServersComponent,
children:[
{path:":id",component:ServerComponent,
resolve:{serverResolver:ServerResolverDynamicDataService}},
{path:":id/edit",component:EditServerComponent,canDeactivate:[CanDeativateGuardService]}]},
Resolver:
#Injectable()
export class ServerResolverDynamicDataService implements Resolve<ServerModel>{
constructor(private serversService:ServersService){
console.log("ServerResolverDynamicDataService constructor");
}
resolve(activatedRouteSnapshot: ActivatedRouteSnapshot, routerStateSnapshot:
RouterStateSnapshot): ServerModel | Observable<ServerModel> | Promise<ServerModel> {
console.log('ServerResolverDynamicDataService resolve:'+activatedRouteSnapshot.params['id']);
return this.serversService.getServer(+activatedRouteSnapshot.params['id']);
}
}
Update1: app.module.ts has entry of this service in providers
providers: [ServersService,AuthGuardService,AuthService,CanDeativateGuardService,ServerResolverDynamicDataService],
Whenever URL(http://localhost:4200/servers/1?allowToEdit=0&allowTest=2#loadPage) is getting hit, no logs are coming from resolver but logs are there in code and the application is properly refreshing if I edit any other part of the application log. So app changes are reflecting the only problem is resolver is not called.
Update2
As per Angular 2+ route resolvers not being called if I remove parent canActivateChild service which is working.B ut I don't know what is wrong.Please help me to understand.
A Resolver is a service and OnInit isn't executed in a service. In fact, besides OnDestroy, there's no other lifecycle hook in a service.
Anyway, I'm assuming your resolver is provided somewhere. If it's not, you should use an argument in its decorator: #Injectable({providedIn: 'root'}).
I am building an application using Angular 7, I have handled the API calls, the JWT Token authentication system using C#, and also updating the LocalStorage() when necessary, when the user logs in and logs out, and all these are working perfectly.
My problem is I want it to run a login check as a middleware within the application rather than on the lifecycle method - ng.onInit(). How do I go about this?
Is there a way to execute lifecycle events as an entry component or service. That is, before any component loads it is able to check if the user is logged in or not and redirect via Router to a desired page.
Guard is based on the routes... so I think you should prefer a module/service solution.
import { APP_INITIALIZER } from '#angular/core';
then add it as a provider like this :
export function initApp(initService: YourInitService) {
return () => {
initService.Init();
}
}
{ provide: APP_INITIALIZER,useFactory: initApp, deps: [YourInitService], multi: true }
Routing Decisions Based on Token Expiration
If you’re using JSON Web Tokens (JWT) to secure your Angular app (and I recommend that you do), one way to make a decision about whether or not a route should be accessed is to check the token’s expiration time. It’s likely that you’re using the JWT to let your users access protected resources on your backend. If this is the case, the token won’t be useful if it is expired, so this is a good indication that the user should be considered “not authenticated”.
Create a method in your authentication service which checks whether or not the user is authenticated. Again, for the purposes of stateless authentication with JWT, that is simply a matter of whether the token is expired. The JwtHelperService class from angular2-jwt can be used for this.
// src/app/auth/auth.service.ts
import { Injectable } from '#angular/core';
import { JwtHelperService } from '#auth0/angular-jwt';
#Injectable()
export class AuthService {
constructor(public jwtHelper: JwtHelperService) {}
// ...
public isAuthenticated(): boolean {
const token = localStorage.getItem('token');
// Check whether the token is expired and return
// true or false
return !this.jwtHelper.isTokenExpired(token);
}
}
Note: This example assumes that you are storing the user’s JWT in local storage.
Create a new service which implements the route guard. You can call it whatever you like, but something like auth-guard.service is generally sufficient.
// src/app/auth/auth-guard.service.ts
import { Injectable } from '#angular/core';
import { Router, CanActivate } from '#angular/router';
import { AuthService } from './auth.service';
#Injectable()
export class AuthGuardService implements CanActivate {
constructor(public auth: AuthService, public router: Router) {}
canActivate(): boolean {
if (!this.auth.isAuthenticated()) {
this.router.navigate(['login']);
return false;
}
return true;
}
}
The service injects AuthService and Router and has a single method called canActivate. This method is necessary to properly implement the CanActivate interface.
The canActivate method returns a boolean indicating whether or not navigation to a route should be allowed. If the user isn’t authenticated, they are re-routed to some other place, in this case a route called /login.
Now the guard can be applied to any routes you wish to protect.
// src/app/app.routes.ts
import { Routes, CanActivate } from '#angular/router';
import { ProfileComponent } from './profile/profile.component';
import {
AuthGuardService as AuthGuard
} from './auth/auth-guard.service';
export const ROUTES: Routes = [
{ path: '', component: HomeComponent },
{
path: 'profile',
component: ProfileComponent,
canActivate: [AuthGuard]
},
{ path: '**', redirectTo: '' }
];
The /profile route has an extra config value now: canActivate. The AuthGuard that was created above is passed to an array for canActivate which means it will be run any time someone tries to access the /profile route. If the user is authenticated, they get to the route. If not, they are redirected to the /login route.
Note: The canActivate guard still allows the component for a given route to be activated (but not navigated to). If we wanted to prevent activation altogether, we could use the canLoad guard.
more info here
You should check for Guard in angular, especially canActivate Guard: https://angular.io/guide/router
A guard is created like this:
#Injectable({
providedIn: 'root'
})
export class MyGuard implements CanLoad {
constructor() {}
canLoad(route: Route, segments: UrlSegment[]): Observable<boolean> |
Promise<boolean> | boolean {
const x = true;
if (x) {
return true; // It allows access to the route;
} else {
// redirect where you want;
return false; // it doesnt allow to access to the route
}
}
}
Then in your routing Module:
{
path: "yourRoute",
canActivate: [MyGuard],
component: YourComponent
}
For authentication, you have a good library that uses guard here:
https://www.npmjs.com/package/ngx-auth
You should implement an authGuardService or something like that to use as middleware for your routing (using the canActivate section)
See: https://angular.io/api/router/CanActivate
This prevents routes from being loaded if the canActivate fails the condition (which is preferred when using a login system etc instead of checking in lifecycle hooks).
I am having trouble trying to get the queryparams into a component. For now, I just want to console.log(...) it.
I am using the ActivatedRoute from #angular/router for this task.
I am redeveloping a certain platform for work so unfortunately some irrelevant code will have be to substituted with "..."
My Component.ts code:
import { Component, OnInit, ViewEncapsulation } from '#angular/core';
import { RelevantReportService } from './../reportServices/relevantReports.service';
import { ActivatedRoute ,Params, Router } from '#angular/router';
#Component({
selector: 'vr-reports',
templateUrl: './reports.component.html',
styleUrls: ['./reports.component.scss'],
providers: [RelevantReportService],
encapsulation: ViewEncapsulation.None
})
export class ReportsComponent implements OnInit {
reportSections: any;
constructor( private relevantReportService: RelevantReportService,
private router: Router,
private activatedRoute : ActivatedRoute
) { }
ngOnInit() {
...
console.log(this.activatedRoute.queryParams.value.reportName)
// console.log(this.activatedRoute.queryParams._value.reportName)
}
...
}
When I do console.log(this.activatedRoute.queryParams.value.reportName), the console spits out the queryparams (which is exactly what I wanted) HOWEVER it also says
"Property 'value' does not exist on type 'Observable' "
so I believe this not the correct way of tackling it.
It's observable in order to be able to monitor for changes in the params (by subscribing to observable). To get currently passed query params use:
this.activatedRoute.snapshot.queryParams
You could also use ActivatedRouteSnapshot instead of ActivatedRoute
Nothing surprising there!
activatedRoute.queryParams is an observable, and therefore you need to subscribe to it as per https://angular.io/api/router/ActivatedRoute#queryParams
You need to do the following :
ngOnInit() {
this.activatedRoute.queryParams.subscribe(values => {
console.log(values);//Which will print the properties you have passed
});
}
For Angular5 i would say the best option is using URL tree.
Since a router state is a tree, and the URL is nothing but a serialized state, the URL is a serialized tree. UrlTree is a data structure that provides a lot of affordances in dealing with URLs
Details
https://angular.io/api/router/UrlTree
somehow I've got a service that seems to be instantiated twice (its properties are not in sync), by doing the following:
#Component
export class MyComponent extends someOtherComponent {
constructor(service: Service, service2: Service2) {
super(service, service2);
}
isStateEqual() {
return this.service.serviceState === this.service2.service.serviceState;
}
}
#Injectable
export class Service {
serviceState = {}
}
#Injectable
export class Service2 {
constructor(service: Service) {}
}
This is just a very basic example, but that's what it comes down to. To be more precise: We're building our own datepicker and extending NgbDatepicker component which has KeyMapService (this uses NgbDatepickerService) and a local NgbDatepickerService.Here is a link to the component: https://ng-bootstrap.github.io/#/components/datepicker/examples
In our app isStateEqual will always return false (even right after initialising the component) while in the demo you can find in the link above it will always return true (which is how it should be).
Anyone knows why it could be like that?
Thanks in advance.
Regards
Dennis
Application wide singletons must be defined on the bootstrapped module:
platformBrowserDynamic()
.bootstrapModule(AppModule)
#NgModule({
providers: [SingletonService1, SingletonService2],
bootstrap: [AppComponent]
})
export class AppModule {}
source
or by setting providedIn: 'root' on the service decorator:
#Injectable({
providedIn: 'root',
})
export class UserService {
}
source
In my case the service was instantiated twice, because I imported the service using two different approaches (my IDE (VS2019) mishelped me here by automatically generating the incorrect import):
import { Service } from '#mycompany/mymodule'
and
import { Service } from '../../../dist/#mycompany/mymodule/#mycompany-mymodule';
Visual code import my service in this way automatically
import { ConfigService } from 'src/app/services/config.services.js';
And the correct way is:
import { ConfigService } from 'src/app/services/config.services';
It depends where you declare the provider for your services - this determines the scoping. You don't specify in your question where you've done this - try doing it in AppModule, for example.
"service" must be a public propertie of Service2.
#Injectable
export class Service2 {
service:Service
constructor(service: Service)
{
this.service=service;
}
}
I'm looking for the best way to implement tracking codes into my Angular 2 app. Not Google Analytics, but 3rd party suppliers like Marketo and others. I need the codes to fire each time I load a component(page). I've tried using the router changes with only partial success (and some unexpected results). Some version of this kind of worked but not fully.
this.router.events.subscribe(() => {
//tracking code goes here
});
Has anyone else had success with 3rd party tracking on Angular2 or other SPAs? Putting the tags in the template files doesn't work. Thanks.
Just use a guard on any route you want to track. It will be called every time the route is activated:
import {CanActivate, ActivatedRouteSnapshot, RouterStateSnapshot} from "#angular/router";
import {Observable} from "rxjs";
export class TrackingGuard implements CanActivate
{
canActivate(route: ActivatedRouteSnapshot,
state: RouterStateSnapshot): Observable<boolean>|boolean
{
console.log(state.url);
console.log(state.queryParams);
return true;
}
}
And in your route definitions where you specify which component for the route just add canActivate: [TrackingGuard] under it. In the above example you will have access to the url and any query params. Would recommend making some other service which makes the request to the tracking api and just call if from the guard.