diff options
| author | kamtschatka <simon.schatka@gmx.at> | 2024-09-21 21:17:12 +0200 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2024-09-21 20:17:12 +0100 |
| commit | 26521b70a79c42442f44c8053590bbb8c5e5f1b1 (patch) | |
| tree | bdcf9cf835c16e76cc520d92c2e261ca8526746e /apps/browser-extension/src | |
| parent | 9dd6f216ad18c09a28eaad67411d3a0e7f57a04f (diff) | |
| download | karakeep-26521b70a79c42442f44c8053590bbb8c5e5f1b1.tar.zst | |
feature(extension): Allow login directly with an API key
* [Feature request] NextAuth Providers for OAuth/SSO #92
Added API key based authentication to the extension to make the extension usable when OAuth is in use
* Minor UI tweak
---------
Co-authored-by: MohamedBassem <me@mbassem.com>
Diffstat (limited to 'apps/browser-extension/src')
| -rw-r--r-- | apps/browser-extension/src/SignInPage.tsx | 94 |
1 files changed, 86 insertions, 8 deletions
diff --git a/apps/browser-extension/src/SignInPage.tsx b/apps/browser-extension/src/SignInPage.tsx index 4e846070..f1899d5a 100644 --- a/apps/browser-extension/src/SignInPage.tsx +++ b/apps/browser-extension/src/SignInPage.tsx @@ -7,14 +7,20 @@ import Logo from "./Logo"; import usePluginSettings from "./utils/settings"; import { api } from "./utils/trpc"; +const enum LoginState { + NONE = "NONE", + USERNAME_PASSWORD = "USERNAME/PASSWORD", + API_KEY = "API_KEY", +} + export default function SignInPage() { const navigate = useNavigate(); const { setSettings } = usePluginSettings(); const { mutate: login, - error, - isPending, + error: usernamePasswordError, + isPending: userNamePasswordRequestIsPending, } = api.apiKeys.exchange.useMutation({ onSuccess: (resp) => { setSettings((s) => ({ ...s, apiKey: resp.key, apiKeyId: resp.id })); @@ -22,6 +28,20 @@ export default function SignInPage() { }, }); + const { + mutate: validateApiKey, + error: apiKeyValidationError, + isPending: apiKeyValueRequestIsPending, + } = api.apiKeys.validate.useMutation({ + onSuccess: () => { + setSettings((s) => ({ ...s, apiKey: apiKeyFormData.apiKey })); + navigate("/options"); + }, + }); + + const [lastLoginAttemptSource, setLastLoginAttemptSource] = + useState<LoginState>(LoginState.NONE); + const [formData, setFormData] = useState<{ email: string; password: string; @@ -30,18 +50,40 @@ export default function SignInPage() { password: "", }); - const onSubmit = (e: React.FormEvent) => { + const [apiKeyFormData, setApiKeyFormData] = useState<{ + apiKey: string; + }>({ + apiKey: "", + }); + + const onUserNamePasswordSubmit = (e: React.FormEvent) => { e.preventDefault(); + setLastLoginAttemptSource(LoginState.USERNAME_PASSWORD); const randStr = (Math.random() + 1).toString(36).substring(5); login({ ...formData, keyName: `Browser extension: (${randStr})` }); }; + const onApiKeySubmit = (e: React.FormEvent) => { + e.preventDefault(); + setLastLoginAttemptSource(LoginState.API_KEY); + validateApiKey({ ...apiKeyFormData }); + }; + let errorMessage = ""; - if (error) { - if (error.data?.code == "UNAUTHORIZED") { + let loginError; + switch (lastLoginAttemptSource) { + case LoginState.USERNAME_PASSWORD: + loginError = usernamePasswordError; + break; + case LoginState.API_KEY: + loginError = apiKeyValidationError; + break; + } + if (loginError) { + if (loginError.data?.code == "UNAUTHORIZED") { errorMessage = "Wrong username or password"; } else { - errorMessage = error.message; + errorMessage = loginError.message; } } @@ -50,7 +92,10 @@ export default function SignInPage() { <Logo /> <p className="text-lg">Login</p> <p className="text-red-500">{errorMessage}</p> - <form className="flex flex-col gap-y-2" onSubmit={onSubmit}> + <form + className="flex flex-col gap-y-2" + onSubmit={onUserNamePasswordSubmit} + > <div className="flex flex-col gap-y-1"> <label className="my-auto font-bold">Email</label> <Input @@ -78,10 +123,43 @@ export default function SignInPage() { className="h-8 flex-1 rounded-lg border border-gray-300 p-2" /> </div> - <Button type="submit" disabled={isPending}> + <Button + type="submit" + disabled={ + userNamePasswordRequestIsPending || apiKeyValueRequestIsPending + } + > Login </Button> </form> + <div className="flex w-full flex-row items-center gap-3"> + <hr className="flex-1" /> + Or + <hr className="flex-1" /> + </div> + + <form className="flex flex-col gap-y-2" onSubmit={onApiKeySubmit}> + <div className="flex flex-col gap-y-1"> + <label className="my-auto font-bold">API Key</label> + <Input + value={apiKeyFormData.apiKey} + onChange={(e) => + setApiKeyFormData((f) => ({ ...f, apiKey: e.target.value })) + } + type="text" + name="apiKey" + className="h-8 flex-1 rounded-lg border border-gray-300 p-2" + /> + </div> + <Button + type="submit" + disabled={ + userNamePasswordRequestIsPending || apiKeyValueRequestIsPending + } + > + Login with API key + </Button> + </form> </div> ); } |
