This is my first time building a Internet application (im a junior front end dev). Normaly we only do intranet applications but this series has helped me sooooo much to get in touch with JWT Interceptors and a basic login system. THANK YOU SO MUCH!!!!!
Great Video! However I have a question... What do you think about sending /get-profile request to the API and storing user data using behaviour subject? Wouldn't that be as good?
Yes, that would work nicely as well. In fact, you can interact with a behaviourSubject (or a signal ;) ) and have this in sync with your storage (localStorage or sessionStorage)
If the user.roles property has more than one roles and you have to make sure that both roleX and roleY exist in the array, you should make sure to check if all these items exist in the user.roles array. Unless I misunderstood your question
Is it necessary to decode the way you did the role info from token, why don't just send the serialised object from server. Personally I don't like accessing info by using index signatures 😜 In addition I want to learn is it safe to store role in the local storage? Otherwise thanks for the video
No it is not necessary to decode the token like I did. If you choose the tokens to be part of the JWT claims, then you have to decode. An alternative would be to have the roles part of the HTTP response and not part of the JWT. In this case you do not have to decode them. As of the localStorage, it's OK to persist non sensitive information.
You can provide an array of roles in the routing configuration (ua-cam.com/video/YJ4dgoHEmGs/v-deo.html) and then you have to check if the values of the given array exist in the array with the roles you get from the token. The code below will accomplish this. const arr = ['foo', 'bar'] const arr1 = ['foo', 'baz'] const isBoolean = arr.some(it => arr1.includes(it))
Thank you for this awesome tutorial series! I'm just in my first month of learning Angular so I cannot guarantee I got everything perfectly correct, but I'll leave here the issues I've encountered and how I solved them, in case it will help anyone (I used Angular 16) : --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * has-role.guard.ts * - From what I understand 'implements CanActivate' in Angular Guards is deprecated in Angular 16; I found out because I do not have a VSC plugin to generate angular elements by right clicking on folders, I use the CLI instead and this is the command : ng generate guard hasRole - I replaced the assignation of isAuthorized variable from 'this.authService.user.roles.includes(route.data.role);' to 'return authService.userRole?.role.includes(route.data['role']) ?? false;' Because I got the error 'type '(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => boolean | undefined' is not assignable to type 'CanActivateFn'. Type 'boolean | undefined' is not assignable to type 'boolean | UrlTree | Observable | Promise'. Type 'undefined' is not assignable to type 'boolean | UrlTree | Observable | Promise'.' The error is related to the return type mismatch in hasRoleGuard function. The CanActivateFn type expects the guard function to return a boolean, a UrlTree object, an Observable, or a Promise. However, guard function was returning boolean | undefined. To resolve the issue, you can update your guard function to explicitly return a boolean value. In the updated code: The return type of the guard function is explicitly set as boolean. The AuthService is injected using the inject function from @angular/core, and its type is casted to AuthService. The ?. optional chaining operator is used to safely access the role property of authService.userRole. The nullish coalescing operator ?? is used to provide a default value of false if the result of authService.userRole?.role.includes(route.data['role']) is null or undefined. - Here's how my has-role.guard.ts code looked in the end: - import { CanActivateFn, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router'; import { AuthService } from '../services/auth.service'; import { Injectable, inject } from '@angular/core'; export const hasRoleGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean => { const authService = inject(AuthService) as AuthService; const router = inject(Router); // console.log("user role: "+authService.userRole?.role); const isAuthorized = authService.userRole?.role.includes(route.data['role']) ?? false; if(!isAuthorized){ router.navigate(['']); } return isAuthorized; }; --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * auth.service.ts * - In the constructor I replaced the assignation of this.userRole variable from 'this.userRole = this.getUserRole(this.token); to ' this.userRole = this.token ? this.getUserRole(this.token) : null; ' Because I got the error 'Type 'null' is not assignable to type 'string'.' The ternary operator this.token ? this.getUserRole(this.token) : null is used to conditionally assign the value of this.getUserRole(this.token) to this.userRole if this.token is not null. If this.token is null, this.userRole is assigned the value undefined. This way, you avoid the error when this.token is null, and this.userRole will be assigned the appropriate value based on the token's availability. - I replaced the method in tap function from login method too: this.userRole = response.token ? this.getUserRole(response.token) : undefined; - I used 'user?: User' instead of 'user!: User' because I got the error 'Type 'User | undefined' is not assignable to type 'User'. Type 'undefined' is not assignable to type 'User'. ' triggered on the 2 this.user assigns mentioned above --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- * user.Role / user.role / user.roles / user.Roles * - Make sure you use just one of them, in all the places, including JWT property, User model, your auth.service, has-role.guard
This is my first time building a Internet application (im a junior front end dev). Normaly we only do intranet applications but this series has helped me sooooo much to get in touch with JWT Interceptors and a basic login system. THANK YOU SO MUCH!!!!!
Thanks for your feedback and I am really glad it helped you :)
Great Video! However I have a question... What do you think about sending /get-profile request to the API and storing user data using behaviour subject? Wouldn't that be as good?
Yes, that would work nicely as well.
In fact, you can interact with a behaviourSubject (or a signal ;) ) and have this in sync with your storage (localStorage or sessionStorage)
@@CodeShotsWithProfanis thanks for your feedback ❤️
Excellent, Very simple and straight to the point. Thanks
Glad you liked it and thanks for the feedback :)
Nice technique to parse the allowed roles from the access token.
Glad it was helpful!
Just wants to say thank you, saved my semester ❤
Happy to help!
as usual brief and exactly to the point
Thanks for your feedback! :)
Glad you liked it!
Brilliant! What happens when you have several roles that require access? My role setup isn't hierarchical .
If the user.roles property has more than one roles and you have to make sure that both roleX and roleY exist in the array, you should make sure to check if all these items exist in the user.roles array.
Unless I misunderstood your question
Fantastic explanation awaiting for many more
Thanks a lot. Glad you liked it 🙂
Is it necessary to decode the way you did the role info from token, why don't just send the serialised object from server. Personally I don't like accessing info by using index signatures 😜
In addition I want to learn is it safe to store role in the local storage?
Otherwise thanks for the video
No it is not necessary to decode the token like I did. If you choose the tokens to be part of the JWT claims, then you have to decode. An alternative would be to have the roles part of the HTTP response and not part of the JWT. In this case you do not have to decode them.
As of the localStorage, it's OK to persist non sensitive information.
Great , kindly upload Oauth validations , OKTA like that
Great video! Managed to do it thanks to you :D Thanks a lot for the content once more!
Glad you found it usefull! Thanks for the feedback! :)
Great content. May I know extension to create component and guards?
THANKYOU MY FRIEND!
:)
Glad you liked it!
Greet video! Thnx!
What theme you use in VSCode?
Glad you liked it. I am using the Dracula theme
φιλε εισαι απλα θεος
Πολύ χαίρομαι που σου άρεσε φίλε μου!! :)
Great video, really helped me to understand the concept.
Glad it helped you :)
great video for us we wait others thanks
Glad you liked it!
very helpfull
Useful video, thanks
Glad you liked it 😊
Thanks, very helpful.
Glad you liked it :)
What plug in are you using in viscose for schematics?
I use the Angular Schematics extension which you can find here marketplace.visualstudio.com/items?itemName=cyrilletuzi.angular-schematics
Great video!
Thank you! 🙂
Very Nice And Useful
Thanks Mohamed! :)
What is the syntax to allow more than one role to access the page?
You can provide an array of roles in the routing configuration (ua-cam.com/video/YJ4dgoHEmGs/v-deo.html) and then you have to check if the values of the given array exist in the array with the roles you get from the token.
The code below will accomplish this.
const arr = ['foo', 'bar']
const arr1 = ['foo', 'baz']
const isBoolean = arr.some(it => arr1.includes(it))
@@CodeShotsWithProfanis many thanks!
Great video. Really helpful
Glad it was helpful!
can you do a video where the admin sees a different navigation bar then the user?
good idea :)
Hi
After that how to hide components based on role provided in route of data
Hi Gibran. I plan to finish a series of videos with "Libraries How-To' and this will be the next video.
@@CodeShotsWithProfanis now i got that but i could be very help to other's
Thanks for quick reply
Thank you for this awesome tutorial series!
I'm just in my first month of learning Angular so I cannot guarantee I got everything perfectly correct, but I'll leave here the issues I've encountered and how I solved them, in case it will help anyone (I used Angular 16) :
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* has-role.guard.ts *
- From what I understand 'implements CanActivate' in Angular Guards is deprecated in Angular 16; I found out because I do not have a VSC plugin to generate angular elements by right clicking on folders, I use the CLI instead and this is the command :
ng generate guard hasRole
- I replaced the assignation of isAuthorized variable from 'this.authService.user.roles.includes(route.data.role);' to
'return authService.userRole?.role.includes(route.data['role']) ?? false;'
Because I got the error 'type '(route: ActivatedRouteSnapshot, state: RouterStateSnapshot) => boolean | undefined' is not assignable to type 'CanActivateFn'.
Type 'boolean | undefined' is not assignable to type 'boolean | UrlTree | Observable | Promise'.
Type 'undefined' is not assignable to type 'boolean | UrlTree | Observable | Promise'.'
The error is related to the return type mismatch in hasRoleGuard function. The CanActivateFn type expects the guard function to return a boolean, a UrlTree object, an Observable, or a Promise. However, guard function was returning boolean | undefined.
To resolve the issue, you can update your guard function to explicitly return a boolean value.
In the updated code:
The return type of the guard function is explicitly set as boolean.
The AuthService is injected using the inject function from @angular/core, and its type is casted to AuthService.
The ?. optional chaining operator is used to safely access the role property of authService.userRole.
The nullish coalescing operator ?? is used to provide a default value of false if the result of authService.userRole?.role.includes(route.data['role']) is null or undefined.
- Here's how my has-role.guard.ts code looked in the end: -
import { CanActivateFn, ActivatedRouteSnapshot, RouterStateSnapshot, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { Injectable, inject } from '@angular/core';
export const hasRoleGuard: CanActivateFn = (route: ActivatedRouteSnapshot, state: RouterStateSnapshot): boolean => {
const authService = inject(AuthService) as AuthService;
const router = inject(Router);
// console.log("user role: "+authService.userRole?.role);
const isAuthorized = authService.userRole?.role.includes(route.data['role']) ?? false;
if(!isAuthorized){
router.navigate(['']);
}
return isAuthorized;
};
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* auth.service.ts *
- In the constructor I replaced the assignation of this.userRole variable from 'this.userRole = this.getUserRole(this.token); to
' this.userRole = this.token ? this.getUserRole(this.token) : null; '
Because I got the error 'Type 'null' is not assignable to type 'string'.'
The ternary operator this.token ? this.getUserRole(this.token) : null is used to conditionally assign the value of this.getUserRole(this.token) to this.userRole if this.token is not null. If this.token is null, this.userRole is assigned the value undefined.
This way, you avoid the error when this.token is null, and this.userRole will be assigned the appropriate value based on the token's availability.
- I replaced the method in tap function from login method too:
this.userRole = response.token ? this.getUserRole(response.token) : undefined;
- I used 'user?: User' instead of 'user!: User' because I got the error
'Type 'User | undefined' is not assignable to type 'User'. Type 'undefined' is not assignable to type 'User'. ' triggered on the 2 this.user assigns mentioned above
---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
* user.Role / user.role / user.roles / user.Roles *
- Make sure you use just one of them, in all the places, including JWT property, User model, your auth.service, has-role.guard