Part 3: Party assignment
Thread: API
Learn how people become parties in your protocols through authentication and JWT tokens.
Prerequisites
- Have completed Part 1: Introduction to Parties
- Access to deployed NPL protocols via Swagger UI
- Basic understanding of authentication concepts
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:
- A person (Alice) logs in through the IAM system (e.g., Alice logs in with her credentials)
- The IAM issues a JWT token containing information about Alice (her email, username, etc.)
- Alice uses this token to make API calls to create or interact with protocols
- 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
Authorizationheader
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
- Open Swagger UI for your deployed NPL application
- Click the Authorize button
- Log in using Alice's credentials
- 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
editorandapproverparties 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
editpermission requires theeditorparty - It extracts Alice's email (
alice@example.com) from her JWT token - It checks if the
editorparty 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
- In Swagger UI, log out (clear authorization)
- Click Authorize again
- Log in using Bob's credentials
- 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
editpermission requires theeditorparty - It extracts Bob's email (
bob@example.com) from his JWT token - It checks if the
editorparty 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
approvepermission requires theapproverparty - It extracts Bob's email (
bob@example.com) from his JWT token - It checks if the
approverparty 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:
- JWT token contains user information: Your login token has claims like
{ "email": "alice@example.com" } - Entity is derived from token: The system extracts these attributes to identify you as an entity
- Protocol instance has party mappings: Each instance stores which entities are linked to which parties
- Permission requires a party: Each permission specifies which party can call it (e.g.,
permission[editor]) - 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
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.