From e0e238d79ef491fcb4f374083f5699c763f8b05d Mon Sep 17 00:00:00 2001 From: RomanDavlyatshin Date: Mon, 11 Dec 2023 18:55:18 +0400 Subject: [PATCH] feature: add simple auth (username + password) TODO: sign out button --- src/background/backend-api.ts | 14 +--- .../pages/start-new-manual-test/index.tsx | 2 +- src/forms/connection-form/connection-form.tsx | 75 ++++++++++++++----- src/manifest.json | 2 +- src/popup-script/api/index.ts | 1 - src/popup-script/api/login.ts | 14 ---- src/popup-script/app.tsx | 8 +- 7 files changed, 67 insertions(+), 49 deletions(-) delete mode 100644 src/popup-script/api/index.ts delete mode 100644 src/popup-script/api/login.ts diff --git a/src/background/backend-api.ts b/src/background/backend-api.ts index d5af20e..fe04a7f 100644 --- a/src/background/backend-api.ts +++ b/src/background/backend-api.ts @@ -5,7 +5,6 @@ import { DrillSocket } from '../common/connection/drill-socket'; import { SessionActionError } from '../common/errors/session-action-error'; import { TestInfo } from './types'; -const AUTH_TOKEN_HEADER_NAME = 'Authorization'; export default async (backendUrl: string, errorCb: any, completeCb: any) => { const token = await setupAxios(backendUrl); @@ -162,7 +161,7 @@ async function setupAxios(backendUrl: string) { axios.interceptors.request.use(async (config) => { // eslint-disable-next-line no-param-reassign - config.headers[AUTH_TOKEN_HEADER_NAME] = `Bearer ${authToken}`; + config.headers.authorization = `Bearer ${authToken}`; return config; }); @@ -185,15 +184,8 @@ async function setupAxios(backendUrl: string) { async function getAuthToken() { const { token } = await browser.storage.local.get('token'); - if (token) return token; - return login(); -} - -async function login() { - const { headers } = await axios.post('/login'); - const authToken = headers[AUTH_TOKEN_HEADER_NAME.toLowerCase()]; - if (!authToken) throw new Error('Backend authentication failed'); - return authToken; + if (!token) throw new Error("No authorization token found. Sign in required") + return token; } async function sendSessionAction(baseUrl: string, payload: unknown) { diff --git a/src/content-script/pages/start-new-manual-test/index.tsx b/src/content-script/pages/start-new-manual-test/index.tsx index 665c7f3..8e1bf0c 100644 --- a/src/content-script/pages/start-new-manual-test/index.tsx +++ b/src/content-script/pages/start-new-manual-test/index.tsx @@ -48,7 +48,7 @@ export const StartNewManualTest = () => { setIsRealTime(el.target.checked)} + onChange={(el: any) => setIsRealTime(el.target.checked)} />
diff --git a/src/forms/connection-form/connection-form.tsx b/src/forms/connection-form/connection-form.tsx index da3f9f8..26cbf35 100644 --- a/src/forms/connection-form/connection-form.tsx +++ b/src/forms/connection-form/connection-form.tsx @@ -1,8 +1,8 @@ /* eslint-disable react-hooks/rules-of-hooks */ import * as React from 'react'; -import { Button, FormGroup, Spinner } from '@drill4j/ui-kit'; +import axios from 'axios'; +import { Button, Spinner, GeneralAlerts } from '@drill4j/ui-kit'; import { Form, Field } from 'react-final-form'; - import { BackendConnectionStatus } from '../../common/enums'; import { useBackendConnectionStatus } from '../../hooks'; import { Fields } from '../fields'; @@ -11,13 +11,16 @@ import { parseURL } from '../../utils'; import * as localStorageUtil from '../../common/util/local-storage'; const validators = composeValidators( - required('backendAddress', 'Admin URL'), - validateAddress('backendAddress', 'Admin API URL is not correct. Please enter a valid URL matching the "http(s)://host(:port)" format.'), + required('backendAddress', 'Drill4J Admin Address'), + required('username', 'Username'), + required('password', 'Password'), + validateAddress('backendAddress', 'Please enter a valid URL matching the "http(s)://host(:port)" format.'), ); export const ConnectionForm = () => { const [isLoading, setIsLoading] = React.useState(false); const [initial, setInitial] = React.useState | null>(null); + const [signInError, setError] = React.useState("") React.useEffect(() => { (async () => { const data = await localStorageUtil.get('backendAddress'); @@ -31,8 +34,28 @@ export const ConnectionForm = () => {
{ - setIsLoading(true); - await localStorageUtil.save(data); + setIsLoading(true) + setError("") + const { backendAddress, username, password } = data + try { + const response = await axios.post(`${backendAddress}/api/sign-in`, { + username, + password, + }) + const token = response.headers.authorization; + await localStorageUtil.save({ backendAddress, token }) + } catch (e: any) { + if (e.isAxiosError) { + if (e.response.status == 401) { + setError("Invalid username or password") + } else { + console.log('Sign in attempt failed. Reason:',e) + setError("Unexpected error. Please contact Drill4J instance Adminstrator. To find error log press F12 and open 'Console' tab") + } + } + } finally { + setIsLoading(false) + } }} validate={validators} render={({ @@ -45,15 +68,32 @@ export const ConnectionForm = () => { }); const prevValue = prevValueRef.current; return ( -
- - - +
+ + + + + + + {signInError && {signInError}}
); }} diff --git a/src/manifest.json b/src/manifest.json index 5d8a6fd..1f5bc5b 100644 --- a/src/manifest.json +++ b/src/manifest.json @@ -1,6 +1,6 @@ { "name": "Drill4J Browser Extension", - "version": "0.3.40", + "version": "0.3.41", "background": { "page": "background.html", "persistent": true diff --git a/src/popup-script/api/index.ts b/src/popup-script/api/index.ts deleted file mode 100644 index d572cd8..0000000 --- a/src/popup-script/api/index.ts +++ /dev/null @@ -1 +0,0 @@ -export { login } from './login'; diff --git a/src/popup-script/api/login.ts b/src/popup-script/api/login.ts deleted file mode 100644 index edef4b2..0000000 --- a/src/popup-script/api/login.ts +++ /dev/null @@ -1,14 +0,0 @@ -import axios from 'axios'; -import { browser } from 'webextension-polyfill-ts'; - -import { TOKEN_HEADER } from '../../common/constants'; - -export async function login() { - axios.post('/login').then((response) => { - const authToken = response.headers[TOKEN_HEADER.toLowerCase()]; - - if (authToken) { - browser.storage.local.set({ token: authToken }); - } - }); -} diff --git a/src/popup-script/app.tsx b/src/popup-script/app.tsx index d05bfb8..321e9f0 100644 --- a/src/popup-script/app.tsx +++ b/src/popup-script/app.tsx @@ -18,11 +18,11 @@ export const App = () => { {backendConnectionData?.data === BackendConnectionStatus.AVAILABLE && hasAssociatedAgent && } {backendConnectionData?.data === BackendConnectionStatus.AVAILABLE && !hasAssociatedAgent && } {backendConnectionData?.data !== BackendConnectionStatus.AVAILABLE && ( -
-
- No connection with Drill4J Admin Backend service. Please enter the valid address (by default its hosted on port :8090). +
+
+ Sign in
- +
)}