Oystehr
Execution Roles

Execution Roles

Zambdas can be assigned an execution role that automatically provides an access token when the Zambda is invoked. This allows your Zambda to call Oystehr APIs (such as the FHIR API) with a scoped set of permissions, without you needing to manage M2M Client credentials or token retrieval yourself.

How it Works

When you assign an IAM Role to a Zambda as its execution role:

  1. Oystehr automatically creates a behind-the-scenes M2M Client linked to that role.
  2. Each time the Zambda is invoked, Oystehr fetches an access token from this M2M Client and injects it into the Zambda's event payload as accessToken.
  3. Your Zambda code can use this token to make authenticated API calls with the permissions defined by the role's access policy.

If multiple Zambdas share the same execution role, they share the same underlying M2M Client, keeping things efficient.

💡

Execution roles are optional. If you don't assign one, your Zambda will not receive an accessToken in the event payload, and you can continue to manage authentication manually using Secrets or other approaches.

Setting Up an Execution Role

Step 1: Create a Role

First, create an IAM Role with the permissions your Zambda needs. For example, to allow reading FHIR Patient resources:

import Oystehr from '@oystehr/sdk';
 
const oystehr = new Oystehr({
  accessToken: "<your_access_token>",
});
 
const role = await oystehr.role.create({
  name: 'zambda-read-patients',
  accessPolicy: {
    rule: [
      {
        resource: ['FHIR:Patient:*'],
        action: ['FHIR:Read', 'FHIR:Search'],
        effect: 'Allow',
      },
    ],
  },
});
 
console.log(role.id); // Use this ID as the executionRoleId

Step 2: Create or Update a Zambda with the Execution Role

Pass the role's UUID as executionRoleId when creating or updating a Zambda.

Create a Zambda with an execution role
const zambda = await oystehr.zambda.create({
  name: 'patient-reader',
  triggerMethod: 'http_auth',
  executionRoleId: '<role_uuid>',
});
Update an existing Zambda to add an execution role
const zambda = await oystehr.zambda.update({
  id: '<zambda_id>',
  executionRoleId: '<role_uuid>',
});

You can also set the execution role from the Developer Console by entering the role UUID in the Execution Role ID field when creating or editing a Zambda.

Step 3: Use the Access Token in Your Zambda

When a Zambda with an execution role is invoked, the accessToken field is included in the event payload alongside secrets and body. Use it to initialize the Oystehr SDK or make API calls directly.

import Oystehr from '@oystehr/sdk';
 
exports.index = async function (event) {
  const { accessToken, body } = event;
 
  if (!accessToken) {
    return {
      statusCode: 500,
      body: JSON.stringify({ error: 'No access token available' }),
    };
  }
 
  const oystehr = new Oystehr({ accessToken });
 
  // Make API calls scoped to the execution role's permissions
  const patients = await oystehr.fhir.search({
    resourceType: 'Patient',
    params: [{ name: '_count', value: '10' }],
  });
 
  return {
    statusCode: 200,
    body: JSON.stringify(patients),
  };
};

Removing an Execution Role

To remove the execution role from a Zambda, update it with executionRoleId set to null:

curl -X PATCH 'https://project-api.zapehr.com/v1/zambda/<zambda_id>' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer <your_access_token>' \
--header 'x-oystehr-project-id: <your_project_id>' \
--data '{
  "executionRoleId": null
}'

After removing the execution role, the Zambda will no longer receive an accessToken in its event payload.

Changing Permissions

Because the access token's permissions come from the role's access policy, you can change what a Zambda is allowed to do by updating the role itself. All Zambdas sharing that execution role will pick up the new permissions on their next invocation.

await oystehr.role.update({
  id: '<role_uuid>',
  accessPolicy: {
    rule: [
      {
        resource: ['FHIR:*'],
        action: ['FHIR:Read', 'FHIR:Search'],
        effect: 'Allow',
      },
    ],
  },
});

Role Deletion Protection

A role that is assigned as an execution role to one or more Zambdas cannot be deleted. You must first remove the execution role from all Zambdas using it before you can delete the role. Attempting to delete a role that is in use will return an error listing the Zambdas that depend on it.

Additional Resources