I am trying to implement role based access control on Angular
based project where there are multiple types of roles
I want to get user's data(role property) in CanActivate
interface to check if the user has permission to access protected routes
in guard
, but BehaviouralSubject
does not updating initial state after receiving latest values from API
.
Thus I am getting user's data in authentification service,
then passing received value to userRole
subject, but updated property (BehaviouralSubject)
is not available in guard file.
Authentification Service
constructor() {
let localToken: string
if (localStorage.getItem('token')) {
localToken = localStorage.getItem('token')
this.getUserInfo(localToken)
}
}
userRole = new BehaviorSubject(null)
userRole$ = this.userRole.asObservable()
// called in constructor of a auth. service (every time when user called)
// and after login as well, so user's data always available.
getUserInfo(data) {
this.requestService
.post('GET_USER_INFO', data)
.subscribe((response: ILoggedInUser) => {
// data from API {email: "test@test.com", userName: "ccc", role: "viewer"}
this.userRole.next(response.user)
})
}
Auth Guard
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) {
return this.authService.userRole$.pipe(
map(member => {
if (member) {
// check if route is restricted by role
if (
route.data.roles &&
route.data.roles.indexOf(member.role) === -1
) {
// role not authorised so redirect to home page
this.router.navigate(['/login'])
return false
}
return true
} else {
this.router.navigate(['/login'])
return false
}
}),
)
}
Here is routing:
const routes: Routes = [
{
path: 'home',
component: HomeComponent,
},
{
path: 'admin',
component: AdminComponent,
canActivate: [AuthGuard],
data: { roles: ['admin'] },
},
{
path: 'editor',
component: EditorsComponent,
canActivate: [AuthGuard],
data: { roles: ['admin', 'editor'}
},
{
path: 'viewer',
component: ViewersComponent,
canActivate: [AuthGuard],
data: { roles: ['admin', 'viewer'] },
}
]
I tried another approach as well, (for example using Subscriptions) but when I try to subscribe
to userRole
subject like this:
return this.authService.userRole.subscribe((member) => {
.......
.......
}
then there is a
Property 'canActivate' in type 'AuthGuard' is not assignable to the same property in base type 'CanActivate' error,
since Subscription
type is not assignable to type boolean
.
Therefore, could you give any hint OR recommend the better way of handling RBAC in angular
with observables.
P.S.
I have seen solutions with localStorage
, but I don't want to keep user's data (except token) there.