Skip to content
This repository was archived by the owner on Apr 5, 2024. It is now read-only.

Commit e2d4e70

Browse files
authored
Feature/cookies login (#13)
* added cookies check and logout * fixed minor bugs * added loading * fixed bad import
1 parent b909210 commit e2d4e70

File tree

10 files changed

+113
-22
lines changed

10 files changed

+113
-22
lines changed

webapp_frontend/src/background/api/auth.ts

Lines changed: 38 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -5,21 +5,39 @@ import {hostname, userPath} from "./api";
55

66
import {AddUser, UserState} from "../redux/actions/userTypes";
77
import store from "../redux/store";
8-
import {addAccessToken, addRefreshToken} from "../redux/actions/tokens";
8+
import {addAccessToken, addRefreshToken, checkedCookies, removeTokens} from "../redux/actions/tokens";
99
import {addUser} from "../redux/actions/user";
10-
import {AccessToken, AddAccessToken, AddRefreshToken, TokensState} from "../redux/actions/tokenTypes";
10+
import {AccessToken, AddAccessToken, AddRefreshToken, CheckedCookies, RemoveTokens, TokensState} from "../redux/actions/tokenTypes";
11+
import {deleteCookie, getCookie, setCookie} from "../methods/cookies";
1112

1213

1314
// reference: https://daveceddia.com/access-redux-store-outside-react/
1415

16+
17+
const cookieName:string='refreshToken';
18+
1519
export interface BackendLoginData {
1620
refreshToken: string,
1721
user: UserState
1822

1923
}
2024

21-
export const loginWithUsernameAndPassword = (userName: string, password: string): Promise<BackendLoginData> => {
2225

26+
export const checkForCookie=()=>{
27+
let refreshTokenCookieValue=getCookie(cookieName)
28+
if (refreshTokenCookieValue){
29+
store.dispatch(addRefreshToken(refreshTokenCookieValue) as AddRefreshToken)
30+
getAccessTokenWithRefreshToken();
31+
}
32+
store.dispatch(checkedCookies(true) as CheckedCookies)
33+
34+
35+
}
36+
37+
38+
39+
export const loginWithUsernameAndPassword = (userName: string, password: string,stayLoggedIn:boolean): Promise<BackendLoginData> => {
40+
console.log("[Auth] loginWithUsernameAndPassword")
2341
return new Promise<BackendLoginData>((resolve, reject) => {
2442
let config = {
2543
headers: {
@@ -29,9 +47,15 @@ export const loginWithUsernameAndPassword = (userName: string, password: string)
2947

3048
return Axios.get(hostname + userPath + '/login', config)
3149
.then((data) => {
32-
store.dispatch(addRefreshToken(data.data.refreshToken) as AddRefreshToken)
50+
console.log(data.data)
51+
store.dispatch(addRefreshToken(data.data.tokenValue) as AddRefreshToken)
3352
store.dispatch(addUser(data.data.user as UserState) as AddUser)
3453

54+
if (stayLoggedIn){
55+
setCookie(cookieName,data.data.tokenValue,60)
56+
}
57+
58+
3559
getAccessTokenWithRefreshToken()
3660
})
3761
.catch(((error) => {
@@ -57,17 +81,25 @@ export const getAccessTokenWithRefreshToken = () => {
5781

5882
Axios.get(hostname + userPath + '/auth', config)
5983
.then((data) => {
60-
setAuthHeaderToAxios(data.data.token)
84+
setAuthHeaderToAxios(data.data.tokenValue)
6185

62-
store.dispatch(addAccessToken({token: data.data.token, timestamp: data.data.validUntil}as AccessToken) as AddAccessToken);
86+
store.dispatch(addAccessToken({token: data.data.tokenValue, timestamp: data.data.validUntil}as AccessToken) as AddAccessToken);
6387

6488
})
6589
.catch(((error) => {
90+
store.dispatch(removeTokens()as RemoveTokens);
91+
6692
console.log(error)
93+
//you probably want to notify the user, maybe with a toast or similar
6794
}));
6895

6996
}
7097

98+
export const logout=()=>{
99+
store.dispatch(removeTokens()as RemoveTokens);
100+
deleteCookie(cookieName);
101+
}
102+
71103
function setAuthHeaderToAxios(accessToken: string) {
72104
Axios.defaults.headers.common['Authorization'] =
73105
`Bearer ${accessToken}`;

webapp_frontend/src/background/constants.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,8 @@ const prod: constants = {
1111

1212
const dev: constants = {
1313
url: {
14-
API_URL: 'https://cors-anywhere.herokuapp.com/http://filefighter.ddns.net:3001',
14+
API_URL: 'https://cors-anywhere.herokuapp.com/http://filefighter.ddns.net:3001',
15+
// API_URL: 'http://localhost:8080',
1516
}
1617
};
1718
export const constants = process.env.NODE_ENV === 'development' ? dev : prod;
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
export function getCookie(cname:string) {
2+
let name = cname + "=";
3+
let decodedCookie = decodeURIComponent(document.cookie);
4+
let ca = decodedCookie.split(';');
5+
for(let i = 0; i <ca.length; i++) {
6+
let c = ca[i];
7+
while (c.charAt(0) === ' ') {
8+
c = c.substring(1);
9+
}
10+
if (c.indexOf(name) === 0) {
11+
return c.substring(name.length, c.length);
12+
}
13+
}
14+
return "";
15+
}
16+
17+
18+
export function setCookie(cname:string, cvalue:string, exdays:number) {
19+
let d = new Date();
20+
d.setTime(d.getTime() + (exdays*24*60*60*1000));
21+
let expires = "expires="+ d.toUTCString();
22+
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/";
23+
}
24+
25+
26+
export function deleteCookie(cname:string){
27+
document.cookie = cname+"=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;";
28+
}

webapp_frontend/src/background/redux/actions/tokenTypes.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
export const ADD_REFRESH_TOKEN = "ADD_REFRESH_TOKEN";
22
export const ADD_ACCESS_TOKEN = "ADD_ACCESS_TOKEN";
33
export const CHECKED_COOKIES = "CHECKED_COOKIES";
4+
export const REMOVE_TOKENS = "REMOVE_TOKENS";
45

56

67
export interface AccessToken {
@@ -26,10 +27,14 @@ export interface AddAccessToken {
2627
payload: AccessToken
2728
}
2829

29-
export interface CheckedCookies{
30+
export interface RemoveTokens {
31+
type: typeof REMOVE_TOKENS
32+
}
33+
34+
export interface CheckedCookies {
3035
type: typeof CHECKED_COOKIES,
3136
payload: boolean
3237
}
3338

34-
export type TokenActionsTypes = AddRefreshToken | AddAccessToken|CheckedCookies;
39+
export type TokenActionsTypes = AddRefreshToken | AddAccessToken | RemoveTokens | CheckedCookies;
3540

webapp_frontend/src/background/redux/actions/tokens.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ADD_REFRESH_TOKEN, ADD_ACCESS_TOKEN, AccessToken, CHECKED_COOKIES} from "./tokenTypes";
1+
import {ADD_REFRESH_TOKEN, ADD_ACCESS_TOKEN, AccessToken, CHECKED_COOKIES, REMOVE_TOKENS} from "./tokenTypes";
22

33

44
export const addRefreshToken = (content: string) => ({
@@ -11,6 +11,9 @@ export const addAccessToken = (content: AccessToken) => ({
1111
payload: content
1212
});
1313

14+
export const removeTokens = ()=>({
15+
type: REMOVE_TOKENS
16+
})
1417

1518
export const checkedCookies = (content: boolean) => ({
1619
type: CHECKED_COOKIES,

webapp_frontend/src/background/redux/reducers/tokens.tsx

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import {ADD_REFRESH_TOKEN, ADD_ACCESS_TOKEN, TokenActionsTypes, TokensState, AccessToken, CHECKED_COOKIES} from "../actions/tokenTypes";
1+
import {ADD_REFRESH_TOKEN, ADD_ACCESS_TOKEN, TokenActionsTypes, TokensState, AccessToken, CHECKED_COOKIES, REMOVE_TOKENS} from "../actions/tokenTypes";
22

33
const initialState: TokensState = {
44
refreshToken: null,
@@ -26,6 +26,13 @@ export default function (state = initialState, action: TokenActionsTypes) {
2626
checkedCookies: state.checkedCookies
2727
};
2828
}
29+
case REMOVE_TOKENS:{
30+
return {
31+
refreshToken: null,
32+
accessToken: null,
33+
checkedCookies: true
34+
}
35+
}
2936
case CHECKED_COOKIES: {
3037
return {
3138
refreshToken: state.refreshToken,

webapp_frontend/src/components/App.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {addAccessToken, addRefreshToken, checkedCookies} from "../background/red
1313
import {SystemState} from "../background/redux/actions/sytemState";
1414

1515
import Login from "./basicElements/Login";
16+
import {checkForCookie} from "../background/api/auth";
1617

1718

1819

@@ -44,7 +45,7 @@ function App(props: Props): ReactElement {
4445

4546
if (props.tokens.checkedCookies) {
4647

47-
if (props.tokens.refreshToken) {
48+
if (props.tokens.refreshToken && props.tokens.accessToken) {
4849

4950
return (
5051
<div className="App">
@@ -63,7 +64,7 @@ function App(props: Props): ReactElement {
6364
return (<Login/>)
6465
}
6566
} else {
66-
props.checkedCookies(true)
67+
checkForCookie();
6768

6869
return (<div>Loading</div>)
6970
}

webapp_frontend/src/components/basicElements/Login.tsx

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,28 @@
11
import React, {FormEvent, ReactElement, useState} from 'react';
2-
import {Button, Form, Container, Row,Col} from "react-bootstrap";
2+
import {Button, Form, Container, Row,Col,Spinner} from "react-bootstrap";
33
import {loginWithUsernameAndPassword} from "../../background/api/auth";
44

5-
65
function Login(): ReactElement {
76
const [userName, setUsername] = useState<string>("");
87
const [password, setPassword] = useState<string>("");
98
const [stayLoggedIn, setStayLoggedIn] = useState<boolean>(false);
109
const [errorMessage, setErrorMessage] = useState<string>("");
10+
const [loading,setLoading]=useState<boolean>(false);
1111

1212

1313
const handleSubmit = (event: FormEvent) => {
1414
event.preventDefault();
15-
loginWithUsernameAndPassword(userName, password)
15+
setLoading(true)
16+
loginWithUsernameAndPassword(userName, password,stayLoggedIn)
1617
.then(() => {
1718
//nothing to do here :)
1819
setErrorMessage("");
20+
setLoading(false);
1921
})
2022
.catch(((error) => {
2123
console.log(error);
22-
setErrorMessage(error.response.data.message);
24+
setLoading(false)
25+
setErrorMessage(error.response?.data.message);
2326
}))
2427

2528

@@ -44,11 +47,19 @@ function Login(): ReactElement {
4447
</Form.Text>
4548
</Form.Group>
4649
<Form.Group controlId="formBasicCheckbox">
47-
<Form.Check type="checkbox" label="Check me out" onChange={() => setStayLoggedIn(!stayLoggedIn)}/>
50+
<Form.Check type="checkbox" label="Stay logged in. (By clicking this you accept the usage of cookies.)" onChange={() => setStayLoggedIn(!stayLoggedIn)}/>
4851
</Form.Group>
49-
<Button variant="primary" type="submit">
50-
Submit
52+
<Button variant="primary" type="submit" >
53+
<Spinner
54+
as="span"
55+
animation="grow"
56+
size="sm"
57+
role="status"
58+
aria-hidden="true"
59+
className={loading? "" :"d-none"}
60+
/> <span className={loading? "sr-only" :"d-none"}>Loading...</span>Submit
5161
</Button>
62+
5263
<p className="text-danger">{errorMessage}</p>
5364
</Form>
5465
</Col>

webapp_frontend/src/components/pages/Health.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import logo from "../../assets/images/logos/logo.png";
33
import {Button, Table} from "react-bootstrap";
44
import {callBackendHealth} from "../../background/api/api";
55
import {audioOnOff, setAudioVolumeByID} from "../../background/methods/sound"
6+
import {logout} from "../../background/api/auth";
67

78
export default function Health() {
89

@@ -11,7 +12,7 @@ export default function Health() {
1112

1213
useEffect(() => {
1314
updateVariables()
14-
},[]);
15+
}, []);
1516

1617
function updateVariables(): void {
1718
Promise.all([callBackendHealth()])
@@ -58,6 +59,7 @@ export default function Health() {
5859
</tbody>
5960
</Table>
6061
</div>
62+
<Button onClick={() => logout()}>Logout</Button>
6163
</>
6264
)
63-
}
65+
}

webapp_frontend/src/style/custom.scss

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,3 +23,4 @@ $blue: #1a4965;
2323
@import "~bootstrap/scss/tables";
2424
@import "~bootstrap/scss/utilities";
2525
@import "~bootstrap/scss/forms";
26+
@import "~bootstrap/scss/spinners";

0 commit comments

Comments
 (0)