feat(auth): rename external ids to asid psid
This commit is contained in:
Executable
+232
@@ -0,0 +1,232 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
readonly GRAPH_API_BASE_URL="https://graph.facebook.com"
|
||||
readonly GRAPH_API_VERSION="v25.0"
|
||||
readonly PSID="27511427698460020"
|
||||
readonly ASID="122111097783118257"
|
||||
readonly PAGE_ID="TODO_PAGE_ID"
|
||||
readonly APP_ID="TODO_APP_ID"
|
||||
readonly PAGE_ACCESS_TOKEN="TODO_PAGE_ACCESS_TOKEN"
|
||||
readonly ACCESS_TOKEN="$PAGE_ACCESS_TOKEN"
|
||||
readonly APP_SECRET="TODO_APP_SECRET"
|
||||
|
||||
usage() {
|
||||
cat <<'EOF'
|
||||
Verify Meta PSID / ASID matching in both directions.
|
||||
|
||||
Usage:
|
||||
bash scripts/tools/verify-meta-id-match.sh
|
||||
|
||||
Built-in IDs:
|
||||
PSID 27511427698460020
|
||||
ASID 122111097783118257
|
||||
|
||||
Before running:
|
||||
Fill PAGE_ID, APP_ID, PAGE_ACCESS_TOKEN, and APP_SECRET constants at the top
|
||||
of this script.
|
||||
|
||||
Exit code:
|
||||
0 Both directions matched.
|
||||
1 API call succeeded, but one or both directions did not match.
|
||||
2 Missing required input.
|
||||
EOF
|
||||
}
|
||||
|
||||
if [ "${1:-}" = "-h" ] || [ "${1:-}" = "--help" ]; then
|
||||
usage
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if [ "$#" -gt 0 ]; then
|
||||
echo "This script does not accept runtime parameters. Edit constants in the script instead." >&2
|
||||
usage >&2
|
||||
exit 2
|
||||
fi
|
||||
|
||||
require_value() {
|
||||
local name="$1"
|
||||
local value="$2"
|
||||
if [ -n "$value" ]; then
|
||||
return
|
||||
fi
|
||||
echo "Missing required value: $name" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
require_command() {
|
||||
local name="$1"
|
||||
if command -v "$name" >/dev/null 2>&1; then
|
||||
return
|
||||
fi
|
||||
echo "Missing required command: $name" >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
require_configured() {
|
||||
local name="$1"
|
||||
local value="$2"
|
||||
require_value "$name" "$value"
|
||||
if [[ "$value" == TODO_* ]]; then
|
||||
echo "Please fill script constant: $name" >&2
|
||||
exit 2
|
||||
fi
|
||||
}
|
||||
|
||||
require_configured "PAGE_ID" "$PAGE_ID"
|
||||
require_configured "APP_ID" "$APP_ID"
|
||||
require_configured "PAGE_ACCESS_TOKEN" "$PAGE_ACCESS_TOKEN"
|
||||
require_configured "ACCESS_TOKEN" "$ACCESS_TOKEN"
|
||||
require_configured "APP_SECRET" "$APP_SECRET"
|
||||
|
||||
require_command curl
|
||||
require_command node
|
||||
|
||||
urlencode() {
|
||||
node -e 'process.stdout.write(encodeURIComponent(process.argv[1] ?? ""))' "$1"
|
||||
}
|
||||
|
||||
appsecret_proof() {
|
||||
local token="$1"
|
||||
if [ -z "$APP_SECRET" ]; then
|
||||
return
|
||||
fi
|
||||
node -e '
|
||||
const crypto = require("crypto");
|
||||
const token = process.argv[1] ?? "";
|
||||
const secret = process.argv[2] ?? "";
|
||||
process.stdout.write(
|
||||
crypto.createHmac("sha256", secret).update(token).digest("hex")
|
||||
);
|
||||
' "$token" "$APP_SECRET"
|
||||
}
|
||||
|
||||
json_ids() {
|
||||
local file="$1"
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const payload = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
||||
const ids = Array.isArray(payload.data)
|
||||
? payload.data.map((item) => item && item.id).filter(Boolean)
|
||||
: [];
|
||||
process.stdout.write(ids.join("\n"));
|
||||
' "$file"
|
||||
}
|
||||
|
||||
json_has_id() {
|
||||
local file="$1"
|
||||
local expected="$2"
|
||||
node -e '
|
||||
const fs = require("fs");
|
||||
const payload = JSON.parse(fs.readFileSync(process.argv[1], "utf8"));
|
||||
const expected = process.argv[2];
|
||||
const matched = Array.isArray(payload.data)
|
||||
&& payload.data.some((item) => item && String(item.id) === expected);
|
||||
process.exit(matched ? 0 : 1);
|
||||
' "$file" "$expected"
|
||||
}
|
||||
|
||||
print_ids() {
|
||||
local file="$1"
|
||||
local ids
|
||||
ids="$(json_ids "$file")"
|
||||
if [ -z "$ids" ]; then
|
||||
echo "Returned IDs: <empty>"
|
||||
return
|
||||
fi
|
||||
echo "Returned IDs:"
|
||||
while IFS= read -r id; do
|
||||
echo " - $id"
|
||||
done <<<"$ids"
|
||||
}
|
||||
|
||||
request_graph() {
|
||||
local output_file="$1"
|
||||
local endpoint="$2"
|
||||
shift 2
|
||||
|
||||
local http_code
|
||||
if ! http_code="$(
|
||||
curl -sS -w '%{http_code}' -o "$output_file" -G "$endpoint" "$@"
|
||||
)"; then
|
||||
echo "Graph API request failed before receiving a response." >&2
|
||||
return 1
|
||||
fi
|
||||
|
||||
if [ "$http_code" -lt 200 ] || [ "$http_code" -ge 300 ]; then
|
||||
echo "Graph API returned HTTP $http_code:" >&2
|
||||
cat "$output_file" >&2
|
||||
echo >&2
|
||||
return 1
|
||||
fi
|
||||
}
|
||||
|
||||
tmp_psid_to_asid="$(mktemp)"
|
||||
tmp_asid_to_psid="$(mktemp)"
|
||||
cleanup() {
|
||||
rm -f "$tmp_psid_to_asid" "$tmp_asid_to_psid"
|
||||
}
|
||||
trap cleanup EXIT
|
||||
|
||||
encoded_psid="$(urlencode "$PSID")"
|
||||
encoded_asid="$(urlencode "$ASID")"
|
||||
|
||||
psid_to_asid_endpoint="${GRAPH_API_BASE_URL}/${GRAPH_API_VERSION}/${encoded_psid}/ids_for_apps"
|
||||
asid_to_psid_endpoint="${GRAPH_API_BASE_URL}/${GRAPH_API_VERSION}/${encoded_asid}/ids_for_pages"
|
||||
|
||||
psid_proof="$(appsecret_proof "$PAGE_ACCESS_TOKEN")"
|
||||
asid_proof="$(appsecret_proof "$ACCESS_TOKEN")"
|
||||
|
||||
echo "=== PSID -> ASID ==="
|
||||
echo "GET ${psid_to_asid_endpoint}?app=${APP_ID}&access_token=<PAGE_ACCESS_TOKEN>&appsecret_proof=<APPSECRET_PROOF>"
|
||||
|
||||
psid_args=(
|
||||
--data-urlencode "app=${APP_ID}"
|
||||
--data-urlencode "access_token=${PAGE_ACCESS_TOKEN}"
|
||||
)
|
||||
if [ -n "$psid_proof" ]; then
|
||||
psid_args+=(--data-urlencode "appsecret_proof=${psid_proof}")
|
||||
fi
|
||||
|
||||
request_graph "$tmp_psid_to_asid" "$psid_to_asid_endpoint" "${psid_args[@]}"
|
||||
print_ids "$tmp_psid_to_asid"
|
||||
|
||||
psid_to_asid_matched=0
|
||||
if json_has_id "$tmp_psid_to_asid" "$ASID"; then
|
||||
echo "Match: yes, ASID was found from PSID."
|
||||
else
|
||||
echo "Match: no, ASID was not found from PSID."
|
||||
psid_to_asid_matched=1
|
||||
fi
|
||||
|
||||
echo
|
||||
echo "=== ASID -> PSID ==="
|
||||
echo "GET ${asid_to_psid_endpoint}?page=${PAGE_ID}&access_token=<ACCESS_TOKEN>"
|
||||
|
||||
asid_args=(
|
||||
--data-urlencode "page=${PAGE_ID}"
|
||||
--data-urlencode "access_token=${ACCESS_TOKEN}"
|
||||
)
|
||||
if [ -n "$asid_proof" ]; then
|
||||
asid_args+=(--data-urlencode "appsecret_proof=${asid_proof}")
|
||||
fi
|
||||
|
||||
request_graph "$tmp_asid_to_psid" "$asid_to_psid_endpoint" "${asid_args[@]}"
|
||||
print_ids "$tmp_asid_to_psid"
|
||||
|
||||
asid_to_psid_matched=0
|
||||
if json_has_id "$tmp_asid_to_psid" "$PSID"; then
|
||||
echo "Match: yes, PSID was found from ASID."
|
||||
else
|
||||
echo "Match: no, PSID was not found from ASID."
|
||||
asid_to_psid_matched=1
|
||||
fi
|
||||
|
||||
echo
|
||||
if [ "$psid_to_asid_matched" -eq 0 ] && [ "$asid_to_psid_matched" -eq 0 ]; then
|
||||
echo "Result: matched in both directions."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
echo "Result: not matched in both directions."
|
||||
exit 1
|
||||
@@ -13,8 +13,8 @@ const log = new Logger("ExternalEntryPersist");
|
||||
|
||||
interface ExternalEntryPersistProps {
|
||||
deviceId: string | null;
|
||||
facebookId: string | null;
|
||||
facebookAppId: string | null;
|
||||
asid: string | null;
|
||||
psid: string | null;
|
||||
avatarUrl: string | null;
|
||||
target: string | null;
|
||||
redirect: string | null;
|
||||
@@ -23,8 +23,8 @@ interface ExternalEntryPersistProps {
|
||||
|
||||
export default function ExternalEntryPersist({
|
||||
deviceId,
|
||||
facebookId,
|
||||
facebookAppId,
|
||||
asid,
|
||||
psid,
|
||||
avatarUrl,
|
||||
target,
|
||||
redirect,
|
||||
@@ -43,8 +43,8 @@ export default function ExternalEntryPersist({
|
||||
try {
|
||||
await persistExternalEntryPayload({
|
||||
deviceId,
|
||||
facebookId,
|
||||
facebookAppId,
|
||||
asid,
|
||||
psid,
|
||||
avatarUrl,
|
||||
});
|
||||
} catch (error) {
|
||||
@@ -55,10 +55,10 @@ export default function ExternalEntryPersist({
|
||||
})();
|
||||
}, [
|
||||
avatarUrl,
|
||||
asid,
|
||||
deviceId,
|
||||
facebookAppId,
|
||||
facebookId,
|
||||
next,
|
||||
psid,
|
||||
redirect,
|
||||
router,
|
||||
target,
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
* 外部入口落地页(Server Component)
|
||||
*
|
||||
* 外部应用可以通过 query 传入 Facebook 相关信息和最终目标页,例如:
|
||||
* `/external-entry?target=chat&fbid=xxx&fb_app_id=yyy`
|
||||
* `/external-entry?target=chat&asid=xxx&psid=yyy`
|
||||
*
|
||||
* 页面不直接 `redirect()`,而是把数据交给 Client 组件先写入本地存储,
|
||||
* 再通过 `router.replace()` 清理 URL 并跳转到最终页面。
|
||||
@@ -24,20 +24,8 @@ export default async function ExternalEntryPage({
|
||||
return (
|
||||
<ExternalEntryPersist
|
||||
deviceId={pickFirst(params.deviceId, params.device_id)}
|
||||
facebookId={pickFirst(
|
||||
params.fbid,
|
||||
params.fb_id,
|
||||
params.facebookId,
|
||||
params.facebook_id,
|
||||
)}
|
||||
facebookAppId={pickFirst(
|
||||
params.fbAppId,
|
||||
params.fb_app_id,
|
||||
params.facebookAppId,
|
||||
params.facebook_app_id,
|
||||
params.appId,
|
||||
params.app_id,
|
||||
)}
|
||||
asid={pickFirst(params.asid)}
|
||||
psid={pickFirst(params.psid)}
|
||||
avatarUrl={pickFirst(params.avatarUrl, params.avatar_url)}
|
||||
target={pickFirst(params.target)}
|
||||
redirect={pickFirst(params.redirect)}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* 在 OAuth callback 之后、auth state machine 的 `syncingFacebookBackend` actor
|
||||
* 成功换取业务 token 之后调用 —— 用 accessToken 调 `/me` 拉用户资料(id / name
|
||||
* / email / picture),把 pictureUrl 写入 `UserStorage.setAvatarUrl()`、
|
||||
* id 写入 `AuthStorage.setFacebookId()`。
|
||||
* id 写入 `AuthStorage.setAsid()`。
|
||||
*
|
||||
* 端点:`https://graph.facebook.com/me?fields=id,name,email,picture.type(large)&access_token=...`
|
||||
* - `picture.type(large)` 返回 ~200px 头像 URL(默认 square 50px 太小)
|
||||
|
||||
@@ -89,28 +89,32 @@ export class AuthStorage implements IAuthStorage {
|
||||
return SpAsyncUtil.remove(StorageKeys.refreshToken);
|
||||
}
|
||||
|
||||
// ---- facebook id ----
|
||||
// ---- ASID / PSID ----
|
||||
|
||||
getFacebookId(): Promise<ResultT<string | null>> {
|
||||
return SpAsyncUtil.getString(StorageKeys.facebookId);
|
||||
async getAsid(): Promise<ResultT<string | null>> {
|
||||
return getStringWithLegacyFallback(
|
||||
StorageKeys.asid,
|
||||
StorageKeys.legacyFacebookId,
|
||||
);
|
||||
}
|
||||
setFacebookId(id: string): Promise<ResultT<void>> {
|
||||
return SpAsyncUtil.setString(StorageKeys.facebookId, id);
|
||||
setAsid(id: string): Promise<ResultT<void>> {
|
||||
return SpAsyncUtil.setString(StorageKeys.asid, id);
|
||||
}
|
||||
clearFacebookId(): Promise<ResultT<void>> {
|
||||
return SpAsyncUtil.remove(StorageKeys.facebookId);
|
||||
clearAsid(): Promise<ResultT<void>> {
|
||||
return removeKeys(StorageKeys.asid, StorageKeys.legacyFacebookId);
|
||||
}
|
||||
|
||||
// ---- facebook app id ----
|
||||
|
||||
getFacebookAppId(): Promise<ResultT<string | null>> {
|
||||
return SpAsyncUtil.getString(StorageKeys.facebookAppId);
|
||||
async getPsid(): Promise<ResultT<string | null>> {
|
||||
return getStringWithLegacyFallback(
|
||||
StorageKeys.psid,
|
||||
StorageKeys.legacyFacebookAppId,
|
||||
);
|
||||
}
|
||||
setFacebookAppId(id: string): Promise<ResultT<void>> {
|
||||
return SpAsyncUtil.setString(StorageKeys.facebookAppId, id);
|
||||
setPsid(id: string): Promise<ResultT<void>> {
|
||||
return SpAsyncUtil.setString(StorageKeys.psid, id);
|
||||
}
|
||||
clearFacebookAppId(): Promise<ResultT<void>> {
|
||||
return SpAsyncUtil.remove(StorageKeys.facebookAppId);
|
||||
clearPsid(): Promise<ResultT<void>> {
|
||||
return removeKeys(StorageKeys.psid, StorageKeys.legacyFacebookAppId);
|
||||
}
|
||||
|
||||
// ---- bulk clear ----
|
||||
@@ -120,9 +124,9 @@ export class AuthStorage implements IAuthStorage {
|
||||
if (!r1.success) return r1;
|
||||
const r2 = await this.clearRefreshToken();
|
||||
if (!r2.success) return r2;
|
||||
const r3 = await this.clearFacebookId();
|
||||
const r3 = await this.clearAsid();
|
||||
if (!r3.success) return r3;
|
||||
const r4 = await this.clearFacebookAppId();
|
||||
const r4 = await this.clearPsid();
|
||||
if (!r4.success) return r4;
|
||||
return this.clearLoginProvider();
|
||||
}
|
||||
@@ -136,12 +140,30 @@ export class AuthStorage implements IAuthStorage {
|
||||
if (!r3.success) return r3;
|
||||
const r4 = await this.clearRefreshToken();
|
||||
if (!r4.success) return r4;
|
||||
const r5 = await this.clearFacebookId();
|
||||
const r5 = await this.clearAsid();
|
||||
if (!r5.success) return r5;
|
||||
return this.clearFacebookAppId();
|
||||
return this.clearPsid();
|
||||
}
|
||||
}
|
||||
|
||||
async function getStringWithLegacyFallback(
|
||||
key: string,
|
||||
legacyKey: string,
|
||||
): Promise<ResultT<string | null>> {
|
||||
const value = await SpAsyncUtil.getString(key);
|
||||
if (!value.success) return value;
|
||||
if (value.data !== null && value.data.length > 0) return value;
|
||||
return SpAsyncUtil.getString(legacyKey);
|
||||
}
|
||||
|
||||
async function removeKeys(...keys: string[]): Promise<ResultT<void>> {
|
||||
for (const key of keys) {
|
||||
const result = await SpAsyncUtil.remove(key);
|
||||
if (!result.success) return result;
|
||||
}
|
||||
return Result.ok(undefined);
|
||||
}
|
||||
|
||||
function isLoginStatus(value: string): value is LoginStatusT {
|
||||
return Object.values(LoginStatus).includes(value as LoginStatusT);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
* IAuthStorage 接口
|
||||
*
|
||||
* 对齐 Dart 端 `IAuthStorage`(lib/data/services/storage/iauth_storage.dart):
|
||||
* - loginToken / loginProvider / guestToken / deviceId / refreshToken / facebookId / facebookAppId 字段的 CRUD
|
||||
* - loginToken / loginProvider / guestToken / deviceId / refreshToken / ASID / PSID 字段的 CRUD
|
||||
* - `clearAuthData()` 批量清空登录态
|
||||
* - `clearBusinessAuthData()` 仅清空真实用户登录态,保留 guest token / deviceId
|
||||
* - 所有方法返回 `Promise<Result<T>>`,与 Dart `Future<Result<T>>` 对齐
|
||||
@@ -35,13 +35,13 @@ export interface IAuthStorage {
|
||||
setRefreshToken(token: string): Promise<Result<void>>;
|
||||
clearRefreshToken(): Promise<Result<void>>;
|
||||
|
||||
getFacebookId(): Promise<Result<string | null>>;
|
||||
setFacebookId(id: string): Promise<Result<void>>;
|
||||
clearFacebookId(): Promise<Result<void>>;
|
||||
getAsid(): Promise<Result<string | null>>;
|
||||
setAsid(id: string): Promise<Result<void>>;
|
||||
clearAsid(): Promise<Result<void>>;
|
||||
|
||||
getFacebookAppId(): Promise<Result<string | null>>;
|
||||
setFacebookAppId(id: string): Promise<Result<void>>;
|
||||
clearFacebookAppId(): Promise<Result<void>>;
|
||||
getPsid(): Promise<Result<string | null>>;
|
||||
setPsid(id: string): Promise<Result<void>>;
|
||||
clearPsid(): Promise<Result<void>>;
|
||||
|
||||
clearBusinessAuthData(): Promise<Result<void>>;
|
||||
clearAuthData(): Promise<Result<void>>;
|
||||
|
||||
@@ -13,8 +13,10 @@ export const StorageKeys = {
|
||||
guestToken: "guest_token",
|
||||
deviceId: "device_id",
|
||||
refreshToken: "refresh_token",
|
||||
facebookId: "facebook_id",
|
||||
facebookAppId: "facebook_app_id",
|
||||
asid: "asid",
|
||||
psid: "psid",
|
||||
legacyFacebookId: "facebook_id",
|
||||
legacyFacebookAppId: "facebook_app_id",
|
||||
|
||||
// user
|
||||
user: "user",
|
||||
|
||||
@@ -32,12 +32,12 @@ function makeAuthStorageMock(input: {
|
||||
),
|
||||
setRefreshToken: vi.fn(async () => Result.ok(undefined)),
|
||||
clearRefreshToken: vi.fn(async () => Result.ok(undefined)),
|
||||
getFacebookId: vi.fn(async () => Result.ok(null)),
|
||||
setFacebookId: vi.fn(async () => Result.ok(undefined)),
|
||||
clearFacebookId: vi.fn(async () => Result.ok(undefined)),
|
||||
getFacebookAppId: vi.fn(async () => Result.ok(null)),
|
||||
setFacebookAppId: vi.fn(async () => Result.ok(undefined)),
|
||||
clearFacebookAppId: vi.fn(async () => Result.ok(undefined)),
|
||||
getAsid: vi.fn(async () => Result.ok(null)),
|
||||
setAsid: vi.fn(async () => Result.ok(undefined)),
|
||||
clearAsid: vi.fn(async () => Result.ok(undefined)),
|
||||
getPsid: vi.fn(async () => Result.ok(null)),
|
||||
setPsid: vi.fn(async () => Result.ok(undefined)),
|
||||
clearPsid: vi.fn(async () => Result.ok(undefined)),
|
||||
clearBusinessAuthData: vi.fn(async () => Result.ok(undefined)),
|
||||
clearAuthData: vi.fn(async () => Result.ok(undefined)),
|
||||
};
|
||||
|
||||
@@ -8,26 +8,26 @@ import { UrlLauncherUtil } from "@/utils";
|
||||
export async function openChatInExternalBrowser(): Promise<void> {
|
||||
const authStorage = AuthStorage.getInstance();
|
||||
const userStorage = UserStorage.getInstance();
|
||||
const [deviceIdR, facebookIdR, facebookAppIdR, avatarUrlR] =
|
||||
const [deviceIdR, asidR, psidR, avatarUrlR] =
|
||||
await Promise.all([
|
||||
authStorage.getDeviceId(),
|
||||
authStorage.getFacebookId(),
|
||||
authStorage.getFacebookAppId(),
|
||||
authStorage.getAsid(),
|
||||
authStorage.getPsid(),
|
||||
userStorage.getAvatarUrl(),
|
||||
]);
|
||||
|
||||
const deviceId = deviceIdR.success ? deviceIdR.data : null;
|
||||
const facebookId = facebookIdR.success ? facebookIdR.data : null;
|
||||
const facebookAppId = facebookAppIdR.success ? facebookAppIdR.data : null;
|
||||
const asid = asidR.success ? asidR.data : null;
|
||||
const psid = psidR.success ? psidR.data : null;
|
||||
const avatarUrl = avatarUrlR.success ? avatarUrlR.data : null;
|
||||
|
||||
if (deviceId && facebookId) {
|
||||
if (deviceId && asid) {
|
||||
UrlLauncherUtil.openUrlWithExternalBrowser(ROUTES.externalEntry, {
|
||||
queryParams: {
|
||||
target: "chat",
|
||||
deviceId,
|
||||
fbid: facebookId,
|
||||
...(facebookAppId ? { fb_app_id: facebookAppId } : {}),
|
||||
asid,
|
||||
...(psid ? { psid } : {}),
|
||||
...(avatarUrl ? { avatarUrl } : {}),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -11,8 +11,8 @@ export type ExternalEntryTarget =
|
||||
|
||||
export interface ExternalEntryPayload {
|
||||
deviceId?: string | null;
|
||||
facebookId?: string | null;
|
||||
facebookAppId?: string | null;
|
||||
asid?: string | null;
|
||||
psid?: string | null;
|
||||
avatarUrl?: string | null;
|
||||
}
|
||||
|
||||
@@ -37,8 +37,8 @@ export function resolveExternalEntryTarget({
|
||||
|
||||
export async function persistExternalEntryPayload({
|
||||
deviceId,
|
||||
facebookId,
|
||||
facebookAppId,
|
||||
asid,
|
||||
psid,
|
||||
avatarUrl,
|
||||
}: ExternalEntryPayload): Promise<void> {
|
||||
const authStorage = AuthStorage.getInstance();
|
||||
@@ -48,11 +48,11 @@ export async function persistExternalEntryPayload({
|
||||
if (hasValue(deviceId)) {
|
||||
tasks.push(authStorage.setDeviceId(deviceId));
|
||||
}
|
||||
if (hasValue(facebookId)) {
|
||||
tasks.push(authStorage.setFacebookId(facebookId));
|
||||
if (hasValue(asid)) {
|
||||
tasks.push(authStorage.setAsid(asid));
|
||||
}
|
||||
if (hasValue(facebookAppId)) {
|
||||
tasks.push(authStorage.setFacebookAppId(facebookAppId));
|
||||
if (hasValue(psid)) {
|
||||
tasks.push(authStorage.setPsid(psid));
|
||||
}
|
||||
if (hasValue(avatarUrl)) {
|
||||
tasks.push(userStorage.setAvatarUrl(avatarUrl));
|
||||
|
||||
@@ -148,7 +148,7 @@ export const syncFacebookBackendActor = fromPromise<
|
||||
|
||||
/**
|
||||
* 用 accessToken 调 Facebook Graph `/me` 拉用户资料,写本地:
|
||||
* - `id` → `AuthStorage.setFacebookId()`(独立于后端 userId)
|
||||
* - `id` → `AuthStorage.setAsid()`(独立于后端 userId)
|
||||
* - `pictureUrl` → `UserStorage.setAvatarUrl()`(头像专用 slot,便于快速读取)
|
||||
*
|
||||
* 全部 best-effort:单条失败只 warn,不抛错。`pictureUrl == null` 也算成功
|
||||
@@ -174,11 +174,11 @@ async function persistFacebookProfile(accessToken: string): Promise<void> {
|
||||
|
||||
const authStorage = AuthStorage.getInstance();
|
||||
if (fbUser.id) {
|
||||
const r = await authStorage.setFacebookId(fbUser.id);
|
||||
const r = await authStorage.setAsid(fbUser.id);
|
||||
if (!r.success) {
|
||||
log.warn(
|
||||
{ err: r.error.message },
|
||||
"[auth-machine] syncFacebookBackendActor: setFacebookId failed",
|
||||
"[auth-machine] syncFacebookBackendActor: setAsid failed",
|
||||
);
|
||||
}
|
||||
}
|
||||
@@ -247,13 +247,13 @@ export const checkAuthStatusActor = fromPromise<LoginStatus, void>(async () => {
|
||||
return "email" as LoginStatus;
|
||||
}
|
||||
|
||||
// 3. 外部浏览器深链同步:仅 fbid 落地时,用它换业务 token。
|
||||
const facebookIdR = await storage.getFacebookId();
|
||||
if (facebookIdR.success && facebookIdR.data) {
|
||||
// 3. 外部浏览器深链同步:仅 asid 落地时,用它换业务 token。
|
||||
const asidR = await storage.getAsid();
|
||||
if (asidR.success && asidR.data) {
|
||||
const authRepo = getAuthRepository();
|
||||
const avatarUrlR = await UserStorage.getInstance().getAvatarUrl();
|
||||
const result = await authRepo.facebookIdLogin({
|
||||
fbId: facebookIdR.data,
|
||||
fbId: asidR.data,
|
||||
avatarUrl: avatarUrlR.success ? avatarUrlR.data ?? undefined : undefined,
|
||||
});
|
||||
|
||||
|
||||
Reference in New Issue
Block a user