Expo and hono.js

In my hono.js
app.get('/api/auth/callback/google', async c => {
try {
return c.json({
status: 'ok',
});
} catch (error) {
console.error('Google callback error:', error);
}
app.get('/api/auth/callback/google', async c => {
try {
return c.json({
status: 'ok',
});
} catch (error) {
console.error('Google callback error:', error);
}
after logging in, the client sends the endpoint to the backend. In the backend what should I do next for this? And after logging in, the user table got updated but the useSession returns null.
61 Replies
KiNFiSH
KiNFiSH•3w ago
Are you trynna use Google oauth ?
Grmayt
GrmaytOP•3w ago
Yes, I am
const handleSocialLogin = async () => {
const { data, error } = await authClient.signIn.social(
{
provider: "google",
disableRedirect: true,
callbackURL: "/(fitness)/(tabs)",
}
);

if (error) {
console.log("šŸš€ ~ handleSocialLogin ~ error:", error);
}

if (data?.url) {
await Linking.openURL(data?.url);
}
};
const handleSocialLogin = async () => {
const { data, error } = await authClient.signIn.social(
{
provider: "google",
disableRedirect: true,
callbackURL: "/(fitness)/(tabs)",
}
);

if (error) {
console.log("šŸš€ ~ handleSocialLogin ~ error:", error);
}

if (data?.url) {
await Linking.openURL(data?.url);
}
};
From my expo app And it made the user in the table but not sure what to do next in the backend And why the session is null in the client even though it's logged in? The doc isn't completely saying what to do 😦 This is when trying to login
app.post('/api/auth/sign-in/social', async c => {
try {
const body = await c.req.json();
console.log('šŸš€ ~ body:', body);

const session = await auth.api.signInSocial({
body: {
provider: body.provider,
},
});
console.log('šŸš€ ~ session:', session);

if (!session) {
return c.json({ error: 'Failed to create session' }, 500);
}

return c.json({
url: session.url,
redirectUrl: session.redirect,
status: 'ok',
});
} catch (error) {
console.error('Social sign-in error:', error);
return c.json({ error: 'Authentication failed' }, 500);
}
});
app.post('/api/auth/sign-in/social', async c => {
try {
const body = await c.req.json();
console.log('šŸš€ ~ body:', body);

const session = await auth.api.signInSocial({
body: {
provider: body.provider,
},
});
console.log('šŸš€ ~ session:', session);

if (!session) {
return c.json({ error: 'Failed to create session' }, 500);
}

return c.json({
url: session.url,
redirectUrl: session.redirect,
status: 'ok',
});
} catch (error) {
console.error('Social sign-in error:', error);
return c.json({ error: 'Authentication failed' }, 500);
}
});
KiNFiSH
KiNFiSH•3w ago
Pls refer to this - https://github.com/LovelessCodes/hono-better-auth and lmk if u had an issue
GitHub
GitHub - LovelessCodes/hono-better-auth: šŸš€ Hono x Better Auth: S...
šŸš€ Hono x Better Auth: Seamlessly integrate powerful authentication with Hono, Better-Auth and Drizzle ORM. Secure, scalable, and built for modern web applications. šŸ›”ļøāœØ - LovelessCodes/hono-better-auth
Grmayt
GrmaytOP•3w ago
Okay, thanks. I will let you know. I hope it works 😦
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { auth, configuredProviders } from './lib/auth';

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
const app = new Hono();

app.on(['GET'], '/api/auth-provider', c => {
return c.json(Object.keys(configuredProviders));
});

app.on(['POST', 'GET'], '/api/auth/**', c => {
return auth.handler(c.req.raw);
});

app.use(
'/api/auth/**', // or replace with "*" to enable cors for all routes
cors({
origin: (origin, _) => {
if (allowedOrigins.includes(origin)) {
return origin;
}
return undefined;
},
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['POST', 'GET', 'OPTIONS'],
exposeHeaders: ['Content-Length'],
maxAge: 600,
credentials: true,
})
);

app.get('/', async c => {
return c.json({
status: 'ok',
});
});

export default {
fetch: app.fetch,
};
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { auth, configuredProviders } from './lib/auth';

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
const app = new Hono();

app.on(['GET'], '/api/auth-provider', c => {
return c.json(Object.keys(configuredProviders));
});

app.on(['POST', 'GET'], '/api/auth/**', c => {
return auth.handler(c.req.raw);
});

app.use(
'/api/auth/**', // or replace with "*" to enable cors for all routes
cors({
origin: (origin, _) => {
if (allowedOrigins.includes(origin)) {
return origin;
}
return undefined;
},
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['POST', 'GET', 'OPTIONS'],
exposeHeaders: ['Content-Length'],
maxAge: 600,
credentials: true,
})
);

app.get('/', async c => {
return c.json({
status: 'ok',
});
});

export default {
fetch: app.fetch,
};
in hono.js and in expo
const handleSocialLogin = async () => {
const { error } = await authClient.signIn.social(
{
provider: "google",
disableRedirect: true,
callbackURL: "/(fitness)/(tabs)",
},
{
onSuccess: () => {
router.replace("/(fitness)/(tabs)");
},
}
);

if (error) {
console.log("šŸš€ ~ handleSocialLogin ~ error:", error);
}
};
const handleSocialLogin = async () => {
const { error } = await authClient.signIn.social(
{
provider: "google",
disableRedirect: true,
callbackURL: "/(fitness)/(tabs)",
},
{
onSuccess: () => {
router.replace("/(fitness)/(tabs)");
},
}
);

if (error) {
console.log("šŸš€ ~ handleSocialLogin ~ error:", error);
}
};
It still returns null for session in the client.. when using this
const { data: isAuthenticated, isPending } = authClient.useSession();
const { data: isAuthenticated, isPending } = authClient.useSession();
Grmayt
GrmaytOP•3w ago
It all makes thses tho
No description
Grmayt
GrmaytOP•3w ago
app.post('/api/auth/sign-in/social', async c => {
try {
const body = await c.req.json();
console.log('šŸš€ ~ body:', body);

const session = await auth.api.signInSocial({
body: {
provider: body.provider,
},
});
console.log('šŸš€ ~ session:', session);

if (!session) {
return c.json({ error: 'Failed to create session' }, 500);
}

return c.json({
url: session.url,
redirectUrl: session.redirect,
status: 'ok',
});
} catch (error) {
console.error('Social sign-in error:', error);
return c.json({ error: 'Authentication failed' }, 500);
}
});

app.get('/api/auth/callback/google', async c => {
return c.json({
status: 'ok',
});
});
app.post('/api/auth/sign-in/social', async c => {
try {
const body = await c.req.json();
console.log('šŸš€ ~ body:', body);

const session = await auth.api.signInSocial({
body: {
provider: body.provider,
},
});
console.log('šŸš€ ~ session:', session);

if (!session) {
return c.json({ error: 'Failed to create session' }, 500);
}

return c.json({
url: session.url,
redirectUrl: session.redirect,
status: 'ok',
});
} catch (error) {
console.error('Social sign-in error:', error);
return c.json({ error: 'Authentication failed' }, 500);
}
});

app.get('/api/auth/callback/google', async c => {
return c.json({
status: 'ok',
});
});
Do I need these two endpoints?
KiNFiSH
KiNFiSH•3w ago
Can you do manually fetching for session endpoint itself No you don't actually need the endpoints
Grmayt
GrmaytOP•3w ago
Could you tell me how to do that? I'm not sure
app.get('/api/auth/get-session', async c => {
const session = await auth.api.getSession({ headers: c.req.raw.headers });
console.log('šŸš€ ~ session:', session);
return c.json({
session,
});
});
app.get('/api/auth/get-session', async c => {
const session = await auth.api.getSession({ headers: c.req.raw.headers });
console.log('šŸš€ ~ session:', session);
return c.json({
session,
});
});
You mean like this?
KiNFiSH
KiNFiSH•3w ago
Wait I'm in the middle of sth one minute
Grmayt
GrmaytOP•3w ago
ok
Grmayt
GrmaytOP•3w ago
I dropped the tables in DB but now it doesn't insert a user
No description
Grmayt
GrmaytOP•3w ago
But without sign-in/social endpoint it returns 404
Grmayt
GrmaytOP•3w ago
No description
Grmayt
GrmaytOP•3w ago
in the client That's why I added that endpoint
KiNFiSH
KiNFiSH•3w ago
you have not tweaked your hono backend like /api/auth ? does that apply to your callbacks ? and make sure to add expo plugin on your backend hono as well and expoClient on your app
Grmayt
GrmaytOP•3w ago
They're added
No description
No description
Grmayt
GrmaytOP•3w ago
already What do you mean by "you have not tweaked your hono backend like /api/auth-provider ? "?
KiNFiSH
KiNFiSH•3w ago
i mean you should not have to handle the callback by yourself which you are defining also and /auth is meant to handle all your callbacks so make sure to adjust it to your need like api/auth ? or sth
Grmayt
GrmaytOP•3w ago
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { auth, configuredProviders } from './lib/auth';

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
const app = new Hono();

// Enable CORS for all routes
app.use(
'*',
cors({
origin: (origin, _) => {
if (allowedOrigins.includes(origin)) {
return origin;
}
return undefined;
},
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['POST', 'GET', 'OPTIONS'],
exposeHeaders: ['Content-Length'],
maxAge: 600,
credentials: true,
})
);

// Get available auth providers
app.get('/api/auth-provider', c => {
return c.json(Object.keys(configuredProviders));
});

// Let the auth library handle all auth routes
app.on(['POST', 'GET'], '/api/auth/**', c => {
return auth.handler(c.req.raw);
});

app.get('/', async c => {
return c.json({
status: 'ok',
});
});

export default {
fetch: app.fetch,
};
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { auth, configuredProviders } from './lib/auth';

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
const app = new Hono();

// Enable CORS for all routes
app.use(
'*',
cors({
origin: (origin, _) => {
if (allowedOrigins.includes(origin)) {
return origin;
}
return undefined;
},
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['POST', 'GET', 'OPTIONS'],
exposeHeaders: ['Content-Length'],
maxAge: 600,
credentials: true,
})
);

// Get available auth providers
app.get('/api/auth-provider', c => {
return c.json(Object.keys(configuredProviders));
});

// Let the auth library handle all auth routes
app.on(['POST', 'GET'], '/api/auth/**', c => {
return auth.handler(c.req.raw);
});

app.get('/', async c => {
return c.json({
status: 'ok',
});
});

export default {
fetch: app.fetch,
};
So after remove the /auth-provider endpoint it doesn't return 404 but after clicking signIn.socical function it doesn't do anything
Grmayt
GrmaytOP•3w ago
No description
Grmayt
GrmaytOP•3w ago
It just does this request It doesn't go to the Google sign-in page
KiNFiSH
KiNFiSH•3w ago
what about the backend endpoint ? the port it is running and make sure to have that while creating an auth client on expo
Grmayt
GrmaytOP•3w ago
Ah it returns this data
No description
Grmayt
GrmaytOP•3w ago
So I sholud login with this link? I got this in the client from server
KiNFiSH
KiNFiSH•3w ago
how are you calling it on the client ?
Grmayt
GrmaytOP•3w ago
Wit that URL I was able to create a user
const handleSocialLogin = async () => {
const { data, error } = await authClient.signIn.social(
{
provider: "google",
disableRedirect: true,
callbackURL: "myapp://(fitness)/(tabs)",
},
{
onRequest: (ctx) => {
console.log("requesting", JSON.stringify(ctx, null, 2));
},
onSuccess: () => {
router.replace("/(fitness)/(tabs)");
},
}
);
console.log(
"šŸš€ ~ handleSocialLogin ~ data:",
JSON.stringify(data, null, 2)
);

if (error) {
console.log("šŸš€ ~ handleSocialLogin ~ error:", error);
}

if (data?.url) {
await Linking.openURL(data?.url);
}
};
const handleSocialLogin = async () => {
const { data, error } = await authClient.signIn.social(
{
provider: "google",
disableRedirect: true,
callbackURL: "myapp://(fitness)/(tabs)",
},
{
onRequest: (ctx) => {
console.log("requesting", JSON.stringify(ctx, null, 2));
},
onSuccess: () => {
router.replace("/(fitness)/(tabs)");
},
}
);
console.log(
"šŸš€ ~ handleSocialLogin ~ data:",
JSON.stringify(data, null, 2)
);

if (error) {
console.log("šŸš€ ~ handleSocialLogin ~ error:", error);
}

if (data?.url) {
await Linking.openURL(data?.url);
}
};
Like this
KiNFiSH
KiNFiSH•3w ago
so what does the data looks like
Grmayt
GrmaytOP•3w ago
like this
data: {
"url": "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=1016798230758-h4ohprqe4go9bcb5suqfg9b8207rq9ia.apps.googleusercontent.com&state=amakHSWzGzj8Vy-eLusMvEi6YcYwGszI&scope=email+profile+openid&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fcallback%2Fgoogle&code_challenge_method=S256&code_challenge=J8YeRZ2YD_amRWUkzVJ5Va8saUdSv9lFYLaegW9VLzk",
"redirect": false
}
data: {
"url": "https://accounts.google.com/o/oauth2/auth?response_type=code&client_id=1016798230758-h4ohprqe4go9bcb5suqfg9b8207rq9ia.apps.googleusercontent.com&state=amakHSWzGzj8Vy-eLusMvEi6YcYwGszI&scope=email+profile+openid&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fapi%2Fauth%2Fcallback%2Fgoogle&code_challenge_method=S256&code_challenge=J8YeRZ2YD_amRWUkzVJ5Va8saUdSv9lFYLaegW9VLzk",
"redirect": false
}
I can get url and redirect
KiNFiSH
KiNFiSH•3w ago
nice soo can you inspect the db for user ? and session
Grmayt
GrmaytOP•3w ago
The user
No description
Grmayt
GrmaytOP•3w ago
The session
No description
Grmayt
GrmaytOP•3w ago
But when using useSessions in the client, it returns null
KiNFiSH
KiNFiSH•3w ago
now we have hooked up with the backend
Grmayt
GrmaytOP•3w ago
😭 struggling Yeah, but why it doesn't return session?
KiNFiSH
KiNFiSH•3w ago
so how are you trynna get the session ? the snippet
Grmayt
GrmaytOP•3w ago
const { data: isAuthenticated, isPending } = authClient.useSession();

console.log("šŸš€ ~ FitnessLayout ~ isAuthenticated:", isAuthenticated);
// NOTE - You can keep the splash screen open, or render a loading screen like we do here.
if (isPending) {
return (
<View className="flex-1 items-center justify-center">
<Text>Loading...</Text>
</View>
);
}

if (!isAuthenticated) {
return <Redirect href="/sign-up" />;
}
const { data: isAuthenticated, isPending } = authClient.useSession();

console.log("šŸš€ ~ FitnessLayout ~ isAuthenticated:", isAuthenticated);
// NOTE - You can keep the splash screen open, or render a loading screen like we do here.
if (isPending) {
return (
<View className="flex-1 items-center justify-center">
<Text>Loading...</Text>
</View>
);
}

if (!isAuthenticated) {
return <Redirect href="/sign-up" />;
}
In the client The bakcend is the same
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { auth } from './lib/auth';

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
const app = new Hono();

app.use(
'*',
cors({
origin: (origin, _) => {
if (allowedOrigins.includes(origin)) {
return origin;
}
return undefined;
},
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['POST', 'GET', 'OPTIONS'],
exposeHeaders: ['Content-Length'],
maxAge: 600,
credentials: true,
})
);

app.on(['POST', 'GET'], '/api/auth/**', c => {
return auth.handler(c.req.raw);
});

app.get('/', async c => {
return c.json({
status: 'ok',
});
});

export default {
fetch: app.fetch,
};
import { Hono } from 'hono';
import { cors } from 'hono/cors';
import { auth } from './lib/auth';

const allowedOrigins = process.env.ALLOWED_ORIGINS?.split(',') || [];
const app = new Hono();

app.use(
'*',
cors({
origin: (origin, _) => {
if (allowedOrigins.includes(origin)) {
return origin;
}
return undefined;
},
allowHeaders: ['Content-Type', 'Authorization'],
allowMethods: ['POST', 'GET', 'OPTIONS'],
exposeHeaders: ['Content-Length'],
maxAge: 600,
credentials: true,
})
);

app.on(['POST', 'GET'], '/api/auth/**', c => {
return auth.handler(c.req.raw);
});

app.get('/', async c => {
return c.json({
status: 'ok',
});
});

export default {
fetch: app.fetch,
};
Grmayt
GrmaytOP•3w ago
It hit the endpoint
No description
Grmayt
GrmaytOP•3w ago
in the backend
KiNFiSH
KiNFiSH•3w ago
can u able to get the cookies - const cookies = authClient.getCookie(); with this
Grmayt
GrmaytOP•3w ago
No, I can't unfortunately
(NOBRIDGE) LOG šŸš€ ~ FitnessLayout ~ cookies:
(NOBRIDGE) LOG šŸš€ ~ FitnessLayout ~ isAuthenticated: null
(NOBRIDGE) LOG šŸš€ ~ FitnessLayout ~ cookies:
(NOBRIDGE) LOG šŸš€ ~ FitnessLayout ~ isAuthenticated: null
Cookies: nothing
KiNFiSH
KiNFiSH•3w ago
do you have securestorage with your client config - expoClient({ scheme: "myapp", storagePrefix: "myapp", storage: SecureStore, })
Grmayt
GrmaytOP•3w ago
export const authClient = createAuthClient({
baseURL: "http://localhost:3000",
disableDefaultFetchPlugins: true,
plugins: [
expoClient({
storage: SecureStore,
scheme: "myapp",
storagePrefix: "myapp",
}),
],
});
export const authClient = createAuthClient({
baseURL: "http://localhost:3000",
disableDefaultFetchPlugins: true,
plugins: [
expoClient({
storage: SecureStore,
scheme: "myapp",
storagePrefix: "myapp",
}),
],
});
Do you mean this one? Yes, it's set
KiNFiSH
KiNFiSH•3w ago
what version of better auth are you using ?
Grmayt
GrmaytOP•3w ago
"better-auth": "^1.2.6-beta.4", "@better-auth/expo": "^1.2.6-beta.4", 1.2.6-beta.4
KiNFiSH
KiNFiSH•3w ago
can u confirm that it is hiting the backend with /get-session endpoint when u use the useSession hook
Grmayt
GrmaytOP•3w ago
Confirmed
No description
Grmayt
GrmaytOP•3w ago
It's hitting the /get-sessions endpoint
KiNFiSH
KiNFiSH•3w ago
check your backend log as well
Grmayt
GrmaytOP•3w ago
It's from the backend
KiNFiSH
KiNFiSH•3w ago
this endpoint is not actually a custom one ryt ?
Grmayt
GrmaytOP•3w ago
It's not the frontend is hitting the get-session when using useSession
app.on(['POST', 'GET'], '/api/auth/**', c => {
console.log(':rocket: ~ c:', c.req.raw);
return auth.handler(c.req.raw);
});
app.on(['POST', 'GET'], '/api/auth/**', c => {
console.log(':rocket: ~ c:', c.req.raw);
return auth.handler(c.req.raw);
});
I'm checking the log here
KiNFiSH
KiNFiSH•3w ago
yeah yeah i mean the you have not made custom endpoint for getting session with that api route ?
Grmayt
GrmaytOP•3w ago
No I haven't made any endpoint for getting it
KiNFiSH
KiNFiSH•3w ago
this is strange
Grmayt
GrmaytOP•3w ago
app.use('*', async (c, next) => {
console.log('šŸš€ ~ app.use ~ c:', c.req.raw);
const session = await auth.api.getSession({ headers: c.req.raw.headers });

console.log('šŸš€ ~ app.use ~ session:', session);
if (!session) {
c.set('user', null);
c.set('session', null);
return next();
}

c.set('user', session.user);
c.set('session', session.session);
return next();
});
app.use('*', async (c, next) => {
console.log('šŸš€ ~ app.use ~ c:', c.req.raw);
const session = await auth.api.getSession({ headers: c.req.raw.headers });

console.log('šŸš€ ~ app.use ~ session:', session);
if (!session) {
c.set('user', null);
c.set('session', null);
return next();
}

c.set('user', session.user);
c.set('session', session.session);
return next();
});
Would this one do something it in the doc
KiNFiSH
KiNFiSH•3w ago
it will make it tied with the context on backend not actually to expo client
Grmayt
GrmaytOP•3w ago
ok method: "GET", url: "http://localhost:3000/api/auth/callback/google?state=RwWTDprNonWUPi3oGtE3n4G7T-7jo5AM&code=4%2F0Ab_5qlnLv4NRU3wENSEJ3UEdQjlQghjVdn7zYssnwvYL0k01xW0Iydy1PHisK3sB-qzkZQ&scope=email+profile+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.profile+openid&authuser=0&prompt=none", headers: Headers { "host": "localhost:3000", "accept": "text/html,application/xhtml+xml,application/xml;q=0.9,/;q=0.8", "sec-fetch-mode": "navigate", "user-agent": "Mozilla/5.0 (iPhone; CPU iPhone OS 18_1 like Mac OS X) AppleWebKit/605.1.15 (KHTML, like Gecko) Version/18.1 Mobile/15E148 Safari/604.1", "accept-language": "en-CA,en-US;q=0.9,en;q=0.8", "sec-fetch-dest": "document", "accept-encoding": "gzip, deflate", "cookie": "better-auth.session_token=FhCbI3ebCrNdvvd9oXVzmUfdChg7KjMl.1lWf7Jt%2F1FQ0yx8kvsq6dnfgNsF6vYcSW%2BtK8%2FFcmd0%3D", "connection": "keep-alive", "sec-fetch-site": "none", "priority": "u=0, i", } } when logging in I can see the cookie
"cookie": "better-auth.session_token=FhCbI3ebCrNdvvd9oXVzmUfdChg7KjMl.1lWf7Jt%2F1FQ0yx8kvsq6dnfgNsF6vYcSW%2BtK8%2FFcmd0%3D",
"cookie": "better-auth.session_token=FhCbI3ebCrNdvvd9oXVzmUfdChg7KjMl.1lWf7Jt%2F1FQ0yx8kvsq6dnfgNsF6vYcSW%2BtK8%2FFcmd0%3D",
KiNFiSH
KiNFiSH•3w ago
so did you run the hook ? useSession ?
Grmayt
GrmaytOP•3w ago
Thank you very much. Appreciate that. haha I'm closing one
KiNFiSH
KiNFiSH•3w ago
no worries glad it worked, feel free to optimize the docs from what we experience
Grmayt
GrmaytOP•3w ago
Okay, will do. Thanks!! šŸ”„

Did you find this page helpful?