From c47782eed24b73afe3a0933a3c42b1f77a1924ae Mon Sep 17 00:00:00 2001 From: Saad Jutt Date: Wed, 10 Nov 2021 08:05:47 +0500 Subject: [PATCH] chore(web): added login component + use access token --- web/src/App.tsx | 17 ++++++ web/src/components/login.tsx | 96 +++++++++++++++++++++++++++++++++ web/src/components/useTokens.ts | 96 +++++++++++++++++++++++++++++++++ 3 files changed, 209 insertions(+) create mode 100644 web/src/components/login.tsx create mode 100644 web/src/components/useTokens.ts diff --git a/web/src/App.tsx b/web/src/App.tsx index 68f959a..22ec1e7 100644 --- a/web/src/App.tsx +++ b/web/src/App.tsx @@ -3,11 +3,28 @@ import { Route, HashRouter, Switch } from 'react-router-dom' import { ThemeProvider } from '@mui/material/styles' import { theme } from './theme' +import Login from './components/login' import Header from './components/header' import Home from './components/home' import Drive from './containers/Drive' import Studio from './containers/Studio' + +import useTokens from './components/useTokens' + function App() { + const { tokens, setTokens } = useTokens() + + if (!tokens) { + return ( + + +
+ + + + ) + } + return ( diff --git a/web/src/components/login.tsx b/web/src/components/login.tsx new file mode 100644 index 0000000..0fb4a55 --- /dev/null +++ b/web/src/components/login.tsx @@ -0,0 +1,96 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' + +import { CssBaseline, Box, TextField, Button } from '@mui/material' + +const getAuthCode = async (credentials: any) => { + return fetch('http://localhost:5000/SASjsApi/auth/authorize', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(credentials) + }).then((data) => data.json()) +} +const getTokens = async (payload: any) => { + return fetch('http://localhost:5000/SASjsApi/auth/token', { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify(payload) + }).then((data) => data.json()) +} + +const Login = ({ setTokens }: any) => { + const [clientId, setClientId] = useState() + const [username, setUserName] = useState() + const [password, setPassword] = useState() + + const handleSubmit = async (e: any) => { + e.preventDefault() + const { code } = await getAuthCode({ + clientId, + username, + password + }) + + const { accessToken, refreshToken } = await getTokens({ + clientId, + code + }) + + setTokens(accessToken, refreshToken) + } + + return ( + :not(style)': { m: 1, width: '25ch' } + }} + > + +
+

Welcome to SASjs Server!

+
+ + setClientId(e.target.value)} + autoFocus + required + /> + setUserName(e.target.value)} + required + /> + setPassword(e.target.value)} + required + /> + +
+ ) +} + +Login.propTypes = { + setTokens: PropTypes.func.isRequired +} + +export default Login diff --git a/web/src/components/useTokens.ts b/web/src/components/useTokens.ts new file mode 100644 index 0000000..33f2480 --- /dev/null +++ b/web/src/components/useTokens.ts @@ -0,0 +1,96 @@ +import axios from 'axios' +import { useEffect, useState } from 'react' + +export default function useTokens() { + const getTokens = () => { + const accessTokenString = localStorage.getItem('accessToken') + const accessToken: string = accessTokenString + ? JSON.parse(accessTokenString) + : undefined + + const refreshTokenString = localStorage.getItem('refreshToken') + const refreshToken: string = refreshTokenString + ? JSON.parse(refreshTokenString) + : undefined + + if (accessToken && refreshToken) { + setAxiosRequestHeader(accessToken) + return { accessToken, refreshToken } + } + return undefined + } + + const [tokens, setTokens] = useState(getTokens()) + + useEffect(() => { + if (tokens === undefined) { + localStorage.removeItem('accessToken') + localStorage.removeItem('refreshToken') + } + }, [tokens]) + setAxiosResponse(setTokens) + + const saveTokens = (accessToken: string, refreshToken: string) => { + localStorage.setItem('accessToken', JSON.stringify(accessToken)) + localStorage.setItem('refreshToken', JSON.stringify(refreshToken)) + setAxiosRequestHeader(accessToken) + setTokens({ accessToken, refreshToken }) + } + + return { + setTokens: saveTokens, + tokens + } +} + +const baseUrl = 'http://localhost:5000' +const isAbsoluteURLRegex = /^(?:\w+:)\/\// + +const setAxiosRequestHeader = (accessToken: string) => { + axios.interceptors.request.use(function (config: any) { + if (!isAbsoluteURLRegex.test(config.url)) { + config.url = baseUrl + config.url + } + config.headers.Authorization = `Bearer ${accessToken}` + + return config + }) +} + +const setAxiosResponse = (setTokens: Function) => { + // Add a response interceptor + axios.interceptors.response.use( + function (response) { + // Any status code that lie within the range of 2xx cause this function to trigger + return response + }, + async function (error) { + if (error.response?.status === 401) { + // refresh token + // const { accessToken, refreshToken: newRefresh } = await refreshMyToken( + // refreshToken + // ) + + // if (accessToken && newRefresh) { + // setTokens(accessToken, newRefresh) + // error.config.headers['Authorization'] = 'Bearer ' + accessToken + // error.config.baseURL = undefined + + // return axios.request(error.config) + // } + setTokens(undefined) + } + + return Promise.reject(error) + } + ) +} + +// const refreshMyToken = async (refreshToken: string) => { +// return fetch('http://localhost:5000/SASjsApi/auth/refresh', { +// method: 'POST', +// headers: { +// Authorization: `Bearer ${refreshToken}` +// } +// }).then((data) => data.json()) +// }