00:00:04.319
My name is Katie Miller. I'm a senior staff engineer at NFI, and I live in Berlin. I work remotely; it's a globally remote company. I'm here to talk to you about "The Boring Bits Bite Back."
00:00:20.760
Authorization is important. Often, it's referred to as 'auth,' which can be a little confusing because it actually involves two systems: authentication and authorization. These systems live under the same umbrella. Authentication is essentially the keys to the castle; it determines who can enter. On the other hand, authorization is about who can access the VIP areas or different parts within the castle.
00:01:05.239
Throughout my career, I've found myself drawn to fixing messy authorization systems. This can be tricky and isn't something most people expect to spend their careers working on; yet, it seems to be where I shine. I understand why: Who wants to discuss access to specific API endpoints? While this might excite security people, it's not exactly the flashy part of any SaaS product. In fact, I would argue that the subject is fairly boring, and it should be boring, predictable, and simple for everyone to use without overthinking it as they build features.
00:01:43.000
Unfortunately, authorization often isn't a feature that's marketed, so very few people give it the attention it deserves. Over time, as it gets ignored, it tends to become more complicated and less predictable. Even worse, neglected limitations can hinder company growth or, in the worst-case scenario, lead to security breaches. It's normal for the complexity of an app to grow over time. We've all experienced the shift from a nice, fresh greenfield app to working in legacy systems. Unfortunately, this complexity, especially in authorization, can create a real mess and make things very difficult to manage.
00:02:05.439
I want to emphasize that it doesn't have to be this way. If you think about authorization early on in your app development, there will be less pain later. So, let's build an app together as an example of how these issues can arise over time—not necessarily because anyone intends for them to happen, but because things naturally evolve as you move quickly and aim to get to market.
00:02:35.240
Our app will be called 'ptrax,' a place where people can manage and track their pet's care. Users will be able to share care instructions with other caretakers, avoiding the hassle of tracking everything on paper. We'll set up a new Rails app, which is my favorite way to create fast applications. Let's assume this crowd is also on board, and we'll fast-forward a bit.
00:03:06.080
We receive a feature request to allow multiple people to care for a pet because typically, it's not just one person responsible. Using Rails systems, we'll create an account, add users, and ensure that those users belong to an account. However, there's a complication: some caretakers, like a boarding facility or a dog walker, may not actually own the pet. We still want them to participate in caring for pets without being the official owners. This introduces an authorization feature: should all caretakers have the same abilities?
00:03:44.360
Some users might not want another caretaker to adjust their pet's medications or even change their pet’s name in the app. We need to ensure that we check roles throughout the application. We'll add an owner ID, so if the owner ID matches the user ID, they qualify as the owner. We’ll store other user IDs in an array and check against it to verify caretaker status. In our controllers, we can check if a user is the owner, allowing them to perform certain actions; otherwise, we'll render an unauthorized response. Quick and done—that was fast! Now, let's move forward again.
00:04:37.720
As we progress, we're going to add support for our admin users. We'll do this as simply as possible by checking the user's email. Since we control access to Google Suite, we know that only our users have their respective emails. We'll let admins see what users can access, but we don't want to create a new admin interface; instead, we'll allow them to make the same requests as any other user, ensuring they're either part of the account or designated as admins.
00:05:15.560
Continuing onward, we've grown quite a bit and are adding larger features, including vet office integration. This allows vet staff to manage pet health records, which necessitates new roles. We'll introduce a veterinarian role; if a user is an owner, they're the veterinarian, while veterinary assistants take on another specific role. As we continue to update our controllers, we need to ensure that we're verifying ownership or veterinarian status in our logic. Otherwise, access will be denied.
00:06:18.280
As we recap this journey, you will notice an increasing list of roles as we move forward. At this point, part of the feature requirements is that we should update pet medications at appropriate times. If an account is canceled, we shouldn't allow updates, although the account owner may still view certain information. We'll implement checks to determine whether an account is active before allowing updates. It’s important to note that our use of if-else statements is growing tedious and complex.
00:06:49.920
As growth continues, we see the charts thrive, but complexity increases significantly. Fortunately, we’ve gained some breathing room to tackle our tech debt. I’d like to introduce the concept of the policy authorization pattern. You may be familiar with this pattern, but essentially, we want to answer the question of whether a user can perform a specific action on a resource.
00:07:17.040
In code, this can appear quite simple to read. For example, can they update a pet’s medication? The reason we favor the policy pattern is due to its straightforward implementation without needing to install any additional gems. There are plenty of examples available to guide you, enabling us to take checks from our controllers and transfer them into easily manageable policies.
00:08:12.480
Here's a general example of a pet medication policy where we will place our role checks in designated methods. Additionally, I think we should clean up our code as we implement this change. For instance, we can check the account cancellation state here, issuing an unauthorized response when necessary. This approach will significantly streamline our controllers, leading to better organization.
00:08:49.920
As our application evolves, we notice that many vet offices fall under corporate management. Corporate entities want to oversee these operations, which means we need new roles. For instance, we’ll need roles for financial management and data analysis, including sales and potentially multi-tiered support systems. This will introduce different levels of access, but fortunately, we’ve refactored our earlier policies, allowing us to simply add these new roles to our policy action methods.
00:09:48.200
One day, you might receive a message from a product manager informing you about a significant deal in the works. They might mention that the head of security has specific suggestions for how to adjust current roles and permissions. You might think, 'No big deal; we already have RBAC—Role-Based Access Control.' However, when you receive a detailed list of changes, you realize that our roles are expanding exponentially.
00:10:35.960
They're also requesting modifications to existing roles, raising questions about whether our current capabilities align with customer needs. Users may want to define their own roles and set unique permissions based on their specific environments, leading to a demand for greater flexibility in our role management, as well as importantly, the ability to display these structures in an easily understandable format.
00:11:14.040
At this point, you'll notice that many of our existing roles might have convoluted ways of granting access, whether it’s through checking email or arrays of IDs. As our policies expand, they might also scrutinize the state of other resources, complicating the straightforwardness of our operations. Additionally, many new features added by team members come with their own unique policy methods, creating overlapping responsibilities and confusion.
00:12:15.960
Eventually, you might hear from the PM that it’s time for a rewrite, even after just having refactored the system. They might ask how long this would take, and while you might optimistically guess two to four months, you know the reality is that a major overhaul can extend over six months to a year—or longer.
00:12:56.480
This scenario is common among startups, where configurable authorization is often not requested until significant amounts of code have been written. While aiming for a minimal viable product, teams often overlook how the enterprise phase will demand flexible systems that adapt to changing requirements.
00:13:32.680
How could we have navigated this progression differently? To start with, we should have incorporated relational data structures to clarify how roles are assigned, avoiding redundant string checks that cause confusion. By implementing join tables for roles and permissions, we can centralize our access controls, enabling easy updates and retirements.
00:14:20.720
Let's talk about the policy authorization pattern again. While it seemed innovative at first, it's crucial to recognize that it complicates the customization process. Combining various concerns into our policy action methods dilutes their focus and makes it hard to locate permissions across the codebase.
00:14:53.640
Instead, we should consolidate our permissions per role and resource. I recommend exploring existing libraries and gems, looking for those that streamline the separation of roles and permissions. An example would be the 'cancan' gem, which allows you to define permissions more clearly, making it easier to manage authorization as your application escalates in complexity.
00:15:28.840
As a final proposal, everyone should stick to CRUD (Create, Read, Update, Delete) structures. By adhering to RESTful API design principles, we can prevent uncontrolled growth in the size of controllers and maintain clarity in resource hierarchy.
00:16:03.080
If you're managing permissions, have clear deliberations on how these structures will look. Moreover, consider designating ownership over certain elements to ensure a streamlined process. Clear documentation concerning new permissions is crucial to ensuring all team members are aligned.
00:16:52.880
In conclusion, the key points are: leverage relational data; avoid the pitfalls of overly complex policy authorization systems; maintain simplicity around CRUD principles; and focus on flexibility. Authorizing actions based on roles should answer, 'Can a user perform this action on a resource?' It's vital to minimize creativity in this area; focus your innovation on other domains of your application.
00:18:09.200
Thank you for your attention!