Skip to content

Part 3: Party assignment

Thread: API

Learn how people become parties in your protocols through authentication and JWT tokens.

Prerequisites

Learning outcomes

After completing this part, you'll be able to:

  • Understand how people interact with NPL protocols through an IAM system
  • Use JWT tokens to authenticate as specific users
  • Create protocol instances with user identities as parties
  • Call permissions as authenticated users
  • Understand how entities (person or group) map to parties in protocol instances

From NPL code to running systems

In the previous parts, we used abstract party values in NPL code:

var alice = uniquePartyOf();
var bob = uniquePartyOf();
var doc = Document[alice, bob]("Initial content");

// In NPL, we explicitly specify which party is acting
doc.edit[alice]("Updated content");

But in a running system, you need to connect actual users (human, or technical) to these protocols. This is where authentication and party assignment come in.

How people become parties

When your NPL application is deployed with an Identity and Access Management (IAM) system like [Keycloak] (https://www.keycloak.org), the flow works like this:

  1. A person (Alice) logs in through the IAM system (e.g., Alice logs in with her credentials)
  2. The IAM issues a JWT token containing information about Alice (her email, username, etc.)
  3. Alice uses this token to make API calls to create or interact with protocols
  4. The NPL Runtime maps Alice's user information to a party in the protocol based on the token information

More details on these concepts are provided below.

Understanding JWT tokens

A JWT (JSON Web Token) is a secure way to transmit user information. When Alice logs in, she receives a token that looks like this (simplified):

{
  "iss": "https://example.net/oidc",
  "sub": "alice-user-id-123",
  "email": "alice@example.com",
  "preferred_username": "alice",
  "name": "Alice Smith"
}

These are called claims — pieces of information about Alice's user information.

Key points about JWT tokens:

  • They contain information about the authenticated user
  • They're required for the system to allow you to interact with it
  • They provide information like email, username, and other attributes
  • They're sent with every API request in the Authorization header

Note: The iss claim identifies the issuer of the jwt as part of the jwt payload.

Entities and party values are bundles of claims

In NPL's party model:

  • An entity (as introduced in Part 1: Introduction to parties) represents a person or group
  • An entity is defined by a set of attributes, called claims in both the JWT token context and the NPL context
  • These attributes (like email, username, department) uniquely identify who you are, as an individual or a group

For example, Alice as an entity is defined by:

{
  "email": ["alice@example.com"]
}

Entities linked to parties per protocol instance

Here's the crucial concept: Entities are linked to parties separately for each protocol instance.

  • When you create a Document instance, you specify which entity (person or group) plays which party role
  • This linking is stored with the protocol instance
  • Different instances can have different party assignments

For example:

  • In Document #1: Alice is the editor, Bob is the approver
  • In Document #2: Alice is the approver, Charlie is the editor
  • The same entity (Alice), has different party roles in different instances

This last point is what sets NPL apart from more traditional role-based authorization.

Practical walkthrough: Document approval with concrete users

Let's walk through a complete example using Swagger UI with two users: Alice and Bob.

Step 1: Log in as Alice

  1. Open Swagger UI for your deployed NPL application
  2. Click the Authorize button
  3. Log in using Alice's credentials
  4. Alice receives a JWT token that includes:
{
  "email": "alice@example.com",
  "preferred_username": "alice"
}

The Swagger UI stores this token and sends it with all subsequent API calls.

Step 2: Create a Document instance

Alice creates a document where she is the editor and Bob is the approver.

API Call:

POST /npl/document/Document
Authorization: Bearer <ALICE_JWT_TOKEN>
Content-Type: application/json

Request Body:

{
  "@parties": {
    "editor": {
      "claims": {
        "email": ["alice@example.com"]
      }
    },
    "approver": {
      "claims": {
        "email": ["bob@example.com"]
      }
    }
  },
  "content": "Q4 Financial Report - Draft"
}

What's happening:

  • Alice's entity is defined by { "email": ["alice@example.com"] }
  • Bob's entity is defined by { "email": ["bob@example.com"] }
  • These entities are linked to the editor and approver parties for this specific document instance
  • The NPL Runtime stores this mapping

Response:

{
  "@id": "doc-abc-123",
  "@state": "created",
  "content": "string",
  "@parties": {
    "editor": {
      "claims": {
        "email": [
          "alice@example.com"
        ]
      }
    },
    "approver": {
      "claims": {
        "email": [
          "bob@example.com"
        ]
      }
    }
  },
  "@actions": {
    "edit": "http://localhost:12000/npl/document/Document/doc-abc-123/edit",
    "requestReview": "http://localhost:12000/npl/document/Document/doc-abc-123/requestReview",
    "approve": "http://localhost:12000/npl/document/Document/doc-abc-123/approve"
  }
}

Save this protocol's @id—you'll need it for subsequent calls.

Step 3: Alice edits the document

Alice updates the document content.

API Call:

POST /npl/document/Document/doc-abc-123/edit
Authorization: Bearer <ALICE_JWT_TOKEN>
Content-Type: application/json

Request Body:

{
  "newContent": "Q4 Financial Report - Updated figures"
}

What's happening:

  • The NPL Runtime sees that the edit permission requires the editor party
  • It extracts Alice's email (alice@example.com) from her JWT token
  • It checks if the editor party can be represented by this entity—it can, so the call succeeds

Response:

{}

Step 4: Alice requests review

API Call:

POST /npl/document/Document/doc-abc-123/requestReview
Authorization: Bearer <ALICE_JWT_TOKEN>

Request Body: no body

Response:

{}

The document is now in review, ready for Bob to approve.

Step 5: Log out and log in as Bob

  1. In Swagger UI, log out (clear authorization)
  2. Click Authorize again
  3. Log in using Bob's credentials
  4. Bob receives a JWT token:
{
  "email": "bob@example.com",
  "preferred_username": "bob"
}

Step 6: Bob lists all Document instances

Bob can query for documents where he's involved.

API Call:

GET /npl/document/Document
Authorization: Bearer <BOB_JWT_TOKEN>

Response:

{
  "items": [
    {
      "@id": "doc-abc-123",
      "@state": "inReview",
      "@parties": {
        "editor": { "email": ["alice@example.com"] },
        "approver": { "email": ["bob@example.com"] }
      }
    }
  ]
}

Bob can see the document because his entity (with issuer and email) can represent the approver party in this instance.

Step 7: Bob tries to edit (fails)

Bob attempts to call the edit permission.

API Call:

POST /npl/document/Document/doc-abc-123/edit
Authorization: Bearer <BOB_JWT_TOKEN>
Content-Type: application/json

Request Body:

{
  "newContent": "Bob tries to edit"
}

What's happening:

  • The NPL Runtime sees that the edit permission requires the editor party
  • It extracts Bob's email (bob@example.com) from his JWT token
  • It checks if the editor party can be represented by Bob's entity—it cannot (requires Alice's email), so the call fails

Step 8: Bob approves the document

API Call:

POST /npl/document/Document/doc-abc-123/approve
Authorization: Bearer <BOB_JWT_TOKEN>

Request Body: no body

What's happening:

  • The NPL Runtime sees that the approve permission requires the approver party
  • It extracts Bob's email (bob@example.com) from his JWT token
  • It checks if the approver party can be represented by this entity—it can, so the call succeeds

Response:

{}

How entity-to-party mapping works

Let's break down what happens when you make an API call:

  1. JWT token contains user information: Your login token has claims like { "email": "alice@example.com" }
  2. Entity is derived from token: The system extracts these attributes to identify you as an entity
  3. Protocol instance has party mappings: Each instance stores which entities are linked to which parties
  4. Permission requires a party: Each permission specifies which party can call it (e.g., permission[editor])
  5. System checks the match:
    • Find which party is required by the permission
    • Extract entity from JWT token
    • Check if that party can be represented by the entity
    • Allow or deny accordingly

Party representation in the API

When creating instances or making calls, parties are represented as objects with attributes:

{
  "editor": {
    "email": ["alice@example.com"]
  },
  "approver": {
    "email": ["bob@example.com"]
  }
}

Important format details:

  • Each party is an object with attribute key-value pairs
  • The most common attribute is "email"
  • The value is an array of strings (e.g., ["alice@example.com"])

Extending with delegation

You can combine party assignment with delegation (from Part 2) for more complex workflows.

For example, a coordinator could trigger approvals on behalf of multiple approvers:

protocol[coordinator, doc1Approver, doc2Approver] BulkApproval(
    document1: Document,
    document2: Document
) {
    @api
    permission[coordinator] approveAll() {
        document1.approve[doc1Approver]();
        document2.approve[doc2Approver]();
    }
}

When creating the BulkApproval instance via API:

{
  "parties": {
    "coordinator": {
      "email": ["manager@example.com"]
    },
    "doc1Approver": {
      "email": ["alice@example.com"]
    },
    "doc2Approver": {
      "email": ["bob@example.com"]
    }
  },
  "parameters": {
    "document1": "doc-abc-123",
    "document2": "doc-def-456"
  }
}

The manager can then approve both documents atomically by calling approveAll, even though the actual approvals happen as Alice and Bob (via delegation).

Key concepts recap

Concept Description
JWT token Contains information (claims) about the authenticated user
Claims Key-value pairs in the JWT (e.g., email, username) that describe the user
Entity A person or group defined by a set of claims
Party-entity linking The mapping of entities to parties, stored per protocol instance
Party representation Objects with claims like { "email": ["alice@example.com"] }
Authorization check System verifies the entity from the JWT matches the required party

Common JWT claims used for parties

Claim Example Use case
email "alice@example.com" Most common, human-readable
preferred_username "alice" Simple username-based systems
sub "auth0\|507f1f77bcf86cd799439011" Unique user ID from OAuth providers
upn "alice@company.com" Active Directory environments

Choose the claim that best fits your IAM configuration and use it consistently.

Best practices

Use email for simplicity

For most applications, email is the clearest identifier:

{
  "editor": {
    "email": ["alice@example.com"]
  }
}

Match party attributes to JWT claims

Ensure the attributes you use for party assignment match claims in your JWT tokens.

Test authorization thoroughly

Always test:

  • ✅ Authorized users can call their permissions
  • ❌ Unauthorized users cannot call permissions they don't have access to
  • ❌ Users cannot act as parties they're not assigned to

Document which claim is used

Make it clear in your API documentation:

Party Assignment: All parties use the email claim from the JWT token.

What's next?

In this part, you learned how people become parties in NPL protocols through authentication. You now understand:

  • How JWT tokens contain user information
  • How entities (defined by attributes) map to parties per protocol instance
  • How to create instances and call permissions as authenticated users
  • How the system checks authorization based on JWT claims

In the next part, you'll learn about party assignment automation, which simplifies protocol instantiation API calls by automatically deriving the party values to bind to party parameters from the JWT token.

Further reading