From fa467ede6f32d199f10a14a77d911667445e3303 Mon Sep 17 00:00:00 2001 From: BadAimWeeb Date: Fri, 17 Jan 2025 20:49:20 -0600 Subject: [PATCH 1/6] implement phone login --- src/screens/Login.module.scss | 28 +++++- src/screens/Login.tsx | 155 ++++++++++++++++++++++++++++------ src/services/api.ts | 24 +++++- src/types/auth.ts | 11 ++- src/utils/string.ts | 5 ++ 5 files changed, 196 insertions(+), 27 deletions(-) diff --git a/src/screens/Login.module.scss b/src/screens/Login.module.scss index e56042b..0bf386e 100644 --- a/src/screens/Login.module.scss +++ b/src/screens/Login.module.scss @@ -42,8 +42,11 @@ .Input { width: 100%; max-width: 350px; + transition: 0.2s; + border-radius: 10px; + border-color: #000; - &[type="text"] { + &[name="email"] { border-radius: 15px 15px 0px 0px; border-bottom: 1px solid #000; } @@ -51,6 +54,21 @@ &[type="password"] { border-radius: 0px 0px 15px 15px; } + + + } + + &[data-otp-step="2"] { + .Input { + &[name="phone"] { + border-radius: 15px 15px 0px 0px; + border-bottom: 1px solid #000; + } + + &[name="otp"] { + border-radius: 0px 0px 15px 15px; + } + } } .Button { @@ -65,6 +83,14 @@ gap: 0.5rem; } } + + .anotherMethod { + font-size: 12px; + margin-top: 0.6rem; + font-weight: 600; + color: var(--accent); + cursor: pointer; + } } .forkMe { diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx index ea062b6..cefba2a 100644 --- a/src/screens/Login.tsx +++ b/src/screens/Login.tsx @@ -2,7 +2,7 @@ import { useCallback, useState } from "react"; import LuckitLogo from "../components/Logo"; import cls from "./Login.module.scss"; import clsx from "clsx"; -import { validateEmail } from "../utils/string"; +import { validateE164, validateEmail } from "../utils/string"; import Spinner from "../components/Spinner"; import { API, GenericError, ResponseError } from "../services/api"; import { useMainContext } from "../MainContext"; @@ -15,12 +15,75 @@ export default function LoginScreen() { const [showWarn, setShowWarn] = useState(false); const [loading, setLoading] = useState(false); const [error, setError] = useState(""); + const [otpLogin, setOtpLogin] = useState(false); + const [phoneNumber, setPhoneNumber] = useState(""); + const [otpCode, setOtpCode] = useState(""); + const [sentOtp, setSentOtp] = useState(false); + + const handleSendOtp = useCallback(async () => { + setError(""); + setLoading(true); + + try { + const res = await API.requestOTP(phoneNumber); + + if (res) { + setSentOtp(true); + setLoading(false); + return; + } + setError("Unable to send OTP!"); + setLoading(false); + } catch (e: any) { + const error = e as ResponseError; + setLoading(false); + setError( + error.error.message === 'INVALID_PHONE_NUMBER' ? "Invalid phone number" : + "Unable to send OTP: (" + error.error.message + ")"); + } + }, [phoneNumber]); const handleLogin = useCallback(async () => { setError(""); setShowWarn(false); setLoading(true); + if (otpLogin) { + try { + const res = await API.verifyOTP(phoneNumber, otpCode); + + if (res.token) { + const user = await API.getAccountInfo(res.token); + + if (!user.users[0]) { + setError("Something went wrong, please try again"); + setLoading(false); + return; + } + + chrome.storage.local.set({ + token: res.token, + refreshToken: res.token, + user: user.users[0] as UserType + }, () => { + chrome.runtime.sendMessage({ fetchLatestMoment: true, login: true }); + mainCtx.setLoggedIn(true); + }); + return; + } + + setError("Unable to login!"); + setLoading(false); + } catch (e: any) { + const error = e as ResponseError; + setLoading(false); + setError( + error.error.message === 'INVALID_CODE' ? "Invalid OTP code" : + "We encountered an error: (" + error.error.message + ")"); + } + return; + } + try { const res = await API.login(email, password); @@ -55,7 +118,7 @@ export default function LoginScreen() { error.error.message === "USER_DISABLED" ? "User is disabled" : "We encountered an error: (" + error.error.message + ")"); } - }, [email, mainCtx, password]); + }, [otpLogin, phoneNumber, otpCode, mainCtx, email, password]); return (
@@ -98,35 +161,79 @@ export default function LoginScreen() {

-
- setEmail(e.target.value)} - /> - setPassword(e.target.value)} - /> - + : !validateE164(phoneNumber) ? "invalid phone number" : "send otp"} + : + + } + { setOtpLogin(!otpLogin); setPhoneNumber(""); setOtpCode(""); }}> + {otpLogin ? "login with email and password" : "login with phone number"} +
+

made with luv by michioxd - fork me on github

diff --git a/src/services/api.ts b/src/services/api.ts index 4083374..5f2267f 100644 --- a/src/services/api.ts +++ b/src/services/api.ts @@ -1,4 +1,4 @@ -import { LoginPayloadType, LoginResponseType, RefreshTokenPayloadType, RefreshTokenResponseType } from "../types/auth" +import { LoginPayloadType, LoginResponseType, RefreshTokenPayloadType, RefreshTokenResponseType, RequestPhoneOTPResponseType, VerifyPhoneOTPResponseType } from "../types/auth" import { MomentType } from "../types/moments" import { GetAccountInfoResponseType, UserInfoType } from "../types/user" @@ -117,6 +117,28 @@ export const API = { clientType: "CLIENT_TYPE_IOS" } }), + requestOTP: (phoneE164: string) => fetchLocket({ + endpoint: "sendVerificationCode", + method: "POST", + body: { + data: { + deviceModel: "iPhone12,1", + operation: "hybrid", + phone: phoneE164, + use_password_if_available: false + } + } + }), + verifyOTP: (phoneE164: string, code: string) => fetchLocket({ + endpoint: "verifyVerificationCode", + method: "POST", + body: { + data: { + phone: phoneE164, + verification_code: code + } + } + }), refreshToken: (refreshToken: string) => fetchFirebase({ endpoint: "https://securetoken.googleapis.com/v1/token", method: "POST", diff --git a/src/types/auth.ts b/src/types/auth.ts index 9983c1d..724bdad 100644 --- a/src/types/auth.ts +++ b/src/types/auth.ts @@ -30,4 +30,13 @@ export interface RefreshTokenResponseType { id_token: string; user_id: string; project_id: string; -} \ No newline at end of file +} + +export interface RequestPhoneOTPResponseType { + method: string; + provider: string; +} + +export interface VerifyPhoneOTPResponseType { + token: string; +} diff --git a/src/utils/string.ts b/src/utils/string.ts index dc8be9e..53d78ea 100644 --- a/src/utils/string.ts +++ b/src/utils/string.ts @@ -12,6 +12,11 @@ export function parseJwt(token: string) { return JSON.parse(jsonPayload); } +export function validateE164(phoneNumber: string) { + const reg = /^\+[1-9]\d{1,14}$/; + return reg.test(phoneNumber); +} + export const timeSinceOf = function (date: number) { const text = [ From a5e9202251c9a89c831c46992ae6e672dc4c2dfb Mon Sep 17 00:00:00 2001 From: michioxd Date: Sat, 18 Jan 2025 11:22:31 +0700 Subject: [PATCH 2/6] country selector for phone number --- package.json | 2 + pnpm-lock.yaml | 16 ++++ src/components/PhoneNumber.tsx | 166 +++++++++++++++++++++++++++++++++ src/index.scss | 19 ++++ src/modules/phone/flags.ts | 3 + src/modules/phone/phone.ts | 1 + src/screens/Login.module.scss | 15 --- src/screens/Login.tsx | 15 ++- 8 files changed, 219 insertions(+), 18 deletions(-) create mode 100644 src/components/PhoneNumber.tsx create mode 100644 src/modules/phone/flags.ts create mode 100644 src/modules/phone/phone.ts diff --git a/package.json b/package.json index eb34124..9382b6c 100644 --- a/package.json +++ b/package.json @@ -14,8 +14,10 @@ "@fontsource/manrope": "^5.1.1", "@szhsin/react-menu": "^4.2.4", "clsx": "^2.1.1", + "country-flag-icons": "^1.5.14", "events": "^3.3.0", "js-md5": "^0.8.3", + "libphonenumber-js": "^1.11.18", "react": "^18.3.1", "react-dom": "^18.3.1", "react-icons": "^5.4.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 601f31f..8346f70 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -20,12 +20,18 @@ importers: clsx: specifier: ^2.1.1 version: 2.1.1 + country-flag-icons: + specifier: ^1.5.14 + version: 1.5.14 events: specifier: ^3.3.0 version: 3.3.0 js-md5: specifier: ^0.8.3 version: 0.8.3 + libphonenumber-js: + specifier: ^1.11.18 + version: 1.11.18 react: specifier: ^18.3.1 version: 18.3.1 @@ -903,6 +909,9 @@ packages: convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} + country-flag-icons@1.5.14: + resolution: {integrity: sha512-GAFsVzHDu3bdAhbQ1LwBRqk/Ad8+ZzS5zU49P+lRla0KGy/V1V8ywNa1SxBOAmI/lyEOT9dfH3Q++q1lqJlvBA==} + cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} @@ -1207,6 +1216,9 @@ packages: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} + libphonenumber-js@1.11.18: + resolution: {integrity: sha512-okMm/MCoFrm1vByeVFLBdkFIXLSHy/AIK2AEGgY3eoicfWZeOZqv3GfhtQgICkzs/tqorAMm3a4GBg5qNCrqzg==} + locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} @@ -2375,6 +2387,8 @@ snapshots: convert-source-map@2.0.0: optional: true + country-flag-icons@1.5.14: {} + cross-spawn@7.0.6: dependencies: path-key: 3.1.1 @@ -2703,6 +2717,8 @@ snapshots: prelude-ls: 1.2.1 type-check: 0.4.0 + libphonenumber-js@1.11.18: {} + locate-path@6.0.0: dependencies: p-locate: 5.0.0 diff --git a/src/components/PhoneNumber.tsx b/src/components/PhoneNumber.tsx new file mode 100644 index 0000000..6dc6a22 --- /dev/null +++ b/src/components/PhoneNumber.tsx @@ -0,0 +1,166 @@ +import { CountryCode } from "libphonenumber-js"; +import { createElement, useEffect, useMemo, useRef, useState } from "react"; +import { Menu, MenuButton, MenuItem } from "@szhsin/react-menu"; +import clsx from "clsx"; + +export default function PhoneNumberInput(props: { + defaultCountry?: string; + value?: string; + defaultValue?: string; + onValueChange?: (value: string) => void; + onE164ValueChange?: (value: string) => void; + onSuccessChange?: (success: boolean) => void; + className?: string; +}) { + const [libPhoneNumber, setLibPhoneNumber] = useState(null); + const [flagsHandler, setFlagsHandler] = useState(null); + const inputRef = useRef(null); + + useEffect(() => { + import("../modules/phone/phone").then(setLibPhoneNumber); + import("../modules/phone/flags").then(setFlagsHandler); + }, []); + + const oldCursorPos = useRef(0); + const setCursorPos = useRef(NaN); + + const [internalValue, setInternalValue] = useState(props.defaultValue || ""); + const [country, setCountry] = useState(props.defaultCountry || "ZZ"); + + useEffect(() => { + if (props.defaultCountry) { + setCountry(props.defaultCountry); + } + }, [props.defaultCountry]); + + const formatter = useMemo(() => { + if (libPhoneNumber) { + const ayt = new libPhoneNumber.AsYouType(country as CountryCode); + setInternalValue(ayt.input(internalValue)); + if (ayt.isValid()) { + const international = (["800", "808", "870", "870", "878", "881", "882", "883", "888", "979"] as string[]).includes(ayt.getCallingCode() ?? ""); + if (ayt.getCountry() || international) { + setCountry(ayt.getCountry() || "ZZ"); + } + } + return ayt; + } else return null; + }, [country, internalValue, libPhoneNumber]); + + useEffect(() => { + if (typeof props.value === "string") + setInternalValue(props.value); + }, [props.value]); + + useEffect(() => { + if (libPhoneNumber) { + props.onValueChange?.(internalValue); + props.onE164ValueChange?.(formatter!.getNumberValue() ?? ""); + props.onSuccessChange?.(formatter!.isValid() === true); + } + }, [formatter, internalValue, libPhoneNumber, props]); + + useEffect(() => { + if (inputRef.current && !isNaN(setCursorPos.current)) { + inputRef.current.selectionStart = setCursorPos.current; + inputRef.current.selectionEnd = setCursorPos.current; + + setCursorPos.current = NaN; + } + }, [internalValue]); + + const selectFlagList = useMemo(() => { + if (!libPhoneNumber) return []; + + return libPhoneNumber?.getCountries().map(country => { + const FlagEl = flagsHandler?.hasFlag(country) ? flagsHandler!.Flags[country as any as keyof typeof flagsHandler.Flags] : null; + + return [( + { + setCountry(country); + if (formatter) { + formatter.reset(); + setInternalValue(formatter.input(internalValue)); + } + }}> +
+
+ {FlagEl ? : country} +
+
{country}
+
+
+ ), country] as const; + }).sort((a, b) => a[1].localeCompare(b[1])).map(x => x[0]); + }, [flagsHandler, formatter, internalValue, libPhoneNumber]); + + return ( + <> +
+ + {flagsHandler?.hasFlag(country) ? createElement(flagsHandler.Flags[country as any as keyof typeof flagsHandler.Flags], { height: 18 }) :
} + }> + {selectFlagList} +
+ { + oldCursorPos.current = e.currentTarget.selectionEnd ?? e.currentTarget.selectionStart ?? 0; + }} + onChange={e => { + if (!formatter) return; // do not allow any input if libPhoneNumber is not loaded + + const oldValue = internalValue; + let newValue = e.target.value; + + const oldValueNumber = oldValue.match(/\d|\+/g)?.join("") ?? ""; + const newValueNumber = newValue.match(/\d|\+/g)?.join("") ?? ""; + + formatter.reset(); + newValue = formatter.input(newValue); + if ((formatter.getCountry() !== country)) { + const international = (["800", "808", "870", "870", "878", "881", "882", "883", "888", "979"] as string[]).includes(formatter.getCallingCode() ?? ""); + if (formatter.getCountry() || international) { + setCountry(formatter.getCountry() || "ZZ"); + } + } + setInternalValue(newValue); + + // retain cursor position to correct number position + const cursorPos = oldCursorPos.current; + const cursorNumberPos = oldValue.slice(0, cursorPos).match(/\d|\+/g)?.length ?? 0; + let newCursorNumberPos = cursorNumberPos; + + // set new cursor position + newCursorNumberPos += newValueNumber.length - oldValueNumber.length; + + let tmpCursorNumberPos = newCursorNumberPos; + let newCursorPos = 0; + for (const char of newValue) { + if (tmpCursorNumberPos === 0) { + break; + } + + newCursorPos++; + + if (char.match(/\d|\+/)) { + tmpCursorNumberPos--; + } + } + + setCursorPos.current = newCursorPos; + }} + ref={inputRef} + /> +
+ + ) +} \ No newline at end of file diff --git a/src/index.scss b/src/index.scss index 7fe138f..5b95873 100644 --- a/src/index.scss +++ b/src/index.scss @@ -185,4 +185,23 @@ a { align-items: center; justify-content: center; } +} + +.szh-menu { + background: #000000bb; + backdrop-filter: blur(10px); + -webkit-backdrop-filter: blur(10px); + box-shadow: 0px 0px 10px 0px #0000002c; + border: 1px solid #333333b0; + border-radius: 5px; + + .szh-menu__item { + color: var(--color); + padding: 0.3rem 0.5rem; + + &:hover, + &.szh-menu__item--hover { + background: #222222bb; + } + } } \ No newline at end of file diff --git a/src/modules/phone/flags.ts b/src/modules/phone/flags.ts new file mode 100644 index 0000000..a45a48a --- /dev/null +++ b/src/modules/phone/flags.ts @@ -0,0 +1,3 @@ +export * from "country-flag-icons"; +import * as Flags from "country-flag-icons/react/3x2"; +export { Flags }; \ No newline at end of file diff --git a/src/modules/phone/phone.ts b/src/modules/phone/phone.ts new file mode 100644 index 0000000..66967fb --- /dev/null +++ b/src/modules/phone/phone.ts @@ -0,0 +1 @@ +export * from "libphonenumber-js/max"; \ No newline at end of file diff --git a/src/screens/Login.module.scss b/src/screens/Login.module.scss index 0bf386e..39979ab 100644 --- a/src/screens/Login.module.scss +++ b/src/screens/Login.module.scss @@ -54,21 +54,6 @@ &[type="password"] { border-radius: 0px 0px 15px 15px; } - - - } - - &[data-otp-step="2"] { - .Input { - &[name="phone"] { - border-radius: 15px 15px 0px 0px; - border-bottom: 1px solid #000; - } - - &[name="otp"] { - border-radius: 0px 0px 15px 15px; - } - } } .Button { diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx index cefba2a..65034e1 100644 --- a/src/screens/Login.tsx +++ b/src/screens/Login.tsx @@ -8,6 +8,7 @@ import { API, GenericError, ResponseError } from "../services/api"; import { useMainContext } from "../MainContext"; import { VscClose } from "react-icons/vsc"; import { UserType } from "../types/user"; +import PhoneNumberInput from "../components/PhoneNumber"; export default function LoginScreen() { const mainCtx = useMainContext(); const [email, setEmail] = useState(""); @@ -16,6 +17,7 @@ export default function LoginScreen() { const [loading, setLoading] = useState(false); const [error, setError] = useState(""); const [otpLogin, setOtpLogin] = useState(false); + const [pNInput, setPNInput] = useState(""); const [phoneNumber, setPhoneNumber] = useState(""); const [otpCode, setOtpCode] = useState(""); const [sentOtp, setSentOtp] = useState(false); @@ -163,7 +165,13 @@ export default function LoginScreen() {
{otpLogin ? <> - setPNInput(v)} + onE164ValueChange={(v) => setPhoneNumber(v)} + className={clsx("input", cls.Input)} + /> + {/* setPhoneNumber(e.target.value)} - /> + /> */} {sentOtp && } - { setOtpLogin(!otpLogin); setPhoneNumber(""); setOtpCode(""); }}> + { setOtpLogin(!otpLogin); setPhoneNumber(""); setPNInput(""); setOtpCode(""); }}> {otpLogin ? "login with email and password" : "login with phone number"}
From e1f0b37cb0a9a5d9aa9b23f185a7ad4bf3a53e59 Mon Sep 17 00:00:00 2001 From: michioxd Date: Sat, 18 Jan 2025 11:26:29 +0700 Subject: [PATCH 3/6] add animation --- src/components/PhoneNumber.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/PhoneNumber.tsx b/src/components/PhoneNumber.tsx index 6dc6a22..9541eaa 100644 --- a/src/components/PhoneNumber.tsx +++ b/src/components/PhoneNumber.tsx @@ -104,7 +104,9 @@ export default function PhoneNumberInput(props: { menuButton={ {flagsHandler?.hasFlag(country) ? createElement(flagsHandler.Flags[country as any as keyof typeof flagsHandler.Flags], { height: 18 }) :
} -
}> + } + transition + > {selectFlagList} Date: Sat, 18 Jan 2025 11:37:03 +0700 Subject: [PATCH 4/6] add country name for country selector --- src/components/PhoneNumber.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/PhoneNumber.tsx b/src/components/PhoneNumber.tsx index 9541eaa..b7ff50b 100644 --- a/src/components/PhoneNumber.tsx +++ b/src/components/PhoneNumber.tsx @@ -83,11 +83,13 @@ export default function PhoneNumberInput(props: { setInternalValue(formatter.input(internalValue)); } }}> -
+
{FlagEl ? : country}
-
{country}
+
+ +{libPhoneNumber.getCountryCallingCode(country as CountryCode)} {(new Intl.DisplayNames([navigator.language], { type: 'region' })).of(country)} +
), country] as const; From 00495e7a444fe34ed960794135fab62dd24751f2 Mon Sep 17 00:00:00 2001 From: BadAimWeeb Date: Fri, 17 Jan 2025 23:21:36 -0600 Subject: [PATCH 5/6] update missing token exchange --- manifest.json | 16 +++++++++++++--- net_ruleset/otpapi.json | 25 +++++++++++++++++++++++++ src/screens/Login.tsx | 17 +++++------------ src/services/api.ts | 28 +++++++++++++++++++++++----- src/types/auth.ts | 13 +++++++++++++ 5 files changed, 79 insertions(+), 20 deletions(-) create mode 100644 net_ruleset/otpapi.json diff --git a/manifest.json b/manifest.json index 06589f4..321512b 100644 --- a/manifest.json +++ b/manifest.json @@ -13,7 +13,8 @@ "permissions": [ "storage", "unlimitedStorage", - "downloads" + "downloads", + "declarativeNetRequestWithHostAccess" ], "host_permissions": [ "https://firebasestorage.googleapis.com/*", @@ -29,5 +30,14 @@ "name": "michioxd", "email": "michio.haiyaku@gmail.com" }, - "homepage_url": "https://github.com/michioxd/luckit" -} \ No newline at end of file + "homepage_url": "https://github.com/michioxd/luckit", + "declarative_net_request": { + "rule_resources": [ + { + "id": "otpapi", + "enabled": true, + "path": "net_ruleset/otpapi.json" + } + ] + } +} diff --git a/net_ruleset/otpapi.json b/net_ruleset/otpapi.json new file mode 100644 index 0000000..2f87330 --- /dev/null +++ b/net_ruleset/otpapi.json @@ -0,0 +1,25 @@ +[ + { + "id": 1, + "priority": 1, + "action": { + "type": "modifyHeaders", + "requestHeaders": [ + { + "header": "user-agent", + "operation": "set", + "value": "okhttp/4.12.0" + } + ] + }, + "condition": { + "regexFilter": "^https://api\\.locketcamera\\.com/(?:sendVerificationCode|verifyVerificationCode)$", + "initiatorDomains": [ + "api.locketcamera.com" + ], + "resourceTypes": [ + "xmlhttprequest" + ] + } + } +] \ No newline at end of file diff --git a/src/screens/Login.tsx b/src/screens/Login.tsx index 65034e1..023b05a 100644 --- a/src/screens/Login.tsx +++ b/src/screens/Login.tsx @@ -55,7 +55,9 @@ export default function LoginScreen() { const res = await API.verifyOTP(phoneNumber, otpCode); if (res.token) { - const user = await API.getAccountInfo(res.token); + const exchange = await API.exchangeOTPTokenForIDToken(res.token); + + const user = await API.getAccountInfo(exchange.idToken); if (!user.users[0]) { setError("Something went wrong, please try again"); @@ -64,8 +66,8 @@ export default function LoginScreen() { } chrome.storage.local.set({ - token: res.token, - refreshToken: res.token, + token: exchange.idToken, + refreshToken: exchange.refreshToken, user: user.users[0] as UserType }, () => { chrome.runtime.sendMessage({ fetchLatestMoment: true, login: true }); @@ -171,15 +173,6 @@ export default function LoginScreen() { onE164ValueChange={(v) => setPhoneNumber(v)} className={clsx("input", cls.Input)} /> - {/* setPhoneNumber(e.target.value)} - /> */} {sentOtp && ({ } export async function fetchLocket({ - endpoint, method, body, token + endpoint, method, body, token, headers: cHeaders }: { endpoint: string, method: string, body?: any, - token?: string + token?: string, + headers?: HeadersInit }) { const headers = new Headers(); + + if (cHeaders) { + for (const [key, value] of Object.entries(cHeaders)) { + headers.append(key, value); + } + } + headers.append('Content-Type', 'application/json'); if (token) { @@ -78,7 +86,9 @@ export async function fetchLocket({ } else { await new Promise((res) => { chrome.storage.local.get("token", (data) => { - headers.append('Authorization', `Bearer ${data.token}`); + if (data.token) + headers.append('Authorization', `Bearer ${data.token}`); + res(null); }); }); @@ -130,7 +140,7 @@ export const API = { } }), verifyOTP: (phoneE164: string, code: string) => fetchLocket({ - endpoint: "verifyVerificationCode", + endpoint: "checkVerificationCode", method: "POST", body: { data: { @@ -139,6 +149,14 @@ export const API = { } } }), + exchangeOTPTokenForIDToken: (token: string) => fetchFirebase({ + endpoint: "https://www.googleapis.com/identitytoolkit/v3/relyingparty/verifyCustomToken", + method: "POST", + body: { + returnSecureToken: true, + token + } + }), refreshToken: (refreshToken: string) => fetchFirebase({ endpoint: "https://securetoken.googleapis.com/v1/token", method: "POST", diff --git a/src/types/auth.ts b/src/types/auth.ts index 724bdad..e2b627c 100644 --- a/src/types/auth.ts +++ b/src/types/auth.ts @@ -40,3 +40,16 @@ export interface RequestPhoneOTPResponseType { export interface VerifyPhoneOTPResponseType { token: string; } + +export interface ExchangeCustomTokenPayloadType { + returnSecureToken: boolean; + token: string; +} + +export interface ExchangeCustomTokenResponseType { + expiresIn: string; + idToken: string; + isNewUser: boolean; + kind: string; + refreshToken: string; +} From cbb31d3b71de581bb81ae29461d04a0ee9d65c5c Mon Sep 17 00:00:00 2001 From: BadAimWeeb Date: Sat, 18 Jan 2025 01:36:45 -0600 Subject: [PATCH 6/6] net request modification is not needed --- manifest.json | 14 ++------------ net_ruleset/otpapi.json | 25 ------------------------- 2 files changed, 2 insertions(+), 37 deletions(-) delete mode 100644 net_ruleset/otpapi.json diff --git a/manifest.json b/manifest.json index 321512b..d6dece3 100644 --- a/manifest.json +++ b/manifest.json @@ -13,8 +13,7 @@ "permissions": [ "storage", "unlimitedStorage", - "downloads", - "declarativeNetRequestWithHostAccess" + "downloads" ], "host_permissions": [ "https://firebasestorage.googleapis.com/*", @@ -30,14 +29,5 @@ "name": "michioxd", "email": "michio.haiyaku@gmail.com" }, - "homepage_url": "https://github.com/michioxd/luckit", - "declarative_net_request": { - "rule_resources": [ - { - "id": "otpapi", - "enabled": true, - "path": "net_ruleset/otpapi.json" - } - ] - } + "homepage_url": "https://github.com/michioxd/luckit" } diff --git a/net_ruleset/otpapi.json b/net_ruleset/otpapi.json deleted file mode 100644 index 2f87330..0000000 --- a/net_ruleset/otpapi.json +++ /dev/null @@ -1,25 +0,0 @@ -[ - { - "id": 1, - "priority": 1, - "action": { - "type": "modifyHeaders", - "requestHeaders": [ - { - "header": "user-agent", - "operation": "set", - "value": "okhttp/4.12.0" - } - ] - }, - "condition": { - "regexFilter": "^https://api\\.locketcamera\\.com/(?:sendVerificationCode|verifyVerificationCode)$", - "initiatorDomains": [ - "api.locketcamera.com" - ], - "resourceTypes": [ - "xmlhttprequest" - ] - } - } -] \ No newline at end of file