Id RFC 006 identity service

RFC-006 — Identity Service

  • *tatus:*Draft
  • *ate:*20260408
  • *uthor:*Koder Team
  • *epends on:*RFC001, RFC002

Summary

The Identity service is the single source of truth for user data: profiles, credentials (password hashes), and MFA device registrations. It does NOT authenticate users (that's the Auth service) — it stores and manages identity data.

Responsibilities

  1. *ser CRUD*— create, read, update, delete user profiles
  2. *redential management*— store/update Argon2id password hashes
  3. *FA device management*— register/remove TOTP secrets and WebAuthn credentials
  4. *mail verification*— generate and validate verification tokens
  5. *assword reset*— generate and validate reset tokens
  6. *vatar management*— store and serve user avatars

API Endpoints (REST)

Users

Method Endpoint Description
POST /v1/users Create user
GET /v1/users/{id} Get user by ID
GET /v1/users?email={email} Find user by email
PATCH /v1/users/{id} Update user profile
DELETE /v1/users/{id} Soft-delete user
GET /v1/users List users (paginated)

Credentials

Method Endpoint Description
POST /v1/users/{id}/password Set password
PUT /v1/users/{id}/password Change password (requires current)
POST /v1/users/{id}/password/reset Request password reset
POST /v1/users/{id}/password/reset/confirm Confirm password reset

MFA Devices

Method Endpoint Description
GET /v1/users/{id}/mfa List MFA devices
POST /v1/users/{id}/mfa/totp Register TOTP device
POST /v1/users/{id}/mfa/totp/verify Verify and activate TOTP
DELETE /v1/users/{id}/mfa/{device_id} Remove MFA device
POST /v1/users/{id}/mfa/webauthn/register/begin Begin WebAuthn registration
POST /v1/users/{id}/mfa/webauthn/register/finish Complete WebAuthn registration

Email Verification

Method Endpoint Description
POST /v1/users/{id}/email/verify Send verification email
POST /v1/users/{id}/email/verify/confirm Confirm email with token

gRPC Service Definition

syntax = "proto3";
package koder.id.identity.v1;

service IdentityService {
    // User management
    rpc CreateUser(CreateUserRequest) returns (User);
    rpc GetUser(GetUserRequest) returns (User);
    rpc GetUserByEmail(GetUserByEmailRequest) returns (User);
    rpc UpdateUser(UpdateUserRequest) returns (User);
    rpc DeleteUser(DeleteUserRequest) returns (DeleteUserResponse);
    rpc ListUsers(ListUsersRequest) returns (ListUsersResponse);

    // Credential management (called by Auth service)
    rpc GetCredential(GetCredentialRequest) returns (Credential);
    rpc SetPassword(SetPasswordRequest) returns (SetPasswordResponse);
    rpc CreatePasswordResetToken(CreateResetTokenRequest) returns (ResetToken);
    rpc ValidatePasswordResetToken(ValidateResetTokenRequest) returns (ValidateResetTokenResponse);

    // MFA management
    rpc ListMFADevices(ListMFADevicesRequest) returns (ListMFADevicesResponse);
    rpc RegisterTOTP(RegisterTOTPRequest) returns (TOTPRegistration);
    rpc ActivateTOTP(ActivateTOTPRequest) returns (ActivateTOTPResponse);
    rpc GetTOTPSecret(GetTOTPSecretRequest) returns (TOTPSecret);
    rpc BeginWebAuthnRegistration(BeginWebAuthnRegRequest) returns (WebAuthnRegistrationChallenge);
    rpc FinishWebAuthnRegistration(FinishWebAuthnRegRequest) returns (WebAuthnDevice);
    rpc GetWebAuthnCredentials(GetWebAuthnCredentialsRequest) returns (WebAuthnCredentials);
    rpc UpdateWebAuthnSignCount(UpdateSignCountRequest) returns (UpdateSignCountResponse);
    rpc RemoveMFADevice(RemoveMFADeviceRequest) returns (RemoveMFADeviceResponse);

    // Email verification
    rpc CreateEmailVerificationToken(CreateEmailTokenRequest) returns (EmailToken);
    rpc VerifyEmail(VerifyEmailRequest) returns (VerifyEmailResponse);
}

User Registration Flow

Client                    Identity Service              External
  │                             │                          │
  │  POST /v1/users             │                          │
  │  {email, username, pass}    │                          │
  │────────────────────────────▶│                          │
  │                             │                          │
  │                             │ 1. Validate input        │
  │                             │ 2. Check email unique    │
  │                             │ 3. Hash password         │
  │                             │    (Argon2id)            │
  │                             │ 4. Create user record    │
  │                             │ 5. Create credential     │
  │                             │ 6. Generate email        │
  │                             │    verification token    │
  │                             │                          │
  │                             │  Send verification email │
  │                             │─────────────────────────▶│
  │                             │                          │
  │  201 Created                │                          │
  │  {user, email_sent: true}   │                          │
  │◀────────────────────────────│                          │

TOTP Registration Flow

Client                    Identity Service
  │                             │
  │  POST /v1/users/{id}/       │
  │       mfa/totp              │
  │────────────────────────────▶│
  │                             │ 1. Generate TOTP secret
  │                             │ 2. Store encrypted (unverified)
  │  {secret, qr_uri,          │
  │   device_id}                │
  │◀────────────────────────────│
  │                             │
  │  (user scans QR code)       │
  │                             │
  │  POST /v1/users/{id}/       │
  │       mfa/totp/verify       │
  │  {device_id, code: "123456"}│
  │────────────────────────────▶│
  │                             │ 3. Validate code against secret
  │                             │ 4. Mark device as verified
  │  200 OK                     │
  │  {verified: true,           │
  │   recovery_codes: [...]}    │
  │◀────────────────────────────│

*ecovery codes:*10 single-use codes generated on first MFA activation, stored as Argon2id hashes.

WebAuthn Registration Flow

Client                    Identity Service
  │                             │
  │  POST .../webauthn/         │
  │       register/begin        │
  │────────────────────────────▶│
  │                             │ 1. Generate challenge
  │  {publicKey: {              │ 2. Set RP ID, user info
  │    challenge, rp, user,     │
  │    pubKeyCredParams,        │
  │    attestation: "none"}}    │
  │◀────────────────────────────│
  │                             │
  │  (browser: navigator.       │
  │   credentials.create())     │
  │                             │
  │  POST .../webauthn/         │
  │       register/finish       │
  │  {id, rawId, response,      │
  │   type: "public-key"}       │
  │────────────────────────────▶│
  │                             │ 3. Verify attestation
  │                             │ 4. Store credential
  │  200 OK                     │
  │  {device_id, name}          │
  │◀────────────────────────────│

Password Hashing

import "golang.org/x/crypto/argon2"

type Argon2Params struct {
    Time    uint32 // 3
    Memory  uint32 // 64 * 1024 (64 MB)
    Threads uint8  // 4
    KeyLen  uint32 // 32
    SaltLen uint32 // 16
}

// Hash format stored in DB:
// $argon2id$v=19$m=65536,t=3,p=4$BASE64_SALT$BASE64_HASH

*ersion migration:*The credentials.version field tracks the hash algorithm version. When parameters change, existing hashes are re-hashed on next successful login (transparent upgrade).

Password Policy

Configurable per tenant, defaults:

{
    "min_length": 10,
    "max_length": 128,
    "require_uppercase": false,
    "require_lowercase": false,
    "require_digit": false,
    "require_special": false,
    "check_breach_database": true,
    "check_common_passwords": true,
    "prevent_reuse_count": 5
}

*hilosophy:*Long passwords over complex passwords. A 20-character passphrase is stronger than P@ssw0rd!. Complexity requirements are off by default but configurable.

check_breach_database: validates password against a local k-Anonymity dataset (HaveIBeenPwned top 1M hashes, shipped with the binary, updated periodically).

Security Considerations

  • Credentials table is access-restricted — only Auth service can read hashes via gRPC
  • TOTP secrets encrypted at rest with per-tenant envelope key
  • WebAuthn private keys never leave the authenticator device
  • Password reset tokens: 256bit random, 1hour expiry, single-use
  • Email verification tokens: 256bit random, 24hour expiry, single-use
  • User enumeration prevention: registration returns 201 even if email exists (sends "already registered" email instead)
  • All mutations logged in audit trail

Source: ../home/koder/dev/koder/meta/docs/stack/rfcs/id-RFC-006-identity-service.md