Skip to content

feat(be): add ICRC-3 attribute sharing API with nonce support#3726

Merged
aterga merged 17 commits intomainfrom
claude/gracious-moser
Apr 2, 2026
Merged

feat(be): add ICRC-3 attribute sharing API with nonce support#3726
aterga merged 17 commits intomainfrom
claude/gracious-moser

Conversation

@aterga
Copy link
Copy Markdown
Collaborator

@aterga aterga commented Apr 1, 2026

Summary

  • Add two new ICRC-3 attribute sharing API endpoints: prepare_icrc3_attributes (update) and get_icrc3_attributes (query)
  • Unlike the existing per-attribute prepare_attributes/get_attributes flow, the ICRC-3 variant produces a single canister signature over a Candid-encoded ICRC-3 Value map containing all requested attribute key-value pairs
  • Caller specifies attributes via AttributeSpec with optional value matching, scope control, and a relying-party nonce:
    • value: opt blob — if set, validates against stored credential; if unset, uses stored value directly
    • omit_scope: bool — if true, certified key is just the attribute name (e.g., "email"); if false, full scoped key (e.g., "openid:https://...:email")
    • nonce: blob — 32-byte value from the relying party, included as "implicit:nonce" in the signed ICRC-3 message map to prevent replay attacks
  • Domain separator is "ic-request-auth-info" (used for canister signature computation)
  • get_icrc3_attributes returns a typed NoSuchSignature error instead of trapping when the signature is not found
  • New types: Icrc3Value, AttributeSpec, PrepareIcrc3AttributeRequest/Response/Error, GetIcrc3AttributeRequest/Response/Error
  • All changes are purely additive — the existing prepare_attributes/get_attributes implementation is not modified

Test plan

  • Unit tests for ValidatedPrepareIcrc3AttributeRequest validation (attribute count, nonce length, key parsing, scope requirement)
  • Unit tests for ValidatedGetIcrc3AttributeRequest validation
  • Unit tests for Anchor::prepare_icrc3_attributes error paths (value mismatch, unknown issuer, scopeless attribute)
  • Unit tests for icrc3_attribute_message encoding (determinism, decodability, scoped vs unscoped keys)
  • Integration test: full prepare → get → signature verification flow with mixed omit_scope and nonce value assertion
  • Integration test: typed NoSuchSignature error for unknown message
  • cargo check passes for all affected crates

| Next PR >

aterga and others added 2 commits April 1, 2026 19:04
…ions

Add two new ICRC-3 attribute sharing endpoints that produce a single
canister signature over a Candid-encoded ICRC-3 Value map of all
attributes, instead of per-attribute signatures.

- prepare_icrc3_attributes: validates caller-provided attribute values
  against stored OpenID credentials, Candid-encodes them as an ICRC-3
  Map, and signs the encoded bytes
- get_icrc3_attributes: retrieves the single signature for the message
  blob returned by prepare

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aterga aterga requested a review from a team as a code owner April 1, 2026 17:09
Copilot AI review requested due to automatic review settings April 1, 2026 17:09
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces a new ICRC-3-flavored attribute sharing flow to Internet Identity, adding endpoints that sign a single Candid-encoded ICRC-3 Value map containing all requested attribute key/value pairs, rather than per-attribute signatures.

Changes:

  • Added prepare_icrc3_attributes (update) and get_icrc3_attributes (query) canister APIs, including new request/response/error types.
  • Implemented ICRC-3 message construction (Candid-encoded map) and canister-signature issuance/lookup for the message blob.
  • Added test plumbing: PocketIC API helpers, signature verification helper, and a new integration test covering the prepare → get → verify flow.

Reviewed changes

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/internet_identity/src/main.rs Exposes the new prepare_icrc3_attributes / get_icrc3_attributes canister methods and wires validation/auth/account lookup.
src/internet_identity/src/attributes.rs Implements ICRC-3 message building, validation against stored OpenID credential claims, and signature issuance/lookup.
src/internet_identity/internet_identity.did Adds Candid types and service methods for the new ICRC-3 attribute sharing API.
src/internet_identity_interface/src/internet_identity/types/attributes.rs Adds public interface types + validation logic for ICRC-3 prepare/get requests.
src/internet_identity_interface/src/internet_identity/types/icrc3.rs Introduces the Icrc3Value enum used to build the signed message.
src/internet_identity_interface/src/internet_identity/types.rs Exports the new icrc3 module.
src/canister_tests/src/api/internet_identity.rs Adds PocketIC API wrappers to call the new endpoints.
src/canister_tests/src/framework.rs Adds a helper to verify ICRC-3 attribute signatures in tests.
src/internet_identity/tests/integration/attributes.rs Adds an integration test for the new ICRC-3 attribute sharing flow.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/internet_identity/src/attributes.rs Outdated
Comment thread src/internet_identity_interface/src/internet_identity/types/attributes.rs Outdated
- Import `candid::Encode` macro explicitly
- Remove unreachable wildcard pattern in scope match

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread src/internet_identity/internet_identity.did Outdated
aterga and others added 7 commits April 2, 2026 12:28
Introduce `AttributeSpec` type for `prepare_icrc3_attributes` with:
- `key`: full attribute key with scope
- `value: opt blob`: optional value matching against stored credentials
- `omit_scope: bool`: strip scope prefix from certified key for privacy

When `value` is None, the stored value is used without validation.
When `omit_scope` is true, the certified key is just the attribute name
(e.g., "email" instead of "openid:https://...:email").

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…tests

- Return typed NoSuchSignature error from get_icrc3_attributes instead of trapping
- Fix ICRC3_MESSAGE_MAX_BYTES to include key sizes and Candid overhead
- Add unit tests for ValidatedPrepareIcrc3AttributeRequest and ValidatedGetIcrc3AttributeRequest
- Add 9 integration tests covering value set/unset, omit_scope true/false corner cases

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Drop should_certify_icrc3_attributes_without_value_check and
should_certify_icrc3_attributes_with_omit_scope since the mixed_omit_scope
test already covers composability of omit_scope true/false.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The should_reject_icrc3_attributes_with_correct_and_wrong_value test
already covers the wrong-value case while also testing partial mismatch.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Tests that don't rely on certification (value mismatch, unknown issuer,
scopeless attribute, message encoding) are now unit tests on the core
logic directly, keeping only certification-dependent tests as PocketIc
integration tests.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aterga aterga requested a review from sea-snake April 2, 2026 11:09
@aterga aterga changed the title feat: add prepare_icrc3_attributes and get_icrc3_attributes API feat(be): add prepare_icrc3_attributes and get_icrc3_attributes API Apr 2, 2026
Comment thread src/internet_identity/src/attributes.rs
Comment thread src/internet_identity/src/attributes.rs Outdated
Comment thread src/internet_identity/internet_identity.did
aterga and others added 3 commits April 2, 2026 15:39
The nonce is a 32-byte value generated by the relying party to prevent
replay attacks and enable attribute expiration per flow. It is included
in the signed ICRC-3 message as `implicit:nonce`.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The nonce field is now included in the certified pairs, so the ICRC-3
message map has 3 entries (email, name, implicit:nonce) instead of 2.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aterga aterga requested a review from Copilot April 2, 2026 13:58
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 9 out of 9 changed files in this pull request and generated 3 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/internet_identity/src/attributes.rs Outdated
Comment thread src/internet_identity_interface/src/internet_identity/types/attributes.rs Outdated
Comment thread src/internet_identity/tests/integration/attributes.rs
aterga and others added 2 commits April 2, 2026 16:08
…validation

Extend should_certify_icrc3_attributes_mixed_omit_scope to use a
specific nonce, verify the canister signature, and assert that the
nonce value in the certified message map matches the provided bytes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aterga aterga changed the title feat(be): add prepare_icrc3_attributes and get_icrc3_attributes API feat(be): add prepare_icrc3_attributes and get_icrc3_attributes API Apr 2, 2026
@aterga aterga changed the title feat(be): add prepare_icrc3_attributes and get_icrc3_attributes API feat(be): add ICRC-3 attribute sharing API with nonce support Apr 2, 2026
aterga and others added 2 commits April 2, 2026 16:30
…e bound

- Detect duplicate certified attribute keys in prepare_icrc3_attributes:
  if two specs resolve to the same key (e.g., two issuers with
  omit_scope=true both become "email"), return an error instead of
  silently overwriting.
- Fix ICRC3_ATTRIBUTE_KEY_MAX_BYTES to account for the full scoped key
  format ("openid:" + issuer + ":" + attribute_name) rather than just
  the issuer length, ensuring ICRC3_MESSAGE_MAX_BYTES is a sound bound.
- Add unit test for duplicate key detection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@aterga aterga added this pull request to the merge queue Apr 2, 2026
Merged via the queue into main with commit 815f7bd Apr 2, 2026
52 checks passed
@aterga aterga deleted the claude/gracious-moser branch April 2, 2026 15:06
github-merge-queue bot pushed a commit that referenced this pull request Apr 2, 2026
## Summary
- Add `list_available_attributes` query endpoint that lists attribute
key/value pairs stored on an identity's OpenID credentials
- Supports optional filtering via `attributes: opt vec text`:
  - `None` → returns all available attributes across all credentials
- Scoped keys (e.g., `"openid:https://accounts.google.com:email"`) →
exact match
  - Unscoped keys (e.g., `"email"`) → wildcard match across all scopes
- Response always uses fully-scoped keys (e.g.,
`"openid:https://accounts.google.com:email"`)
- Authenticated the same way as `prepare_icrc3_attributes` (via
`check_authorization`)
- No origin/account_number needed since no signing is performed

## Test plan
- [x] Unit tests for `ValidatedListAvailableAttributesRequest`
validation (valid/invalid inputs)
- [x] Unit tests for `Anchor::list_available_attributes` (all
attributes, scoped filter, unscoped wildcard, missing attributes, no
credentials, wrong issuer)
- [x] Integration test `should_list_all_available_attributes` — full
flow with real credential storage
- [x] All 203 binary unit tests pass
- [x] All 30 interface unit tests pass

< [Previous PR](#3726)
| [Next PR](#3730) >

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com>
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.

3 participants