Building Healthcare App Authorization in Space with Next.js and Permit.io
- Share:
The safety of our user's personal information is always a top priority when building an application, and the stakes are even higher when it comes to sensitive medical data.
In Building Immune Authorization: AppSec in Healthcare Apps, we discussed the importance of implementing proper authorization that safeguards patient data and ensures strict regulatory compliance.
This time - let’s get down to business and see how authorization for a healthcare app should be implemented with the help of Galactic Health Corporation - a Rick & Morty inspired healthcare application.
The GHC app is designed to address the complex needs of a modern healthcare system while adhering to stringent security and compliance standards by using Next.js and Permit.io.
Galactic Health Corporation - a Demo Healthcare App
Galactic Health Corporation (GHC) is a sample application that demonstrates all the common application permission models (Role-Based Access Control (RBAC), Attribute-Based Access Control (ABAC), and Relationship-Based Access Control (ReBAC), in a single healthcare application.
The fictional GHC is a health organization set in the universe of "Rick and Morty," where genius scientist Rick Sanchez and his loyal grandson Morty Smith embark on mind-bending adventures, and even the world of healthcare demands its own unique solutions.
GHC App Key Features:
Before delving into the application code, let’s have a brief on the key features of our healthcare application:
User Roles: GHC accommodates various user types, each assigned with a corresponding role. These include doctors, patients, caretakers, and administrators. Each user type has distinct responsibilities and access levels tailored to their role.
Patient Management: Patients can access their own health records, medical history, and treatment plans. They can also grant limited access to caregivers or family members as needed.
Doctor-Patient Relationships: Doctors can view and manage the medical records of patients under their care. Patient data is only accessible to authorized medical professionals.
Attribute-Based Access: Access to certain resources within the application is restricted by using attributes like geographic location, patient age, date scoping, or treatment status - making sure they are only accessible under the right conditions.
Feature testing pilot Group: GHC allows the management of user feature testing pilot groups, which allows them to test new application features based on users fitting specific criteria.
Delegated Permissions: Users can delegate limited access to their health information to others, such as caregivers, with specific limitations.
Prerequisites
Implementing all of these features might seem overwhelming at first, but, if you follow this step-by-step tutorial, I assure you will gain a great understanding of this process, and easily replicate it yourself.
If you just want a quick glimpse of how this works, you can skip this step.
Here are a few essential prerequisites that you need to have in place in order to set up this application yourself -
Node.js Environment -
You'll need a working Node.js environment installed on your local machine.Docker Environment -
GHC relies on Docker for running the decision point container locally. Make sure you have Docker installed and configured.Clerk Account -
GHC uses Clerk to authenticate users, allowing them to log into the app.Permit.io Account -
GHC leverages Permit.io for managing user permissions. You'll need a Permit.io account to configure and manage access control.
Setting up the application
Clone the repository to your local machine.
In the root folder, create a file named
.env.local
and copy the content of.env.example
to it.From the Clerk dashboard, go to API Keys, choose Next.js example, and copy the
NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY
andCLERK_SECRET_KEY
values to the.env.local
file.In the Permit dashboard, go to Connect, and copy the token variable to the
PERMIT_SDK_KEY
value into the.env.local
file.Run
npm install
to install the application dependencies.
At this point, we have all the required setup to run the application locally. We have configured the runtime and the keys we need to authenticate and authorize the users.
Configuration Setup
To experiment with authorization and permissions, we’ll have to configure the relevant data in Clerk.com and Permit. To do that -
Open the terminal in the repository's root folder and run
npm run config
. This will seed the permissions data into Permit.
At this step, have all the relevant resources, actions, and roles configured in Permit.io.
Running the Application
To authorize our users to use the data we configured in Permit, we should run the policy decision point (PDP) locally and run the following docker command to spin up the decision point container:
docker run -it \
-e PDP_API_KEY=<YOUR_PERMIT_API_KEY> \
-p 7766:7000 \
-p 8081:8081 \
permitio/pdp-v2:latest
As the decision point is up and running, open a new terminal window in the root folder of the repository and run npm run dev
. This will start the application locally, and you can access it at http://localhost:3000
.
The GHC Permission Model - RBAC and ReBAC
With the application running, let’s dive into how the GHC permission model is designed, and what it allows us to do. First of all, let’s take a look at the entities defined within our app:
Entities
Resources - A resource is the object we want to manage access to. Each resource can have multiple instances (Think about “Blood-Test Results” as a resource type and “Blood-test result #83742093b” as an instance of this type). The Member resource, for example, has a resource instance per user in the system.
Actions - The various actions that users can perform on a resource. Each action can be either allowed or denied for each user as defined in the permission model. Back to the “Blood-Test Results” example, a user might be able to read, write, or analyze them in the system.
Role Derevations - ReBAC allows us to derive authorization policies based on existing application-level relationships. Put in the simplest way, permissions can be derived from the Member Profile to the Medical Record. I.e., if someone has ownership access to the Member Profile, they will also have ownership access to the Medical Record.
Relationships - These define the link between different resources, allowing us to derive permissions from one resource to another. In the “Blood-Test Results” example, a doctor with permission to view a member's medical records should also be allowed to view all their blood test results.
Resource Roles - Roles that can be assigned to specific resource instances. For example, the Owner role can be assigned to a specific Member Profile instance and to the specific user that is the owner of this Member Profile.
The combination of roles, resources, actions, and relationships allows us to define our application’s policy. Let’s go over all of the permissions we want to set up in an organized fashion:
Requirements
So what does this thing do?
Users can view their own profile details, health plans, and medical records.
Users can view the profiles of other users they have a relationship with (for example, family members).
Users can delegate permissions to other users to view their health plan and medical records; this delegation is limited to a specific date range.
Users can view particular features in the application based on their pilot groups.
Let’s see how this structure looks in a diagram and then put it in a list:
Here is a general ReBAC schema that we defined here:
Here’s a list of all of the Resources, Actions, Relationships, etc. we set up in this model:
Resource | Actions | Relationships | Resource Roles | Derived Roles | Description |
Member | Write, Read | Parent of: Medical RecordsHealth PlanProfile | - Owner (read, write) - Caregiver (read) | A root entity for all member-related resources. | |
Profile | Write, Read | Child of Member Belongs to Member Group | - Owner (read, write) - Caregiver (read) | Member:Caregiver Member Group:Org Member | A member profile. |
Health Plan | Write, Read | Child of Member | - Owner (read, write) - Caregiver (read) | Member:Caregiver | A member’s health plan. |
Medical Records | Write, Read | Child of Member | - Owner (read, write) - Caregiver (read) | Member:Caregiver | A member’s medical records. |
Member Group | List, Assign | - Admin (list, assign) - Org member (list) | A member group. | ||
New feature pilot group | View | Pilot group member (general role) | A member group that has access to beta features |
API Endpoints
To demonstrate the permissions in the application, we implemented the following API endpoints:
Endpoint | Description | Roles | Derived Roles |
GET /account/dashboard/profile/{user} | Get the (current) user's details. | Profile:Caregiver | Member:Owner#Profile Member Group:Org Member#Profile |
GET /account/dashboard/health-plan/{user} | Get the (current) user's health plan. | Health Plan:Caregiver | Member:Owner#Health Plan Member:Caregiver#Health Plan |
GET /account/dashboard/medical-records/{user} | Get the (current) user's medical records. | Medical Records:Caregiver | Member:Owner#Medical Records Member:Caregiver#Medical Records |
GET /account/member | Get all the members from all the member groups | Member Group:Org Member | |
GET /account/caregiver | Get all the user's caregivers | Member:Owner | |
POST /account/caregiver | Add a new caregiver to the user | Member:Owner | |
DELETE /account/caregiver | Remove a caregiver from the user | Member:Owner |
Let’s see it in action!
Testing the application flow -
Let’s test this application with an actual use case example.
If you want to run a cloud version of this application, feel free to open Rick and Morty accounts and play with permissions at https://ghc.up.railway.app/
In the crazy and multidimensional world of Rick and Morty, where eccentric scientist Rick Sanchez and his grandson Morty Smith embark on interdimensional adventures, there's also a need for cutting-edge healthcare. Enter Galactic Health Corporation (GHC), a healthcare application that perfectly captures the essence of complexity and control in a world where literally anything can happen.
User Roles:
Rick Sanchez (Adventurous Patient): Rick, known for his genius and recklessness, is a patient using GHC. He trusts his loyal grandson Morty to assist him with his healthcare needs.
Morty Smith (Caregiver Extraordinaire): Morty, Rick's faithful companion, is a caregiver in GHC. He assists Rick with managing his healthcare data and has access to certain medical information under Rick's supervision.
Bird Person (Interdimensional Friend): Bird Person, an avian humanoid from a distant dimension, represents an ordinary user in GHC. He's neither a doctor nor a caregiver but has his own healthcare data to manage.
Let's look at this example on a graph:
Here, we can see Rick has a member role in the member group, while Morty has the Admin role in the member group. User profiles belong to the member group, and Morty gets a derived viewer role on Rick's profile.
Here, we can see how the Caregiver role works:
Rick has the member role on his member resource. This gives him access to his own medical records, health plan, and user profile. Morty has a caregiver role assigned to Rick's member resource. This allows him to derive a caregiver role on Rick’s medical records, health plan, and user profile as well.
With these roles in mind, let's go over the application usage flow:
Application Usage Flow:
Rick and Morty both need to sign up for GHC accounts using their respective email addresses: rick@sanchez.app, and morty@smith.app. We’ll also add another account for bird@person.app
Note: If you do not have the option to sign up with these emails to your application, you can use any email you’d like. You’ll have to edit the user keys in Permit.io’s dashboard at https://app.permit.io/user-managementLog in to the app as rick@sanchez.app, you will notice that Rick’s dashboard only allows him to see his own data. This is because, in GHC, users are granted access to their own information by default.
GHC offers a "Delegate Permissions Wizard," a tool that allows users like Rick to share specific healthcare data with others. Use the wizard to share all the data, and assign Morty, as our trusty caregiver.
As you can see, Rick can give access only to their member groups members Morty and Bird Person.
Rick can give Morty access limited to a specific date range, ensuring that Morty can view Rick's data only within that timeframe. More on that in the next section.- With Morty assigned as a caregiver to Rick, let’s log in to Morty’s account. In the "Shared Access" section, you can see that Morty can now access Rick's health plan and medical records as permitted by Rick. This access is derived from the "Member:Caregiver" role.
- Log into Bird Person's account. You will see that he can only access his own healthcare data and doesn't have any caregiving relationships with other users.
Using this simple usage flow, we demonstrate the following ReBAC permissions:
Rick's Access: Rick, as a member of GHC, can view all of his own healthcare data, as the Owner role is assigned to him.
Morty's Access: Morty, as Rick's caregiver, can see Rick's health plan and medical records, thanks to the "Member:Caregiver" role assigned to him by Rick.
Bird Person's Access: Bird Person can only view his own healthcare data, as he doesn't have any specific relationships with other users.
Feature testing pilot group: In Rick's dashboard, there's a special box only visible to the "Feature testing pilot group" This is an example of Role-Based Access Control (RBAC), where Rick, as a member of the "Feature testing pilot group" role, can access this specific feature. Other users can't see this box.
This GHC scenario in the Rick and Morty universe showcases how complex authorization models like ReBAC and RBAC can be easily applied in a healthcare application in a world where interdimensional adventures are just a portal away.
Now that we have covered the RBAC and ReBAC sections of this GHC’s authorization layer, let’s see how ABAC works in this context.
The GHC Permission Model - ABAC
Requirements
So, why do we need attributes here?
The ABAC requirement for our app is to limit the delegation of permissions to a specific date range. For example, Rick can give access to his health plan and medical records to Morty, but only for the next 30 days.
Model
Rick and Morty's relationship determines Morty's instance-level role, so we can't limit the delegation based on the relationship itself. This means we need to add attributes to the mix.
To implement the ABAC, we will use a date attribute that can define the boundaries to each role assignment. For example, if we assign the Member:Caregiver role to Morty with the start_date attribute set to 2024-01-01 and the end_date attribute set to 2024-01-31, Morty will be able to view Rick's health plan and medical records only between 2024-01-01 and 2024-01-31.
The result will be an object that looks like this:
{
"{user}": {
"Member:Caregiver:${instance}": {
"start_date": "2024-01-01",
"end_date": "2024-01-31"
}
}
}
And the query will be something similar to this:
if (user[permission].start_date <= today && user[permission].end_date >= today) {
return true;
}
Implementation
To implement the ABAC permissions using Permit, we can do the following:
Configure a Resource Set based on the condition we defined, and configure it within the Permit UI.
For the sake of this demo, we will use the second option, as we want to demonstrate the flexibility of the Permit as a solution.
Configure GitOps
The way that Permit.io lets you write your own custom policy code is by using the GitOps feature. This feature allows you to use Git-based features such as pull requests, branches, and merge requests to manage your policy configuration.
To configure GitOps, follow the guide in the Permit documentation.
Configure the Policy
In the relevant branch, replace the content of custom/root.rego
with the content of the scripts/custom.rego
file in the repository.
In the relevant branch, edit the root.rego
file so all the conditions will sit on the same allow rule, like this (remember to remove the second allow rule):
allow {
policies.allow
custom.allow
}
Application Flow
To test the ABAC permissions, run the same caregiver delegation flow we did in the ReBAC and RBAC Permissions section, but this time, limit the delegation to a specific date range.
As you can see, the permissions are limited to the date range we defined.
Conclusion
As we ventured through the bizarre world of Rick and Morty, we demonstrated how complex authorization models can seamlessly integrate into a healthcare application, even in a universe where interdimensional adventures are the norm.
The GHC demo and the concepts explored here should serve as a valuable blueprint for any app developer looking to better understand authorization in the context of healthcare applications and the benefits of creating it with the help of Permit.
With the right authorization model in place, your application can boldly navigate the complex and ever-changing healthcare landscape while safeguarding the well-being of its users.
Want to learn more about Authorization? Join our Slack community, where there are hundreds of devs building and implementing authorization.
Written by
Daniel Bass
Application authorization enthusiast with years of experience as a customer engineer, technical writing, and open-source community advocacy. Comunity Manager, Dev. Convention Extrovert and Meme Enthusiast.