GuidesComponentsAuth Components

Auth Hooks and Rate Limits

Handle session state and rate limits in custom auth screens

  • Stable unknown
  • Release candidate since 1.16.1-rc.3
  • Development since 1.16.1-dev.2111+9796905

Use the auth hooks and helpers when a custom form needs to share session state or implement the same retry behavior as the built-in auth forms. These hooks require the active client from OvokProvider.

Apply rate limits to a custom form

The SDK's sign-in, registration, and password-reset buttons already disable submission during a supported cooldown. For a custom Formik screen, use setRateLimitFormError in the submit handler and useRateLimitCooldown in the rendering component.

import { useOvok } from "@ovok/core/next/react";
import { useFormik } from "formik";
import {
  setRateLimitFormError,
  useRateLimitCooldown,
} from "@ovok/native";
import * as Yup from "yup";
import { Button, Text, TextInput } from "react-native-paper";

type RecoveryValues = { email: string; submit: string };

export function CustomRecoveryForm() {
  const client = useOvok();
  const formik = useFormik<RecoveryValues>({
    initialValues: { email: "", submit: "" },
    validationSchema: Yup.object({
      email: Yup.string().email("Enter a valid email").required("Enter an email"),
      submit: Yup.string(),
    }),
    onSubmit: async (values, helpers) => {
      try {
        await client.auth.resetPassword({ email: values.email, type: "Patient" });
      } catch (error) {
        const details = setRateLimitFormError(error, helpers);
        if (!details) helpers.setFieldError("submit", "auth.request-failed");
      }
    },
  });

  const rateLimitUntil = (
    formik.status as { rateLimitUntil?: number } | undefined
  )?.rateLimitUntil;
  const remainingMs = useRateLimitCooldown(rateLimitUntil);

  return (
    <>
      <TextInput
        label="Email"
        value={formik.values.email}
        onChangeText={formik.handleChange("email")}
        onBlur={formik.handleBlur("email")}
        error={!!formik.errors.email}
      />
      {formik.errors.submit && <Text>{formik.errors.submit}</Text>}
      <Button
        disabled={formik.isSubmitting || remainingMs > 0}
        loading={formik.isSubmitting}
        onPress={() => void formik.submitForm()}
      >
        {remainingMs > 0
          ? `Try again in ${Math.ceil(remainingMs / 1000)}s`
          : "Continue"}
      </Button>
    </>
  );
}

Localize the button label and error keys for your app. setRateLimitFormError stores an absolute rateLimitUntil timestamp in Formik status, sets the submit error to auth.rate-limit, and returns the parsed details.

Recognized response shapes

getRateLimitDetails(error) recognizes code: "rate_limit", status: 429, and response.status: 429. It returns a message key and retry delay. A positive retryAfterMs from @ovok/core's rate-limit error is used when present; otherwise DEFAULT_RATE_LIMIT_RETRY_MS is 60_000. Unrecognized errors return undefined, so your form should handle them normally.

The built-in sign-in, registration, and password-reset forms use the same server delay for their submit cooldown. A wrong password answers 401 (older servers answered 400); both show the invalid-credentials message. A password the server refuses, such as one shorter than 12 characters or a common one, answers 422 and its message is shown on the password field. Field errors come from @ovok/core's readOvokError, so each one has a path; update @ovok/core to read the field errors of newer servers. Their onError callback receives the parsed SignInError, including retryAfterMs when the server provides it.

useRateLimitCooldown(until) takes the absolute timestamp from Formik status, returns remaining milliseconds, and updates once per second while active. It returns 0 when no cooldown remains.

Session state

useSession exposes the current auth status, profile, sessions, refresh operations, logout, and session revocation. It does not protect routes or choose where the app navigates. It also exposes switching during account changes and reason: "session-ended" when the active login ends unexpectedly. See Session Management for its lifecycle, error behavior, and backend requirements.

Last updated on

On this page