Idverse

An Elixir client library for the IDVerse / IDKit identity verification API.

⚠️ AI Generated! ⚠️

Full disclosure, this repo was largely generated by Claude Code. Use at your own risk.

Installation

Add idverse to your list of dependencies in mix.exs:

def deps do
  [
    {:idverse, "~> 0.3.0"}
  ]
end

Configuration

Configure environments in your config/runtime.exs:

# Optional mock server for development
config :idverse, :mock_server,
  enabled: true,
  embedded: true,
  base_url: "http://localhost:4000/idverse-mock",
  repo: Ubre.Repo

config :idverse,
  environments: [
    prod: [
      base_url: System.get_env("IDVERSE_BASE_URL"),
      oauth2_url: System.get_env("IDVERSE_OAUTH2_URL"),
      api_key: System.get_env("IDVERSE_API_KEY")
    ],
    mock: [
      base_url: "http://localhost:4000/idverse-mock/v1",
      oauth2_url: "http://localhost:4000/idverse-mock",
      api_key: "mock_api_key"
    ]
  ]

Each environment gets its own supervised TokenManager process that handles OAuth2 token acquisition and automatic refresh.

Usage

Creating a Client

# Create a client for an environment
client = Idverse.Client.new(environment: :prod)

Transaction Operations

# Create a new verification transaction
{:ok, transaction} = Idverse.Client.Transactions.create(client, %{
  name: "John Doe",
  reference: "order-123",
  flow_type: "NORMAL2",
  phone_region_code: "61",
  phone_number: "400000000",
  redirect_url_success: "https://example.com/success",
  redirect_url_exit: "https://example.com/exit"
})

# Get transaction details
{:ok, details} = Idverse.Client.Transactions.get(client, transaction["transaction_id"])

# Get PDF report
{:ok, pdf_binary} = Idverse.Client.Transactions.get(client, transaction_id, "pdf")

# Resend SMS notification
{:ok, response} = Idverse.Client.Transactions.resend(client, transaction_id)

# Cancel a transaction
{:ok, response} = Idverse.Client.Transactions.cancel(client, transaction_id)

# Redact transaction data
{:ok, response} = Idverse.Client.Transactions.redact(client, transaction_id, ["records", "assets", "face", "data"])

Webhook Notifications

You can configure webhook notifications when creating a transaction:

{:ok, transaction} = Idverse.Client.Transactions.create(client, %{
  name: "John Doe",
  flow_type: "NORMAL2",
  phone_region_code: "61",
  phone_number: "400000000",
  notifications: %{
    complete: [
      %{
        type: "https",
        url: "http://localhost:4000/webhook/idverse",
        auth_header_name: "Authorization",
        auth_header_value: "Bearer token123"
      }
    ]
  }
})

Architecture

The library uses a supervised TokenManager GenServer for each configured environment. This provides:

Mock Server

The library includes a built-in mock server for local development and testing. This allows you to:

Enabling the Mock Server

Add to your config files:

# config/config.exs
config :idverse, :mock_server,
  enabled: true,
  embedded: true,
  base_url: "http://localhost:4000/idverse-mock"

Using the Mock Server

Configure your client to use the mock server URLs:

# Configure a dev environment pointing to the mock server
config :idverse,
  environments: [
    mock: [
      base_url: "http://localhost:4000/idverse-mock/v1",
      oauth2_url: "http://localhost:4000/idverse-mock",
      api_key: "any_key_works"
    ]
  ]

# Then create a client
client = Idverse.Client.new(environment: :mock)

Verification Flow UI

When you create a transaction, the returned url points to a mock verification flow. Open this URL in a browser to walk through the verification screens:

  1. Privacy Consent - User confirms they accept the privacy terms
  2. Capture Photo ID - Simulates capturing an ID document
  3. Reviewing ID Data - Brief loading screen
  4. Check ID Details - Displays mock extracted data for confirmation
  5. Face Verification - Simulates the liveness check
  6. Verification Complete - Success screen

Completing this flow automatically marks the transaction as "completed" with realistic verification data.

Programmatic Completion

For automated testing, you can skip the UI and complete transactions programmatically:

# Via helper function
Idverse.MockServer.complete_transaction(transaction_id)

# Via HTTP endpoint
POST http://localhost:4000/mock/simulate/complete/:transaction_id

Completed transactions include realistic mock verification data (documents, liveness checks, facematch scores, etc.).

Mock Server API Endpoints

The mock server implements all IDVerse API endpoints:

Endpoint Description
POST /oauth2/token OAuth2 token endpoint
POST /v1/transactions Create transaction
GET /v1/transactions/:id/json Get transaction JSON
GET /v1/transactions/:id/pdf Get transaction PDF
POST /v1/transactions/resend Resend SMS
POST /v1/transactions/cancel Cancel transaction
POST /v1/transactions/redact Redact transaction data
GET /verify/:id Start verification flow (HTML)
POST /mock/simulate/complete/:id Simulate completion
GET /health Health check

Test Helpers

# Clear all mock state between tests
Idverse.MockServer.clear_all()

# Get configured URLs
Idverse.MockServer.base_url()    # => "http://localhost:4000/v1"
Idverse.MockServer.oauth2_url()  # => "http://localhost:4000"

PostgreSQL Persistence (Optional)

By default, the mock server stores transactions in ETS (in-memory), meaning data is lost when the application restarts. For development scenarios where you need persistent transactions, you can configure the mock server to use your application's PostgreSQL database.

Setup

  1. Add the optional dependencies to your mix.exs:
def deps do
  [
    {:idverse, "~> 0.3.0"},
    {:ecto_sql, "~> 3.10"},
    {:postgrex, "~> 0.17"}
  ]
end
  1. Configure the mock server with your Ecto repo:
# config/dev.exs
config :idverse, :mock_server,
  enabled: true,
  embedded: true,
  base_url: "http://localhost:4000/idverse-mock",
  repo: MyApp.Repo
  1. Create a migration in your application:
mix ecto.gen.migration add_idverse_mock_server
  1. Call the library's migration functions:
# priv/repo/migrations/YYYYMMDDHHMMSS_add_idverse_mock_server.exs
defmodule MyApp.Repo.Migrations.AddIdverseMockServer do
  use Ecto.Migration

  def up, do: Idverse.MockServer.Migrations.up(version: 1)
  def down, do: Idverse.MockServer.Migrations.down(version: 1)
end
  1. Run the migration:
mix ecto.migrate

Database Tables

The migration creates two tables:

Schema Prefix (Multi-tenant)

For multi-tenant setups, you can specify a database schema prefix:

def up, do: Idverse.MockServer.Migrations.up(version: 1, prefix: "tenant_schema")
def down, do: Idverse.MockServer.Migrations.down(version: 1, prefix: "tenant_schema")

Backward Compatibility

Phoenix Integration (Embedded Mode)

When deploying to production, your application typically serves traffic on a single port. The mock server's verification pages (/verify/*) need to be accessible to end users, which is a problem if the mock server runs on a separate port.

Embedded mode solves this by skipping the mock server's built-in HTTP server (Bandit) and letting you mount the router directly in your Phoenix application.

Setup

  1. Enable embedded mode in your config:
# config/dev.exs or config/runtime.exs
config :idverse, :mock_server,
  enabled: true,
  embedded: true,  # Skip starting Bandit
  base_url: "http://localhost:4000/idverse-mock",  # For verification URLs
  repo: MyApp.Repo
  1. Forward routes in your Phoenix router:
# lib/my_app_web/router.ex
defmodule MyAppWeb.Router do
  use MyAppWeb, :router

  # Mount the entire mock server router
  forward "/idverse-mock", Idverse.MockServer.Router

  # ... your other routes
end
  1. Update your client configuration to use Phoenix URLs:
config :idverse,
  environments: [
    mock: [
      base_url: "http://localhost:4000/idverse-mock/v1",
      oauth2_url: "http://localhost:4000/idverse-mock",
      api_key: "any_key_works"
    ]
  ]

Route Mounting Options

You can mount the mock server at any path:

# Mount at root (mock server handles /verify/*, /v1/*, /oauth2/*)
forward "/", Idverse.MockServer.Router

# Mount under a prefix
forward "/idverse-mock", Idverse.MockServer.Router

# Mount in a specific pipeline
scope "/", MyAppWeb do
  pipe_through [:browser]

  forward "/idverse-mock", Idverse.MockServer.Router
end

Production Considerations

When using embedded mode in production:

# config/runtime.exs
config :idverse, :mock_server,
  enabled: true,
  embedded: true,
  base_url: System.get_env("APP_URL") <> "/idverse-mock",
  repo: MyApp.Repo

License

MIT