diff --git a/apps/mobile/app.config.ts b/apps/mobile/app.config.ts index ffc3512a2..ea2d31647 100644 --- a/apps/mobile/app.config.ts +++ b/apps/mobile/app.config.ts @@ -91,6 +91,7 @@ export default ({ config }: ConfigContext): ExpoConfig => ({ "expo-apple-authentication", [require("./scripts/with-follow-assets.js")], [require("./scripts/with-follow-app-delegate.js")], + "expo-secure-store", ], experiments: { typedRoutes: true, diff --git a/apps/mobile/package.json b/apps/mobile/package.json index c3e49948d..e4a715d54 100644 --- a/apps/mobile/package.json +++ b/apps/mobile/package.json @@ -51,6 +51,7 @@ "expo-linear-gradient": "~14.0.1", "expo-linking": "~7.0.3", "expo-router": "4.0.11", + "expo-secure-store": "^14.0.1", "expo-sharing": "~13.0.0", "expo-splash-screen": "~0.29.18", "expo-sqlite": "15.0.3", diff --git a/apps/mobile/src/lib/api-fetch.ts b/apps/mobile/src/lib/api-fetch.ts index ea80d3a3f..272a6cad3 100644 --- a/apps/mobile/src/lib/api-fetch.ts +++ b/apps/mobile/src/lib/api-fetch.ts @@ -3,7 +3,7 @@ import type { AppType } from "@follow/shared" import { router } from "expo-router" import { ofetch } from "ofetch" -import { getSessionToken } from "./cookie" +import { getCookie } from "./auth" import { getApiUrl } from "./env" const { hc } = require("hono/dist/cjs/client/client") as typeof import("hono/client") @@ -13,20 +13,10 @@ export const apiFetch = ofetch.create({ baseURL: getApiUrl(), onRequest: async ({ options, request }) => { - const header = new Headers(options.headers) - - header.set("x-app-name", "Follow Mobile") - - const sessionToken = await getSessionToken() - if (sessionToken) { - header.set("cookie", `__Secure-better-auth.session_token=${sessionToken}`) - } if (__DEV__) { // Logger console.log(`---> ${options.method} ${request as string}`) } - - options.headers = header }, onRequestError: ({ error, request, options }) => { if (__DEV__) { @@ -60,6 +50,7 @@ export const apiClient = hc(getApiUrl(), { headers() { return { "X-App-Name": "Follow Mobile", + cookie: getCookie(), } }, }) diff --git a/apps/mobile/src/lib/auth.ts b/apps/mobile/src/lib/auth.ts index af1316553..7ae3e2ff1 100644 --- a/apps/mobile/src/lib/auth.ts +++ b/apps/mobile/src/lib/auth.ts @@ -2,11 +2,9 @@ import { expoClient } from "@better-auth/expo/client" import { useQuery } from "@tanstack/react-query" import { createAuthClient } from "better-auth/react" import type * as better_call from "better-call" -import { parse } from "cookie-es" +import * as SecureStore from "expo-secure-store" import { getApiUrl } from "./env" -import { kv } from "./kv" -import { queryClient } from "./query-client" const storagePrefix = "follow_auth" export const cookieKey = `${storagePrefix}_cookie` @@ -22,17 +20,7 @@ const authClient = createAuthClient({ expoClient({ scheme: "follow", storagePrefix, - storage: { - setItem(key, value) { - kv.setSync(key, value) - if (key === cookieKey) { - queryClient.invalidateQueries({ queryKey: ["cookie"] }) - } - }, - getItem(key) { - return kv.getSync(key) - }, - }, + storage: SecureStore, }), ], }) @@ -54,18 +42,6 @@ export const useAuthProviders = () => { }) } -export const getSessionTokenFromCookie = () => { - const cookie = getCookie() - return cookie ? parse(cookie)[sessionTokenKey] : null -} - -export const useAuthToken = () => { - return useQuery({ - queryKey: ["cookie"], - queryFn: getSessionTokenFromCookie, - }) -} - // eslint-disable-next-line unused-imports/no-unused-vars declare const authPlugins: { id: "getProviders" diff --git a/apps/mobile/src/lib/cookie.ts b/apps/mobile/src/lib/cookie.ts deleted file mode 100644 index 60ffc2e9e..000000000 --- a/apps/mobile/src/lib/cookie.ts +++ /dev/null @@ -1,35 +0,0 @@ -import CookieManager from "@react-native-cookies/cookies" - -import { cookieKey, getSessionTokenFromCookie, sessionTokenKey } from "./auth" -import { getApiUrl } from "./env" -import { kv } from "./kv" - -export const setSessionToken = (token: string) => { - kv.setSync( - cookieKey, - JSON.stringify({ - [sessionTokenKey]: { - value: token, - expires: new Date(Date.now() + 1000 * 60 * 60 * 24 * 365).toISOString(), - }, - }), - ) - - return CookieManager.set(getApiUrl(), { - name: sessionTokenKey, - value: token, - httpOnly: true, - secure: true, - }) -} - -export const getSessionToken = async () => { - const cookies = await CookieManager.get(getApiUrl()) - - return cookies[sessionTokenKey] || getSessionTokenFromCookie() -} - -export const clearSessionToken = async () => { - await kv.delete(cookieKey) - return CookieManager.clearAll() -} diff --git a/apps/mobile/src/screens/(headless)/debug.tsx b/apps/mobile/src/screens/(headless)/debug.tsx index 13cfca3c1..2538264ba 100644 --- a/apps/mobile/src/screens/(headless)/debug.tsx +++ b/apps/mobile/src/screens/(headless)/debug.tsx @@ -2,6 +2,7 @@ import { sleep } from "@follow/utils" import * as Clipboard from "expo-clipboard" import * as FileSystem from "expo-file-system" import { Sitemap } from "expo-router/build/views/Sitemap" +import * as SecureStore from "expo-secure-store" import type { FC } from "react" import * as React from "react" import { useRef, useState } from "react" @@ -18,7 +19,7 @@ import { import { useSafeAreaInsets } from "react-native-safe-area-context" import { getDbPath } from "@/src/database" -import { clearSessionToken, getSessionToken, setSessionToken } from "@/src/lib/cookie" +import { cookieKey, getCookie, sessionTokenKey, signOut } from "@/src/lib/auth" import { loading } from "@/src/lib/loading" import { toast } from "@/src/lib/toast" @@ -44,14 +45,14 @@ export default function DebugPanel() { { title: "Get Current Session Token", onPress: async () => { - const token = await getSessionToken() - Alert.alert(`Current Session Token: ${token?.value}`) + const token = getCookie() + Alert.alert(`Current Session Token: ${token}`) }, }, { title: "Clear Session Token", onPress: async () => { - await clearSessionToken() + await signOut() Alert.alert("Session Token Cleared") }, }, @@ -178,7 +179,14 @@ const UserSessionSetting = () => { { - setSessionToken(input) + SecureStore.setItem( + cookieKey, + JSON.stringify({ + [sessionTokenKey]: { + value: input, + }, + }), + ) Alert.alert("Session Token Saved") }} > diff --git a/apps/mobile/src/screens/(headless)/webview.tsx b/apps/mobile/src/screens/(headless)/webview.tsx index 8553007f7..9eba5a925 100644 --- a/apps/mobile/src/screens/(headless)/webview.tsx +++ b/apps/mobile/src/screens/(headless)/webview.tsx @@ -1,41 +1,38 @@ -import { Redirect } from "expo-router" -import { useEffect, useRef, useState } from "react" +import { useRef } from "react" import { TouchableOpacity, View } from "react-native" import { useSafeAreaInsets } from "react-native-safe-area-context" import type { WebView } from "react-native-webview" -import { FollowWebView } from "@/src/components/common/FollowWebView" import { BugCuteReIcon } from "@/src/icons/bug_cute_re" import { ExitCuteReIcon } from "@/src/icons/exit_cute_re" import { Refresh2CuteReIcon } from "@/src/icons/refresh_2_cute_re" import { World2CuteReIcon } from "@/src/icons/world_2_cute_re" -import { signOut, useAuthToken } from "@/src/lib/auth" -import { setSessionToken } from "@/src/lib/cookie" +import { signOut } from "@/src/lib/auth" export default function Index() { const webViewRef = useRef(null) const insets = useSafeAreaInsets() - const { data: token, isPending } = useAuthToken() + // const { data: token, isPending } = useAuthToken() - const [isCookieReady, setIsCookieReady] = useState(false) - useEffect(() => { - if (!token) { - return - } + // const [isCookieReady, setIsCookieReady] = useState(false) + // useEffect(() => { + // if (!token) { + // return + // } - setSessionToken(token).then(() => { - setIsCookieReady(true) - }) - }, [token]) + // // setSessionToken(token).then(() => { + // // setIsCookieReady(true) + // // }) + // }, [token]) - if (!token && !isPending) { - return - } + // if (!token && !isPending) { + // return + // } return ( - {isCookieReady && } + {/* {isCookieReady && } */} {__DEV__ && (