fix(mobile): use folo callback for social auth
This commit is contained in:
parent
dfcfb0ddff
commit
e48a8f4f2e
|
|
@ -0,0 +1,92 @@
|
|||
import { afterEach, describe, expect, it, vi } from "vitest"
|
||||
|
||||
const mocks = vi.hoisted(() => ({
|
||||
getCookie: vi.fn(() => "better-auth.session_token=session"),
|
||||
getInstallerPackageName: vi.fn(async () => "com.android.vending"),
|
||||
getUserAgent: vi.fn(async () => "Folo/0.5.5"),
|
||||
oneTimeTokenApply: vi.fn(async () => {}),
|
||||
}))
|
||||
|
||||
vi.mock("@follow/utils/headers", () => ({
|
||||
createMobileAPIHeaders: vi.fn(() => ({
|
||||
"x-app-version": "0.5.5",
|
||||
})),
|
||||
}))
|
||||
|
||||
vi.mock("expo-application", () => ({
|
||||
nativeApplicationVersion: "0.5.5",
|
||||
}))
|
||||
|
||||
vi.mock("react-native", () => ({
|
||||
Platform: {
|
||||
OS: "android",
|
||||
isPad: false,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("react-native-device-info", () => ({
|
||||
default: {
|
||||
getInstallerPackageName: mocks.getInstallerPackageName,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("./auth", () => ({
|
||||
getCookie: mocks.getCookie,
|
||||
oneTimeToken: {
|
||||
apply: mocks.oneTimeTokenApply,
|
||||
},
|
||||
}))
|
||||
|
||||
vi.mock("./client-session", () => ({
|
||||
getClientId: () => "client-id",
|
||||
getSessionId: () => "session-id",
|
||||
}))
|
||||
|
||||
vi.mock("./native/user-agent", () => ({
|
||||
getUserAgent: mocks.getUserAgent,
|
||||
}))
|
||||
|
||||
vi.mock("./proxy-env", () => ({
|
||||
proxyEnv: {
|
||||
API_URL: "https://api.folo.is",
|
||||
},
|
||||
}))
|
||||
|
||||
const { migrateLegacyApiSession } = await import("./auth-cookie-migration")
|
||||
|
||||
describe("migrateLegacyApiSession", () => {
|
||||
afterEach(() => {
|
||||
vi.unstubAllGlobals()
|
||||
vi.clearAllMocks()
|
||||
})
|
||||
|
||||
it("uses the Folo native app scheme as the migration auth origin", async () => {
|
||||
const fetchMock = vi.fn(async (input: string, _options?: RequestInit): Promise<Response> => {
|
||||
if (input === "https://api.folo.is/better-auth/get-session") {
|
||||
return new Response(null, { status: 401 })
|
||||
}
|
||||
|
||||
if (input === "https://api.follow.is/better-auth/get-session") {
|
||||
return Response.json({ user: { id: "user-id" } })
|
||||
}
|
||||
|
||||
if (input === "https://api.follow.is/better-auth/one-time-token/generate") {
|
||||
return Response.json({ token: "one-time-token" })
|
||||
}
|
||||
|
||||
return new Response(null, { status: 404 })
|
||||
})
|
||||
|
||||
vi.stubGlobal("fetch", fetchMock)
|
||||
|
||||
await migrateLegacyApiSession()
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(3)
|
||||
for (const [, options] of fetchMock.mock.calls) {
|
||||
expect(options?.headers).toMatchObject({
|
||||
"expo-origin": "folo://",
|
||||
})
|
||||
}
|
||||
expect(mocks.oneTimeTokenApply).toHaveBeenCalledWith({ token: "one-time-token" })
|
||||
})
|
||||
})
|
||||
|
|
@ -46,7 +46,7 @@ const createMigrationHeaders = async () => {
|
|||
"X-Client-Id": getClientId(),
|
||||
"X-Session-Id": getSessionId(),
|
||||
"User-Agent": await getUserAgent(),
|
||||
"expo-origin": "follow://",
|
||||
"expo-origin": "folo://",
|
||||
"x-skip-oauth-proxy": "true",
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -54,7 +54,7 @@ const refreshSessionQueries = () =>
|
|||
const plugins = [
|
||||
...baseAuthPlugins,
|
||||
expoClient({
|
||||
scheme: "follow",
|
||||
scheme: "folo",
|
||||
storagePrefix,
|
||||
storage: {
|
||||
setItem(key: string, value: string) {
|
||||
|
|
|
|||
|
|
@ -32,7 +32,7 @@ describe("loginWithSocialProvider", () => {
|
|||
})
|
||||
|
||||
expect(result).toBe(true)
|
||||
expect(signInWithProvider).toHaveBeenCalledWith("google")
|
||||
expect(signInWithProvider).toHaveBeenCalledWith("google", { callbackURL: "folo://" })
|
||||
expect(trackLogin).toHaveBeenCalledTimes(1)
|
||||
expect(sequence).toEqual(["pending:google", "sign-in:google", "sync", "track", "pending:none"])
|
||||
})
|
||||
|
|
@ -58,6 +58,21 @@ describe("loginWithSocialProvider", () => {
|
|||
expect(setPendingProviderId).toHaveBeenLastCalledWith(null)
|
||||
})
|
||||
|
||||
it("uses the Folo native app scheme as the OAuth callback URL", async () => {
|
||||
const signInWithProvider = vi.fn(async () => {})
|
||||
|
||||
await loginWithSocialProvider({
|
||||
providerId: "github",
|
||||
setPendingProviderId: vi.fn(),
|
||||
signInWithProvider,
|
||||
signInWithAppleIdentityToken: vi.fn(async () => {}),
|
||||
syncSession: async () => false,
|
||||
trackLogin: vi.fn(),
|
||||
})
|
||||
|
||||
expect(signInWithProvider).toHaveBeenCalledWith("github", { callbackURL: "folo://" })
|
||||
})
|
||||
|
||||
it("uses the Apple token flow for Apple sign in", async () => {
|
||||
const signInWithAppleIdentityToken = vi.fn(async () => {})
|
||||
const signInWithProvider = vi.fn(async () => {})
|
||||
|
|
|
|||
|
|
@ -1,7 +1,12 @@
|
|||
export const nativeOAuthCallbackURL = "folo://"
|
||||
|
||||
type LoginWithSocialProviderOptions = {
|
||||
providerId: string
|
||||
setPendingProviderId: (providerId: string | null) => void
|
||||
signInWithProvider: (providerId: string) => Promise<void>
|
||||
signInWithProvider: (
|
||||
providerId: string,
|
||||
options: { callbackURL: typeof nativeOAuthCallbackURL },
|
||||
) => Promise<void>
|
||||
signInWithAppleIdentityToken: () => Promise<void>
|
||||
syncSession: () => Promise<boolean>
|
||||
trackLogin: () => void
|
||||
|
|
@ -23,7 +28,7 @@ export async function loginWithSocialProvider({
|
|||
if (providerId === "apple") {
|
||||
await signInWithAppleIdentityToken()
|
||||
} else {
|
||||
await signInWithProvider(providerId)
|
||||
await signInWithProvider(providerId, { callbackURL: nativeOAuthCallbackURL })
|
||||
}
|
||||
|
||||
const hasSession = await syncSession()
|
||||
|
|
|
|||
|
|
@ -78,10 +78,10 @@ export function SocialLogin({ onPressEmail }: { isRegister: boolean; onPressEmai
|
|||
void loginWithSocialProvider({
|
||||
providerId: provider.id,
|
||||
setPendingProviderId,
|
||||
signInWithProvider: async (providerId) => {
|
||||
signInWithProvider: async (providerId, { callbackURL }) => {
|
||||
await signIn.social({
|
||||
provider: providerId as SocialProviderSignInInput["provider"],
|
||||
callbackURL: "/",
|
||||
callbackURL,
|
||||
})
|
||||
},
|
||||
signInWithAppleIdentityToken: async () => {
|
||||
|
|
|
|||
Loading…
Reference in New Issue