Skip to content

user-service API

User registration, profile management, and authentication coordination canister.

Candid file: user-service/src/user_service.did

Types

IndividualRecord

User profile data:

candid
type IndividualRecord = record {
  id: text;
  first_name: text;
  last_name: text;
  email: text;
  verification_token: text;
  verified: bool;
  submitted_at: nat64;
  verified_at: opt nat64;
  ip_hash: opt text;
  ii_principal: opt principal;
  auth_methods: vec AuthMethod;
  preferred_auth_method: opt AuthMethodType;
};

AuthMethod

Authentication method record:

candid
type AuthMethod = record {
  method_type: AuthMethodType;
  identifier: text;
  credential: opt Credential;
  verified: bool;
  linked_at: nat64;
  last_used: opt nat64;
};

type Credential = variant {
  PasswordHash: blob;
  OidcToken: OidcTokenData;
  Principal: principal;
};

CreateAccountRequest

Account creation payload:

candid
type CreateAccountRequest = record {
  first_name: text;
  last_name: text;
  email: text;
  password: text;
  encrypted_recovery_key: blob;  // AES-256-GCM encrypted
  password_salt: blob;            // PBKDF2 salt
};

Methods

Account Creation

create_account

Create a new user account with email/password.

candid
"create_account": (CreateAccountRequest) -> (CreateAccountResponse);

TypeScript Example:

typescript
// Client-side: Generate recovery key and encrypt
const recoveryKey = crypto.getRandomValues(new Uint8Array(32));
const salt = crypto.getRandomValues(new Uint8Array(16));
const passwordKey = await deriveKeyFromPassword(password, salt);
const encryptedRecoveryKey = await encrypt(recoveryKey, passwordKey);

const result = await userActor.create_account({
  first_name: 'John',
  last_name: 'Doe',
  email: 'john@example.com',
  password: 'securePassword123',
  encrypted_recovery_key: encryptedRecoveryKey,
  password_salt: salt,
});

submit_individual

Submit individual registration (legacy/interest form).

candid
"submit_individual": (IndividualRequest, opt AddressRequest) -> (IndividualResult);

Email Verification

verify_email

Verify email with token from email link.

candid
"verify_email": (token: text) -> (VerifyResult);

verify_code

Verify email with 6-digit code.

candid
"verify_code": (email: text, code: text, first_name: text, last_name: text) -> (VerifyResult);

resend_verification_code

Request new verification code.

candid
"resend_verification_code": (email: text) -> (VerifyResult);

Authentication

authenticate_with_password

Login with email/password.

candid
"authenticate_with_password": (
  AuthRequest,
  device_fingerprint: text,
  ip_address: opt text,
  timezone: opt text,
  user_agent: opt text
) -> (AuthResponse);

authenticate_with_internet_identity

Login with Internet Identity.

candid
"authenticate_with_internet_identity": (
  device_fingerprint: text,
  ip_address: opt text,
  timezone: opt text,
  user_agent: opt text
) -> (AuthResponse);

Uses ic_cdk::caller() to get II principal automatically.

Password Management

set_password

Set or change password.

candid
"set_password": (user_id: text, SetPasswordRequest) -> (variant { Ok; Err: text });

type SetPasswordRequest = record {
  current_password: opt text;
  new_password: text;
  encrypted_recovery_key: opt blob;
  password_salt: opt blob;
};

initiate_password_reset

Start password reset flow.

candid
"initiate_password_reset": (email: text) -> (variant { Ok: text; Err: text });

complete_password_reset

Complete password reset.

candid
"complete_password_reset": (
  email: text,
  reset_code: text,
  new_password: text,
  encrypted_recovery_key_base64: text,
  password_salt_base64: text
) -> (variant { Ok: text; Err: text });

Internet Identity Linking

Link II to existing account.

candid
"link_ii_principal": (user_id: text, ii_principal: principal) -> (variant { Ok; Err: text });

Remove II link from account.

candid
"unlink_ii_principal": (user_id: text) -> (variant { Ok; Err: text });

get_user_by_ii_principal (query)

Find user by II principal.

candid
"get_user_by_ii_principal": (principal) -> (variant { Ok: text; Err: text }) query;

Self-Custody Verification

check_self_custody_status (query)

Check wallet verification status.

candid
"check_self_custody_status": (user_id: text) -> (variant { Ok: SelfCustodyStatus; Err: text }) query;

type SelfCustodyStatus = variant {
  Verified;
  Expired;
  NeverVerified;
};

Address Management

add_address

Add address to user profile.

candid
"add_address": (individual_id: text, AddressRequest) -> (variant { Ok: text; Err: text });

get_addresses_for_individual (query)

Get all addresses for user.

candid
"get_addresses_for_individual": (individual_id: text) -> (vec Address) query;

GDPR Compliance

request_kyc_data_deletion

Request deletion of KYC data (30-day grace period).

candid
"request_kyc_data_deletion": () -> (variant { Ok; Err: text });

cancel_kyc_data_deletion

Cancel pending deletion request.

candid
"cancel_kyc_data_deletion": () -> (variant { Ok; Err: text });

Duplicate Prevention

check_duplicate_id_hash

Check if ID hash already exists (for KYC).

candid
"check_duplicate_id_hash": (id_type: text, country: text, id_number: text) -> (variant { Ok: bool; Err: text });

Membership Integration

check_and_mint_membership

Mint membership NFT for verified user.

candid
"check_and_mint_membership": (user_id: text) -> (variant { Ok: nat64; Err: text });

Returns ICRC-7 token ID on success.

Statistics

get_stats (query)

Get user statistics.

candid
"get_stats": () -> (Stats) query;

type Stats = record {
  total_individuals: nat64;
  verified_individuals: nat64;
  pending_verifications: nat64;
};

Error Messages

ErrorCauseResolution
Email already registeredDuplicate emailUse password reset
Invalid verification codeWrong/expired codeRequest new code
Password too weakDoesn't meet requirementsUse stronger password
User not foundInvalid user IDCheck user exists
Duplicate ID detectedKYC ID already registeredContact support

Hello World Co-Op DAO