Id RFC 006 identity service
RFC-006 — Identity Service
- *tatus:*Draft
- *ate:*2026
0408 - *uthor:*Koder Team
- *epends on:*RFC
001, 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
- *ser CRUD*— create, read, update, delete user profiles
- *redential management*— store/update Argon2id password hashes
- *FA device management*— register/remove TOTP secrets and WebAuthn credentials
- *mail verification*— generate and validate verification tokens
- *assword reset*— generate and validate reset tokens
- *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: 256
bit random, 1hour expiry, single-use - Email verification tokens: 256
bit 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