Skip to content

KOKOSde/identity-risk-engine

Repository files navigation

identity-risk-engine

Add suspicious-login detection, auth-risk scoring, and step-up decisions to your app.

MIT License Python 3.9+ Tests Passing pip installable

Problem

Fintech and crypto teams must decide risk at authentication time, before transaction monitoring can help. Most teams rebuild this stack internally, and existing OSS tools usually cover only one slice (device, geo, or behavior). identity-risk-engine packages multi-signal auth risk scoring, policy decisions, and synthetic benchmarking into one local-first toolkit.

πŸ“Š Case Study: Protecting a Crypto Airdrop

For some use cases examples see the full analysis with interactive visualizations:
β†’ Case Study Notebook Authentication Funnel Sankey: Login -> Risk -> Outcome

Covers: attack surface exploration, good-actor vs bad-actor classification (AUROC 0.99), A/B threshold experiment, policy engine demo, and a production metrics framework β€” all with real data from this toolkit.

Architecture

Auth Events
  |
  v
+---------------------------+
| Signal Extraction         |
| device + geo + behavior   |
| passkey + recovery        |
+---------------------------+
  |
  v
+---------------------------+
| Risk Scoring              |
| signal fusion + ensemble  |
+---------------------------+
  |
  v
+---------------------------+
| Policy Engine             |
| tenant/method thresholds  |
+---------------------------+
  |
  v
Action: allow | step-up | review | block | revoke

Install

Requires Python 3.9+ β€” check with python3 --version

git clone https://github.com/KOKOSde/identity-risk-engine.git
cd identity-risk-engine
python3 -m pip install -e .

Quickstart

from identity_risk_engine.policy_engine import PolicyEngine
from identity_risk_engine.risk_engine_ire import score_event
from identity_risk_engine.simulator_ire import generate_synthetic_auth_events

events = generate_synthetic_auth_events(num_users=20, num_sessions=200, attack_ratio=0.2, seed=42)
result = score_event(event=events.iloc[40].to_dict(), history_df=events.iloc[:40], policy_engine=PolicyEngine())
print(round(result["risk_score"], 4), result["decision"]["action"])
print(result["explanation"]["human_summary"])

PolicyEngine uses decide() (not evaluate()), and explanations are available via result["explanation"] from risk_engine_ire.score_event(...) or explainer_ire.explain_scored_event(...).

CLI Quickstart

python3 -m identity_risk_engine.cli_ire simulate --users 500 --sessions 20000 --attack-ratio 0.2 --out synthetic.csv
python3 -m identity_risk_engine.cli_ire score --events synthetic.csv --policy configs/default_policy.yaml --out scored.csv
python3 -m identity_risk_engine.cli_ire report --events scored.csv --out report.html

simulate example output:

Generated 37926 events -> /tmp/ire_verify.csv
Attack mix:
  account_takeover: 159
  bot_behavior: 1014
  credential_stuffing: 1705
  impossible_travel: 318
  mfa_fatigue: 1496
  multi_account_sybil: 1155
  new_account_fraud: 188
  normal: 29587
  passkey_registration_abuse: 760
  recovery_abuse: 1208
  session_hijack: 336

score example output:

Auto-selecting fast mode for 37926 events (use --full for complete signal extraction)
Scored 37926 events -> /tmp/ire_verify_scored.csv
Scoring mode: fast-auto (history_window=8)
Elapsed seconds: 27.90
Mean risk score: 0.1016
Action counts:
  allow: 24285
  allow_with_monitoring: 8678
  step_up_with_passkey: 2044
  step_up_with_totp: 1623
  require_recovery_review: 411
  block: 318

report example output:

Report summary:
  total_events: 37926
  avg_risk_score: 0.101567
  p95_risk_score: 0.44
  positive_rate: 0.219876
Top actions:
  allow: 24285
  allow_with_monitoring: 8678
  step_up_with_passkey: 2044
  step_up_with_totp: 1623
  require_recovery_review: 411
  block: 318
Report written -> /tmp/ire_verify_report.html

FastAPI Quickstart

uvicorn examples.fastapi_demo.app_ire:app --reload
curl -s http://127.0.0.1:8000/health

POST /events request schema:

{
  "dry_run": false,
  "event": {
    "event_id": "evt_demo_001",
    "event_type": "login_success",
    "user_id": "user_demo_01",
    "session_id": "sess_demo_01",
    "timestamp": "2026-03-22T12:30:00Z",
    "ip": "34.23.11.9",
    "country": "US",
    "city_coarse": "San Francisco",
    "lat_coarse": 37.77,
    "lon_coarse": -122.42,
    "user_agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) Chrome/122.0",
    "device_hash": "dev_demo_01",
    "device_type": "desktop",
    "browser": "Chrome",
    "os": "Windows",
    "auth_method": "password",
    "success": true,
    "tenant_id": "tenant_1",
    "metadata": {"ip_asn": "AS12345"}
  }
}

Example curl:

curl -s -X POST http://127.0.0.1:8000/events \
  -H "Content-Type: application/json" \
  -d '{"dry_run":false,"event":{"event_id":"evt_demo_001","event_type":"login_success","user_id":"user_demo_01","session_id":"sess_demo_01","timestamp":"2026-03-22T12:30:00Z","ip":"34.23.11.9","country":"US","city_coarse":"San Francisco","lat_coarse":37.77,"lon_coarse":-122.42,"user_agent":"Mozilla/5.0","device_hash":"dev_demo_01","device_type":"desktop","browser":"Chrome","os":"Windows","auth_method":"password","success":true,"tenant_id":"tenant_1","metadata":{"ip_asn":"AS12345"}}}'

Supported Auth Flows

  • Password login (attempt/success/failure)
  • Passkey registration and authentication
  • MFA challenge flows (sent/passed/failed)
  • Password reset and account recovery
  • Session creation/revocation
  • Credential profile changes (email_changed, phone_changed)
  • OAuth/magic-link style flows normalized through the same event schema

Risk Signals

Impossible travel uses 5 layered signals: pairwise geo-velocity against recent login history, country rarity (user-level and population-level), device-location mismatch scoring, and geo-session continuity analysis β€” matching the approach used by enterprise identity platforms.

Category Signals
Device new_device, device_dormant, multi_account_device, device_velocity, session_churn, emulator_heuristic
Geo/Network impossible_travel, impossible_travel_composite, geo_velocity, new_country, new_country_for_user, rare_country_global, device_location_mismatch, geo_session_break, new_asn, tor_vpn_proxy, ip_velocity, residential_vs_datacenter
Behavior failure_burst, success_after_burst, unusual_hour, auth_method_switch, mfa_fatigue, recovery_abuse, login_cadence_anomaly, account_fanout, new_account_high_value, metadata_attack_hints
Passkey new_passkey_unfamiliar_device, passkey_registration_burst, passkey_after_password_failure, authenticator_churn, credential_novelty
Recovery recovery_unfamiliar_env, recovery_after_lockout, recovery_plus_credential_change, recovery_fanout, recovery_impossible_travel

Policy Engine

Default config is at configs/default_policy.yaml.

dry_run: false
thresholds:
  - { max_score: 0.15, action: allow }
  - { max_score: 0.30, action: allow_with_monitoring }
  - { max_score: 0.45, action: step_up_with_passkey }
  - { max_score: 0.60, action: step_up_with_totp }
  - { max_score: 0.72, action: step_up_with_email_code }
  - { max_score: 0.82, action: require_recovery_review }
  - { max_score: 0.90, action: manual_review }
  - { max_score: 0.97, action: block }
  - { max_score: 1.00, action: revoke_session }

Supported actions: allow, allow_with_monitoring, step_up_with_passkey, step_up_with_totp, step_up_with_email_code, require_recovery_review, manual_review, block, revoke_session.

Benchmark Results

Generated from code with fixed seed using:

python3 scripts/generate_benchmark_table_ire.py --num-users 100 --num-sessions 4000 --attack-ratio 0.2 --seed 42

Script: scripts/generate_benchmark_table_ire.py
Outputs: demo_outputs/benchmark_table_ire.md, demo_outputs/benchmark_table_ire.json

Cohort AUC Precision@0.95Recall Recall@0.95Precision
Global 0.993942 0.885479 0.791677
account_takeover 0.880385 0.035433 0.000000
bot_behavior 0.882872 0.090328 0.000000
credential_stuffing 0.880016 0.195859 0.000000
impossible_travel 0.947102 0.037392 0.000000
mfa_fatigue 0.825472 0.179117 0.000000
multi_account_sybil 0.930255 0.123943 0.000000
new_account_fraud 0.926204 0.026531 0.000000
passkey_registration_abuse 0.948031 0.136252 0.000000
recovery_abuse 0.959391 0.297699 0.000000
session_hijack 0.948349 0.077661 0.000000

Scorer Quality Check (Seed 42)

  • AUROC: 0.9907
  • Near-zero attack scores (<0.1): 166/8275 (2.0%)
  • impossible_travel: mean score 1.000, near-zero 0.0%
  • session_hijack: mean score 1.000, near-zero 0.0%
  • passkey_registration_abuse: mean score 0.998, near-zero 0.0%

Who This Is For

  • Crypto exchanges
  • Fintech identity teams
  • Authentication platform builders
  • Fraud analysts
  • Security researchers

Related Projects

Contributing

See CONTRIBUTING.md.

License

MIT. See LICENSE.

About

Open-source session-level identity risk scoring for fintech

Resources

License

Contributing

Stars

Watchers

Forks

Packages

 
 
 

Contributors