Skip to content

Commit

Permalink
feature: add simple auth (username + password)
Browse files Browse the repository at this point in the history
TODO: sign out button
  • Loading branch information
RomanDavlyatshin committed Dec 11, 2023
1 parent 823c9e9 commit e0e238d
Show file tree
Hide file tree
Showing 7 changed files with 67 additions and 49 deletions.
14 changes: 3 additions & 11 deletions src/background/backend-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
});

Expand All @@ -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) {
Expand Down
2 changes: 1 addition & 1 deletion src/content-script/pages/start-new-manual-test/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ export const StartNewManualTest = () => {
<Checkbox
disabled={isFormSubmitting}
checked={isRealtime}
onChange={(el) => setIsRealTime(el.target.checked)}
onChange={(el: any) => setIsRealTime(el.target.checked)}
/>
</span>
<div title={!testName ? 'Enter a test name to start testing' : undefined}>
Expand Down
75 changes: 58 additions & 17 deletions src/forms/connection-form/connection-form.tsx
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<Record<string, unknown> | null>(null);
const [signInError, setError] = React.useState("")
React.useEffect(() => {
(async () => {
const data = await localStorageUtil.get('backendAddress');
Expand All @@ -31,8 +34,28 @@ export const ConnectionForm = () => {
<Form
initialValues={initial}
onSubmit={async (data: any) => {
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={({
Expand All @@ -45,15 +68,32 @@ export const ConnectionForm = () => {
});
const prevValue = prevValueRef.current;
return (
<div className="d-flex flex-column gy-6">
<FormGroup label="Admin API URL">
<Field
name="backendAddress"
component={Fields.Input}
placeholder="http(s)://host(:port)"
disabled={submitting || isLoading || isReconnecting}
/>
</FormGroup>
<div className="d-flex flex-column gy-2">
<label htmlFor="backendAddress">Drill4J Admin Backend Address</label>
<Field
label="Admin API URL"
name="backendAddress"
component={Fields.Input}
placeholder="http(s)://host(:port)"
disabled={submitting || isLoading || isReconnecting}
/>
<label htmlFor="username">Username</label>
<Field
label="Username"
name="username"
component={Fields.Input}
placeholder="Enter username"
disabled={submitting || isLoading || isReconnecting}
/>
<label htmlFor="password">Password</label>
<Field
label="Password"
name="password"
type='password'
component={Fields.Input}
placeholder="Enter password"
disabled={submitting || isLoading || isReconnecting}
/>
<Button
className="mr-auto"
type="primary"
Expand All @@ -64,7 +104,7 @@ export const ConnectionForm = () => {
{(submitting || isLoading) && (
<>
<Spinner className="mr-2" />
<span>Connecting...</span>
<span>Signing in...</span>
</>
)}
{isReconnecting && (
Expand All @@ -75,8 +115,9 @@ export const ConnectionForm = () => {
</span>
</>
)}
{!submitting && !isLoading && !isReconnecting && 'Connect'}
{!submitting && !isLoading && !isReconnecting && 'Sign in'}
</Button>
{signInError && <GeneralAlerts type="ERROR">{signInError}</GeneralAlerts>}
</div>
);
}}
Expand Down
2 changes: 1 addition & 1 deletion src/manifest.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "Drill4J Browser Extension",
"version": "0.3.40",
"version": "0.3.41",
"background": {
"page": "background.html",
"persistent": true
Expand Down
1 change: 0 additions & 1 deletion src/popup-script/api/index.ts

This file was deleted.

14 changes: 0 additions & 14 deletions src/popup-script/api/login.ts

This file was deleted.

8 changes: 4 additions & 4 deletions src/popup-script/app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,11 @@ export const App = () => {
{backendConnectionData?.data === BackendConnectionStatus.AVAILABLE && hasAssociatedAgent && <MainPage agent={agent} />}
{backendConnectionData?.data === BackendConnectionStatus.AVAILABLE && !hasAssociatedAgent && <AgentNotFound />}
{backendConnectionData?.data !== BackendConnectionStatus.AVAILABLE && (
<div className="d-flex flex-column h-100 pt-12 pb-4 px-4 gy-8">
<div className="regular monochrome-default fs-14 lh-20">
No connection with Drill4J Admin Backend service. Please enter the valid address (by default its hosted on port :8090).
<div className="d-flex flex-column h-100 p-4">
<div className="regular monochrome-default fs-14 mb-2">
Sign in
</div>
<ConnectionForm />
<ConnectionForm/>
</div>
)}
</div>
Expand Down

0 comments on commit e0e238d

Please sign in to comment.