Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Expo Android + Better Auth: 403 MISSING_OR_NULL_ORIGIN on sign-up/sign-in#5750

Unanswered
nicholsss asked this question inQ&A
Discussion options

Hello! Im having issues with better auth and expo integration. I keep getting403 MISSING_OR_NULL_ORIGINwhen i try to signIn or signUp.

Im using android simulator on MacOs.

here is my server/lib/auth.ts:

import{betterAuth}from"better-auth";import{drizzleAdapter}from"better-auth/adapters/drizzle";import{db}from"@/db";// your drizzle instanceimport{expo}from"@better-auth/expo";exportconstauth=betterAuth({plugins:[expo()],database:drizzleAdapter(db,{provider:"pg",}),trustedOrigins:["myapp://","http://10.0.2.2:8787","http://localhost:8787"],emailAndPassword:{enabled:true,},});

Here is my server/.env:

...BETTER_AUTH_URL=http://10.0.2.2:8787CORS_ORIGINS=http://localhost:3000,http://127.0.0.1:3000

Here is my client/auth-client.ts:

import{createAuthClient}from'better-auth/react'import{expoClient}from'@better-auth/expo/client'import*asSecureStorefrom'expo-secure-store'import{API_BASE_URL}from'@/src/api/config'exportconstauthClient=createAuthClient({baseURL:"http://10.0.2.2:8787",plugins:[expoClient({scheme:'client',storagePrefix:'client',storage:SecureStore,}),],})

client/config.ts:

import{Platform}from"react-native";constnormalizeBaseUrl=(url:string)=>url.trim().replace(/\/+$/,'');constfallbackBaseUrl=Platform.select({android:"http://10.0.2.2:8787",// your local Hono portios:"http://localhost:8787",default:"http://localhost:8787",})!;constenvBaseUrl=process.env.EXPO_PUBLIC_API_BASE_URL??process.env.API_BASE_URL??fallbackBaseUrl;exportconstAPI_BASE_URL=normalizeBaseUrl(envBaseUrl);console.log("API_BASE_URL:",API_BASE_URL);

When i try to signIn or signUp my server hits me with this error:

[auth] → GET /api/auth/get-session (expo-origin: myapp://)[auth] ← GET /api/auth/get-session 200 (28ms)[auth] → POST /api/auth/sign-in/email (expo-origin: myapp://)[auth] ← POST /api/auth/sign-in/email 403 (36ms)

here is my client error:

 API_BASE_URL:TESTI http://10.0.2.2:8787 LOG  [auth] session update null LOG  [auth] attempting sign-in {"email": "root@root"} {"password": "rootroot"} LOG  [auth] sign-in result {"data": null, "error": {"code": "MISSING_OR_NULL_ORIGIN", "message": "Missing or null Origin", "status": 403, "statusText": ""}} ERROR  [auth] sign-in ERROR {"code": "MISSING_OR_NULL_ORIGIN", "message": "Missing or null Origin", "status": 403, "statusText": ""} Code: index.tsx  125 |       console.log('[auth] sign-in result', result)  126 |       if (result.error) {> 127 |         console.error('[auth] sign-in ERROR', result.error)      |                      ^  128 |         setAuthMessage(result.error.message ?? 'Sign-in failed')  129 |         return  130 |       }Call Stack  handleSignIn (src/app/(tabs)/index.tsx:127:22)
{"expo": {"name":"client","slug":"client","version":"1.0.0","orientation":"portrait","icon":"./assets/images/icon.png","scheme":"client",}}
So could someone with experience help me out. I been struggling to implement the better auth to my expo app.I did:```dev@MacBookAir client % npx expo prebuild```and ```dev@MacBookAir client % npx expo start --dev-client```Then i chose development build and android.
You must be logged in to vote

Replies: 2 comments 6 replies

Comment options

Hi@nicholsss! I'mDosu and I’m helping the better-auth team.

Your 403 MISSING_OR_NULL_ORIGIN error is caused by a mismatch between the scheme your Expo client is using and what your Better Auth server expects for origin validation. The Expo client sends an expo-origin header (and now, thanks to a recent Better Auth update, this is used to populate the Origin header if it's missing), and the server checks if this value matches any entry in your trustedOrigins array. If they don't match exactly, you get a 403 error (source,source).

In your setup, your Expo client is configured with scheme: 'client' (so it sends expo-origin: client://), but your server's trustedOrigins includes "myapp://", not "client://". To fix this, make sure the scheme in all three places matches:

  • In expoClient({ scheme: 'client', ... }) on the client
  • In app.json: "scheme": "client"
  • In your server's trustedOrigins: add "client://"

So, update your server config to:

trustedOrigins:["client://","http://10.0.2.2:8787","http://localhost:8787"],

If you want to quickly unblock yourself in development, you can also add advanced: { disableOriginCheck: true } to your betterAuth config, but only do this in dev since it disables CSRF protection (source).

If you still have issues, log the Origin and expo-origin headers on the server to see what values are actually being sent and matched.

Let me know if this resolves it!

To reply, just mention@dosu.


How did I do?Good |Irrelevant |Incorrect |Verbose |Hallucination |Report 🐛 |Other  Chat with Dosu Join Discord Share on X

You must be logged in to vote
6 replies
@dosubot
Comment options

Your Expo client is sending the correctexpo-origin header, but the Better Auth server expects either a validOrigin header or will mapexpo-origin toOrigin only if your plugin/config is set up for it. Recent Better Auth versions (v1.3.31+) now automatically mapexpo-origin toOrigin unless you explicitly disable it withdisableOriginOverride (source).

To fix your 403 error, make sure you are using the latest versions of bothbetter-auth and@better-auth/expo. Your server config should look like this:

import{betterAuth}from"better-auth";import{drizzleAdapter}from"better-auth/adapters/drizzle";import{db}from"@/db";import{expo}from"@better-auth/expo";exportconstauth=betterAuth({plugins:[expo()// No need for extra options unless you want to disable the override],database:drizzleAdapter(db,{provider:"pg"}),trustedOrigins:["myapp://","http://10.0.2.2:8787","http://localhost:8787"],emailAndPassword:{enabled:true},debug:true,allowDangerousConnections:process.env.NODE_ENV!=='production',});

If you are still seeing the error, check your installed package versions and upgrade bothbetter-auth and@better-auth/expo to the latest release. If you need to quickly unblock in dev, you can addadvanced: { disableOriginCheck: true } to your config, but this disables CSRF protection and should only be used temporarily (source).

Let me know if updating resolves it!

To reply, just mention@dosu.


How did I do?Good |Irrelevant |Incorrect |Verbose |Hallucination |Report 🐛 |Other  Chat with Dosu Join Discord Share on X

@nicholsss
Comment options

@dosu I added and it started to work.

      ...advanced:{disableOriginCheck:true,},

Is this valid option even in production, as react native expo app does not send origin, but ExpoOrigin.

@dosubot
Comment options

No, using advanced: { disableOriginCheck: true } is not safe or recommended in production. This option disables CSRF protection and exposes your app to security risks, so it should only be used in isolated development environments reference reference.

For production, Better Auth (v1.3.31+) now automatically maps expo-origin to Origin unless you explicitly disable it with disableOriginOverride. This means you can keep CSRF protection enabled, and as long as your trustedOrigins includes your app scheme (like "myapp://"), Expo/React Native clients will work securely reference reference.

So: remove disableOriginCheck, make sure you're on the latest better-auth and @better-auth/expo, and keep your trustedOrigins up to date with your app's scheme. This is the secure and recommended setup for production.

To reply, just mention@dosu.


How did I do?Good |Irrelevant |Incorrect |Verbose |Hallucination |Report 🐛 |Other  Chat with Dosu Join Discord Share on X

@rickafds
Comment options

I have the same problem with version Better Auth (v1.3.34)

@tormgibbs
Comment options

#5750 (comment)

Comment options

I had this issue too...after some research. this fixed the issue for me. btw im using hono as my backend

app.use(async (c, next) => {const ExpoOrigin = c.req.header('expo-origin')if (ExpoOrigin) {c.req.raw.headers.set('origin', ExpoOrigin)}await next()})
You must be logged in to vote
0 replies
Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment
Category
Q&A
Labels
None yet
3 participants
@nicholsss@rickafds@tormgibbs

[8]ページ先頭

©2009-2025 Movatter.jp