ping-identity
    Preparing search index...

    Ping Identity

    Ping Identity React Native OATH

    Native-backed OATH TOTP and HOTP one-time password management for React Native.

    OATH (Open Authentication) is the standard behind time-based (TOTP) and counter-based (HOTP) one-time passwords. This package provides a handle-based client for managing OATH credentials — including registration, storage, lifecycle, and code generation — on both iOS and Android.

    Key characteristics:

    • Credentials are stored in the native OATH store — the shared secret never crosses the bridge.
    • Each call to createOathClient creates an independent native session; multiple clients can coexist.
    • The client must be closed with close() when it is no longer needed to release native resources.

    Note: This module requires that the @ping-identity/rn-core module is already set up and installed.

    # Install & setup the core module
    yarn add @ping-identity/rn-core
    # Install the rn-oath module
    yarn add @ping-identity/rn-oath
    # If you are developing your app using iOS, run this command
    cd ios && pod install

    Optional integrations:

    yarn add @ping-identity/rn-logger   # JS/native log channel
    yarn add @ping-identity/rn-storage # custom credential storage backend
    import { createOathClient } from '@ping-identity/rn-oath';

    // 1. Initialise a native OATH session
    const client = await createOathClient();

    try {
    // 2. Register a credential from an otpauth:// URI
    const credential = await client.addCredentialFromUri(
    'otpauth://totp/Example:alice@example.com?secret=JBSWY3DPEHPK3PBZ&issuer=Example',
    );

    // 3. Generate an OTP code
    const code = await client.generateCode(credential.id);
    console.log('OTP:', code);

    // 4. Generate a code with timing metadata (TOTP only)
    const info = await client.generateCodeWithValidity(credential.id);
    console.log(`OTP: ${info.code} — valid for ${info.timeRemaining}s`);
    } finally {
    // 5. Release native resources when done
    await client.close();
    }
    import { createOathClient } from '@ping-identity/rn-oath';
    import { logger } from '@ping-identity/rn-logger';

    const log = logger({ level: 'debug' });

    const client = await createOathClient({ logger: log });

    try {
    const credentials = await client.getCredentials();
    console.log('Stored credentials:', credentials.length);
    } finally {
    await client.close();
    }

    By default the native SDK enforces both biometricAvailable and deviceTampering policies. Use configureOathPolicyEvaluator to customise which policies are enforced.

    import {
    createOathClient,
    configureOathPolicyEvaluator,
    } from '@ping-identity/rn-oath';

    const evaluator = configureOathPolicyEvaluator({
    policies: [{ kind: 'biometricAvailable' }, { kind: 'deviceTampering' }],
    });

    const client = await createOathClient({ policyEvaluator: evaluator });

    try {
    const credentials = await client.getCredentials();
    console.log('Stored credentials:', credentials.length);
    } finally {
    await client.close();
    }
    import {
    createOathClient,
    configureOathPolicyEvaluator,
    } from '@ping-identity/rn-oath';
    import { logger } from '@ping-identity/rn-logger';
    import { configureOathStorage } from '@ping-identity/rn-storage';

    const log = logger({ level: 'debug' });

    const storage = configureOathStorage({
    android: { fileName: 'oath', keyAlias: 'oath', strongBoxPreferred: true },
    ios: { account: 'com.example.oath', encryptor: true },
    });

    const evaluator = configureOathPolicyEvaluator({
    policies: [{ kind: 'biometricAvailable' }],
    });

    const client = await createOathClient({
    logger: log,
    timeout: 30, // seconds; omit to use the native default (15 s)
    enableCredentialCache: true,
    encryptionEnabled: true, // iOS-only; silently ignored on Android
    storage,
    policyEvaluator: evaluator,
    });

    try {
    const credentials = await client.getCredentials();
    console.log('Stored credentials:', credentials.length);
    } finally {
    await client.close();
    }

    addCredentialFromUri accepts two URI schemes, both parsed natively by the underlying SDK:

    Scheme Type Example
    otpauth://totp/ TOTP (time-based) otpauth://totp/Issuer:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Issuer&algorithm=SHA1&digits=6&period=30
    otpauth://hotp/ HOTP (counter-based) otpauth://hotp/Issuer:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Issuer&algorithm=SHA1&digits=6&counter=0
    mfauth://totp/ TOTP (Ping alias) mfauth://totp/Issuer:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Issuer&algorithm=SHA1&digits=6&period=30
    mfauth://hotp/ HOTP (Ping alias) mfauth://hotp/Issuer:user@example.com?secret=JBSWY3DPEHPK3PXP&issuer=Issuer&algorithm=SHA1&digits=6&counter=0

    All schemes carry OATH parameters directly as query components. The mfauth:// scheme is a Ping-specific alias for otpauth:// and supports the same totp and hotp types.

    import {
    createOathClient,
    configureOathPolicyEvaluator,
    OathError,
    } from '@ping-identity/rn-oath';
    import type {
    OathClient,
    OathClientConfig,
    OathCodeInfo,
    OathCredential,
    OathErrorCode,
    OathMfaPolicy,
    OathPolicyEvaluatorConfig,
    OathPolicyEvaluatorHandle,
    OathStorageHandle,
    } from '@ping-identity/rn-oath';

    function createOathClient(config?: OathClientConfig): Promise<OathClient>;

    function configureOathPolicyEvaluator(
    config: OathPolicyEvaluatorConfig,
    ): OathPolicyEvaluatorHandle;

    interface OathClientConfig {
    logger?: LoggerInstance; // optional; must be from @ping-identity/rn-logger
    timeout?: number; // seconds; delegates to native SDK default (15 s) when omitted
    enableCredentialCache?: boolean; // default: false on both platforms
    encryptionEnabled?: boolean; // iOS-only; delegates to native default (true) when omitted; ignored on Android
    storage?: OathStorageHandle; // optional; handle from configureOathStorage() in @ping-identity/rn-storage
    policyEvaluator?: OathPolicyEvaluatorHandle; // optional; handle from configureOathPolicyEvaluator()
    }

    interface OathPolicyEvaluatorConfig {
    policies: OathMfaPolicy[]; // non-empty; valid kinds: 'biometricAvailable' | 'deviceTampering'
    loggerId?: string; // optional native logger id; inherits from OathClientConfig.logger when omitted
    }

    type OathMfaPolicy =
    | { kind: 'biometricAvailable' } // fails when no biometric hardware or no enrolled credentials
    | { kind: 'deviceTampering' }; // fails when root/jailbreak score meets the server-configured threshold

    interface OathClient {
    addCredentialFromUri(uri: string): Promise<OathCredential>;
    getCredential(credentialId: string): Promise<OathCredential | null>;
    getCredentials(): Promise<OathCredential[]>;
    saveCredential(credential: OathCredential): Promise<OathCredential>;
    deleteCredential(credentialId: string): Promise<boolean>;
    generateCode(credentialId: string): Promise<string>;
    generateCodeWithValidity(credentialId: string): Promise<OathCodeInfo>;
    close(): Promise<void>;
    }

    interface OathCodeInfo {
    code: string;
    timeRemaining: number; // seconds remaining in TOTP window; -1 for HOTP
    counter: number; // HOTP counter after generation; -1 for TOTP
    progress: number; // fraction of TOTP period elapsed (0.0–1.0); 0.0 for HOTP
    totalPeriod: number; // TOTP period in seconds; 0 for HOTP
    }

    All rejected promises throw an OathError instance, which extends PingError extends Error. Use instanceof to narrow the type:

    import { OathError } from '@ping-identity/rn-oath';

    try {
    const client = await createOathClient();
    const code = await client.generateCode('my-credential-id');
    } catch (err) {
    if (err instanceof OathError) {
    console.log(err.code, err.type, err.message);
    }
    }
    Code Platform Description
    OATH_INVALID_URI Both The provided otpauth:// URI could not be parsed.
    OATH_INVALID_PARAMETER Both A method argument has an invalid value.
    OATH_MISSING_PARAMETER iOS only A required method argument was not provided.
    OATH_CREDENTIAL_NOT_FOUND Both No credential with the given ID exists in the native store.
    OATH_CREDENTIAL_LOCKED Both The credential is locked by a device policy; code generation is not allowed.
    OATH_DUPLICATE_CREDENTIAL Both A credential with the same ID already exists in the native store.
    OATH_CODE_GENERATION_FAILED Both The native SDK could not generate a code for this credential.
    OATH_POLICY_VIOLATION Both The operation was blocked by a platform security policy.
    OATH_INITIALIZATION_FAILED Both The native OATH session could not be created during createOathClient.
    OATH_CLEANUP_FAILED iOS only Native cleanup failed internally. Not thrown from close() — iOS always resolves close() regardless of cleanup outcome.
    OATH_STORAGE_FAILURE Both The native credential store encountered an unspecified I/O error.
    OATH_STORAGE_CORRUPTED iOS only Stored credential data is corrupted and cannot be read.
    OATH_STORAGE_ACCESS_DENIED iOS only The app does not have permission to access the native credential store.
    OATH_STATE_ERROR Both A method was called after close(), or the client is in an unexpected internal state.
    OATH_UNKNOWN_ERROR Both An unexpected error occurred that does not map to a specific code.

    © Copyright 2025-2026 Ping Identity Corporation. All Rights Reserved