Authorization directive in Angular with Msal-Angular
In this blog, I would like to share with you a way to dynamically add or remove elements based on the user’s authentication status using an authorized directive.
When I was working on a Blazor project, I used an AuthorizedView component, as described in the Microsoft documentation on Blazor security. This component made displaying authorized data incredibly easy. However, when I switched back to Angular, I found myself missing this component a lot. So, I decided to create my own directive based on the NgIf directive for displaying authorized data.
For the purpose of this blog, we will be using the following context:
- Angular 14 web app
- MSAL Library for authentication (@azure/msal-browser & @azure/msal-angular)
The idea is to create a directive that injects the MSAL services, and based on the user’s authentication status, dynamically creates or clears the embedded view.
Blazor AuthorizationView
What is an AuthorizeView component?
An AuthorizeView component selectively displays UI content depending on whether the user is authorized. This approach is useful when you only need to display data for the user and don’t need to use the user’s identity in procedural logic.
This is the default example from the microsoft website for using the AuthorizedView component. The content of <Authorized>
and <NotAuthorized>
tags can include arbitrary items, such as other interactive components.
If authorization conditions aren’t specified, AuthorizeView uses a default policy:
Authenticated (signed-in) users are authorized.
Unauthenticated (signed-out) users are unauthorized.
What is an AuthorizeView component? An AuthorizeView component selectively displays UI content depending on whether the user is authorized. This approach is useful when you only need to display data for the user and don’t need to use the user’s identity in procedural logic.
This is the default example from the Microsoft website for using the AuthorizeView component. The content of and tags can include arbitrary items, such as other interactive components.
If authorization conditions aren’t specified, AuthorizeView uses a default policy:
Authenticated (signed-in) users are authorized. Unauthenticated (signed-out) users are unauthorized.
Structural directive
For creating the authorized directive we use an structural directive.
Structural directives are directives which change the DOM layout by adding and removing DOM elements.
Angular provides a set of built-in structural directives, such as NgIf, NgForOf, NgSwitch, and others, which are commonly used in all Angular projects.
This is exactly what we need for adding or removing DOM elements based on the user’s authentication status. So, let’s create a structural directive that injects the MSAL services and dynamically adds or removes the element based on the user’s authentication status.
*authorized directive
So lets create the authorized structural directive. First we start with injecting the services in the constructor.
constructor(
private templateRef: TemplateRef<any>,
private viewContainer: ViewContainerRef,
private authService: MsalService,
private broadcastService: MsalBroadcastService
) {
}
templateRef & ViewContainer
TemplateRef helps you get to the contents and ViewContainerRef accesses the view container
authService $ broadcastServices
These services are provided by the Msal library and can be used to determine if a user is authorized (authService) and listen to authorization-related events (broadcastService).
In the ngOnInit function, we subscribe to the InteractionStatus “None” event. This event is fired when an interaction is complete. We subscribe to this event so that when this event is fired, we can determine if the element should be added or removed from the DOM.
public ngOnInit(): void {
this.broadcastSubscription = this.broadcastService.inProgress$.pipe(
filter((eventType: InteractionStatus) => eventType === InteractionStatus.None),
takeUntil(this._destroying$)
).subscribe(() => {
this.setDisplay();
});
}
As you can see, we use the rxjs takeUntil operator for cleaning up the subscription. The takeUntil operator emits the values emitted by the source Observable until a notifier Observable emits a value.
To make this work, we need to declare two private properties.
private readonly _destroying$ = new Subject<void>();
private broadcastSubscription!: Subscription;
- _destroying$: observable for notifying the takeUntil
- broadcastSubscription: Subscription we need to clean up when the element is destroyed Now it’s time to add the function which contains the logic for adding or removing the element based on the authentication status.
private setDisplay(): void {
const account = this.authService.instance.getActiveAccount();
if (account && !this.hasView) {
this.viewContainer.createEmbeddedView(this.templateRef);
this.hasView = true;
} else if (!account && this.hasView) {
this.viewContainer.clear();
this.hasView = false;
}
}
First, we get the active account from the authService. If an active account is returned, we know that the user is authenticated. Also, we check if the element is already added. If not, then we can create the element in the container and update the hasView value. When the user isn’t authenticated and the template was created in the container, we need to clear the container.
To clean up the mess we made, we use the ngOnDestroy to clean up the subscriptions when the component is destroyed.
public ngOnDestroy(): void {
this._destroying$.next(undefined);
this._destroying$.complete();
this.broadcastSubscription.unsubscribe();
}
Now we have a structural directive that we can place on elements which should be added or removed based on the user’s authentication status. If we put it all together, we get the following directive: