aboutsummaryrefslogtreecommitdiffstats
path: root/apps/browser-extension
diff options
context:
space:
mode:
authorkamtschatka <simon.schatka@gmx.at>2024-09-21 21:17:12 +0200
committerGitHub <noreply@github.com>2024-09-21 20:17:12 +0100
commit26521b70a79c42442f44c8053590bbb8c5e5f1b1 (patch)
treebdcf9cf835c16e76cc520d92c2e261ca8526746e /apps/browser-extension
parent9dd6f216ad18c09a28eaad67411d3a0e7f57a04f (diff)
downloadkarakeep-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')
-rw-r--r--apps/browser-extension/src/SignInPage.tsx94
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>
);
}