This is a submission for the Permit.io Authorization Challenge: API-First Authorization Reimagined

What I Built

I've created MediSecure API Gateway, a specialized data exchange platform for healthcare providers that implements API-first authorization for sensitive medical information.

The platform enables secure, compliant data sharing between healthcare systems while maintaining strict adherence to privacy regulations and patient consent preferences.

In healthcare, data exchange faces unique challenges:

  • Strict regulatory requirements (HIPAA, GDPR)
  • Complex contextual access needs that change based on purpose of use
  • Variable patient consent preferences
  • Emergency access scenarios that may override normal restrictions
  • Delegation of access between providers

Traditional authorization approaches fail in this environment because they:

  • Are tightly coupled to application code
  • Cannot adapt to changing contexts without code modifications
  • Lack granularity for healthcare's complex access patterns
  • Don't provide adequate audit trails for compliance

MediSecure solves these challenges through an API-first authorization approach where:

  1. Authorization is treated as core API infrastructure, not an application concern
  2. Access policies are declaratively defined and externalized
  3. APIs include rich contextual parameters for authorization decisions
  4. All API endpoints consistently enforce authorization policies

Demo

You can access the demo with these credentials:

Admin credentials:

  • username: admin
  • password: 2025DEVChallenge

User credentials:

  • username: newuser
  • password: 2025DEVChallenge

The demo showcases:

  • API-based access to simulated healthcare records
  • Different access scenarios (normal care, emergency, research)
  • Patient consent management affecting data access
  • Comprehensive audit logs of all authorization decisions

Project Repo

The repository includes:

  • A Node.js-based API gateway with Permit.io integration
  • Microservices for different healthcare domains (patient records, imaging, prescriptions)
  • OpenAPI/Swagger documentation with authorization attributes
  • Docker configuration for containerized deployment
  • Comprehensive test suite focused on authorization scenarios

My Journey

Building an API-first authorization system for healthcare data changed my perspective on API design and security.

Starting with API Design

Unlike traditional projects where authorization is added to existing endpoints, I started with authorization as a core design principle. Every API endpoint was designed with these considerations:

  1. What resources are being accessed?
  2. What context information is needed for authorization decisions?
  3. How do we handle partial authorization scenarios?
  4. How will this endpoint behave in emergency access situations?

This "authorization-first" approach led to cleaner, more consistent APIs with better security properties.

The Healthcare Authorization Challenge

Healthcare authorization is particularly challenging because:

  1. Context determines access - The same user might have different access rights depending on their purpose (treatment, billing, research)
  2. Patient consent matters - Patient preferences can override organizational permissions
  3. Emergency scenarios - Standard rules may need to be bypassed in emergencies
  4. Delegation is common - Providers frequently need to temporarily delegate access

These factors make it impossible to rely on simple role-based access control. My solution was to implement advanced attribute-based access control (ABAC) using Permit.io's policy engine.

Key Implementation Decisions

1. Authorization Context in API Design

I made authorization context explicit in API design:

// Example API endpoint with explicit authorization context
app.get('/api/patients/:patientId/records', (req, res) => {
  // Authorization context is explicitly part of the API
  const accessPurpose = req.query.purpose || 'treatment';
  const emergencyAccess = req.headers['x-emergency-access'] === 'true';

  // This context is used in authorization decisions
  // ...
});

2. Authorization Middleware for Consistent Enforcement

I implemented API gateway middleware to enforce consistent authorization:

// API gateway authorization middleware
function authorizationMiddleware(req, res, next) {
  // Extract resource information from request
  const resource = extractResourceFromRequest(req);

  // Extract action from HTTP method
  const action = mapMethodToAction(req.method);

  // Build authorization context
  const context = buildAuthContext(req);

  // Make authorization decision
  permit.check({
    user: req.user.id,
    action: action,
    resource: resource,
    context: context
  }).then(permitted => {
    if (permitted) {
      // Authorization successful
      next();
    } else {
      // Authorization failed
      res.status(403).json({
        error: 'Forbidden',
        message: 'You do not have permission to access this resource'
      });
    }
  }).catch(error => {
    // Error in authorization
    console.error('Authorization error:', error);
    res.status(500).json({
      error: 'Internal Server Error',
      message: 'An error occurred during authorization'
    });
  });
}

3. Patient Consent Integration

I built a consent management system that feeds into authorization decisions:

// Consent-aware authorization check
async function checkAccessWithConsent(user, patientId, dataCategory, accessPurpose) {
  // Get patient consent preferences
  const consentPreferences = await getPatientConsentPreferences(patientId);

  // Check if consent allows this access purpose for this data category
  const consentAllows = consentPreferences.purposes[accessPurpose]?.categories.includes(dataCategory) || false;

  // Include consent in authorization context
  const authContext = {
    patientId: patientId,
    dataCategory: dataCategory,
    accessPurpose: accessPurpose,
    consentStatus: consentAllows ? 'granted' : 'denied'
  };

  // Make authorization decision
  return permit.check({
    user: user.id,
    action: 'read',
    resource: `patient_data:${patientId}:${dataCategory}`,
    context: authContext
  });
}

Challenges Faced

Challenge 1: Performance at Scale

With thousands of API calls per minute, each requiring detailed authorization checks, performance became a concern. I addressed this through:

  1. Optimized authorization caching for frequently accessed resources
  2. Batch authorization checks for listing operations
  3. Authorization decision prefetching for likely next actions

Challenge 2: Maintaining Consistency Across Microservices

Ensuring consistent authorization across multiple microservices was challenging. My solution was a centralized API gateway that:

  1. Handles all authorization decisions
  2. Enriches requests with authorization context before forwarding to services
  3. Provides a consistent audit trail for all access decisions

Challenge 3: Testing Complex Authorization Scenarios

Testing the myriad permutations of user roles, patient consent, access purposes, and emergency scenarios was daunting. I solved this by developing:

  1. A specialized authorization testing framework
  2. Automated test generation for common scenarios
  3. Policy simulation tools for authorization administrators

Key Learnings

This project transformed my understanding of API-first authorization:

  1. Authorization belongs in API design, not just implementation
  2. APIs should communicate authorization context explicitly
  3. Consistent patterns improve both security and developer experience
  4. Externalized authorization dramatically improves flexibility and compliance
  5. Policy-as-code enables version control and testing of authorization rules

API-First Authorization

I implemented several innovative authorization patterns using Permit.io:

Declarative Resource Definitions

I defined healthcare resources with rich attributes for authorization:

// Define patient record resource
await permit.api.resources.create({
  key: "patient_record",
  name: "Patient Medical Record",
  description: "Electronic health record for a patient",
  actions: {
    "read": { name: "Read Record" },
    "update": { name: "Update Record" },
    "delete": { name: "Delete Record" },
    "share": { name: "Share Record" }
  },
  attributes: {
    "record_type": {
      type: "string",
      enum: ["demographics", "medications", "lab_results", "imaging", "notes", "billing"]
    },
    "sensitivity": {
      type: "string",
      enum: ["normal", "sensitive", "restricted"]
    },
    "department": { type: "string" },
    "attending_physician": { type: "string" }
  }
});

Policy-as-Code Implementation

I implemented healthcare authorization policies as code:

// Create policy for patient record access
await permit.api.policies.create({
  key: "patient_record_access",
  resource: "patient_record",
  actions: ["read"],
  effect: "allow",
  conditions: [
    // Treating physicians always have access
    `resource.attending_physician == user.id`,

    // Department-based access for normal sensitivity
    `resource.sensitivity == "normal" && resource.department == user.department`,

    // Emergency access override
    `context.emergency_access == true && user.role == "physician"`,

    // Consent-based research access
    `context.access_purpose == "research" && 
     context.consent_status == "granted" && 
     user.role == "researcher"`
  ]
});

API Gateway Integration

I integrated Permit.io at the API gateway level:

// API Gateway route with integrated authorization
apiGateway.route({
  method: 'GET',
  path: '/api/patients/:patientId/records/:recordType',
  preHandlers: [
    // Authentication middleware
    authenticate,

    // Authorization middleware
    async (req, res, next) => {
      try {
        // Extract resource information
        const patientId = req.params.patientId;
        const recordType = req.params.recordType;

        // Build rich authorization context
        const context = {
          access_purpose: req.query.purpose || 'treatment',
          emergency_access: req.headers['x-emergency-access'] === 'true',
          access_time: new Date().toISOString(),
          consent_status: await getConsentStatus(patientId, recordType, req.query.purpose)
        };

        // Check permission
        const permitted = await permit.check({
          user: req.user.id,
          action: 'read',
          resource: `patient_record:${patientId}:${recordType}`,
          context: context
        });

        if (!permitted) {
          return res.status(403).json({
            error: 'Forbidden',
            message: 'Not authorized to access this patient record'
          });
        }

        // Log access for audit
        await logAccess({
          user: req.user.id,
          resource: `patient_record:${patientId}:${recordType}`,
          action: 'read',
          context: context,
          timestamp: new Date(),
          status: 'permitted'
        });

        next();
      } catch (error) {
        console.error('Authorization error:', error);
        res.status(500).json({
          error: 'Internal Server Error',
          message: 'Error during authorization check'
        });
      }
    }
  ],
  // Forward to appropriate microservice
  target: 'http://patient-records-service/records'
});

Authorization-Aware API Documentation

I enhanced OpenAPI documentation with authorization attributes:

paths:
  /patients/{patientId}/records/{recordType}:
    get:
      summary: Get patient record by type
      parameters:
        - name: patientId
          in: path
          required: true
          schema:
            type: string
        - name: recordType
          in: path
          required: true
          schema:
            type: string
            enum: [demographics, medications, lab_results, imaging, notes, billing]
        - name: purpose
          in: query
          description: Purpose of access (authorization context)
          schema:
            type: string
            enum: [treatment, payment, research]
            default: treatment
      security:
        - BearerAuth: []
      x-authorization:
        resource: patient_record
        action: read
        contextParams:
          - name: purpose
            in: query
          - name: emergency_access
            in: header
      responses:
        '200':
          description: Patient record retrieved successfully
        '403':
          description: Not authorized to access this record

Policy Testing Framework

I built a comprehensive testing framework for authorization policies:

// Authorization policy test
test('Physician can access patient records in emergency', async () => {
  // Setup user
  const physicianUser = {
    id: 'dr-smith',
    attributes: {
      role: 'physician',
      department: 'cardiology'
    }
  };

  // Setup resource
  const patientRecord = {
    id: 'patient-123-cardiology',
    attributes: {
      record_type: 'notes',
      sensitivity: 'restricted',
      department: 'neurology',  // Different department
      attending_physician: 'dr-jones'  // Different physician
    }
  };

  // Test normal access (should fail)
  const normalAccess = await testPolicyDecision({
    user: physicianUser,
    action: 'read',
    resource: patientRecord,
    context: {
      access_purpose: 'treatment',
      emergency_access: false,
      consent_status: 'not_required'
    }
  });

  expect(normalAccess.decision).toBe('deny');

  // Test emergency access (should succeed)
  const emergencyAccess = await testPolicyDecision({
    user: physicianUser,
    action: 'read',
    resource: patientRecord,
    context: {
      access_purpose: 'treatment',
      emergency_access: true,
      consent_status: 'not_required'
    }
  });

  expect(emergencyAccess.decision).toBe('allow');
});

By implementing API-first authorization with Permit.io, I was able to create a healthcare data exchange platform that maintains strong security and compliance while providing the flexibility needed for real-world healthcare scenarios.