In today’s post, we are going to see how to modify the User Interface (UI) based their user role. This is useful if you wish to display different UI Elements/Components based on the user role. While this is not a security measure, it has the potential to greatly enhance the User Experience (UX).
To achieve this, we will be using NGXS – a state management library which was built as an easy to user alternative to the popular NGRX. You can learn more about NGXS here. It will act as a single source of truth, where we can subscribe to changes to the user role and make changes to the UI Accordingly.
Ideally, you will want to read the user role from an authentication token – JWT or some other alternative. To keep things simple for this demo, we are going to just use a form, to allow us to change the user role quickly. I will also show you how to decode JWT tokens just in case you need to.
Getting Started
First, we will start by creating a new angular project.
$ ng new ngxs-role-based-authorization
Then, we are going install the packages we need for our project.
$ yarn add @ngxs/store @ngxs/storage-plugin
// or using npm
$ npm install @ngxs/store @ngxs/storage-plugin
We also need to install bootstrap for our UI.
$ npm install bootstrap
// or using yarn
$ yarn add bootstrap
Next, we need to import NGXS and the NGSX storage plugin into our app module:
import { NgxsStoragePluginModule } from '@ngxs/storage-plugin';
import { NgxsModule } from '@ngxs/store';
@NgModule({
declarations: [AppComponent],
imports: [
//...
NgxsModule.forRoot([UserRoleState]),
NgxsStoragePluginModule.forRoot()
],
providers: [],
bootstrap: [AppComponent]
})
export class AppModule {}
And lastly, import bootstrap into our project, using the style section of the /angular.json
.
// ...
"styles": [
//...
"node_modules/bootstrap/scss/bootstrap.scss"
]
Building an NGXS State
The easiest way to do this is to use NGXS CLI, which will scaffold a state for you, you can learn more about here. I wish they could use angular schematics though. But in this case, we will do this manually.
First, we need to create an action for our role, which we will use to change the role of our state. This is where we will pass the role of the current user. You might also want to pass other information to store regarding the user such as the name, email etc.
export class ChangeUserRoleAction {
static readonly type = '[UserRole] Add role';
constructor(public role: 'guest' | 'moderator' | 'admin') {}
}
Next, let’s create a model for our state. This is where we will add all fields we are going to store in our state. In our case, we just have role, but you can basically add any sort of data such name, email etc.
export class UserRoleStateModel {
public role: 'guest' | 'moderator' | 'admin';
}
And finally, let’s define our state.
@State<UserRoleStateModel>({
name: 'userRole'
})
export class UserRoleState {
//...
}
Next, inside our state class, let’s add a Selector method to get the role of the current user. We are going to pass the state model, so that we can access to values stored in the state.
@Selector()
static getCurrentUserRole(state: UserRoleStateModel) {
return state.role;
}
And then, let’s add a method to set the current user role, passing the action class and state context:
@Action(ChangeUserRoleAction)
ChangeRole(ctx: StateContext<UserRoleStateModel>, action: ChangeUserRoleAction) {
ctx.setState({ role: action.role });
}
You can put all the above inside a single file or divide it into multiple files – your choice.
Setting the Current User Role
Now that we have a state, let’s set our current user role. For our simple demo, you can set the current role of the user with a single line of code.
this.store.dispatch(new ChangeUserRoleAction(role));
NB: In the above line of code, we are dispatching an action, in this case to change the user role, to our store. You can learn more about NGXS Stores here.
If a user has to login to set the user role, you can decode the JWT to read the user role setting it up. First, we are going to install angular-jwt library using your favorite package manager:
$ npm install @auth0/angular-jwt
// or use yarn
$ yarn add @auth0/angular-jwt
And add it to your list of imports in the app module:
imports: [
// ...
JwtModule.forRoot({})
];
Then in your service, import the jwtHelperService
from the library we just installed above.
constructor(private jwtHelperService: JwtHelperService) {}
And finally, in your method, decode the JWT Token and return the user role:
getUserRole(token: string): string {
const decodeToken = this.jwtHelperService.decodeToken(token);
return decodeToken['role'];
}
And finally, you can store the new role as shown below:
this.store.dispatch(new ChangeUserRoleAction(role));
Subscribing to Changes to the User Role
Reading the current user roles is a straight forward matter. You can use the selectSnapshot method, which you can learn more about here. The method returns a raw value for use without subscribing.
this.store.selectSnapshot(UserRoleState.getCurrentUserRole);
But for this demo, we are going to subscribe to the changes that will occur. Take for instance, if you show a user a popup to sign in, you would like the UI to update accordingly based on the new user role without reloading the page.
We are going to use async pipe to subscribe to the user role and display and hide UI elements accordingly. First, inside our component class, we need an observable GetUserRole$
property. And then on component initialization, set the property to the observable that returns the current user role.
ngOnInit() {
this.GetUserRole$ = this.store.select(UserRoleState.getCurrentUserRole);
}
And then inside the template, subscribe to checkUserRole$
observable using async pipe.
<ng-container *ngIf="checkUserRole$ | async as role"></ng-container>
Your role is accessible by the role variable from the template. You can use it any how you want to manipulate the UI of your app. For instance, you can use ngSwitch to show a role message like our demo project does.
<div [ngSwitch]="role">
<div *ngSwitchCase="'moderator'">Moderator Template</div>
<div *ngSwitchCase="'admin'">Admin Template</div>
<div *ngSwitchDefault>Guest Template (Default)</div>
</div>
NB: You can pass the role to your components to avoid subscribing to the state on multiple instances.
Source Code
You can find the source code of this demo here.
Final Thoughts
In this post, we saw how we can use NGXS to keep track of user role and manipulate the UI accordingly. I will be covering more topics related to NGXS in the future. Thank you for getting this far on this post. Here is another post you might be interested in - Role Based Authorization in Angular using Routes Guard.