Ir al contenido
En producción, en modo shadow: FingerTrackID recomienda, su sistema decide.Estado →

Su primera evaluación

En producción · modo shadowAPI v1.1Node.js 24.16+

Su backend consulta a FingerTrackID sobre una acción, aquí un inicio de sesión, y recibe la respuesta en la misma llamada. En modo shadow nada cambia en su producto: usted guarda la respuesta junto a la decisión que su sistema ya toma.

Antes de empezar, necesita:

  • el key_id de una credencial de servidor, y su secreto ya en su gestor de secretos (alta de la empresa);
  • Node.js 24.16 o posterior en la serie 24, o 26.1 o posterior. Otro lenguaje también sirve, mediante las reglas de firma.

Fije la versión exacta. self-check reproduce el vector de firma público en el Node.js que lo ejecuta, para que sepa que la firma funciona antes de tocar una credencial. No necesita ninguna.

El paquete no tiene ninguna dependencia en tiempo de instalación ni script de instalación. Compare el integrity de su lockfile con el del registro.

Terminal
npm install --save-exact @fingertrackid/server@0.1.2
npx --no fingertrackid-server self-check
Compare con su lockfile
npm view @fingertrackid/server@0.1.2 dist.integrity

Cree un solo cliente cuando arranque el backend, leyendo ambos valores por nombre desde el entorno. El secreto proviene de su gestor de secretos: nunca de un archivo del repositorio, de un argumento de comando ni de un asistente de IA.

Un valor ausente o mal formado lanza FtidConfigurationError con un code fijo y nunca reproduce el valor. Emita una alerta y mantenga su producto funcionando sin FingerTrackID.

ftid.ts
import { createFtidServerClient, type FtidServerClient } from '@fingertrackid/server'
export let ftid: FtidServerClient | null = null
try {
ftid = createFtidServerClient({
keyId: process.env.FTID_KEY_ID ?? '',
secretHex: process.env.FTID_HMAC_SECRET_HEX ?? '',
})
} catch (error) {
// Alert on it, and keep the product running without FingerTrackID.
logger.error('ftid.configuration_error', { code: (error as { code?: string }).code })
}

Después de sus propias comprobaciones, llame a assess con la etiqueta que eligió para este momento, un seudónimo con clave de la persona y el token que el Web SDK generó para esta acción.

Mientras el Web SDK no esté en su frontend, envíe null como token: la llamada funciona y la evaluación vuelve sin puntuación.

assess nunca lanza una excepción después del arranque. Cada desenlace es un resultado, y ninguno debería detener su inicio de sesión:

result.kind Qué hace su backend
ASSESSED Guárdelo; su propia política decide
INVALID_INPUT Corrija la llamada; no se envió nada
PROBLEM Siga result.problem.category
UNAVAILABLE Mantenga su decisión; es posible que la solicitud se haya guardado
UNEXPECTED_RESPONSE Mantenga su decisión; informe el correlationId
login.ts
// After your own checks. `ftid` is null after a configuration error: the login goes on.
const result = await ftid?.assess({
tag: 'login',
subjectRef: pseudonymOf(user), // a keyed pseudonym, never an email or tax ID
eventContextToken: body.ftidToken ?? null,
eventContextExpiresAt: body.ftidTokenExpiresAt ?? null,
})
if (result?.kind === 'ASSESSED') {
await storeBesideYourDecision(result.assessment)
} else if (result !== undefined) {
// Log identifiers and verdicts only: never the secret, the token or the body.
logger.warn('ftid.assess', { kind: result.kind, severity: result.severity })
}

Tres campos contienen la respuesta:

  • assessment_status es COMPLETED solo cuando había evidencia reciente del navegador. Sin token, con el perfil de producción gestionado actual, es INCONCLUSIVE.
  • trust_score va de 0 a 100, más alto es mejor, y solo existe en COMPLETED. null significa sin puntuación: nunca 0.
  • recommended_action es una recomendación. Nunca bloquea ni permite nada por sí sola.

Compruebe que external_event_id y tag sean los que envió, y guarde la respuesta tal como la recibió. Todos los campos están en la página de respuesta.

201 · INCONCLUSIVE
{
"schema_version": "event-assessment.v1.1",
"assessment_id": "01996f2a-0000-7000-8000-000000000401",
"assessment_version": 1,
"supersedes_assessment_id": null,
"business_event_id": "01996f2a-0000-7000-8000-000000000402",
"external_event_id": "login_20260904_000002",
"tag": "login",
"assessment_status": "INCONCLUSIVE",
"retryable": false,
"trust_score": null,
"device_intelligence_snapshot_id": null,
"device_intelligence_snapshot_version": null,
"device_id": null,
"device_match_confidence": null,
"evidence_coverage": 0,
"data_freshness": {
"evidence_cutoff_at": "2026-09-04T12:00:00.105Z",
"oldest_required_observed_at": null,
"maximum_required_age_ms": 300000,
"all_required_fresh": false
},
"signals": [
{
"signal_code": "ANTI_FINGERPRINTING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BOT_AUTOMATION",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_PRIVACY_BRAVE",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_PRIVACY_SAFARI",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_PRIVACY_SAMSUNG_INTERNET",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "BROWSER_SPOOFING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "DEVICE_MULTI_USER",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "EMULATED_ENVIRONMENT",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_HOSTING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_PROXY",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_TOR",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "NETWORK_VPN",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
},
{
"signal_code": "PRIVATE_BROWSING",
"channel": "WEB",
"signal_state": "NOT_EVALUATED",
"applicability": "APPLICABLE",
"source": "DERIVED",
"detector_version": "unavailable-web-detectors.v1",
"detector_confidence": null,
"observed_at": null,
"evaluated_at": "2026-09-04T12:00:00.143Z",
"freshness_ms": null,
"not_evaluated_reason_code": "COLLECTOR_NOT_LOADED",
"applicability_reason_code": null
}
],
"limitation_codes": [
"COLLECTOR_NOT_LOADED",
"REQUIRED_EVIDENCE_MISSING"
],
"reason_codes": [
"REQUIRED_EVIDENCE_MISSING"
],
"recommended_action": "MONITOR",
"recommendation_basis": "INSUFFICIENT_EVIDENCE",
"policy_version": "production-detectors-01996f29-4d2c-7b18-9e6a-3f0c8d1b5a72-login",
"fallback_policy_version": "production-observation-fallback-v2",
"ruleset_version": "d95-detectors.v1",
"signal_catalog_version": "web-detectors-v3",
"feature_schema_version": "configured-assessment-features.v1",
"resolver_version": "first-party-installation-v1",
"sdk_version": null,
"provider_versions": [],
"evidence_cutoff_at": "2026-09-04T12:00:00.105Z",
"evaluated_at": "2026-09-04T12:00:00.143Z",
"expires_at": "2026-09-04T12:01:00.143Z",
"correlation_id": "01996f2a-1111-7000-8000-000000000405"
}

check envía una evaluación en modo shadow, con la etiqueta integration_check y sin token del navegador, e imprime el resultado. El secreto proviene solo de FTID_HMAC_SECRET_HEX o de la entrada estándar, nunca de un argumento.

--no hace que npx ejecute solo la copia que instaló su proyecto, nunca un paquete con el mismo nombre del registro.

Terminal
your-secret-manager read ftid-hmac-secret | FTID_KEY_ID=<key_id> npx --no fingertrackid-server check