From 2d49049bdd57ea2be5a2cfa82770a7932f4d08dc Mon Sep 17 00:00:00 2001 From: Frederik Rothenberger Date: Wed, 20 Dec 2023 13:07:00 +0100 Subject: [PATCH] API V2 Refactor (#2151) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * API V2 Refactor This refactors the existing API v2 to be consistent with new patterns introduced in the issuer API: * Use camel case variants * Use `Result` return values The `Err` variants will be extended in the future with more types. These will be breaking changes. However, given we mark the API v2 as experimental this should be ok. In order to have CI pass, we have an exception in the form of a patch file that undoes the breaking changes that we intentionally made. If we introduce additional breaking changes, the patch will no longer apply and thus make the check fail. * 🤖 npm run generate auto-update * Replace unwrap with expect * Replace more unwraps with expect * Replace even more unwraps with expect --------- Co-authored-by: github-actions <41898282+github-actions[bot]@users.noreply.github.com> --- .github/workflows/canister-tests.yml | 19 +++ allowed_breaking_change.patch | 25 +++ .../src/api/internet_identity/api_v2.rs | 12 +- src/canister_tests/src/framework.rs | 24 --- .../generated/internet_identity_idl.js | 44 +++--- .../generated/internet_identity_types.d.ts | 42 ++--- src/internet_identity/internet_identity.did | 57 ++----- src/internet_identity/src/main.rs | 59 +++---- .../activity_stats/authn_methods.rs | 18 ++- .../tests/integration/archive_integration.rs | 3 +- .../integration/v2_api/authn_method_add.rs | 52 +++--- .../integration/v2_api/authn_method_remove.rs | 35 ++-- .../v2_api/authn_method_test_helpers.rs | 34 ++-- .../tests/integration/v2_api/identity_info.rs | 40 +++-- .../integration/v2_api/identity_metadata.rs | 27 ++-- .../integration/v2_api/identity_register.rs | 149 +++++++++--------- .../src/internet_identity/conversions.rs | 12 -- .../src/internet_identity/types/api_v2.rs | 44 +----- 18 files changed, 300 insertions(+), 396 deletions(-) create mode 100644 allowed_breaking_change.patch diff --git a/.github/workflows/canister-tests.yml b/.github/workflows/canister-tests.yml index a0c29f36d5..0168f138b8 100644 --- a/.github/workflows/canister-tests.yml +++ b/.github/workflows/canister-tests.yml @@ -892,8 +892,27 @@ jobs: runs-on: ubuntu-latest steps: - uses: actions/checkout@v3 + with: + # we need the history for the tags, but we don't need the files (except for the GitHub workflows / actions) + # --> sparse checkout of only the needed things + fetch-depth: 0 + sparse-checkout: | + src/internet_identity/internet_identity.did + .github + .didc-release + allowed_breaking_change.patch + sparse-checkout-cone-mode: false - uses: ./.github/actions/setup-didc - name: "Check canister interface compatibility" run: | + release="release-2023-12-15" + # undo the breaking changes that we _explicitly_ made + # remove after the next release + # if we accidentally introduced other breaking changes, the patch would no longer apply / fix them + # making this job fail. + if [ "$(git describe --tags --match="release-[0-9-]*" HEAD --abbrev=0)" == "$release" ]; then + echo "Rolling back intentionally made breaking changes $release" + git apply allowed_breaking_change.patch + fi curl -sSL https://github.com/dfinity/internet-identity/releases/latest/download/internet_identity.did -o internet_identity_previous.did didc check src/internet_identity/internet_identity.did internet_identity_previous.did diff --git a/allowed_breaking_change.patch b/allowed_breaking_change.patch new file mode 100644 index 0000000000..de71e48ab4 --- /dev/null +++ b/allowed_breaking_change.patch @@ -0,0 +1,25 @@ +diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did +index 09937178..e545e0ba 100644 +--- a/src/internet_identity/internet_identity.did ++++ b/src/internet_identity/internet_identity.did +@@ -313,16 +313,16 @@ type PublicKeyAuthn = record { + + // The authentication methods currently supported by II. + type AuthnMethod = variant { +- WebAuthn: WebAuthn; +- PubKey: PublicKeyAuthn; ++ webauthn: WebAuthn; ++ pubkey: PublicKeyAuthn; + }; + + // This describes whether an authentication method is "protected" or not. + // When protected, a authentication method can only be updated or removed if the + // user is authenticated with that very authentication method. + type AuthnMethodProtection = variant { +- Protected; +- Unprotected; ++ protected; ++ unprotected; + }; + + type AuthnMethodData = record { diff --git a/src/canister_tests/src/api/internet_identity/api_v2.rs b/src/canister_tests/src/api/internet_identity/api_v2.rs index 185cee59fe..5d58e63762 100644 --- a/src/canister_tests/src/api/internet_identity/api_v2.rs +++ b/src/canister_tests/src/api/internet_identity/api_v2.rs @@ -7,7 +7,7 @@ use std::collections::HashMap; pub fn captcha_create( env: &StateMachine, canister_id: CanisterId, -) -> Result, CallError> { +) -> Result, CallError> { call_candid(env, canister_id, "captcha_create", ()).map(|(x,)| x) } @@ -18,7 +18,7 @@ pub fn identity_register( authn_method: &AuthnMethodData, challenge_attempt: &ChallengeAttempt, temp_key: Option, -) -> Result, CallError> { +) -> Result, CallError> { call_candid_as( env, canister_id, @@ -34,7 +34,7 @@ pub fn identity_info( canister_id: CanisterId, sender: Principal, identity_number: IdentityNumber, -) -> Result, CallError> { +) -> Result, CallError> { call_candid_as( env, canister_id, @@ -51,7 +51,7 @@ pub fn identity_metadata_replace( sender: Principal, identity_number: IdentityNumber, metadata: &HashMap, -) -> Result, CallError> { +) -> Result, CallError> { call_candid_as( env, canister_id, @@ -68,7 +68,7 @@ pub fn authn_method_add( sender: Principal, identity_number: IdentityNumber, authn_method: &AuthnMethodData, -) -> Result, CallError> { +) -> Result, CallError> { call_candid_as( env, canister_id, @@ -85,7 +85,7 @@ pub fn authn_method_remove( sender: Principal, identity_number: IdentityNumber, public_key: &PublicKey, -) -> Result, CallError> { +) -> Result, CallError> { call_candid_as( env, canister_id, diff --git a/src/canister_tests/src/framework.rs b/src/canister_tests/src/framework.rs index c7c141da4e..8aa0a21162 100644 --- a/src/canister_tests/src/framework.rs +++ b/src/canister_tests/src/framework.rs @@ -684,27 +684,3 @@ pub fn test_principal(n: u64) -> Principal { bytes.push(0xfe); // internal marker for user test ids Principal::from_slice(&bytes[..]) } - -/// Macro to easily match a value against a pattern, and panic if the match fails. -/// -/// This makes API v2 return types easier to handle. -/// API v2 calls all return variants, requiring a match on the result. -/// This macro allows to write the match in terms of the expected variant, with a fallback -/// on unexpected variants. -/// Example: -/// ``` -/// use canister_tests::match_value; -/// match_value!( -/// api_v2::identity_info(&env, canister_id, principal, identity_number)?, // value -/// Some(IdentityInfoResponse::Ok(identity_info)) // expected pattern, with binding to identity_info -/// ); -/// ``` -#[macro_export] -#[rustfmt::skip] // cargo fmt seems to have a bug with this macro (it indents the panic! way too far) -macro_rules! match_value { - ($target: expr, $pat: pat_param) => { - let $pat = $target else { - panic!("expected {}", stringify!($pat)); - }; - }; -} diff --git a/src/frontend/generated/internet_identity_idl.js b/src/frontend/generated/internet_identity_idl.js index ff4b6e0675..0bf10a4d01 100644 --- a/src/frontend/generated/internet_identity_idl.js +++ b/src/frontend/generated/internet_identity_idl.js @@ -70,17 +70,17 @@ export const idlFactory = ({ IDL }) => { }); const IdentityNumber = IDL.Nat64; const AuthnMethodProtection = IDL.Variant({ - 'unprotected' : IDL.Null, - 'protected' : IDL.Null, + 'Protected' : IDL.Null, + 'Unprotected' : IDL.Null, }); + const PublicKeyAuthn = IDL.Record({ 'pubkey' : PublicKey }); const WebAuthn = IDL.Record({ 'pubkey' : PublicKey, 'credential_id' : CredentialId, }); - const PublicKeyAuthn = IDL.Record({ 'pubkey' : PublicKey }); const AuthnMethod = IDL.Variant({ - 'webauthn' : WebAuthn, - 'pubkey' : PublicKeyAuthn, + 'PubKey' : PublicKeyAuthn, + 'WebAuthn' : WebAuthn, }); const AuthnMethodData = IDL.Record({ 'metadata' : MetadataMap, @@ -89,17 +89,12 @@ export const idlFactory = ({ IDL }) => { 'authn_method' : AuthnMethod, 'purpose' : Purpose, }); - const AuthnMethodAddResponse = IDL.Variant({ - 'ok' : IDL.Null, - 'invalid_metadata' : IDL.Text, - }); - const AuthnMethodRemoveResponse = IDL.Variant({ 'ok' : IDL.Null }); + const AuthnMethodAddError = IDL.Variant({ 'InvalidMetadata' : IDL.Text }); const ChallengeKey = IDL.Text; const Challenge = IDL.Record({ 'png_base64' : IDL.Text, 'challenge_key' : ChallengeKey, }); - const CaptchaCreateResponse = IDL.Variant({ 'ok' : Challenge }); const DeployArchiveResult = IDL.Variant({ 'creation_in_progress' : IDL.Null, 'success' : IDL.Principal, @@ -213,18 +208,15 @@ export const idlFactory = ({ IDL }) => { 'metadata' : MetadataMap, 'authn_method_registration' : IDL.Opt(AuthnMethodRegistrationInfo), }); - const IdentityInfoResponse = IDL.Variant({ 'ok' : IdentityInfo }); - const IdentityMetadataReplaceResponse = IDL.Variant({ 'ok' : IDL.Null }); const ChallengeResult = IDL.Record({ 'key' : ChallengeKey, 'chars' : IDL.Text, }); const CaptchaResult = ChallengeResult; - const IdentityRegisterResponse = IDL.Variant({ - 'ok' : IdentityNumber, - 'invalid_metadata' : IDL.Text, - 'bad_captcha' : IDL.Null, - 'canister_full' : IDL.Null, + const IdentityRegisterError = IDL.Variant({ + 'BadCaptcha' : IDL.Null, + 'CanisterFull' : IDL.Null, + 'InvalidMetadata' : IDL.Text, }); const UserKey = PublicKey; const PrepareIdAliasRequest = IDL.Record({ @@ -274,15 +266,19 @@ export const idlFactory = ({ IDL }) => { ), 'authn_method_add' : IDL.Func( [IdentityNumber, AuthnMethodData], - [IDL.Opt(AuthnMethodAddResponse)], + [IDL.Variant({ 'Ok' : IDL.Null, 'Err' : AuthnMethodAddError })], [], ), 'authn_method_remove' : IDL.Func( [IdentityNumber, PublicKey], - [IDL.Opt(AuthnMethodRemoveResponse)], + [IDL.Variant({ 'Ok' : IDL.Null, 'Err' : IDL.Null })], + [], + ), + 'captcha_create' : IDL.Func( + [], + [IDL.Variant({ 'Ok' : Challenge, 'Err' : IDL.Null })], [], ), - 'captcha_create' : IDL.Func([], [IDL.Opt(CaptchaCreateResponse)], []), 'create_challenge' : IDL.Func([], [Challenge], []), 'deploy_archive' : IDL.Func([IDL.Vec(IDL.Nat8)], [DeployArchiveResult], []), 'enter_device_registration_mode' : IDL.Func([UserNumber], [Timestamp], []), @@ -313,17 +309,17 @@ export const idlFactory = ({ IDL }) => { 'http_request_update' : IDL.Func([HttpRequest], [HttpResponse], []), 'identity_info' : IDL.Func( [IdentityNumber], - [IDL.Opt(IdentityInfoResponse)], + [IDL.Variant({ 'Ok' : IdentityInfo, 'Err' : IDL.Null })], [], ), 'identity_metadata_replace' : IDL.Func( [IdentityNumber, MetadataMap], - [IDL.Opt(IdentityMetadataReplaceResponse)], + [IDL.Variant({ 'Ok' : IDL.Null, 'Err' : IDL.Null })], [], ), 'identity_register' : IDL.Func( [AuthnMethodData, CaptchaResult, IDL.Opt(IDL.Principal)], - [IDL.Opt(IdentityRegisterResponse)], + [IDL.Variant({ 'Ok' : IdentityNumber, 'Err' : IdentityRegisterError })], [], ), 'init_salt' : IDL.Func([], [], []), diff --git a/src/frontend/generated/internet_identity_types.d.ts b/src/frontend/generated/internet_identity_types.d.ts index 4a4274da15..b5839a3027 100644 --- a/src/frontend/generated/internet_identity_types.d.ts +++ b/src/frontend/generated/internet_identity_types.d.ts @@ -27,10 +27,9 @@ export interface ArchiveInfo { 'archive_config' : [] | [ArchiveConfig], 'archive_canister' : [] | [Principal], } -export type AuthnMethod = { 'webauthn' : WebAuthn } | - { 'pubkey' : PublicKeyAuthn }; -export type AuthnMethodAddResponse = { 'ok' : null } | - { 'invalid_metadata' : string }; +export type AuthnMethod = { 'PubKey' : PublicKeyAuthn } | + { 'WebAuthn' : WebAuthn }; +export type AuthnMethodAddError = { 'InvalidMetadata' : string }; export interface AuthnMethodData { 'metadata' : MetadataMap, 'protection' : AuthnMethodProtection, @@ -38,20 +37,18 @@ export interface AuthnMethodData { 'authn_method' : AuthnMethod, 'purpose' : Purpose, } -export type AuthnMethodProtection = { 'unprotected' : null } | - { 'protected' : null }; +export type AuthnMethodProtection = { 'Protected' : null } | + { 'Unprotected' : null }; export interface AuthnMethodRegistrationInfo { 'expiration' : Timestamp, 'authn_method' : [] | [AuthnMethodData], } -export type AuthnMethodRemoveResponse = { 'ok' : null }; export interface BufferedArchiveEntry { 'sequence_number' : bigint, 'entry' : Uint8Array | number[], 'anchor_number' : UserNumber, 'timestamp' : Timestamp, } -export type CaptchaCreateResponse = { 'ok' : Challenge }; export type CaptchaResult = ChallengeResult; export interface Challenge { 'png_base64' : string, @@ -136,13 +133,10 @@ export interface IdentityInfo { 'metadata' : MetadataMap, 'authn_method_registration' : [] | [AuthnMethodRegistrationInfo], } -export type IdentityInfoResponse = { 'ok' : IdentityInfo }; -export type IdentityMetadataReplaceResponse = { 'ok' : null }; export type IdentityNumber = bigint; -export type IdentityRegisterResponse = { 'ok' : IdentityNumber } | - { 'invalid_metadata' : string } | - { 'bad_captcha' : null } | - { 'canister_full' : null }; +export type IdentityRegisterError = { 'BadCaptcha' : null } | + { 'CanisterFull' : null } | + { 'InvalidMetadata' : string }; export interface InternetIdentityInit { 'max_num_latest_delegation_origins' : [] | [bigint], 'assigned_user_number_range' : [] | [[bigint, bigint]], @@ -239,13 +233,15 @@ export interface _SERVICE { >, 'authn_method_add' : ActorMethod< [IdentityNumber, AuthnMethodData], - [] | [AuthnMethodAddResponse] + { 'Ok' : null } | + { 'Err' : AuthnMethodAddError } >, 'authn_method_remove' : ActorMethod< [IdentityNumber, PublicKey], - [] | [AuthnMethodRemoveResponse] + { 'Ok' : null } | + { 'Err' : null } >, - 'captcha_create' : ActorMethod<[], [] | [CaptchaCreateResponse]>, + 'captcha_create' : ActorMethod<[], { 'Ok' : Challenge } | { 'Err' : null }>, 'create_challenge' : ActorMethod<[], Challenge>, 'deploy_archive' : ActorMethod<[Uint8Array | number[]], DeployArchiveResult>, 'enter_device_registration_mode' : ActorMethod<[UserNumber], Timestamp>, @@ -265,14 +261,20 @@ export interface _SERVICE { 'get_principal' : ActorMethod<[UserNumber, FrontendHostname], Principal>, 'http_request' : ActorMethod<[HttpRequest], HttpResponse>, 'http_request_update' : ActorMethod<[HttpRequest], HttpResponse>, - 'identity_info' : ActorMethod<[IdentityNumber], [] | [IdentityInfoResponse]>, + 'identity_info' : ActorMethod< + [IdentityNumber], + { 'Ok' : IdentityInfo } | + { 'Err' : null } + >, 'identity_metadata_replace' : ActorMethod< [IdentityNumber, MetadataMap], - [] | [IdentityMetadataReplaceResponse] + { 'Ok' : null } | + { 'Err' : null } >, 'identity_register' : ActorMethod< [AuthnMethodData, CaptchaResult, [] | [Principal]], - [] | [IdentityRegisterResponse] + { 'Ok' : IdentityNumber } | + { 'Err' : IdentityRegisterError } >, 'init_salt' : ActorMethod<[], undefined>, 'lookup' : ActorMethod<[UserNumber], Array>, diff --git a/src/internet_identity/internet_identity.did b/src/internet_identity/internet_identity.did index 8d6ff6e46f..099371786a 100644 --- a/src/internet_identity/internet_identity.did +++ b/src/internet_identity/internet_identity.did @@ -313,16 +313,16 @@ type PublicKeyAuthn = record { // The authentication methods currently supported by II. type AuthnMethod = variant { - webauthn: WebAuthn; - pubkey: PublicKeyAuthn; + WebAuthn: WebAuthn; + PubKey: PublicKeyAuthn; }; // This describes whether an authentication method is "protected" or not. // When protected, a authentication method can only be updated or removed if the // user is authenticated with that very authentication method. type AuthnMethodProtection = variant { - protected; - unprotected; + Protected; + Unprotected; }; type AuthnMethodData = record { @@ -358,36 +358,17 @@ type IdentityInfo = record { metadata: MetadataMap; }; -type CaptchaCreateResponse = variant { - ok: Challenge; -}; - -type IdentityRegisterResponse = variant { - // Registration successful. - ok: IdentityNumber; +type IdentityRegisterError = variant { // No more registrations are possible in this instance of the II service canister. - canister_full; + CanisterFull; // The captcha check was not successful. - bad_captcha; + BadCaptcha; // The metadata of the provided authentication method contains invalid entries. - invalid_metadata: text; -}; - -type IdentityInfoResponse = variant { - ok: IdentityInfo; + InvalidMetadata: text; }; -type AuthnMethodAddResponse = variant { - ok; - invalid_metadata: text; -}; - -type AuthnMethodRemoveResponse = variant { - ok; -}; - -type IdentityMetadataReplaceResponse = variant { - ok; +type AuthnMethodAddError = variant { + InvalidMetadata: text; }; type PrepareIdAliasRequest = record { @@ -480,38 +461,32 @@ service : (opt InternetIdentityInit) -> { // V2 API // WARNING: The following methods are experimental and may change in the future. - // - // Note: the responses of v2 API calls are `opt` for compatibility reasons - // with future variant extensions. - // A client decoding a response as `null` indicates outdated type information - // and should be treated as an error. - // Creates a new captcha. The solution needs to be submitted using the // `identity_register` call. - captcha_create: () -> (opt CaptchaCreateResponse); + captcha_create: () -> (variant {Ok: Challenge; Err;}); // Registers a new identity with the given authn_method. // A valid captcha solution to a previously generated captcha (using create_captcha) must be provided. // The sender needs to match the supplied authn_method. - identity_register: (AuthnMethodData, CaptchaResult, opt principal) -> (opt IdentityRegisterResponse); + identity_register: (AuthnMethodData, CaptchaResult, opt principal) -> (variant {Ok: IdentityNumber; Err: IdentityRegisterError;}); // Returns information about the identity with the given number. // Requires authentication. - identity_info: (IdentityNumber) -> (opt IdentityInfoResponse); + identity_info: (IdentityNumber) -> (variant {Ok: IdentityInfo; Err;}); // Replaces the authentication method independent metadata map. // The existing metadata map will be overwritten. // Requires authentication. - identity_metadata_replace: (IdentityNumber, MetadataMap) -> (opt IdentityMetadataReplaceResponse); + identity_metadata_replace: (IdentityNumber, MetadataMap) -> (variant {Ok; Err;}); // Adds a new authentication method to the identity. // Requires authentication. - authn_method_add: (IdentityNumber, AuthnMethodData) -> (opt AuthnMethodAddResponse); + authn_method_add: (IdentityNumber, AuthnMethodData) -> (variant {Ok; Err: AuthnMethodAddError;}); // Removes the authentication method associated with the public key from the identity. // Requires authentication. - authn_method_remove: (IdentityNumber, PublicKey) -> (opt AuthnMethodRemoveResponse); + authn_method_remove: (IdentityNumber, PublicKey) -> (variant {Ok; Err;}); // Attribute Sharing MVP API // The methods below are used to generate ID-alias credentials during attribute sharing flow. diff --git a/src/internet_identity/src/main.rs b/src/internet_identity/src/main.rs index e383db6581..6d44f401ec 100644 --- a/src/internet_identity/src/main.rs +++ b/src/internet_identity/src/main.rs @@ -511,8 +511,7 @@ fn check_authentication(anchor_number: AnchorNumber) -> Result<(Anchor, DeviceKe /// New v2 API that aims to eventually replace the current API. /// The v2 API: /// * uses terminology more aligned with the front-end and is more consistent in its naming. -/// * uses opt variant return types consistently in order to by able to extend / change return types -/// in the future without breaking changes. +/// * uses [Result] return types consistently. /// /// TODO, API v2 rollout plan: /// 1. Develop API v2 far enough so that front-ends can switch to it. @@ -525,9 +524,9 @@ mod v2_api { #[update] #[candid_method] - async fn captcha_create() -> Option { + async fn captcha_create() -> Result { let challenge = anchor_management::registration::create_challenge().await; - Some(CaptchaCreateResponse::Ok(challenge)) + Ok(challenge) } #[update] @@ -536,23 +535,20 @@ mod v2_api { authn_method: AuthnMethodData, challenge_result: ChallengeAttempt, temp_key: Option, - ) -> Option { - let result = match DeviceWithUsage::try_from(authn_method) - .map_err(|err| IdentityRegisterResponse::InvalidMetadata(err.to_string())) - { - Ok(device) => IdentityRegisterResponse::from(register( - DeviceData::from(device), - challenge_result, - temp_key, - )), - Err(err) => err, - }; - Some(result) + ) -> Result { + let device = DeviceWithUsage::try_from(authn_method) + .map_err(|err| IdentityRegisterError::InvalidMetadata(err.to_string()))?; + + match register(DeviceData::from(device), challenge_result, temp_key) { + RegisterResponse::Registered { user_number } => Ok(user_number), + RegisterResponse::CanisterFull => Err(IdentityRegisterError::CanisterFull), + RegisterResponse::BadChallenge => Err(IdentityRegisterError::BadCaptcha), + } } #[update] #[candid_method] - fn identity_info(identity_number: IdentityNumber) -> Option { + fn identity_info(identity_number: IdentityNumber) -> Result { authenticate_and_record_activity(identity_number); let anchor_info = anchor_management::get_anchor_info(identity_number); let metadata = state::anchor(identity_number) @@ -571,7 +567,7 @@ mod v2_api { .map(AuthnMethodRegistration::from), metadata, }; - Some(IdentityInfoResponse::Ok(identity_info)) + Ok(identity_info) } #[update] @@ -579,17 +575,10 @@ mod v2_api { fn authn_method_add( identity_number: IdentityNumber, authn_method: AuthnMethodData, - ) -> Option { - let result = match DeviceWithUsage::try_from(authn_method) - .map_err(|err| AuthnMethodAddResponse::InvalidMetadata(err.to_string())) - { - Ok(device) => { - add(identity_number, DeviceData::from(device)); - AuthnMethodAddResponse::Ok - } - Err(err) => err, - }; - Some(result) + ) -> Result<(), AuthnMethodAddError> { + DeviceWithUsage::try_from(authn_method) + .map(|device| add(identity_number, DeviceData::from(device))) + .map_err(|err| AuthnMethodAddError::InvalidMetadata(err.to_string())) } #[update] @@ -597,9 +586,9 @@ mod v2_api { fn authn_method_remove( identity_number: IdentityNumber, public_key: PublicKey, - ) -> Option { + ) -> Result<(), ()> { remove(identity_number, public_key); - Some(AuthnMethodRemoveResponse::Ok) + Ok(()) } #[update] @@ -607,14 +596,14 @@ mod v2_api { fn identity_metadata_replace( identity_number: IdentityNumber, metadata: HashMap, - ) -> Option { - let result = authenticated_anchor_operation(identity_number, |anchor| { + ) -> Result<(), ()> { + authenticated_anchor_operation(identity_number, |anchor| { Ok(( - IdentityMetadataReplaceResponse::Ok, + (), anchor_management::identity_metadata_replace(anchor, metadata), )) }); - Some(result) + Ok(()) } } diff --git a/src/internet_identity/tests/integration/activity_stats/authn_methods.rs b/src/internet_identity/tests/integration/activity_stats/authn_methods.rs index 3178648c56..2b86eb77df 100644 --- a/src/internet_identity/tests/integration/activity_stats/authn_methods.rs +++ b/src/internet_identity/tests/integration/activity_stats/authn_methods.rs @@ -47,12 +47,14 @@ fn should_report_daily_active_authn_methods() -> Result<(), CallError> { // repeated activity with the same authn_method on the same identity within the 24h // collection period should not increase the counter - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)?; + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)? + .expect("identity info failed"); env.advance_time(Duration::from_secs(DAY_SECONDS)); // some activity is required to update the stats - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)?; + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)? + .expect("identity info failed"); let metrics = get_metrics(&env, canister_id); assert_metric( @@ -103,12 +105,14 @@ fn should_report_monthly_active_authn_methods() -> Result<(), CallError> { // repeated activity with the same authn_method on the same identity within the 30-day // collection period should not increase the counter - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)?; + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)? + .expect("identity info failed"); env.advance_time(Duration::from_secs(MONTH_SECONDS)); // some activity is required to update the stats - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)?; + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)? + .expect("identity info failed"); let metrics = get_metrics(&env, canister_id); assert_metric( @@ -180,7 +184,8 @@ fn should_only_count_ii_domain_authn_methods() -> Result<(), CallError> { canister_id, non_ii_authn_method.principal(), identity_nr, - )?; + )? + .expect("identity info failed"); env.advance_time(Duration::from_secs(MONTH_SECONDS)); // some activity on an II domain is required to update the stats create_identity_with_authn_method(&env, canister_id, &ii_authn_method); @@ -223,7 +228,8 @@ fn should_keep_stats_across_upgrades() -> Result<(), CallError> { env.advance_time(Duration::from_secs(DAY_SECONDS)); // some activity is required to update the stats - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)?; + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_nr)? + .expect("identity info failed"); assert_metric( &get_metrics(&env, canister_id), diff --git a/src/internet_identity/tests/integration/archive_integration.rs b/src/internet_identity/tests/integration/archive_integration.rs index cce672b1c6..a30cc5cb42 100644 --- a/src/internet_identity/tests/integration/archive_integration.rs +++ b/src/internet_identity/tests/integration/archive_integration.rs @@ -485,7 +485,8 @@ mod pull_entries_tests { device.principal(), anchor, &metadata, - )?; + )? + .expect("identity_metadata_replace failed"); // the archive polls for entries once per second env.advance_time(Duration::from_secs(2)); diff --git a/src/internet_identity/tests/integration/v2_api/authn_method_add.rs b/src/internet_identity/tests/integration/v2_api/authn_method_add.rs index b6f4b51bd1..b2270ed632 100644 --- a/src/internet_identity/tests/integration/v2_api/authn_method_add.rs +++ b/src/internet_identity/tests/integration/v2_api/authn_method_add.rs @@ -6,12 +6,9 @@ use canister_tests::api::internet_identity::api_v2; use canister_tests::framework::{ env, expect_user_error_with_message, install_ii_canister, II_WASM, }; -use canister_tests::match_value; use ic_test_state_machine_client::CallError; use ic_test_state_machine_client::ErrorCode::CanisterCalledTrap; -use internet_identity_interface::internet_identity::types::{ - AuthnMethodAddResponse, IdentityInfoResponse, MetadataEntry, -}; +use internet_identity_interface::internet_identity::types::{AuthnMethodAddError, MetadataEntry}; use regex::Regex; use serde_bytes::ByteBuf; @@ -24,21 +21,18 @@ fn should_add_authn_method() -> Result<(), CallError> { let authn_method_2 = sample_authn_method(2); let identity_number = create_identity_with_authn_method(&env, canister_id, &authn_method_1); - match_value!( - api_v2::authn_method_add( - &env, - canister_id, - principal, - identity_number, - &authn_method_2, - )?, - Some(AuthnMethodAddResponse::Ok) - ); - match_value!( - api_v2::identity_info(&env, canister_id, principal, identity_number)?, - Some(IdentityInfoResponse::Ok(identity_info)) - ); + api_v2::authn_method_add( + &env, + canister_id, + principal, + identity_number, + &authn_method_2, + )? + .expect("authn method add failed"); + + let identity_info = api_v2::identity_info(&env, canister_id, principal, identity_number)? + .expect("identity info failed"); assert!(eq_ignoring_last_authentication( &identity_info.authn_methods[1], @@ -84,16 +78,16 @@ fn should_report_error_on_failed_conversion() -> Result<(), CallError> { let identity_number = create_identity_with_authn_method(&env, canister_id, &authn_method_1); - match_value!( - api_v2::authn_method_add( - &env, - canister_id, - principal, - identity_number, - &authn_method_2, - )?, - Some(AuthnMethodAddResponse::InvalidMetadata(_)) - ); - + let result = api_v2::authn_method_add( + &env, + canister_id, + principal, + identity_number, + &authn_method_2, + )?; + assert!(matches!( + result, + Err(AuthnMethodAddError::InvalidMetadata(_)) + )); Ok(()) } diff --git a/src/internet_identity/tests/integration/v2_api/authn_method_remove.rs b/src/internet_identity/tests/integration/v2_api/authn_method_remove.rs index d61322cd01..449dafdda0 100644 --- a/src/internet_identity/tests/integration/v2_api/authn_method_remove.rs +++ b/src/internet_identity/tests/integration/v2_api/authn_method_remove.rs @@ -6,13 +6,8 @@ use canister_tests::api::internet_identity::api_v2; use canister_tests::framework::{ env, expect_user_error_with_message, install_ii_canister, II_WASM, }; -use canister_tests::match_value; use ic_test_state_machine_client::CallError; use ic_test_state_machine_client::ErrorCode::CanisterCalledTrap; -use internet_identity_interface::internet_identity::types::IdentityInfoResponse; -use internet_identity_interface::internet_identity::types::{ - AuthnMethodAddResponse, AuthnMethodRemoveResponse, -}; use regex::Regex; #[test] @@ -24,34 +19,28 @@ fn should_remove_authn_method() -> Result<(), CallError> { let authn_method_2 = sample_authn_method(2); let identity_number = create_identity_with_authn_method(&env, canister_id, &authn_method_1); - let result = api_v2::authn_method_add( + api_v2::authn_method_add( &env, canister_id, principal, identity_number, &authn_method_2, )? - .unwrap(); - - assert!(matches!(result, AuthnMethodAddResponse::Ok)); + .expect("authn method add failed"); - match_value!( - api_v2::identity_info(&env, canister_id, principal, identity_number)?, - Some(IdentityInfoResponse::Ok(identity_info)) - ); + let identity_info = api_v2::identity_info(&env, canister_id, principal, identity_number)? + .expect("identity info failed"); assert_eq!(identity_info.authn_methods.len(), 2); - match_value!( - api_v2::authn_method_remove( - &env, - canister_id, - principal, - identity_number, - &authn_method_2.public_key(), - )?, - Some(AuthnMethodRemoveResponse::Ok) - ); + api_v2::authn_method_remove( + &env, + canister_id, + principal, + identity_number, + &authn_method_2.public_key(), + )? + .expect("authn method remove failed"); Ok(()) } diff --git a/src/internet_identity/tests/integration/v2_api/authn_method_test_helpers.rs b/src/internet_identity/tests/integration/v2_api/authn_method_test_helpers.rs index fa8d624574..bc5246a518 100644 --- a/src/internet_identity/tests/integration/v2_api/authn_method_test_helpers.rs +++ b/src/internet_identity/tests/integration/v2_api/authn_method_test_helpers.rs @@ -1,10 +1,9 @@ use canister_tests::api::internet_identity::api_v2; -use canister_tests::match_value; use ic_cdk::api::management_canister::main::CanisterId; use ic_test_state_machine_client::StateMachine; use internet_identity_interface::internet_identity::types::{ - AuthnMethod, AuthnMethodData, AuthnMethodProtection, CaptchaCreateResponse, ChallengeAttempt, - IdentityNumber, IdentityRegisterResponse, PublicKeyAuthn, Purpose, + AuthnMethod, AuthnMethodData, AuthnMethodProtection, ChallengeAttempt, IdentityNumber, + PublicKeyAuthn, Purpose, }; use serde_bytes::ByteBuf; @@ -37,27 +36,24 @@ pub fn create_identity_with_authn_method( canister_id: CanisterId, authn_method: &AuthnMethodData, ) -> IdentityNumber { - match_value!( - api_v2::captcha_create(env, canister_id).unwrap(), - Some(CaptchaCreateResponse::Ok(challenge)) - ); + let challenge = api_v2::captcha_create(env, canister_id) + .expect("API call failed") + .expect("captcha_create failed"); let challenge_attempt = ChallengeAttempt { chars: "a".to_string(), key: challenge.challenge_key, }; - match_value!( - api_v2::identity_register( - env, - canister_id, - authn_method.principal(), - authn_method, - &challenge_attempt, - None, - ), - Ok(Some(IdentityRegisterResponse::Ok(user_number))) - ); - user_number + api_v2::identity_register( + env, + canister_id, + authn_method.principal(), + authn_method, + &challenge_attempt, + None, + ) + .expect("API call failed") + .expect("identity_register failed") } pub fn sample_authn_method(i: u8) -> AuthnMethodData { diff --git a/src/internet_identity/tests/integration/v2_api/identity_info.rs b/src/internet_identity/tests/integration/v2_api/identity_info.rs index c0251e7ca8..d22d6126ac 100644 --- a/src/internet_identity/tests/integration/v2_api/identity_info.rs +++ b/src/internet_identity/tests/integration/v2_api/identity_info.rs @@ -3,16 +3,16 @@ use candid::Principal; use canister_tests::api::internet_identity as api; use canister_tests::api::internet_identity::api_v2; +use canister_tests::flows; use canister_tests::framework::{ env, expect_user_error_with_message, install_ii_canister, time, II_WASM, }; -use canister_tests::{flows, match_value}; use ic_cdk::api::management_canister::main::CanisterId; use ic_test_state_machine_client::ErrorCode::CanisterCalledTrap; use ic_test_state_machine_client::{CallError, StateMachine}; use internet_identity_interface::internet_identity::types::{ - AuthnMethodAddResponse, AuthnMethodData, AuthnMethodRegistration, DeviceData, - IdentityInfoResponse, IdentityNumber, KeyType, MetadataEntry, Purpose, + AuthnMethodData, AuthnMethodRegistration, DeviceData, IdentityNumber, KeyType, MetadataEntry, + Purpose, }; use regex::Regex; use serde_bytes::ByteBuf; @@ -26,10 +26,9 @@ fn should_get_identity_info() -> Result<(), CallError> { let devices = sample_devices(); let identity_number = create_identity_with_devices(&env, canister_id, &devices); - match_value!( - api_v2::identity_info(&env, canister_id, devices[0].principal(), identity_number)?, - Some(IdentityInfoResponse::Ok(identity_info)) - ); + let identity_info = + api_v2::identity_info(&env, canister_id, devices[0].principal(), identity_number)? + .expect("identity info failed"); assert_eq_ignoring_last_authentication(&identity_info.authn_methods, &devices); assert_eq!(identity_info.authn_method_registration, None); @@ -80,10 +79,9 @@ fn should_provide_authn_registration() -> Result<(), CallError> { api::enter_device_registration_mode(&env, canister_id, device1.principal(), identity_number)?; api::add_tentative_device(&env, canister_id, identity_number, &device2)?; - match_value!( - api_v2::identity_info(&env, canister_id, device1.principal(), identity_number)?, - Some(IdentityInfoResponse::Ok(identity_info)) - ); + let identity_info = + api_v2::identity_info(&env, canister_id, device1.principal(), identity_number)? + .expect("identity info failed"); assert_eq!( identity_info.authn_method_registration, @@ -125,17 +123,15 @@ fn create_identity_with_devices( let device1 = iter.next().unwrap(); let identity_number = flows::register_anchor_with_device(env, canister_id, device1); for (idx, device) in iter.enumerate() { - match_value!( - api_v2::authn_method_add( - env, - canister_id, - device1.principal(), - identity_number, - &AuthnMethodData::from(device.clone()), - ) - .unwrap_or_else(|_| panic!("could not add device {}", idx)), - Some(AuthnMethodAddResponse::Ok) - ); + api_v2::authn_method_add( + env, + canister_id, + device1.principal(), + identity_number, + &AuthnMethodData::from(device.clone()), + ) + .unwrap_or_else(|_| panic!("could not add device {}", idx)) + .expect("authn method add failed"); } identity_number } diff --git a/src/internet_identity/tests/integration/v2_api/identity_metadata.rs b/src/internet_identity/tests/integration/v2_api/identity_metadata.rs index abd5ca6027..855bb5c17e 100644 --- a/src/internet_identity/tests/integration/v2_api/identity_metadata.rs +++ b/src/internet_identity/tests/integration/v2_api/identity_metadata.rs @@ -6,10 +6,9 @@ use canister_tests::api::internet_identity::api_v2; use canister_tests::framework::{ env, expect_user_error_with_message, install_ii_canister, II_WASM, }; -use canister_tests::match_value; use ic_test_state_machine_client::CallError; use ic_test_state_machine_client::ErrorCode::CanisterCalledTrap; -use internet_identity_interface::internet_identity::types::{IdentityInfoResponse, MetadataEntry}; +use internet_identity_interface::internet_identity::types::MetadataEntry; use regex::Regex; use std::collections::HashMap; @@ -22,10 +21,9 @@ fn should_write_metadata() -> Result<(), CallError> { let authn_method = test_authn_method(); let identity_number = create_identity_with_authn_method(&env, canister_id, &authn_method); - match_value!( - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_number)?, - Some(IdentityInfoResponse::Ok(identity_info)) - ); + let identity_info = + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_number)? + .expect("identity info failed"); assert!(identity_info.metadata.is_empty()); let metadata = HashMap::from_iter(vec![( @@ -39,12 +37,12 @@ fn should_write_metadata() -> Result<(), CallError> { authn_method.principal(), identity_number, &metadata, - )?; + )? + .expect("identity metadata replace failed"); - match_value!( - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_number)?, - Some(IdentityInfoResponse::Ok(identity_info)) - ); + let identity_info = + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_number)? + .expect("identity info failed"); assert_eq!(identity_info.metadata, metadata); Ok(()) } @@ -86,10 +84,9 @@ fn should_not_write_too_large_identity_metadata_map() -> Result<(), CallError> { let authn_method = test_authn_method(); let identity_number = create_identity_with_authn_method(&env, canister_id, &authn_method); - match_value!( - api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_number)?, - Some(IdentityInfoResponse::Ok(identity_info)) - ); + let identity_info = + api_v2::identity_info(&env, canister_id, authn_method.principal(), identity_number)? + .expect("identity info failed"); assert!(identity_info.metadata.is_empty()); let metadata = HashMap::from_iter(vec![( diff --git a/src/internet_identity/tests/integration/v2_api/identity_register.rs b/src/internet_identity/tests/integration/v2_api/identity_register.rs index 0c62b9bf5c..2eed91f5e0 100644 --- a/src/internet_identity/tests/integration/v2_api/identity_register.rs +++ b/src/internet_identity/tests/integration/v2_api/identity_register.rs @@ -7,10 +7,9 @@ use canister_tests::framework::{ arg_with_anchor_range, env, expect_user_error_with_message, install_ii_canister, install_ii_canister_with_arg, II_WASM, }; -use canister_tests::match_value; use ic_test_state_machine_client::ErrorCode::CanisterCalledTrap; use internet_identity_interface::internet_identity::types::{ - CaptchaCreateResponse, ChallengeAttempt, IdentityRegisterResponse, MetadataEntry, + ChallengeAttempt, IdentityRegisterError, MetadataEntry, }; use regex::Regex; use serde_bytes::ByteBuf; @@ -48,25 +47,23 @@ fn should_not_exceed_configured_identity_range() { create_identity_with_authn_method(&env, canister_id, &authn_method); create_identity_with_authn_method(&env, canister_id, &authn_method); - match_value!( - api_v2::captcha_create(&env, canister_id).unwrap(), - Some(CaptchaCreateResponse::Ok(challenge)) - ); + let challenge = api_v2::captcha_create(&env, canister_id) + .expect("API call failed") + .expect("captcha_create failed"); - match_value!( - api_v2::identity_register( - &env, - canister_id, - authn_method.principal(), - &authn_method, - &ChallengeAttempt { - chars: "a".to_string(), - key: challenge.challenge_key, - }, - None, - ), - Ok(Some(IdentityRegisterResponse::CanisterFull)) - ); + let result = api_v2::identity_register( + &env, + canister_id, + authn_method.principal(), + &authn_method, + &ChallengeAttempt { + chars: "a".to_string(), + key: challenge.challenge_key, + }, + None, + ) + .expect("API call failed"); + assert!(matches!(result, Err(IdentityRegisterError::CanisterFull))); } #[test] @@ -74,10 +71,9 @@ fn should_verify_sender_matches_authn_method() { let env = env(); let canister_id = install_ii_canister(&env, II_WASM.clone()); - match_value!( - api_v2::captcha_create(&env, canister_id).unwrap(), - Some(CaptchaCreateResponse::Ok(challenge)) - ); + let challenge = api_v2::captcha_create(&env, canister_id) + .expect("API call failed") + .expect("captcha_create failed"); let result = api_v2::identity_register( &env, @@ -103,25 +99,23 @@ fn should_not_allow_wrong_captcha() { let canister_id = install_ii_canister(&env, II_WASM.clone()); let authn_method = test_authn_method(); - match_value!( - api_v2::captcha_create(&env, canister_id).unwrap(), - Some(CaptchaCreateResponse::Ok(challenge)) - ); + let challenge = api_v2::captcha_create(&env, canister_id) + .expect("API call failed") + .expect("captcha_create failed"); - match_value!( - api_v2::identity_register( - &env, - canister_id, - authn_method.principal(), - &authn_method, - &ChallengeAttempt { - chars: "wrong solution".to_string(), - key: challenge.challenge_key, - }, - None, - ), - Ok(Some(IdentityRegisterResponse::BadCaptcha)) - ); + let result = api_v2::identity_register( + &env, + canister_id, + authn_method.principal(), + &authn_method, + &ChallengeAttempt { + chars: "wrong solution".to_string(), + key: challenge.challenge_key, + }, + None, + ) + .expect("API call failed"); + assert!(matches!(result, Err(IdentityRegisterError::BadCaptcha))); } #[test] @@ -130,27 +124,25 @@ fn should_not_allow_expired_captcha() { let canister_id = install_ii_canister(&env, II_WASM.clone()); let authn_method = test_authn_method(); - match_value!( - api_v2::captcha_create(&env, canister_id).unwrap(), - Some(CaptchaCreateResponse::Ok(challenge)) - ); + let challenge = api_v2::captcha_create(&env, canister_id) + .expect("API call failed") + .expect("captcha_create failed"); env.advance_time(Duration::from_secs(301)); // one second longer than captcha validity - match_value!( - api_v2::identity_register( - &env, - canister_id, - authn_method.principal(), - &authn_method, - &ChallengeAttempt { - chars: "wrong solution".to_string(), - key: challenge.challenge_key, - }, - None, - ), - Ok(Some(IdentityRegisterResponse::BadCaptcha)) - ); + let result = api_v2::identity_register( + &env, + canister_id, + authn_method.principal(), + &authn_method, + &ChallengeAttempt { + chars: "a".to_string(), + key: challenge.challenge_key, + }, + None, + ) + .expect("API call failed"); + assert!(matches!(result, Err(IdentityRegisterError::BadCaptcha))); } #[test] @@ -163,23 +155,24 @@ fn should_fail_on_invalid_metadata() { MetadataEntry::Bytes(ByteBuf::from("invalid")), ); - match_value!( - api_v2::captcha_create(&env, canister_id).unwrap(), - Some(CaptchaCreateResponse::Ok(challenge)) - ); + let challenge = api_v2::captcha_create(&env, canister_id) + .expect("API call failed") + .expect("captcha_create failed"); - match_value!( - api_v2::identity_register( - &env, - canister_id, - authn_method.principal(), - &authn_method, - &ChallengeAttempt { - chars: "a".to_string(), - key: challenge.challenge_key, - }, - None, - ), - Ok(Some(IdentityRegisterResponse::InvalidMetadata(_))) - ); + let result = api_v2::identity_register( + &env, + canister_id, + authn_method.principal(), + &authn_method, + &ChallengeAttempt { + chars: "a".to_string(), + key: challenge.challenge_key, + }, + None, + ) + .expect("API call failed"); + assert!(matches!( + result, + Err(IdentityRegisterError::InvalidMetadata(_)) + )); } diff --git a/src/internet_identity_interface/src/internet_identity/conversions.rs b/src/internet_identity_interface/src/internet_identity/conversions.rs index ff98ecce93..3abbf1ebc6 100644 --- a/src/internet_identity_interface/src/internet_identity/conversions.rs +++ b/src/internet_identity_interface/src/internet_identity/conversions.rs @@ -250,15 +250,3 @@ impl TryFrom for DeviceWithUsage { }) } } - -impl From for IdentityRegisterResponse { - fn from(register_response: RegisterResponse) -> Self { - match register_response { - RegisterResponse::Registered { user_number } => { - IdentityRegisterResponse::Ok(user_number) - } - RegisterResponse::CanisterFull => IdentityRegisterResponse::CanisterFull, - RegisterResponse::BadChallenge => IdentityRegisterResponse::BadCaptcha, - } - } -} diff --git a/src/internet_identity_interface/src/internet_identity/types/api_v2.rs b/src/internet_identity_interface/src/internet_identity/types/api_v2.rs index a65090f78c..c4bc72cd23 100644 --- a/src/internet_identity_interface/src/internet_identity/types/api_v2.rs +++ b/src/internet_identity_interface/src/internet_identity/types/api_v2.rs @@ -1,6 +1,4 @@ -use crate::internet_identity::types::{ - Challenge, CredentialId, MetadataEntry, PublicKey, Purpose, Timestamp, -}; +use crate::internet_identity::types::{CredentialId, MetadataEntry, PublicKey, Purpose, Timestamp}; use candid::{CandidType, Deserialize}; use std::collections::HashMap; @@ -8,9 +6,7 @@ pub type IdentityNumber = u64; #[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] pub enum AuthnMethodProtection { - #[serde(rename = "protected")] Protected, - #[serde(rename = "unprotected")] Unprotected, } @@ -28,9 +24,7 @@ pub struct WebAuthn { /// Supported authentication methods #[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] pub enum AuthnMethod { - #[serde(rename = "webauthn")] WebAuthn(WebAuthn), - #[serde(rename = "pubkey")] PubKey(PublicKeyAuthn), } @@ -62,45 +56,13 @@ pub struct IdentityInfo { } #[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] -pub enum CaptchaCreateResponse { - #[serde(rename = "ok")] - Ok(Challenge), -} - -#[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] -pub enum IdentityRegisterResponse { - #[serde(rename = "ok")] - Ok(IdentityNumber), - #[serde(rename = "canister_full")] +pub enum IdentityRegisterError { CanisterFull, - #[serde(rename = "bad_captcha")] BadCaptcha, - #[serde(rename = "invalid_metadata")] InvalidMetadata(String), } #[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] -pub enum IdentityInfoResponse { - #[serde(rename = "ok")] - Ok(IdentityInfo), -} - -#[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] -pub enum AuthnMethodAddResponse { - #[serde(rename = "ok")] - Ok, - #[serde(rename = "invalid_metadata")] +pub enum AuthnMethodAddError { InvalidMetadata(String), } - -#[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] -pub enum AuthnMethodRemoveResponse { - #[serde(rename = "ok")] - Ok, -} - -#[derive(Clone, Debug, CandidType, Deserialize, Eq, PartialEq)] -pub enum IdentityMetadataReplaceResponse { - #[serde(rename = "ok")] - Ok, -}