← Back to NIP Index

DID-Based Authentication Protocol

NIP: 2 | Author: jolestar(@jolestar) | Status: Draft | Created: May 12, 2024


Abstract

This specification defines a general-purpose, decentralized authentication protocol based on NIP-1 DIDs. It enables any entity with a DID to prove its identity and the integrity of a message or request. The protocol leverages cryptographic signatures generated using keys associated with a DID, as registered in the corresponding DID document. It outlines a core authentication mechanism and then details its application across various communication patterns, including Agent-to-Agent (A2A) messaging, JSON-RPC services, and RESTful APIs, primarily utilizing the standard HTTP Authorization header or embedded authentication structures for non-HTTP contexts.

Motivation

As decentralized systems and agent-based architectures evolve, a standardized, flexible, and secure authentication mechanism is crucial. NIP-1 provides a foundational DID key model. This NIP builds upon that by specifying how these DIDs and their associated keys can be used for robust authentication across diverse interaction protocols. The goal is to provide a unified approach to verifying the origin and integrity of communications, whether between autonomous agents, clients and servers, or other interacting components. This NIP addresses the need for a common authentication layer adaptable to HTTP-based interactions (like REST APIs and RPC over HTTP using the standard Authorization header) as well as other message-oriented protocols.

Specification

Core DID Authentication Mechanism

This section describes the fundamental components and processes common to all applications of this DID-based authentication protocol.

1. Authentication Data Structure

At its core, the authentication information relies on the following data elements, typically serialized as a JSON object:

{
  "signer_did": "did:example:...",
  "key_id": "did:example:...#key-id",
  "signature_value": "..."
}

Where:

2. Signed Content and Hashing

The signature_value is a cryptographic signature calculated over a hash of the content being authenticated.

Hashing Process: hash = HASH_ALGORITHM(domainSeparator + contentToSign)

3. General Authentication Flow (Conceptual)

The flow involves a Signer (the entity authenticating) and a Verifier (the entity validating the authentication).

graph TD
    subgraph Signer
        A[Prepare message with timestamp & nonce] --> B[Hash & sign with private key]
        B --> C[Send message + auth data]
    end
    subgraph Verifier
        D[Receive message & auth data] --> E[Check timestamp & nonce]
        E --> F[Resolve DID & verify signature]
        F --> G[Check key permissions]
        G --> H[Authentication successful]
    end
    C --> D

Simplified Steps:

  1. Prepare & Sign (Signer): Build the message containing timestamp, nonce, and (for HTTP scenarios) an audience field identifying the target service, hash it with the fixed domainSeparator, and sign the hash.
  2. Transmit (Signer): Send the message together with {signer_did, key_id, signature_value} (e.g., in an HTTP header or embedded field).
  3. Replay Protection (Verifier): Ensure timestamp is fresh, nonce is unique for the signer_did, and (if present) that audience matches this service’s canonical URL.
  4. Signature Verification (Verifier): Resolve the signer’s DID, locate the public key for key_id, and verify the signature.
  5. Permission Check & Result (Verifier): Confirm the key is authorised for authentication; if everything checks out, accept the request.

Application: HTTP-based Authentication (REST APIs, RPC over HTTP)

For services communicating over HTTP (e.g., RESTful APIs, JSON-RPC over HTTP), authentication is conveyed via the standard Authorization HTTP header.

1. HTTP Header

The Authorization HTTP header is used with the DIDAuthV1 scheme. The header is constructed as follows:

Authorization: DIDAuthV1 <credentials>

Where <credentials> must be the Base64url encoding of the UTF-8 encoded JSON string representing the Core Authentication Data Structure.

2. Signed Content (contentToSign)

For HTTP-based authentication, contentToSign typically includes:

3. Error Handling

Failures should result in standard HTTP error responses (e.g., 401 Unauthorized, 400 Bad Request), potentially with a JSON body detailing the specific error based on NIP-2 codes (see “General Error Codes” below).

Application: Agent-to-Agent (A2A) Communication

For Agent-to-Agent (A2A) communication:

Note: While @nuwa-ai/identity-kit provides the core cryptographic primitives (createSignature) required for this flow, the reference implementation for the full A2A embedded authentication structure and protocol will be provided in a separate, specialized A2A SDK.

1. Protocol Identifier (for embedded method)

The scheme is identified by did-auth-v1.

2. Embedded Authentication Structure (for non-HTTP or preferred embedding)

An A2A message requiring authentication (when not using the HTTP Authorization header method) would include a field (e.g., authentication) structured as:

{
  "schemes": ["did-auth-v1"],
  "credentials": "<json_string_of_core_auth_data>"
}

Where credentials is the JSON string of the Core Authentication Data Structure.

3. Signed Content (contentToSign) (for embedded method)

For A2A using the embedded method, contentToSign is typically the message.parts object (or a similar well-defined part of the A2A message payload), which must include timestamp and nonce.

Example domainSeparator for A2A (embedded method): "EXAMPLE_A2A_EMBEDDED_AUTH_V1:"

4. Error Handling (for embedded method)

Errors are typically handled according to the A2A protocol’s error reporting mechanisms, using NIP-2 error codes (see “General Error Codes” below).

General Error Codes

These error codes can be adapted for use in HTTP responses or embedded error structures.

Rationale

(Placeholder: Detailed rationale to be added. Key design choices include: a general core mechanism for broad applicability, use of domain separators for security, inclusion of timestamp/nonce (and audience) for replay protection, specific adaptations for HTTP and A2A common patterns.)

Using the standard Authorization HTTP header with a custom scheme (DIDAuthV1) for REST/RPC APIs aligns with common HTTP practices (e.g., Bearer, Basic schemes) and avoids proliferation of custom headers. This makes it easier for existing HTTP infrastructure and libraries to handle. For A2A communication, prioritizing the HTTP Authorization header method when A2A occurs over HTTP promotes consistency. The embedded authentication data structure is provided for scenarios where A2A does not run over HTTP or where direct message-level security is preferred. The JSON structure for authentication data is chosen for its widespread support and ease of parsing. Base64url encoding for the credentials in the Authorization header is standard for such use cases.

Backwards Compatibility

(Placeholder: Details on backwards compatibility to be added. This NIP defines new authentication mechanisms. Systems not implementing it will not understand the Authorization header with the DIDAuthV1 scheme or the did-auth-v1 scheme in embedded A2A messages. Services can choose to support multiple authentication schemes for transition periods.)

Test Cases

(Placeholder: Test cases to be added, covering: valid/invalid signatures for HTTP and A2A, replay attempts, DID resolution failures, key permission issues, malformed headers/credentials.)

Reference Implementation

The official reference implementation for this protocol is part of the @nuwa-ai/identity-kit SDK. Specifically, its auth/v1 module provides a complete implementation of the HTTP-based authentication flow:

These tools provide a robust foundation for services wishing to secure their APIs using this NIP.

Security Considerations

Copyright and related rights waived via CC0.