Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .sources/VERSIONS
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ motoko-core v2.4.0
cdk-rs ic-cdk v0.20.1 / ic-cdk-timers v1.0.0 / ic-cdk-executor v2.0.0 317f55c
candid 2025-12-18 # candid v0.10.20, didc v0.5.4 2e4a2cf
response-verification v3.1.0 18c5a37
internetidentity release-2026-06-29 5fe49cfd
internetidentity release-2026-07-06 37670ecf
2 changes: 1 addition & 1 deletion .sources/internetidentity
Submodule internetidentity updated 74 files
+2 −2 .github/workflows/canister-tests.yml
+55 −23 Cargo.lock
+1 −1 Cargo.toml
+23 −33 demos/vc_issuer/Cargo.lock
+1 −1 demos/vc_issuer/Cargo.toml
+329 −0 docs/mcp-server-guide.md
+1 −1 scripts/test-canisters.sh
+7 −1 src/archive/tests/tests.rs
+74 −27 src/canister_tests/src/api/internet_identity/api_v2.rs
+21 −2 src/canister_tests/src/framework.rs
+2 −0 src/frontend/src/lib/components/settings/VerifiedEmailsPanel.svelte
+72 −0 src/frontend/src/lib/components/ui/AccessLevelToggle.svelte
+19 −16 src/frontend/src/lib/generated/internet_identity_idl.js
+67 −41 src/frontend/src/lib/generated/internet_identity_types.d.ts
+12 −0 src/frontend/src/lib/icons/citadel_logo.svg
+ src/frontend/src/lib/icons/nnsfront-enddapp_logo-dark.webp
+54 −0 src/frontend/src/lib/icons/nnsfront-enddapp_logo.svg
+7 −2 src/frontend/src/lib/legacy/flows/dappsExplorer/dapps.json
+16 −0 src/frontend/src/lib/state/featureFlags.ts
+22 −0 src/frontend/src/lib/stores/authorization.store.test.ts
+14 −3 src/frontend/src/lib/stores/authorization.store.ts
+16 −0 src/frontend/src/lib/stores/channelHandlers/delegation.ts
+55 −0 src/frontend/src/lib/utils/accessLevel.test.ts
+44 −0 src/frontend/src/lib/utils/accessLevel.ts
+5 −5 src/frontend/src/lib/utils/analytics/mcpAuthorizeFunnel.ts
+35 −0 src/frontend/src/lib/utils/analytics/verifiedEmailConsentFunnel.ts
+154 −17 src/frontend/src/lib/utils/dnssec/chain.test.ts
+80 −21 src/frontend/src/lib/utils/dnssec/chain.ts
+92 −0 src/frontend/src/lib/utils/mcpConfig.test.ts
+217 −0 src/frontend/src/lib/utils/mcpServer.test.ts
+5 −1 src/frontend/src/routes/(new-styling)/authorize/+layout.svelte
+8 −3 src/frontend/src/routes/(new-styling)/authorize/+page.svelte
+55 −8 src/frontend/src/routes/(new-styling)/authorize/views/AttributeConsentView.svelte
+32 −4 src/frontend/src/routes/(new-styling)/authorize/views/ContinueView.svelte
+7 −2 src/frontend/src/routes/(new-styling)/authorize/views/UpgradeSuccessView.svelte
+3 −1 src/frontend/src/routes/(new-styling)/cli/+page.svelte
+92 −0 src/frontend/src/routes/(new-styling)/cli/utils.test.ts
+10 −0 src/frontend/src/routes/(new-styling)/cli/utils.ts
+26 −3 src/frontend/src/routes/(new-styling)/cli/views/CliAuthorizeView.svelte
+57 −57 src/frontend/src/routes/(new-styling)/mcp/+page.svelte
+29 −56 src/frontend/src/routes/(new-styling)/mcp/+page.ts
+84 −7 src/frontend/src/routes/(new-styling)/mcp/load.test.ts
+375 −0 src/frontend/src/routes/(new-styling)/mcp/utils.test.ts
+164 −120 src/frontend/src/routes/(new-styling)/mcp/utils.ts
+24 −8 src/frontend/src/routes/(new-styling)/mcp/views/McpAuthorizeView.svelte
+44 −6 src/frontend/src/routes/(new-styling)/mcp/views/McpCloseWindowView.svelte
+5 −5 src/frontend/src/routes/(new-styling)/mcp/views/McpConnectingView.svelte
+0 −27 src/frontend/src/routes/(new-styling)/mcp/views/McpErrorView.svelte
+135 −53 src/frontend/tests/e2e-playwright/fixtures/mcp.ts
+6 −0 src/frontend/tests/e2e-playwright/routes/authorize/index.spec.ts
+6 −0 src/frontend/tests/e2e-playwright/routes/cli.spec.ts
+238 −46 src/frontend/tests/e2e-playwright/routes/mcp.spec.ts
+7 −0 src/internet_identity/Cargo.toml
+74 −39 src/internet_identity/internet_identity.did
+48 −8 src/internet_identity/src/account_management.rs
+133 −4 src/internet_identity/src/delegation.rs
+362 −54 src/internet_identity/src/dnssec/verify.rs
+8 −1 src/internet_identity/src/email_inbound/smtp.rs
+76 −40 src/internet_identity/src/main.rs
+340 −147 src/internet_identity/src/mcp.rs
+3 −1 src/internet_identity/src/openid.rs
+3 −1 src/internet_identity/src/session_delegation.rs
+44 −52 src/internet_identity/src/storage.rs
+1 −0 src/internet_identity/src/storage/storable.rs
+64 −0 src/internet_identity/src/storage/storable/mcp_config.rs
+64 −0 src/internet_identity/src/storage/storable/mcp_grant.rs
+220 −1 src/internet_identity/tests/integration/accounts.rs
+16 −2 src/internet_identity/tests/integration/archive_integration.rs
+18 −8 src/internet_identity/tests/integration/attributes.rs
+482 −0 src/internet_identity/tests/integration/delegation_ingress.rs
+1 −0 src/internet_identity/tests/integration/main.rs
+969 −128 src/internet_identity/tests/integration/mcp.rs
+28 −104 src/internet_identity_frontend/src/main.rs
+42 −11 src/internet_identity_interface/src/internet_identity/types.rs
113 changes: 74 additions & 39 deletions public/references/internet-identity.did
Original file line number Diff line number Diff line change
Expand Up @@ -157,8 +157,21 @@ type Delegation = record {
pubkey : PublicKey;
expiration : Timestamp;
targets : opt vec principal;
// Restricts the kinds of calls the delegation permits: `"queries"`
// restricts the sender to query calls (the IC rejects update calls
// authenticated through such a delegation). Absent means unrestricted.
permissions : opt text;
};

// The delegation permissions a caller requests, mirroring the ICP protocol's
// request-delegation `permissions` values. `queries` yields a queries-only
// delegation (the issued `Delegation` carries `permissions = "queries"`);
// `all` yields an unrestricted, update-capable delegation. Passed as an
// optional argument; an absent argument means `all`, preserving the
// pre-feature behavior and matching the interface spec's default for an
// absent `permissions` field.
type Permissions = variant { queries; all };

type SignedDelegation = record {
delegation : Delegation;
signature : blob;
Expand Down Expand Up @@ -1078,11 +1091,11 @@ type PrepareAccountDelegation = record {
expiration : Timestamp;
};

// Result of mcp_prepare_account_delegation. Carries the account_number the
// canister used (the one named in the request, or the anchor's default account
// at target_origin when none was named) so the MCP server can thread the same
// account into mcp_get_account_delegation — the default is mutable, so
// re-resolving it in `get` could otherwise diverge and yield NoSuchDelegation.
// Result of mcp_prepare_delegation. Carries the account_number the canister
// used (the one named in the request, or the anchor's default account at
// target_origin when none was named) so the MCP server can thread the same
// account into mcp_get_delegation — the default is mutable, so re-resolving
// it in `get` could otherwise diverge and yield NoSuchDelegation.
type McpPrepareDelegation = record {
user_key : UserKey;
expiration : Timestamp;
Expand Down Expand Up @@ -1111,6 +1124,13 @@ type McpConfig = record {
url : opt text;
};

// Result of mcp_register: the expiration (ns since epoch) of the MCP session
// grant just registered. Every server-facing mcp_* call returns Unauthorized
// once it passes; the server reconnects through a new consent flow.
type McpRegistration = record {
expiration : Timestamp;
};

type GetAccountsError = variant {
InternalCanisterError : text;
Unauthorized : principal;
Expand Down Expand Up @@ -1779,35 +1799,47 @@ service : (opt InternetIdentityInit) -> {
origin : FrontendHostname,
account_number : opt AccountNumber, // Null is unreserved default account
session_key : SessionKey,
max_ttl : opt nat64
max_ttl : opt nat64,
// `opt (queries)` restricts the prepared delegation to query calls: it
// carries `permissions = "queries"`, which makes the IC reject update
// calls authenticated through it. Omitted (`null`) or `opt (all)`
// yields an unrestricted delegation — the original behavior, preserved
// for existing callers and matching the interface spec.
permissions : opt Permissions
) -> (variant { Ok : PrepareAccountDelegation; Err : AccountDelegationError });

get_account_delegation : (
anchor_number : UserNumber,
origin : FrontendHostname,
account_number : opt AccountNumber, // Null is unreserved default account
session_key : SessionKey,
expiration : Timestamp
expiration : Timestamp,
// Must match the value passed to `prepare_account_delegation` (including
// the omitted/`null` case, which means unrestricted).
permissions : opt Permissions
) -> (variant { Ok : SignedDelegation; Err : AccountDelegationError }) query;

// Enable/disable the backend /mcp delegation path for an anchor at a given
// MCP server origin. Enabling binds the principal II derives for the anchor
// at that origin; disabling unbinds exactly that principal. No account is
// chosen here (accounts are per-origin and the connector isn't an app) — the
// app account is selected per call on mcp_prepare_account_delegation. The
// origin comes from the connect request, so each user trusts the MCP server
// they choose.
mcp_set_access : (
anchor_number : UserNumber,
mcp_server_origin : FrontendHostname,
enabled : bool
) -> (variant { Ok; Err : text });

// Whether the anchor has MCP access enabled at mcp_server_origin.
mcp_access_enabled : (
// Register the trusted MCP server's session key for the anchor: grant the
// key's self-authenticating principal access to the server-facing mcp_*
// methods until the grant expires (grant_ttl_ns clamped to [10 min, 30
// days]). Called by the /mcp connect flow after user consent, with a key
// the frontend fetched from the *trusted* server's callback — never taken
// from the unauthenticated connect link. No account is chosen here
// (accounts are per-origin and the connector isn't an app) — the app
// account is selected per call on mcp_prepare_delegation.
//
// At most one session per identity: registering replaces any previous
// grant. Requires the identity's MCP config to be enabled with a trusted
// server set, so every session stays revocable via mcp_set_config.
mcp_register : (
anchor_number : UserNumber,
mcp_server_origin : FrontendHostname
) -> (bool) query;
session_key : SessionKey,
grant_ttl_ns : nat64,
// `opt (queries)` makes every per-app delegation this session later
// mints queries-only (read-only, chosen once for the whole session).
// Omitted (`null`) or `opt (all)` means full, update-capable access.
permissions : opt Permissions
) -> (variant { Ok : McpRegistration; Err : text });

// Read the identity's synced trusted-MCP-server config (master toggle + the
// trusted server URL). Persisted on-chain, so it follows the identity across
Expand All @@ -1819,41 +1851,44 @@ service : (opt InternetIdentityInit) -> {
// Persist the identity's trusted-MCP-server config so it syncs across the
// identity's devices. Authenticated as the identity, so only the user — never
// a page that initiates a connect request — can change what it trusts.
// Disabling MCP or changing the trusted server URL revokes the identity's
// active MCP session in the same message.
mcp_set_config : (
anchor_number : UserNumber,
config : McpConfig
) -> (variant { Ok; Err : text });

// Called by the MCP server, authorized by caller() == the principal bound for
// its anchor at the connect-time mcp_server_origin; the anchor is recovered
// from the caller. Mints a per-app delegation at target_origin. account_number
// Called by the MCP server, signed with its registered session key and
// authorized by caller()'s unexpired grant; the anchor is recovered from
// the caller. Mints a per-app delegation at target_origin. account_number
// names one of the anchor's accounts there to act as (discover them with
// mcp_get_accounts), and null uses the anchor's default account there; an
// account_number that isn't the anchor's at target_origin is rejected as
// Unauthorized. max_ttl is the requested lifetime in ns, defaulting to and
// capped at 5 minutes. The resolved account_number is returned in
// McpPrepareDelegation so it can be threaded into mcp_get_account_delegation
// (the default account at an origin is mutable).
mcp_prepare_account_delegation : (
// capped at 1 hour, and never outliving the session grant. The resolved
// account_number is returned in McpPrepareDelegation so it can be threaded
// into mcp_get_delegation (the default account at an origin is mutable).
mcp_prepare_delegation : (
target_origin : FrontendHostname,
account_number : opt AccountNumber,
session_key : SessionKey,
max_ttl : opt nat64
) -> (variant { Ok : McpPrepareDelegation; Err : AccountDelegationError });

// Fetch the delegation prepared above; the anchor is recovered from caller().
// account_number and expiration must be the values returned by the matching
// mcp_prepare_account_delegation, else this returns NoSuchDelegation.
mcp_get_account_delegation : (
// Fetch the delegation prepared above; the anchor is recovered from
// caller()'s grant. account_number and expiration must be the values
// returned by the matching mcp_prepare_delegation, else this returns
// NoSuchDelegation.
mcp_get_delegation : (
target_origin : FrontendHostname,
account_number : opt AccountNumber,
session_key : SessionKey,
expiration : Timestamp
) -> (variant { Ok : SignedDelegation; Err : AccountDelegationError }) query;

// Called by the MCP server (anchor recovered from caller()): list the anchor's
// accounts at target_origin so the agent can pick which account_number to
// request a delegation for via mcp_prepare_account_delegation.
// Called by the MCP server (anchor recovered from caller()'s grant): list
// the anchor's accounts at target_origin so the agent can pick which
// account_number to request a delegation for via mcp_prepare_delegation.
mcp_get_accounts : (
target_origin : FrontendHostname
) -> (variant { Ok : vec AccountInfo; Err : AccountDelegationError }) query;
Expand Down
Loading