Skip to content

Commit 747110c

Browse files
committed
Fix capacitor prod build authentication issues.
1 parent 198fddc commit 747110c

File tree

10 files changed

+131
-27
lines changed

10 files changed

+131
-27
lines changed

mobile/android/app/capacitor.build.gradle

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ dependencies {
1414
implementation project(':capacitor-browser')
1515
implementation project(':capacitor-filesystem')
1616
implementation project(':capacitor-keyboard')
17+
implementation project(':capacitor-preferences')
1718
implementation project(':capacitor-screen-orientation')
1819
implementation project(':capacitor-status-bar')
1920
implementation project(':capawesome-capacitor-file-picker')

mobile/android/capacitor.settings.gradle

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ project(':capacitor-filesystem').projectDir = new File('../../node_modules/@capa
1717
include ':capacitor-keyboard'
1818
project(':capacitor-keyboard').projectDir = new File('../../node_modules/@capacitor/keyboard/android')
1919

20+
include ':capacitor-preferences'
21+
project(':capacitor-preferences').projectDir = new File('../../node_modules/@capacitor/preferences/android')
22+
2023
include ':capacitor-screen-orientation'
2124
project(':capacitor-screen-orientation').projectDir = new File('../../node_modules/@capacitor/screen-orientation/android')
2225

mobile/capacitor.config.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,9 @@ const config: CapacitorConfig = {
1717
SafeArea: {
1818
enabled: true,
1919
},
20+
CapacitorHttp: {
21+
enabled: true,
22+
},
2023
},
2124
};
2225

mobile/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,7 @@
55
"type": "module",
66
"scripts": {
77
"android-dev": "node dev.js",
8-
"android-run": "npx cap run android",
9-
"android-build": "npx cap build android"
8+
"android-run": "npx cap run android"
109
},
1110
"dependencies": {
1211
"@capacitor-community/safe-area": "^7.0.0-alpha.1",
@@ -17,6 +16,7 @@
1716
"@capacitor/core": "^7.2.0",
1817
"@capacitor/filesystem": "^7.0.1",
1918
"@capacitor/keyboard": "^7.0.1",
19+
"@capacitor/preferences": "^7.0.2",
2020
"@capacitor/screen-orientation": "^7.0.1",
2121
"@capacitor/status-bar": "^7.0.1",
2222
"@capawesome/capacitor-file-picker": "^7.0.1"

package-lock.json

Lines changed: 12 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,4 +28,4 @@
2828
"prettier-plugin-organize-imports": "^4.3.0",
2929
"prettier-plugin-tailwindcss": "^0.6.14"
3030
}
31-
}
31+
}

web/components/providers/capacitor-provider.tsx

Lines changed: 22 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
"use client";
22

33
import { App, URLOpenListenerEvent } from "@capacitor/app";
4-
import { Capacitor, CapacitorCookies } from "@capacitor/core";
4+
import { Capacitor } from "@capacitor/core";
5+
import { Preferences } from "@capacitor/preferences";
56
import { ScreenOrientation } from "@capacitor/screen-orientation";
67
import { StatusBar } from "@capacitor/status-bar";
78
import { useContext, useEffect } from "react";
@@ -39,7 +40,7 @@ export default function CapacitorProvider({
3940

4041
// Set deep linking listener
4142
useEffect(() => {
42-
App.addListener("appUrlOpen", (event: URLOpenListenerEvent) => {
43+
App.addListener("appUrlOpen", async (event: URLOpenListenerEvent) => {
4344
/*
4445
Custom Scheme
4546
*/
@@ -51,24 +52,30 @@ export default function CapacitorProvider({
5152
const token = params.get("token");
5253
const exp = params.get("exp");
5354

54-
// Navigate to the slug
55+
// Set token in Preferences and refresh session.
56+
// After refresh, the cookie will be set in the webview automatically.
57+
// Hence, no need to set cookie manually here.
5558
if (token) {
56-
// Set token in cookie
57-
CapacitorCookies.setCookie({
58-
// must match your WebView origin
59-
url: window.location.origin,
59+
await Preferences.set({
6060
key: "pulse-editor.session-token",
6161
value: token,
62-
path: "/",
63-
expires: exp ?? undefined,
64-
}).then(() => {
65-
editorContext?.setEditorStates((prev) => ({
66-
...prev,
67-
isRefreshSession: true,
68-
isSigningIn: false,
69-
}));
62+
});
63+
if (exp) {
64+
await Preferences.set({
65+
key: "pulse-editor.session-expiration",
66+
value: exp,
67+
});
68+
}
69+
} else {
70+
await Preferences.remove({
71+
key: "pulse-editor.session-token",
7072
});
7173
}
74+
editorContext?.setEditorStates((prev) => ({
75+
...prev,
76+
isRefreshSession: true,
77+
isSigningIn: false,
78+
}));
7279
}
7380
/*
7481
Google Verified Links

web/lib/hooks/use-auth.ts

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import { EditorContext } from "@/components/providers/editor-context-provider";
44
import { Browser } from "@capacitor/browser";
55
import { CapacitorCookies } from "@capacitor/core";
6+
import { Preferences } from "@capacitor/preferences";
67
import { useContext, useEffect } from "react";
78
import useSWR from "swr";
89
import { PlatformEnum } from "../enums";
@@ -60,14 +61,38 @@ export function useAuth() {
6061
);
6162

6263
useEffect(() => {
63-
if (editorContext?.editorStates.isRefreshSession) {
64-
mutate().then(() => {
64+
async function refreshSession() {
65+
if (editorContext?.editorStates.isRefreshSession) {
66+
const token = await Preferences.get({
67+
key: "pulse-editor.session-token",
68+
});
69+
70+
/*
71+
Sometimes other hooks using useSWR are fired right after retuning from deep linking
72+
before session is refreshed (triggered by window re-focus), causing cookies to be
73+
set again between when it is removed (if removed in deep link handler) and when
74+
session is refreshed.
75+
76+
So a better approach is to clear cookies right here before refreshing session, but
77+
possibly after other hooks are fired.
78+
*/
79+
if (!token.value) {
80+
// CapacitorCookies.clearAllCookies();
81+
CapacitorCookies.deleteCookie({
82+
key: "pulse-editor.session-token",
83+
url: process.env.NEXT_PUBLIC_BACKEND_URL,
84+
});
85+
}
86+
87+
await mutate();
6588
editorContext.setEditorStates((prev) => ({
6689
...prev,
6790
isRefreshSession: false,
6891
}));
69-
});
92+
}
7093
}
94+
95+
refreshSession();
7196
}, [editorContext?.editorStates.isRefreshSession]);
7297

7398
// Open a sign-in page if the user is not signed in.
@@ -118,11 +143,6 @@ export function useAuth() {
118143
process.env.NEXT_PUBLIC_BACKEND_URL + "/api/mobile",
119144
);
120145
await Browser.open({ url: url.toString() });
121-
122-
await CapacitorCookies.deleteCookie({
123-
url: window.location.origin,
124-
key: "pulse-editor.session-token",
125-
});
126146
} else {
127147
const url = getAPIUrl(`/api/auth/signout`);
128148
url.searchParams.set("callbackUrl", window.location.href);

web/lib/pulse-editor-website/backend.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
import { CapacitorHttp } from "@capacitor/core";
2+
import { Preferences } from "@capacitor/preferences";
3+
import { PlatformEnum } from "../enums";
4+
import { getPlatform } from "../platform-api/platform-checker";
5+
16
export async function fetchAPI(
27
relativeUrl: string | URL,
38
options?: RequestInit,
@@ -6,6 +11,59 @@ export async function fetchAPI(
611
const url =
712
typeof relativeUrl === "string" ? getAPIUrl(relativeUrl) : relativeUrl;
813

14+
/*
15+
Use Capacitor Http plugin for native http requests.
16+
The native fetch will include cookies,
17+
and when response is processed by client,
18+
the cookies are set in the webview automatically.
19+
*/
20+
if (getPlatform() === PlatformEnum.Capacitor) {
21+
// attach cookie manually
22+
const tokenPref = await Preferences.get({
23+
key: "pulse-editor.session-token",
24+
});
25+
const expPref = await Preferences.get({
26+
key: "pulse-editor.session-expiration",
27+
});
28+
const token = tokenPref.value;
29+
const exp = expPref.value;
30+
31+
const headers = new Headers(options?.headers ?? {});
32+
if (token) {
33+
headers.append(
34+
"Cookie",
35+
`pulse-editor.session-token=${token}; Path=/; Expires=${exp}; SameSite=None; Secure; ${process.env.NEXT_PUBLIC_BACKEND_URL ? "Domain=" + new URL(process.env.NEXT_PUBLIC_BACKEND_URL).hostname : ""}`,
36+
);
37+
options = {
38+
...options,
39+
headers,
40+
};
41+
}
42+
43+
const headerObj = Object.fromEntries(headers.entries());
44+
45+
const nativeResponse = await CapacitorHttp.request({
46+
url: url.toString(),
47+
method: options?.method ?? "GET",
48+
headers: headerObj,
49+
data: options?.body,
50+
});
51+
52+
console.log(
53+
`${url}. \n\nRequest header: ${JSON.stringify(headerObj)} \n\nNative response: ${JSON.stringify(nativeResponse)} \n\nCookie: ${document.cookie}`,
54+
);
55+
56+
const data = JSON.stringify(nativeResponse.data);
57+
58+
// Convert CapacitorHttpResponse to Fetch Response
59+
const fetchResponse = new Response(data, {
60+
status: nativeResponse.status,
61+
headers: nativeResponse.headers,
62+
});
63+
64+
return fetchResponse;
65+
}
66+
967
return await fetch(url, {
1068
credentials: "include",
1169
...options,

web/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
"@capacitor/core": "^7.4.3",
2020
"@capacitor/filesystem": "7.1.4",
2121
"@capacitor/keyboard": "^7.0.3",
22+
"@capacitor/preferences": "^7.0.2",
2223
"@capacitor/screen-orientation": "^7.0.2",
2324
"@capacitor/status-bar": "^7.0.3",
2425
"@capawesome/capacitor-file-picker": "^7.2.0",

0 commit comments

Comments
 (0)