feat: use expo-secure-store and better-auth buildin getCookie for auth

This commit is contained in:
DIYgod 2025-01-17 20:45:40 +08:00
parent 0fc90c7c28
commit accbf7fa60
No known key found for this signature in database
8 changed files with 47 additions and 96 deletions

View File

@ -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,

View File

@ -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",

View File

@ -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<AppType>(getApiUrl(), {
headers() {
return {
"X-App-Name": "Follow Mobile",
cookie: getCookie(),
}
},
})

View File

@ -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"

View File

@ -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()
}

View File

@ -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 = () => {
<TouchableOpacity
className="ml-2"
onPress={() => {
setSessionToken(input)
SecureStore.setItem(
cookieKey,
JSON.stringify({
[sessionTokenKey]: {
value: input,
},
}),
)
Alert.alert("Session Token Saved")
}}
>

View File

@ -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<WebView>(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 <Redirect href="/login" />
}
// if (!token && !isPending) {
// return <Redirect href="/login" />
// }
return (
<View className="flex-1 items-center justify-center pt-safe dark:bg-[#121212]">
{isCookieReady && <FollowWebView webViewRef={webViewRef} />}
{/* {isCookieReady && <FollowWebView webViewRef={webViewRef} />} */}
{__DEV__ && (
<View

View File

@ -508,6 +508,9 @@ importers:
expo-router:
specifier: 4.0.11
version: 4.0.11(4kc7a2oyapueyif6rxtpvh4774)
expo-secure-store:
specifier: ^14.0.1
version: 14.0.1(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)))(bufferutil@4.0.8)(encoding@0.1.13)(graphql@16.8.1)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))
expo-sharing:
specifier: ~13.0.0
version: 13.0.0(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)))(bufferutil@4.0.8)(encoding@0.1.13)(graphql@16.8.1)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))
@ -8859,6 +8862,11 @@ packages:
react-native-reanimated:
optional: true
expo-secure-store@14.0.1:
resolution: {integrity: sha512-QUS+j4+UG4jRQalgnpmTvvrFnMVLqPiUZRzYPnG3+JrZ5kwVW2w6YS3WWerPoR7C6g3y/a2htRxRSylsDs+TaQ==}
peerDependencies:
expo: '*'
expo-sharing@13.0.0:
resolution: {integrity: sha512-b23ymicRmYn/Pjj05sl9tFZHN5cH9I1f0yiqY1Yk8Q3oCx0Aznri82DnTYA4T/J6D9vrkraX0wQ4jWVMOffmlg==}
peerDependencies:
@ -24823,6 +24831,10 @@ snapshots:
- supports-color
- typescript
expo-secure-store@14.0.1(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)))(bufferutil@4.0.8)(encoding@0.1.13)(graphql@16.8.1)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)):
dependencies:
expo: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)))(bufferutil@4.0.8)(encoding@0.1.13)(graphql@16.8.1)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)
expo-sharing@13.0.0(expo@52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)))(bufferutil@4.0.8)(encoding@0.1.13)(graphql@16.8.1)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)):
dependencies:
expo: 52.0.18(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@expo/metro-runtime@4.0.0(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1)))(bufferutil@4.0.8)(encoding@0.1.13)(graphql@16.8.1)(react-native-webview@13.12.5(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1))(react-native@0.76.5(@babel/core@7.26.0)(@babel/preset-env@7.26.0(@babel/core@7.26.0))(@react-native-community/cli-server-api@14.1.0(bufferutil@4.0.8))(@types/react@18.3.14)(bufferutil@4.0.8)(encoding@0.1.13)(react@18.3.1))(react@18.3.1)