[expo] useLocalCredentials throws "Invalid key provided to SecureStore" during render when the publishable key has base64 padding (=)
Maintainer antworten meist innerhalb von 1 Tag
Bewertung
- Schwierigkeit
- 2/5
- Geschätzter Aufwand
- 1-3 Stunden
- Anfängerfreundlichkeit
- 88/100
- Issue-Typ
- Bug
- Klarheit
- Klar beschrieben
- Aktivitätsstatus
- Aktiv
- Tech-Stack
- react-native, typescript
- Bereich
- authentication, mobile
Rechercherichtung
Beginne in packages/expo/src/local-credentials/useLocalCredentials/useLocalCredentials.ts und untersuche, wie der publishable key verwendet wird, um SecureStore-Schlüssel zusammenzusetzen und den Hook zu initialisieren. Reproduziere das Problem mit dem gepaddeten Schlüssel aus dem Issue unter Android oder iOS und verifiziere anschließend, dass das Rendern keinen Fehler mehr auslöst, während vorhandene ungepaddete Schlüssel weiterhin verwendet werden können.
Vom Indexierungsmodell aus dem Issue-Text verfasst.
Beschreibung
Preliminary Checks
- I have reviewed the documentation: https://clerk.com/docs
- I have searched for existing issues: https://github.com/clerk/javascript/issues
- I have not already reached out to Clerk support via email or Discord
- This issue is not a question, general help request, or anything other than a bug report directly related to Clerk
Reproduction
No repo link; the bug is two lines in useLocalCredentials and the snippet below reproduces it on any Android or iOS build.
// ClerkProvider publishableKey = "pk_test_dXNlZnVsLWdhdG9yLTU1LmNsZXJrLmFjY291bnRzLmRldiQ="
// (base64 of "useful-gator-55.clerk.accounts.dev$" WITH its trailing "=" padding;
// parsePublishableKey() accepts it and the rest of the SDK works normally)
import { useLocalCredentials } from "@clerk/expo/local-credentials";
export default function SignIn() {
useLocalCredentials(); // throws during render, see below
return null;
}
The error is thrown by expo-secure-store, inside render, and reaches the nearest error boundary:
Error: Invalid key provided to SecureStore. Keys must not be empty and contain only
alphanumeric characters, ".", "-", and "_".
at SignInScreen
...
isComponentError: true
Publishable key
pk_test_dXNlZnVsLWdhdG9yLTU1LmNsZXJrLmFjY291bnRzLmRldiQ=
Description
useLocalCredentials composes its two secure-store keys from the raw publishable key and reads the first one with the synchronous getItem as a useState initialiser (packages/expo/src/local-credentials/useLocalCredentials/useLocalCredentials.ts):
const key = `__clerk_local_auth_${publishableKey}_identifier`;
const pkey = `__clerk_local_auth_${publishableKey}_password`;
const [hasLocalAuthCredentials, setHasLocalAuthCredentials] = useState(!!getItem(key));
expo-secure-store validates every key against /^[\w.-]+$/ and throws otherwise. A publishable key is pk_*_ + base64 of <frontend host>$, and the base64 of that string carries = padding whenever len(host) + 1 is not a multiple of 3. parsePublishableKey decodes a padded key fine (atob tolerates either form), so the provider, useSignIn, SSO and everything else work with such a key; only this hook crashes, and it crashes the whole screen that renders it rather than the biometric feature alone.
In our case the padded key was produced by base64-encoding the frontend host ourselves for a release build against a dev instance (the keys the dashboard hands out appear to be emitted without padding, which is presumably why this is rarely hit). But since the SDK accepts padded keys everywhere else, this hook should too, and the failure mode (a render-time throw from a useState initialiser) is disproportionate.
Related: #9870 fixed the same class of problem for the offline resource cache by keying on the full publishable key "with trailing = characters removed". The same treatment here fixes it:
const storeKeyBase = publishableKey.replace(/[^\w.-]/g, "");
const key = `__clerk_local_auth_${storeKeyBase}_identifier`;
const pkey = `__clerk_local_auth_${storeKeyBase}_password`;
Keys without padding are unchanged by the replace, so existing stored credentials keep their key. We are shipping exactly this as a pnpm patch on @clerk/[email protected]; I checked 4.8.0 and it still composes the raw key.
Environment
@clerk/expo 4.2.7 (also inspected 4.8.0 dist)
@clerk/shared 4.28.1
expo 57.0.24
expo-secure-store 57.0.4
expo-local-authentication 57.0.3
react-native 0.86.3
react 19.2.3
node 24.10.0
Device: Pixel, Android 17, release (non dev-client) EAS build
- Vorherrschende Sprache
- TypeScript
- Sterne
- 1.8k
- Forks
- 479
- Ø Merge
- 1 T. 18 Std.
- Gemergte PRs (30 T.)
- 313
Entwicklungsumgebung
- Kein Dockerfile und keine Docker-Compose-Datei
- Hat eine Pull-Request-Vorlage
- Beitragsleitfaden lesen
Erste Schritte
- Lesen Sie das ganze Issue und danach den Beitragsleitfaden des Projekts.
- Schreiben Sie ins Issue, dass Sie es übernehmen — das erspart doppelte Arbeit.
- Forken Sie das Repository und arbeiten Sie in einem Branch.
- Öffnen Sie einen Pull Request, der die Issue-Nummer nennt.
Mehr aus clerk/javascript
-
signIn.sso() ignores oidcPrompt for OAuth strategiesEvtl. vergeben @wobsoriano hat das vor 5 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
clerk/javascript#10119 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
clerk/javascript#10026 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
@clerk/nextjs: onBeforeSetActive never settles when invalidateCacheAction rejects (e.g. after a redeploy), so setActive and signOut hang foreverEvtl. vergeben @RaphaelFakhri hat das vor 7 Tagen übernommen. Offen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 78/100
clerk/javascript#9987 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 4/5 3-5 Tage Anfängerfreundlichkeit 25/100
clerk/javascript#10185 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 4/5 1-2 Tage Anfängerfreundlichkeit 45/100
clerk/javascript#10118 ·
Maintainer antworten meist innerhalb von 1 Tag
Alle Issues in clerk/javascript
Ähnliche Issues
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 70/100
NaturalIntelligence/fast-xml-parser#890 · 1 Kommentar ·
Maintainer antworten meist innerhalb von 3 Tagen
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 62/100
zsviczian/obsidian-excalidraw-plugin#3000 ·
Maintainer antworten meist innerhalb von 1 Tag
-
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 68/100
pnpm/get.pnpm.io#68 ·
-
[BUG] Cloudflare KV binding 模式下 kvDriver.list() 沿用 EdgeOne 语义,字段名不匹配导致 key 格式下初始化 500 并耗尽 list 配额Evtl. vergeben @wumingzhinu hat das heute übernommen. Offenbug
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 64/100
OpenListTeam/OpenList-Worker#125 ·
Maintainer antworten meist innerhalb von 2 Tagen
-
Android (vision-camera-location): HybridLocationManager never quits its HandlerThread on disposeOffen
Schwierigkeit 2/5 1-3 Stunden Anfängerfreundlichkeit 74/100
margelo/react-native-vision-camera#4217 ·
Maintainer antworten meist innerhalb von 4 Tagen