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())
+// }