diff --git a/src/background/messages/login.ts b/src/background/messages/login.ts index 27630d6..2982ae4 100644 --- a/src/background/messages/login.ts +++ b/src/background/messages/login.ts @@ -1,24 +1,35 @@ import type { PlasmoMessaging } from "@plasmohq/messaging"; import { BskyClient } from "../../lib/bskyClient"; +import { AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE } from "~lib/constants"; +import { ComAtprotoServerCreateSession } from "@atproto/api"; const handler: PlasmoMessaging.MessageHandler = async (req, res) => { - const { identifier, password } = req.body; + const { identifier, password, authFactorToken } = req.body; try { const agent = await BskyClient.createAgent({ identifier, password, + ...(authFactorToken && { authFactorToken: authFactorToken }), }); res.send({ session: agent.session, }); } catch (e) { - res.send({ - error: { - message: e.message, - }, - }); + if (e instanceof ComAtprotoServerCreateSession.AuthFactorTokenRequiredError) { + res.send({ + error: { + message: AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE, + }, + }); + } else { + res.send({ + error: { + message: e.message, + }, + }); + } } }; diff --git a/src/lib/bskyClient.ts b/src/lib/bskyClient.ts index 111e4f1..0a06a96 100644 --- a/src/lib/bskyClient.ts +++ b/src/lib/bskyClient.ts @@ -6,6 +6,7 @@ const clientCache = new Map(); export type BskyLoginParams = { identifier: string; password: string; + authFactorToken?: string; }; export class BskyClient { @@ -49,11 +50,13 @@ export class BskyClient { public static async createAgent({ identifier, password, + authFactorToken, }: BskyLoginParams): Promise { const client = new BskyClient(); const { data } = await client.agent.login({ identifier, password, + ...(authFactorToken && { authFactorToken }), }); client.me = { did: data.did, diff --git a/src/lib/constants.ts b/src/lib/constants.ts index 08746c5..f9829f1 100644 --- a/src/lib/constants.ts +++ b/src/lib/constants.ts @@ -73,3 +73,5 @@ export const MATCH_TYPE_LABEL_AND_COLOR = { color: "secondary", }, }; + +export const AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE = "AuthFactorTokenRequiredError"; diff --git a/src/popup.tsx b/src/popup.tsx index ddc64cb..c94d02e 100644 --- a/src/popup.tsx +++ b/src/popup.tsx @@ -11,6 +11,7 @@ import { MESSAGE_TYPE, STORAGE_KEYS, TARGET_URLS_REGEX, + AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE, } from "~lib/constants"; function IndexPopup() { @@ -18,6 +19,9 @@ function IndexPopup() { const [password, setPassword] = useState(""); const [identifier, setIdentifier] = useState(""); const [reloadCount, setReloadCount] = useState(0); + const [authFactorToken, setAuthFactorToken] = useState(""); + const [isShowAuthFactorTokenInput, setIsShowAuthFactorTokenInput] = + useState(false); const [message, setMessage] = useState r.test(currentUrl))) { setErrorMessage( - "Error: Invalid page. please open the Twitter following or blocking page.", + "Error: Invalid page. please open the 𝕏 following or blocking or list page.", ); return; } @@ -101,10 +105,15 @@ function IndexPopup() { body: { identifier: formattedIdentifier, password, + ...(authFactorToken && { authFactorToken: authFactorToken.trim() }), }, }); if (res.hasError) { - setErrorMessage(res.message); + if (res.message === AUTH_FACTOR_TOKEN_REQUIRED_ERROR_MESSAGE) { + setIsShowAuthFactorTokenInput(true); + } else { + setErrorMessage(res.message); + } } else { window.close(); } @@ -210,6 +219,40 @@ function IndexPopup() { className="input input-bordered input-sm w-full max-w-xs join-item focus:outline-none" /> + {isShowAuthFactorTokenInput && ( + <> + +

+ A 2FA token has been sent to your email. Please enter the token + above. +

+ + )}