TryMellon
Navigation

Security

WebAuthn security model, infrastructure, compliance status, CSP, SRI, and best practices.

Security

This page covers the WebAuthn security model, TryMellon’s infrastructure and compliance status, and what you need to configure on your side.


WebAuthn security model

  • No shared secrets: Passkeys use public-key cryptography. The private key stays on the user’s device; the server stores only the public key.
  • Phishing-resistant: Credentials are bound to the origin (your domain). A fake site cannot use them.
  • No password reuse: There is no password to steal or reuse.
  • No PII required: TryMellon does not require names, emails, or phone numbers. Your external_user_id is whatever opaque identifier you provide.

The TryMellon API and SDK follow the WebAuthn Level 2 spec. The SDK performs no custom cryptography — it delegates all signing and verification to the browser’s built-in WebAuthn API.


Infrastructure

ComponentProviderDetails
API (api.trymellonauth.com)Railway — Railway US EastCloudflare proxy (DDoS protection, TLS termination)
Landing / DashboardCloudflare Pages — global CDNStatic assets, no server-side auth logic
DatabaseRailway managed PostgresRailway US East
Cache / sessionsRailway managed RedisRailway US East

Data in transit: TLS 1.2+ enforced by Cloudflare on all connections to api.trymellonauth.com.

Data at rest: Credentials (public keys only), session tokens, and audit logs are stored in managed Postgres in the Railway US East region. No private keys are ever stored — they never leave the user’s device.

One-time verification codes and invitation tokens are hashed before persistence. Plaintext material exists only in the outbound email or API response at issuance and is not recoverable from a dump of Redis or Postgres:

  • Email OTPs — used for account recovery, email fallback login, and identity linking. Stored in Redis as HMAC-SHA256 digests, keyed with a server-side pepper that never leaves the API environment. The pepper is versioned to allow zero-downtime rotation.
  • B2B invitation tokens — stored in Postgres as SHA-256 hashes. The plaintext token is returned exactly once, in the accept_link of the invitation response and in the outbound email. Accepting or resending an invitation hashes the incoming token before any database lookup.

Both constructions follow NIST SP 800-63B §5.1.1.2 (keyed hash function with the key stored separately).

Data residency note

All processing and storage occurs in the Railway US East region. If your application is subject to data residency requirements (e.g. EU GDPR, Argentine PDPA, Brazilian LGPD), evaluate whether US-based storage is compliant for your use case. Contact us if you need an EU-region deployment.


Defense-in-depth controls

These are the verifiable controls referenced on /security. Each one has a concrete implementation in the TryMellon API.

Authenticator cloning detection (sign counter)

Every successful WebAuthn authentication increments a hardware-backed sign counter. A regression in that counter against the stored value fails the ceremony with HTTP 401 replay_detected, writes a credential.replay_detected audit row, and emits a Sentry warning for on-call review. The credential is never silently accepted — even during a regression.

Brute-force lockout

Failed authentication attempts are counted in Redis on a sliding 15-minute window, scoped per (tenantId, externalUserId):

  • 5 failures in the window → 30-minute soft lock (auth_lockout:* key).
  • 10 failures in the window → 24-hour hard lock.
  • A successful authentication clears the failure counter on a best-effort basis; a Redis outage leaves the counter intact rather than silently allowing unbounded retries.

Thresholds and TTLs are implemented in the back’s lockout adapter. Audit trail for lockouts is surfaced via the Admin API — see Audit Logs.

HMAC-SHA256 signed webhooks

All webhook deliveries are signed with HMAC-SHA256(secret, timestamp + '.' + body) and verified server-side with timingSafeEqual against a strict ^[0-9a-f]{64}$ hex signature. Timestamps older than 5 minutes are rejected to guard against replay. The SDK ships a drop-in verifier in @trymellon/js — see Webhook signature verification.

Memory-hard secret hashing

Application client_secret values are hashed with Argon2id (memoryCost = 19 456 KiB ≈ 19 MiB, timeCost = 2, parallelism = 1). Verification uses a constant-time comparator. Email OTPs and B2B invitation tokens use keyed HMAC-SHA256 / SHA-256 with a server-side pepper that is versioned for zero-downtime rotation; see the “Data at rest” table above.

Error monitoring

Sentry receives structured events for security-relevant anomalies (sign-counter regressions, attestation failures, lockouts transitioning to hard state). PII transmission to Sentry is disabled by configuration — payloads are scrubbed before send.


Compliance status

TryMellon is currently in Beta. We are transparent about the current compliance posture:

StandardStatus
SOC 2 Type IINot yet audited — on roadmap for GA
ISO 27001Not yet certified
Pen test (public)Not yet published
GDPR (data processor)Data minimization by design; DPA available on request
CCPANo sale of personal data

What we do have:

  • WebAuthn Level 2 implementation (phishing-resistant by design)
  • Cloudflare DDoS and WAF protection on the API
  • Encrypted connections (TLS) on all endpoints
  • WORM-style audit logs with a 90-day default retention, configurable per tenant via auditLogRetentionDays (see Admin API). Mutations are restricted to GDPR subject scrubs and scheduled retention purges.
  • GDPR data export and deletion endpoints (see Privacy / GDPR)
  • No PII stored beyond your external_user_id unless you explicitly pass metadata. Exception: POST /v1/users/recovery/start accepts an email address for OTP delivery — it is used ephemerally and not persisted.

Responsible disclosure: Found a security issue? Email security@trymellonauth.com before public disclosure. We aim to respond within 48 hours.

If your application operates in a regulated sector (fintech, health, B2B enterprise) that requires SOC 2 or ISO 27001, contact us before going to production. We can provide our current security documentation and timeline to certification.


Attestation Policy Engine

TryMellon enforces hardware-level authenticator trust via the FIDO Metadata Service (MDS). You can require that passkeys come from certified hardware — useful for high-security B2B tenants that cannot accept software authenticators.

Configure a policy per tenant via the Admin API:

POST /v1/attestation/policy
Authorization: Bearer <api_key>

{
  "enforcement_mode": "block",       // "block" | "warn" | "off"
  "minimum_certification_level": "L1",  // "L1" | "L1plus" | "L2" | "L3"
  "allowed_aaguids": []              // optional allowlist — empty means all certified devices
}

Enforcement modes:

ModeBehavior
offAll authenticators accepted (default)
warnNon-certified authenticators allowed but flagged in audit logs
blockNon-certified authenticators are rejected at enrollment

Certification levels follow the FIDO Authenticator Certification tiers. L1 covers software and most platform authenticators; L2+ requires hardware security keys with physical isolation.

Attestation policy applies at enrollment time. Existing credentials registered before a policy is enabled are not retroactively revoked.


Device-Bound Session Credentials (DBSC)

DBSC binds a session to the device’s Trusted Platform Module (TPM), preventing session token theft by malware that exfiltrates cookies or bearer tokens to another device.

When DBSC is active for a session:

  1. The browser generates an ephemeral EC key pair inside the TPM during passkey authentication.
  2. The public key is sent to TryMellon and stored alongside the session.
  3. TryMellon issues a short-lived proof token (5–15 min). The browser refreshes it in the background using the TPM-bound private key — without any user interaction.
  4. If the session token is replayed from a different device (no TPM access), the proof token cannot be refreshed and the session is terminated.

Current support: Chrome on Windows 11 (TPM 2.0). TryMellon degrades gracefully on unsupported platforms — sessions continue without TPM binding; only the device-binding layer is absent.

DBSC requires no SDK changes — it is negotiated server-side via the Sec-Session-* headers defined in the W3C DBSC draft spec.


Passkey migration

Passkeys are device-bound by design (WebAuthn spec). The private key never leaves the user’s device, which means:

  • TryMellon stores public keys only — we cannot export private keys
  • If you migrate to another WebAuthn provider, users must re-enroll their passkeys

What migration looks like in practice:

  1. Deploy your new provider alongside TryMellon
  2. On next login, prompt users to register a new passkey with the new provider
  3. Because TryMellon is additive (it does not replace your session layer), the transition is a frontend change in how you call the WebAuthn ceremony

The TryMellon SDK is thin by design — switching to another provider means swapping the SDK, not rearchitecting your auth system.


CSP (Content Security Policy)

If you use a strict CSP, allow the following:

  • script-src: The domain serving the TryMellon SDK (your bundle origin or CDN).
  • connect-src: https://api.trymellonauth.com for SDK API calls.

Example:

Content-Security-Policy: script-src 'self'; connect-src 'self' https://api.trymellonauth.com;

SRI (Subresource Integrity)

If you load the SDK from a CDN, use SRI to ensure the script has not been tampered with. Pin a specific version and include the integrity hash:

<script
  src="https://cdn.jsdelivr.net/npm/@trymellon/js@4.0.1/dist/sdk.min.js"
  integrity="sha384-..."
  crossorigin="anonymous"
></script>

What the SDK does not do

  • No custom crypto: The SDK uses the browser’s native WebAuthn API exclusively.
  • No secret storage: The publishableKey is safe for the browser. Never put your client_secret in frontend code.
  • No PII in logs: The SDK does not log session tokens or credential IDs.

Best practices

  1. Validate the session token on your backend before granting access — see Backend validation.
  2. Use HTTPS in production (required for WebAuthn).
  3. Store your own session in an httpOnly, Secure, SameSite=Strict cookie.
  4. Revoke passkeys when a user reports a lost device — see Admin API → Revoke credential.
  5. Use audit logs to detect anomalous auth patterns — see Admin API → Audit Logs.
  6. Pin the SDK version and use SRI when loading from a CDN.