<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>Forem: Dinesh</title>
    <description>The latest articles on Forem by Dinesh (@dinesht04).</description>
    <link>https://forem.com/dinesht04</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F3860877%2F3918a992-dc61-47a1-a81c-5f0e682d37f8.jpeg</url>
      <title>Forem: Dinesh</title>
      <link>https://forem.com/dinesht04</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/dinesht04"/>
    <language>en</language>
    <item>
      <title>Implementing Relationship Based Access Control (ReBac) using Node and Oso</title>
      <dc:creator>Dinesh</dc:creator>
      <pubDate>Sun, 03 May 2026 00:56:50 +0000</pubDate>
      <link>https://forem.com/dinesht04/implementing-relationship-based-access-control-rebac-using-node-and-oso-iga</link>
      <guid>https://forem.com/dinesht04/implementing-relationship-based-access-control-rebac-using-node-and-oso-iga</guid>
      <description>&lt;p&gt;Modern applications require complex authorization, that's where Relationship-Based Access Control (ReBAC) comes in. I went through this research paper: &lt;a href="https://dl.acm.org/doi/10.1145/1943513.1943539" rel="noopener noreferrer"&gt;Relationship-based access control: protection model and policy language&lt;/a&gt; in order to understand, learn, and implement it. Here's everything I learned along the way.&lt;/p&gt;




&lt;h2&gt;
  
  
  Table of Contents
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;The Paper&lt;/li&gt;
&lt;li&gt;Why I Implemented It&lt;/li&gt;
&lt;li&gt;Tech Stack&lt;/li&gt;
&lt;li&gt;What is Relationship-Based Access Control?&lt;/li&gt;
&lt;li&gt;Context in ReBAC&lt;/li&gt;
&lt;li&gt;How Authorization Works&lt;/li&gt;
&lt;li&gt;The Implementation&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  The Paper
&lt;/h2&gt;

&lt;p&gt;The paper proposes a type of access control characterized by the relationships between users and resources, and control policies based on those relationships. This allows authorization logic to take into account the context of the relationships, giving a high level of control over how resources are accessed.&lt;/p&gt;




&lt;h2&gt;
  
  
  Why I Implemented It
&lt;/h2&gt;

&lt;p&gt;While job hunting, I recently came across an assignment where I had to implement ReBAC. I searched what it was, found it genuinely interesting, and here we are. I was also inspired by a friend on Twitter to go through an actual research paper.&lt;/p&gt;




&lt;h2&gt;
  
  
  Tech Stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;Node.js&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TypeScript&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;&lt;a href="https://www.osohq.com/docs" rel="noopener noreferrer"&gt;Oso Cloud&lt;/a&gt;&lt;/strong&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;h2&gt;
  
  
  What is Relationship-Based Access Control?
&lt;/h2&gt;

&lt;p&gt;A simple way to visualize it:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Student → Courses → Professors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Relationship 1:&lt;/strong&gt; A student is enrolled in a course&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Relationship 2:&lt;/strong&gt; A course is taught by a professor
We can combine these relationships to answer queries like "Which professor teaches which student?" - even though there's no direct relationship between them. What we're really talking about here is &lt;strong&gt;transitivity&lt;/strong&gt;:
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;A =&amp;gt; B
B =&amp;gt; C
∴ A =&amp;gt; C
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Importantly, it doesn't just track whether a relationship exists, it also tracks the type of that relationship. That distinction gives us more context to work with.&lt;/p&gt;

&lt;p&gt;Some important components of ReBac are:-&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;User&lt;/li&gt;
&lt;li&gt;Resource&lt;/li&gt;
&lt;li&gt;Resource Owner&lt;/li&gt;
&lt;li&gt;Relationship Identifiers&lt;/li&gt;
&lt;li&gt;Context&lt;/li&gt;
&lt;/ol&gt;




&lt;h2&gt;
  
  
  Context in ReBAC
&lt;/h2&gt;

&lt;p&gt;An important concept in ReBAC is &lt;strong&gt;context&lt;/strong&gt;. The context includes all the relationships in the network, which are matched against policies to arrive at an authorization decision.&lt;/p&gt;

&lt;p&gt;The &lt;strong&gt;viewer&lt;/strong&gt; must be in a specific kind of relationship with the &lt;strong&gt;owner&lt;/strong&gt; in order to access a resource.&lt;/p&gt;

&lt;p&gt;Associated with every resource is an &lt;strong&gt;access control policy&lt;/strong&gt;, a ternary predicate (3 inputs):&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;Owner&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Viewer&lt;/strong&gt;&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A Social Network&lt;/strong&gt; (the graph of relationships between users, modeled using Relationship Identifiers)
This predicate returns a boolean.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Notably, each context might result in a &lt;strong&gt;different authorization decision&lt;/strong&gt; even if the resource, user, and owner are the same. For example: a close relative of a patient wants to access a medical record, when they opt to &lt;em&gt;view&lt;/em&gt;, they're authorized; when they opt to &lt;em&gt;edit&lt;/em&gt;, they're not.&lt;/p&gt;

&lt;p&gt;There are 3 sources an access control policy can come from:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Mandatory&lt;/strong&gt; — set by the sysadmin on all resources&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Discretionary&lt;/strong&gt; — set by the resource owner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Policy Vocabulary&lt;/strong&gt; — predefined policies users can choose from&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The &lt;strong&gt;social network&lt;/strong&gt; is the graph of users and resources, recording the relations articulated in a given context.&lt;/p&gt;




&lt;h2&gt;
  
  
  How Authorization Works
&lt;/h2&gt;

&lt;p&gt;Authorization is achieved by traversing relationships in the social network. Here's the flow:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;The viewer attempts to access a resource&lt;/li&gt;
&lt;li&gt;The system looks up the &lt;strong&gt;policy predicate&lt;/strong&gt; associated with the resource and its owner&lt;/li&gt;
&lt;li&gt;The &lt;strong&gt;effective social network&lt;/strong&gt; (the context) is derived&lt;/li&gt;
&lt;li&gt;The system applies the predicate to the owner, viewer, and social network to arrive at an authorization decision
Two important rules govern this:&lt;/li&gt;
&lt;/ol&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Rule 1:&lt;/strong&gt; Only the social network aspect of the context is significant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rule 2:&lt;/strong&gt; Effective social networks of descendant contexts can inherit relationships defined in ancestor contexts, a child context contains &lt;em&gt;no fewer&lt;/em&gt; relationships than its parent
The root context is the root node of the contextual hierarchy tree. The &lt;code&gt;extends&lt;/code&gt; relation defines this hierarchy. A tree-shaped hierarchy corresponds to the nested structure of relationship scopes:&lt;/li&gt;
&lt;/ul&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;c1 extends c2 if and only if c1 is either c2 or one of the descendants of c2.&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Implementation
&lt;/h2&gt;

&lt;p&gt;The paper references an EHR (Electronic Health Records) system as its example, so that's what I implemented in TypeScript, using Oso Cloud.&lt;br&gt;
Gitub Repo - &lt;strong&gt;&lt;a href="https://github.com/Dinesht04/rebac-ts-implementation" rel="noopener noreferrer"&gt;rebac-ts-implementation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fne1xq1i9wpvcabbjehe9.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fne1xq1i9wpvcabbjehe9.png" alt="EHR Auth Tree" width="393" height="267"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;h3&gt;
  
  
  Step 1: Define the Rules in Polar
&lt;/h3&gt;

&lt;p&gt;Oso uses its own policy language called &lt;strong&gt;Polar&lt;/strong&gt;. Here's the schema I defined in Oso's rules editor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;actor Physician {}

actor User {
  relations = {
    emergencyContact: User
  };
}

resource Institution {
  roles = ["nurse", "admin"];
}

resource Case {
  permissions = ["edit", "view"];
  roles = ["editor", "viewer"];

  relations = {
    institution: Institution,
    patient: User,
    doctor: Physician
  };

  "edit" if "editor";
  "view" if "viewer";
  "viewer" if "editor";

  # admin permissions
  "editor" if "admin" on "institution";

  # patient permissions
  "viewer" if "patient";

  # emergency contact permissions
  "viewer" if "emergencyContact" on "patient";

  # doctor permissions
  "editor" if "doctor";

  # nurse permissions
  "editor" if "nurse" on "institution";
}

resource Treatment {
  permissions = ["edit", "view"];
  roles = ["editor", "viewer"];

  relations = {
    case: Case,
    patient: User,
    physician: Physician
  };

  "edit" if "editor";
  "view" if "viewer";
  "viewer" if "editor";

  "editor" if "editor" on "case";
  "viewer" if "editor";

  "editor" if "physician";
  "viewer" if "patient";
  "viewer" if "emergencyContact" on "patient";
}
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 2: Initialize the Project
&lt;/h3&gt;

&lt;p&gt;Set up a new TypeScript project and add your Oso Cloud API key to &lt;code&gt;.env&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;OSO_KEY="your_api_key"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then initialize the Oso client:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="nx"&gt;dotenv&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dotenv&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;Oso&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oso-cloud&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;addCases&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addInstitutionStaff&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;addPatientsAndEmergencyContacts&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;treatmentsPatient1&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;treatmentsPatient2&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;./data.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="k"&gt;import&lt;/span&gt; &lt;span class="kd"&gt;type&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;DefaultPolarTypes&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;oso-cloud/dist/src/helpers.js&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;

&lt;span class="nx"&gt;dotenv&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;config&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;OSO_KEY&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="kr"&gt;string&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Oso&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;https://cloud.osohq.com&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;apiKey&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 3: Add Facts (Roles &amp;amp; Relations)
&lt;/h3&gt;

&lt;p&gt;For this example, we use two types of facts — &lt;code&gt;has_role&lt;/code&gt; and &lt;code&gt;has_relation&lt;/code&gt;:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// User to User relationship (emergency contact)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has_relation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;patient_1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;emergencyContact&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;contact_1&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]);&lt;/span&gt;

&lt;span class="c1"&gt;// Resource to Resource relationship (Case belongs to Institution)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has_relation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;case_b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;institution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Institution&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;general_hospital&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]);&lt;/span&gt;

&lt;span class="c1"&gt;// Resource to Actor relationship (Case belongs to patient)&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;insert&lt;/span&gt;&lt;span class="p"&gt;([&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;has_relation&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Case&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;case_b&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;patient&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;patient_2&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;}]);&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Step 4: Authorize
&lt;/h3&gt;

&lt;p&gt;The &lt;code&gt;authorize&lt;/code&gt; function returns a boolean:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Dr. Strange tries to edit a case they're not assigned to&lt;/span&gt;
&lt;span class="kd"&gt;let&lt;/span&gt; &lt;span class="nx"&gt;isAllowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Physician&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;dr_strange&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="c1"&gt;// Actor&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;edit&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;                                   &lt;span class="c1"&gt;// Action&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Case&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;case_a&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;            &lt;span class="c1"&gt;// Resource&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isAllowed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// false — not assigned to dr_strange&lt;/span&gt;

&lt;span class="c1"&gt;// An emergency contact tries to view a treatment&lt;/span&gt;
&lt;span class="nx"&gt;isAllowed&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;oso&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;authorize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;User&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;contact_1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;view&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;Treatment&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;treat_a1&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="nx"&gt;console&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;log&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;isAllowed&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// true — legitimate emergency contact&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;All permissions and roles are derived from the Polar schema defined in the Oso Cloud console. (Oso also has a neat section where you can inspect all facts and query logs — really helpful for debugging.)&lt;/p&gt;

&lt;p&gt;The full code is on GitHub: &lt;strong&gt;&lt;a href="https://github.com/Dinesht04/rebac-ts-implementation" rel="noopener noreferrer"&gt;rebac-ts-implementation&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  References
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="https://www.osohq.com/docs/reference/sdks/facts" rel="noopener noreferrer"&gt;Oso Ts SDK&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.osohq.com/post/relationship-based-access-control-rebac-in-node-js-with-oso-cloud" rel="noopener noreferrer"&gt;Oso Blog Node.js&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="https://dl.acm.org/doi/10.1145/1943513.1943539" rel="noopener noreferrer"&gt;Relationship-based access control: protection model and policy language&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;If you have questions or feedback, feel free to reach out!&lt;/p&gt;

</description>
      <category>node</category>
      <category>authorization</category>
      <category>rebac</category>
      <category>oso</category>
    </item>
    <item>
      <title>Hosting an MCP server on Vercel for FREE</title>
      <dc:creator>Dinesh</dc:creator>
      <pubDate>Sat, 04 Apr 2026 12:01:35 +0000</pubDate>
      <link>https://forem.com/dinesht04/hosting-an-mcp-server-on-vercel-for-free-4nhh</link>
      <guid>https://forem.com/dinesht04/hosting-an-mcp-server-on-vercel-for-free-4nhh</guid>
      <description>&lt;p&gt;So recently for an internship assignment, I had to create and deploy an mcp server. It was my first time working with MCP servers so deploying it was kind of a hassle and I couldn't find any blogs or documentation on how to deploy it quickly.&lt;/p&gt;

&lt;p&gt;Long story short, I deployed it using Vercel for free and in this blog post I'm going to guide you through it. I assume you've already created a remote mcp server using &lt;code&gt;StreamableHTTPServerTransport&lt;/code&gt;and it runs on your localhost. If not, no worries. Follow this example to switch your transport from &lt;code&gt;stdio&lt;/code&gt; to &lt;code&gt;httpStreamable&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/modelcontextprotocol/typescript-sdk/blob/v1.x/src/examples/server/simpleStatelessStreamableHttp.ts" rel="noopener noreferrer"&gt;Github Example - Simple Server&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Setting up for the Vercel deployment
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Inside your tsconfig.json, inside &lt;code&gt;"compilerOptions"&lt;/code&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;File&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;Layout&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"rootDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./src"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"outDir"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"./dist"&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(this assumes your server is inside the 'src' directory)&lt;br&gt;
Example - &lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1dp4cxqhorzyzh8ft1s.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fo1dp4cxqhorzyzh8ft1s.png" alt="Example Directory Layout"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inside your package.json, include the build script
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="nl"&gt;"scripts"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"build"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"tsc"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;//&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;make&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;sure&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;this&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;build&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;script&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;is&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="err"&gt;present&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"start"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"node dist/server.js"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"dev"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"nodemon --watch 'src/**/*.ts' --exec ts-node src/server.ts"&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="err"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;ul&gt;
&lt;li&gt;Inside the server.ts
Where you've created the mcp express app, you have to add the &lt;code&gt;allowedHosts&lt;/code&gt; property and add your hostnames inside.
&lt;/li&gt;
&lt;/ul&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMcpExpressApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;allowedHosts&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;your-vercel-deployed-link.vercel.app.&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Eg-&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;app&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;createMcpExpressApp&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
    &lt;span class="na"&gt;allowedHosts&lt;/span&gt;&lt;span class="p"&gt;:[&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;facebook-ads-mcp-server-six.vercel.app&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;localhost&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;span class="p"&gt;})&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Now in order to retrieve the vercel url first we need to deploy the app so let's head over to vercel&lt;/p&gt;

&lt;h2&gt;
  
  
  Vercel Deployment
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;Import Repository&lt;/li&gt;
&lt;li&gt;Select the Application Preset: &lt;code&gt;Express&lt;/code&gt; and Deploy&lt;/li&gt;
&lt;li&gt;Copy your Project's Domain &lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffd46bo7a3w8anizohlq0.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Ffd46bo7a3w8anizohlq0.png" alt="Vercel Dashboard"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Add it to your allowedHosts array in server.ts :
&lt;/li&gt;
&lt;/ol&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;const app = createMcpExpressApp({
    allowedHosts:["facebook-ads-mcp-server-six.vercel.app","localhost"]
})
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;&lt;br&gt;
 ( Include localhost for local dev purposes )&lt;/p&gt;

&lt;p&gt;Now push the changes to your github repository (the one connected with your vercel project)&lt;/p&gt;

&lt;h2&gt;
  
  
  Final Test
&lt;/h2&gt;

&lt;p&gt;Now use McpInspector to test whether your server&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Note&lt;/strong&gt; : When testing, remember that our server is exposing the server at the &lt;code&gt;/mcp&lt;/code&gt;endpoint. So when you enter the url in Mcp Inspector, make sure to include &lt;code&gt;/mcp&lt;/code&gt; in the end and don't enter the port number since all the traffic coming to the vercel servers is configured to be redirected to their server's &lt;code&gt;443&lt;/code&gt;port, so we need not specify any port number, even if a port number's specified in our express application code.&lt;/p&gt;

&lt;p&gt;eg- &lt;/p&gt;

&lt;p&gt;&lt;code&gt;https://facebook-ads-mcp-server-six.vercel.app/mcp&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny2dlrebnxk8llqyor8q.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.amazonaws.com%2Fuploads%2Farticles%2Fny2dlrebnxk8llqyor8q.png" alt=" "&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>mcp</category>
      <category>vercel</category>
      <category>express</category>
      <category>automation</category>
    </item>
  </channel>
</rss>
