Angular route guards for security

And rebuilding legacy Admin systems

Anja
Haven Technologies’ Developer & Technology Blog
6 min readMar 26, 2021

--

For developers, it’s common to inherit legacy code, and every time a feature pops up that has any level of complexity we cringe at the thought of how the system will handle it.

When we realized 2020 would bring a lot of big feature initiatives at Haven Life within our Admin platform (the platform by which we manually tend to life insurance policies), we had to be honest about the fact that the existing Admin tool could not handle the expansion we were planning:

  • The overhead cost of maintenance.
  • Cleaning up existing bugs.
  • Updating things that were out of sync from other data models.

That’s when we realized what we were really looking for was to create an entirely new application outside of the existing repo. Then the big WHY popped up:

“Why create an entirely new admin application?” - It wasn’t enough for us to say, we want an angular front end or decoupled repos. We really had to ask ourselves what we were trying to solve. In the excitement, we went ahead and created a new project titled: “new admin (ง •̀_•́)ง”, and met up for design sessions to discuss the big Why.

Designing with a side of cabernet.

From the various diagrams, words, and phrases we listed on whiteboards, it all ended up boiling down to two principals: security and sustainability.

Security: and Angular route guards

Security is very important to our company, and our Information Security team works hard to make sure that we not only identify vulnerabilities but work towards security driven development. To improve security we decided to build RADMIN (our new Admin platform) as a standalone platform where all communication and gathering data would be done with APIs. This makes it so that only successfully authenticated authorized users can hit our endpoints. Decoupling gives us a protective layer between the platform and the product databases.

Since the Radmin platform could have X amount of users that could access Y amount of functionality depending on who they are, we had to start thinking about where the Roles of Tomorrow evolution starts. The first simple step we took was creating a RolesGuard for the Angular client that communicates with the RolesService which checks if you’re authenticated and authorized to view certain feature dashboards based on your role and channel. A benefit of Angular is that you can place different “guards” on the application routes to determine if they can be activated or not.

This is now a paradigm for all of our new features to ensure we think about who the user is and what they can view.

We call this an evolution because it is just that, an evolving implementation. Security in the digital age is a moving target, and we wanted to develop an agile setup to easily support future security/permissions granularity. Our current implementation of RolesService is already iterating with maker/checker access control setup, a significant step towards the north star that allows us to have access controls on features, components, and functionality.

When you use a framework like Angular, you get a lot of cool things out of the box. The ability to customize navigation capabilities is an important feature to have, and Angular comes with a list of routing guards you can implement for various navigation needs: CanActivate, CanLoad, CanActivateChild, CanDeactivate and Resolve. To stop unauthorized users from navigating to a route we wield CanActivate, which is an interface that provides the ability to return a true or false value based on some desired logic to the route.​

In order to use this feature, given that you have some authorization service with logic, you must define a class that implements CanActivate and define the canActivate functionality.

export class AuthGuard implements CanActivate {
constructor(private authService: AuthService) {}
// from the interface canActivate(): boolean {
// your top secret authorization logic
return this.authService.isAuthorized();
}
}

Then to start using this functionality, you want to add it to your defined route in the routing module:

{
path: 'fangorn-forest',
component: FangornForestComponent,
canActivate: [AuthGuard]
}

This effectively says that for this path, always run this guard and the user can either navigate to it or not.​

The example above demonstrates a basic authorization check, but you can also perform async functionality within canActivate and even route to another page. For example you want to send them to a custom unauthorized page saying “YOU SHALL NOT PASS”.

export class AuthGuard implements CanActivate {
constructor(
private authService: AuthService,
private router: Router
) {}
async canActivate(): Promise<boolean> { // your top secret authorization logic
const isAuthorized = await this.authService.isAuthorized();
​ if (!isAuthorized) {
// yikes you cant be here
this.router.navigate(['/you-shall-not-pass']);
}
return isAuthorized;
}
}
Fly you fools!

A relatively small amount of code can be a powerful weapon for Angular applications to control access.

Sustainability

Our second principle really encompasses a lot of “ilities” we use in the software development world. We umbrellaed these under one term: sustainability.

What do we mean by this? What is sustainable code? Well, it’s a few things to us, so let’s look at them one by one:

Scalability: It’s code that’s decoupled from the monolith repo. Gone are the days of database coupling, replaced with data connections via APIs, potentially supporting REST, GQL, and any other API framework we need. Scalable code can handle all of that and support an evolving authorization approach to give more granular roles and permissions.

Maintainability: With actual paradigms in place around best practices, we can push for readable code that is designed in a modular way. Maintainable code means we can build decoupled features, which allows better iteration, as well as test driven development with iteration on our automation testing goals.

Reusability: With a design system in place, the front end client can evolve with componentization. We can start to approach designing and building components in such a way that allows for them to be standardized with focus on purely presentational views. Reusable code enables us to “pop in” components that are not bound to features or channels like an input field or displaying beneficiary information.

Usability: Given the small internal audience of the original Admin, usability was never really a focus. As the audience had increased dramatically and was now even including teams outside of Haven, the criticality of the UX required a much higher focus. We want to write code with an eye toward usability. Working with our designer, product owners, and other stakeholders (our amazing customer success team), the features we build can shape meaningful experiences and tools that help our users perform the work they need.

Flexibility: Even with a foundation around componentization, state managements, separation of concerns, and testing, we want to make sure we do not block innovation within our own platform. Keeping our code flexible means that as we continue to evolve and find better ways to do things, we can integrate those into the Radmin codebase without issues. It also means we can also start to aim for decoupled releases.

These “ilities” define our idea of sustainable code, which will give us a stable platform and consistent user experience.

To Infinity and Beyond

With these two principles in place we found our Why; we drew out what this new system would look like, what technologies we wanted to use, the relationship between the client and server side, all along with authentication flows. This is how we decided to name it Reinforced Admin, a name that captures our commitment to security and sustainability.

When we stepped back to view our grand design, we realized that Radmin was not just a new administration platform. It’s a new system design and infrastructure outside of our usual thinking. We realized we could leverage this model to build our future applications and services. This ended up happening sooner than we thought.

This process ended up challenging how we thought about system design, pushing us out of comfort zones, allowing us to adapt and innovate to create something that can evolve just as much as our team.

Conclusion

Radmin is a new system design and is going to give us the ability to rapidly develop quality features and experiences. It also serves as a new template for how we think about building applications or services on our team. We are excited for how this new system will evolve and to share this journey with you.

Stay rad y’all!

--

--