Skip to content

[Expo] SecureStore fails with "Invalid key" error - storage keys use colons #5426

@ingokpp

Description

@ingokpp

Is this suited for github?

  • Yes, this is suited for github

To Reproduce

The @better-auth/expo plugin generates storage keys with colon separators (e.g., prefix:cookie), but expo-secure-store only accepts alphanumeric characters, ., -, and _. This
causes silent storage failures and session persistence issues.

Current vs. Expected behavior

Better Auth creates storage keys like:
myprefix:cookie
myprefix:session_data

But SecureStore throws:
Error: Invalid key provided to SecureStore.
Keys must not be empty and contain only alphanumeric characters, ".", "-", and "_".

import * as SecureStore from "expo-secure-store";
import { expoClient } from "@better-auth/expo/client";

expoClient({
  storagePrefix: "myapp",
  storage: SecureStore,
});

// After sign-in, Better Auth tries:
// SecureStore.setItemAsync("myapp:cookie", value)
// ❌ Fails: Invalid key error

🎯 Expected Behavior

Storage keys should be compatible with SecureStore's key requirements without requiring custom adapters.

What version of Better Auth are you using?

1.3.28

System info

{
  "system": {
    "platform": "darwin",
    "arch": "arm64",
    "version": "Darwin Kernel Version 25.0.0: Wed Sep 17 21:41:26 PDT 2025; root:xnu-12377.1.9~141/RELEASE_ARM64_T6041",
    "release": "25.0.0",
    "cpuCount": 14,
    "cpuModel": "Apple M4 Pro",
    "totalMemory": "24.00 GB",
    "freeMemory": "0.20 GB"
  },
  "node": {
    "version": "v23.10.0",
    "env": "development"
  },
  "packageManager": {
    "name": "npm",
    "version": "11.4.0"
  },
  "frameworks": [
    {
      "name": "react",
      "version": "19.0.0"
    }
  ],
  "databases": null,
  "betterAuth": {
    "version": "^1.3.28",
    "config": null
  }
}

Which area(s) are affected? (Select all that apply)

Client

Auth config (if applicable)

import { expoClient } from "@better-auth/expo/client";
import { createAuthClient } from "better-auth/react";
import Constants from "expo-constants";
import { secureStoreAdapter } from "./secure-store-adapter";

const BASE_URL = process.env.EXPO_PUBLIC_AUTH_URL || "http://localhost:8081";

const scheme = Constants.expoConfig?.scheme as string;

export const authClient = createAuthClient({
  baseURL: BASE_URL,
  plugins: [
    expoClient({
      scheme: scheme,
      storagePrefix: scheme,
      cookiePrefix: "mycookieprefix", // MUST match backend's cookiePrefix
      storage: secureStoreAdapter, // REQUIRED: sync adapter with colon fix
    }),
  ],
});

Additional context

Replace colons with underscores in storage keys:

  const secureStoreAdapter = {
    getItem: (key: string) => {
      const normalized = key.replace(/:/g, '_');
      return SecureStore.getItemAsync(normalized);
    },
    setItem: (key: string, value: string) => {
      const normalized = key.replace(/:/g, '_');
      return SecureStore.setItemAsync(normalized, value);
    },
    removeItem: (key: string) => {
      const normalized = key.replace(/:/g, '_');
      return SecureStore.deleteItemAsync(normalized);
    },
  };

Metadata

Metadata

Assignees

No one assigned

    Labels

    expoIssues related to Expo JS

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions