Skip to content

feat: ESSR protected client APIs - #351

Merged
kentbull merged 12 commits into
WebOfTrust:mainfrom
veridian-id:feat/essrAPI
Jul 21, 2026
Merged

feat: ESSR protected client APIs#351
kentbull merged 12 commits into
WebOfTrust:mainfrom
veridian-id:feat/essrAPI

Conversation

@iFergal

@iFergal iFergal commented Jan 15, 2025

Copy link
Copy Markdown
Collaborator

This PR adds an alternative mechanism to authenticate with the KERIA API from a Signify client, and leaves the current RFC-9421 signed headers in place too. Beyond confidentiality, it also resolves other issues from before such as unsigned query params and bodies. (#287)

The unsigned HTTP request from the client is converted to a HTTP bytestream and embedded in an ESSR payload. This payload becomes the body of a wrapper HTTP request for the "/" path - so tunneled via ESSR. The wrapper could be handled by pure TCP but for now this works quite nicely in the Falcon middlewares with minimal changes.

Because nothing else uses "/" as the path, the authentication type is determined based on the path. A cleaner approach in a TCP world would just to have a CESR based command signed and optionally encrypted following ESSR, rather than complicating things with managing headers, or embedding HTTP requests within each other.

I have written the corresponding code in Signify and all integration tests pass.


Flow for generating request from Signify:

  1. Create HTTP request - e.g. POST /identifiers <body>
  2. Add Signify-Resource header (Encrypt Sender)
  3. Serialize HTTP request as a HTTP string
  4. crypto_box_seal(http_request_string, public_key_of_keria_agent)
  5. Create a new HTTP request to POST / where the body of the request is the sealed box bytes (application/octet-stream)
  6. Add Signify-Resource, Signify-Timestamp and Signify-Receiver headers (receiver is Sign Receiver)
  7. Sign the request and add as a header

Note:
Like the keripy ESSR parser, the commitment to unsigned HTTP request is done by taking a digest:

payload = dict(
    src=<sender>,
    dest=<receiver>,
    d=coring.Diger(ser=cipher, code=MtrDex.Blake3_256).qb64,
    dt=dt,
)

A signature is created over this dict. I'm re-using the signature style of the signed headers.

ESSR drawio

@iFergal iFergal self-assigned this Jan 15, 2025
@iFergal iFergal changed the title feat: ESSR client authentication feat: ESSR protected client APIs Jan 16, 2025
@iFergal

iFergal commented Jan 17, 2025

Copy link
Copy Markdown
Collaborator Author

WebOfTrust/signify-ts#304 created on Signify side

Comment thread src/keria/core/authing.py Outdated
Comment thread src/keria/core/authing.py Outdated
@codecov

codecov Bot commented Jan 23, 2025

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 97.59036% with 4 lines in your changes missing coverage. Please review.
✅ Project coverage is 88.02%. Comparing base (f621ca4) to head (4b7eb6c).
⚠️ Report is 1 commits behind head on main.

Files with missing lines Patch % Lines
src/keria/core/authing.py 98.08% 3 Missing ⚠️
src/keria/app/aiding.py 66.66% 1 Missing ⚠️
Additional details and impacted files
@@            Coverage Diff             @@
##             main     #351      +/-   ##
==========================================
+ Coverage   87.76%   88.02%   +0.26%     
==========================================
  Files          26       26              
  Lines        5826     5929     +103     
==========================================
+ Hits         5113     5219     +106     
+ Misses        713      710       -3     

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment thread src/keria/core/authing.py Outdated
@iFergal
iFergal marked this pull request as draft June 18, 2025 17:45
@iFergal

iFergal commented Jun 18, 2025

Copy link
Copy Markdown
Collaborator Author

Updated PR to allow both signed headers and ESSR. I'm leaving this as draft as I still have the edge case I need to test for in the comment I left on the PR above, and I need to update my Signify PR.

@iFergal
iFergal marked this pull request as ready for review June 24, 2025 12:57
Comment thread src/keria/app/aiding.py
Comment thread src/keria/core/authing.py
@m00sey
m00sey self-requested a review October 22, 2025 17:04
@kentbull

kentbull commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

@iFergal will you rebase this and fix the conflicts? I want to review and get this merged.

Sotatek-Patrick-Vu and others added 2 commits July 17, 2026 15:38
…conflicts) (#27)

* adds MAINTAINERS.md

Signed-off-by: Kevin Griffin <griffin.kev@gmail.com>

* adds MAINTAINERS.md

Signed-off-by: Kevin Griffin <griffin.kev@gmail.com>

* add hab name, prefix to logs; fix GracefulShutdownDoer to use KeyboardInterrupt instead of Doist.exit() (WebOfTrust#363)

* feat: add hab name and prefix partial to log messages

* feat: add TruncatedFormatter for logs; refactor GracefulShutdownDoer

The TruncatedFormatter gives human readability with visual "rivers" and the GracefulShutdownDoer properly throws a KeyboardInterrupt to cause the main Doist loop to exit."

* fix: use full, not abbreviated AID in logs

* feat,docs: correct the graceful shutdown logic

This uses the DoDoer logic properly to check shutdown conditions on each loop and then act appropriately on receiving the shutdown signal.

* docs: add in documentation for various components

* chore: remove extra label from log messages

* chore: bump version to 0.2.0 for release to PyPi and Docker HUb

* chore: add ruff linting (WebOfTrust#385)

* chore: add ruff linting (WebOfTrust#385)

* fix: KERIA server exits (exit code 1) on admit call with 'NoneType' object has no attribute 'ked' (WebOfTrust#379)

* is:issue KERIA server exits (exit code 1) on admit call with 'NoneType' object has no attribute 'ked' WebOfTrust#371

* remove checking grant and change parser for all of enpoints in ipex

---------

Co-authored-by: iFergal <fergaloconnor1998@gmail.com>

* credentialing.py type hints and auto-generated OpenAPI specs (WebOfTrust#373)

* spike: Feasibility of generating Signify types from Python type hints via OpenAPI

* Revert "spike: Feasibility of generating Signify types from Python type hints via OpenAPI"

This reverts commit 4901f19.

* split Operation then using allOf in openapi schemas

* demo generated types for credentials().get()

* fix some option properties and add schema for CredentialState

* Add custom class for empty dict schema

* fix credentials types + add registries's types

* Add remaining schemas for credentialing

* resolve review comments

* fix errors/warning for swagger validator

* resolve review comments

* resolve review comments

* Remove unused import

* fix lints and tests

* integrate mypy check and fix type errors for credentialing.py

* remove some unuse schemas

* resolve review comment + add utils for auto generate dataclass/schema from FieldDom

* support dip, drt for anc

* fix lints issue

* refactor openapi utils

* support dip, drt for anc: fix dataclass

* refactor: move logging and http concerns to modules

Moved both console logging and HTTP request logging to the logs and httping modules to clean up the agenting module and keep concerns separate.

Adds basic HTTP request logging configurable by log level.

* feat: respect temp arg and allow cf to be passed in

This allows for consistent testability where all configuration files and LMDB databases will or will not be created in tempfiles. Being able to pass a cf in means this can be more easily tested from an end-to-end integration test, which is my goal here.

* feat: make Agency configuration more testable

Externalize the Configer instance so that both the Agency and all Agent instances can be configured in a test prior to Agency startup.

* fix: SignifyPy compatibility for CredentialCollectionEnd

For some reason SignifyTS uses the 'ri' field to identify a registry and SignifyPy uses the 'ii' field to identify a registry. They should both use the 'rd' field in the future.

* fix: correct indentation for getAgency find function

* docs: Add ParserDoer.recur documentation

* feat: upgrade to KERI 1.2.7 and new HIO

* refactor: simplify Grant debugging with gathering GrantDoer

GrantDoer collects all dependent KEL and TEL event artifacts for an ACDC into a single list making it easier to debug a transmission.

* refactor: rename, use vars, remove indirection

* refactor: move logging to monitoring package

* refactor: disable request logging by default, add --logrequests option

* refactor: move grant cue processor into recur

* refactor: simplify middleware

* add ruff format check

* run ruff format

* add docker step to ci

* downgrade lmdb

* aiding.py type hints and auto-generated OpenAPI specs (WebOfTrust#391)

* spike: Feasibility of generating Signify types from Python type hints via OpenAPI

* Revert "spike: Feasibility of generating Signify types from Python type hints via OpenAPI"

This reverts commit 4901f19.

* split Operation then using allOf in openapi schemas

* demo generated types for credentials().get()

* fix some option properties and add schema for CredentialState

* Add custom class for empty dict schema

* fix credentials types + add registries's types

* Add remaining schemas for credentialing

* resolve review comments

* fix errors/warning for swagger validator

* resolve review comments

* resolve review comments

* Remove unused import

* fix lints and tests

* integrate mypy check and fix type errors for credentialing.py

* remove some unuse schemas

* resolve review comment + add utils for auto generate dataclass/schema from FieldDom

* support dip, drt for anc

* fix lints issue

* refactor openapi utils

* support dip, drt for anc: fix dataclass

* aiding.py type hints and auto-generated OpenAPI specs

* Add data schema for GroupMemberCollectionEnd

* resolve review comments

* fix review comments

* change type for "nt", "kt"

* do not handle required/optional field for dataclass from FieldDom

* fix lint error

* fix cover tests

* update specing test

* update and use new openapi utils

* update kt, nt field: allow string or array of strings or array of array of strings

* update openapi spec

* fix lints

* resolve review comments

* resolve conflicts, fix lints and update spec test

* Migrate from setuptools to uv depdendency management

* pin uv version to avoid breaking in the future

* simplify config

* fix python install

* fix: renaming identifier can overwrite other identifiers with same name WebOfTrust#322 (WebOfTrust#392)

* agenting.py type hints and OpenAPI specs (WebOfTrust#393)

* spike: Feasibility of generating Signify types from Python type hints via OpenAPI

* Revert "spike: Feasibility of generating Signify types from Python type hints via OpenAPI"

This reverts commit 4901f19.

* split Operation then using allOf in openapi schemas

* demo generated types for credentials().get()

* fix some option properties and add schema for CredentialState

* Add custom class for empty dict schema

* fix credentials types + add registries's types

* Add remaining schemas for credentialing

* resolve review comments

* fix errors/warning for swagger validator

* resolve review comments

* resolve review comments

* Remove unused import

* fix lints and tests

* integrate mypy check and fix type errors for credentialing.py

* remove some unuse schemas

* resolve review comment + add utils for auto generate dataclass/schema from FieldDom

* support dip, drt for anc

* fix lints issue

* refactor openapi utils

* support dip, drt for anc: fix dataclass

* aiding.py type hints and auto-generated OpenAPI specs

* Add data schema for GroupMemberCollectionEnd

* resolve review comments

* fix review comments

* change type for "nt", "kt"

* do not handle required/optional field for dataclass from FieldDom

* fix lint error

* fix cover tests

* update specing test

* update and use new openapi utils

* update kt, nt field: allow string or array of strings or array of array of strings

* update openapi spec

* fix lints

* resolve review comments

* resolve conflicts, fix lints and update spec test

* aiding.py type hints and auto-generated OpenAPI specs

* resolve review comments

* agenting.py type hints and OpenAPI specs WebOfTrust#377

* add response schema for OperationCollectionEnd

* rebase/resolve conflicts from parent branch

* merge parent branch

* remote Receipt msg/ Routed msg in KeyEventRecord dataclass

* fix: GET /credentials/{said} throws 500 for escrowed credentials WebOfTrust#400 (WebOfTrust#402)

* Adding type hints for other End files delegating.py, grouping.py, ipexing.py, notifying.py, exchanging.py (WebOfTrust#399)

* Adding type hints for other End files delegating.py, grouping.py, ipexing.py, notifying.py, exchanging.py

* fix multisig types

* continue to fix Multisig types

* ci(gha): upgrade macos runner to macos-15 (WebOfTrust#407)

* chore: upgrade macos runner

* rename CI step for macos-15

---------

Co-authored-by: Patrick Vu <patrick.vu@sotatek.com>

* feat: index outbound exchange messages (WebOfTrust#401)

* fix kt, nt for KeyStateRecord type

* fix: propagate log level argument to keripy logger (WebOfTrust#410)

* enable keri logs

* cover new test for __init__.py code

* remove unnecessary tests

* fix: split HabState in 2 types to account for optional attributes (WebOfTrust#411)

* change transferable, windexes and state to be optional

* splitting Indentifier object for list method and get method

* fix spec

* fix duplicate HabState

* use HabState schema name instead of Identifier

* revert comments for HabStateBase

* fix: extra fields for getRequest should be optional in exnmultisig (WebOfTrust#417)

* change transferable, windexes and state to be optional

* Specify OP types

* resove review comments: add missing code for KeyStateRecord kt,nt

* add missing commit: fix: extra fields for getRequest should be optional in exnmultisig

* upgrade to keri 1.2.12 and bump keria version to 0.4.0

Signed-off-by: Kent Bull <kent@kentbull.com>

* fix flaky OpenAPI Spec test

* chore: fix generation of ACDC ordering and extra fields

* fix CredentialState schema

* fix: ancatc type

* refactor: remove print

* refactor: formatting

* fix: correct registry state dataclass for openapi spec

* refactor: remove unused import

* fix: better specify keyevents and fix group member ends

* format

* Fixes to sig-fix command

* Fixed formatting on change

* chore: upgrade to KERIpy 1.2.13

Signed-off-by: Kent Bull <kent@kentbull.com>

* add changelog

Signed-off-by: Kent Bull <kent@kentbull.com>

* add older version docs in changelog

Signed-off-by: Kent Bull <kent@kentbull.com>

* add new endpoint for getting locschemes by EID

* docs: fix documentation badge

* chore: fix depends type in some operation metadata (WebOfTrust#441)

* fix depends type in some operation metadata

* remove SubmitOperation in KelOperation

---------

Co-authored-by: iFergal <fergaloconnor1998@gmail.com>

* normalize bool literal schema

* chore: make depends required for RegistryOperationMetadata

* fix: agent config not picked up from parameters (WebOfTrust#397)

When agent URLs where only provided through parameters (from env
variables), the "dt" field was not set, which caused keripy to ignore
the configuration. This PR ensure that "dt" field is set.

* chore: update multisig embeds typing to include delegated events

* chore: format

* chore: format

* resolve review comments

* revert some headers for test auth

---------

Signed-off-by: Kevin Griffin <griffin.kev@gmail.com>
Signed-off-by: Kent Bull <kent@kentbull.com>
Co-authored-by: Kevin Griffin <griffin.kev@gmail.com>
Co-authored-by: Kent Bull <kent@kentbull.com>
Co-authored-by: Daniel Lenksjö <5889538+lenkan@users.noreply.github.com>
Co-authored-by: iFergal <fergaloconnor1998@gmail.com>
Co-authored-by: Charles Lanahan <charles.lanahan@gmail.com>
Co-authored-by: Jay-Alexander Elliot <alexander.elliot.it@protonmail.com>
@iFergal

iFergal commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator Author

@kentbull Conflicts fixed.

@kentbull

Copy link
Copy Markdown
Collaborator

Reviewing and will merge this week unless I have changes to request.

@kentbull kentbull left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I acknowledge AgentResourceEnd.on_put passcode rotation is still broken as signed-header requests reaching the PUT /agent/{caid} controller-rotation branch fail due to SignifyTS not sending a signed request.

This PR adds a further breakage because self.authn.inbound(req) returns None on successful authentication yet the if statement takes that and says if not None then error, which is the opposite of what it should be.

The correct code would look like the following, which I will add in a follow-on PR:

try:
    self.authn.inbound(req)
except (kering.AuthNError, ValueError):
    raise falcon.HTTPForbidden(...)

I'll approve, merge this, and then set up that follow-on PR.

Comment thread src/keria/app/aiding.py
Comment thread src/keria/core/authing.py
@kentbull
kentbull merged commit 78b880e into WebOfTrust:main Jul 21, 2026
6 checks passed
@kentbull

Copy link
Copy Markdown
Collaborator

@iFergal I opened #451 in response to a regression introduced by the change in meaning of the authn.inbound() method compared with the old authn.verify(). When verify/inbound returns None that is a clean validation result and the prior if statement incorrectly interpreted None as truthy and raised an error. That is the inverse of what needs to occur. #451 fixes that.

@iFergal
iFergal deleted the feat/essrAPI branch July 21, 2026 15:46
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants