mirror of
https://github.com/sasjs/server.git
synced 2025-12-11 03:34:35 +00:00
feat: added authorization route for web (#37)
This commit is contained in:
@@ -7,7 +7,7 @@ services:
|
|||||||
context: .
|
context: .
|
||||||
dockerfile: DockerfileApi
|
dockerfile: DockerfileApi
|
||||||
environment:
|
environment:
|
||||||
MODE: ${MODE}
|
MODE: 'server'
|
||||||
CORS: ${CORS}
|
CORS: ${CORS}
|
||||||
PORT: ${PORT_API}
|
PORT: ${PORT_API}
|
||||||
PORT_WEB: ${PORT_WEB}
|
PORT_WEB: ${PORT_WEB}
|
||||||
|
|||||||
@@ -19,7 +19,14 @@ function App() {
|
|||||||
<ThemeProvider theme={theme}>
|
<ThemeProvider theme={theme}>
|
||||||
<HashRouter>
|
<HashRouter>
|
||||||
<Header />
|
<Header />
|
||||||
<Login setTokens={setTokens} />
|
<Switch>
|
||||||
|
<Route exact path="/SASjsLogon">
|
||||||
|
<Login getCodeOnly />
|
||||||
|
</Route>
|
||||||
|
<Route path="/">
|
||||||
|
<Login setTokens={setTokens} />
|
||||||
|
</Route>
|
||||||
|
</Switch>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
)
|
)
|
||||||
@@ -39,6 +46,9 @@ function App() {
|
|||||||
<Route exact path="/SASjsStudio">
|
<Route exact path="/SASjsStudio">
|
||||||
<Studio />
|
<Studio />
|
||||||
</Route>
|
</Route>
|
||||||
|
<Route exact path="/SASjsLogon">
|
||||||
|
<Login getCodeOnly />
|
||||||
|
</Route>
|
||||||
</Switch>
|
</Switch>
|
||||||
</HashRouter>
|
</HashRouter>
|
||||||
</ThemeProvider>
|
</ThemeProvider>
|
||||||
|
|||||||
@@ -1,7 +1,8 @@
|
|||||||
import React, { useState } from 'react'
|
import React, { useState } from 'react'
|
||||||
|
import { useLocation } from 'react-router-dom'
|
||||||
import PropTypes from 'prop-types'
|
import PropTypes from 'prop-types'
|
||||||
|
|
||||||
import { CssBaseline, Box, TextField, Button } from '@mui/material'
|
import { CssBaseline, Box, TextField, Button, Typography } from '@mui/material'
|
||||||
|
|
||||||
const headers = {
|
const headers = {
|
||||||
Accept: 'application/json',
|
Accept: 'application/json',
|
||||||
@@ -18,7 +19,12 @@ const getAuthCode = async (credentials: any) => {
|
|||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers,
|
headers,
|
||||||
body: JSON.stringify(credentials)
|
body: JSON.stringify(credentials)
|
||||||
}).then((data) => data.json())
|
}).then(async (response) => {
|
||||||
|
const resText = await response.text()
|
||||||
|
if (response.status !== 200) throw resText
|
||||||
|
|
||||||
|
return JSON.parse(resText)
|
||||||
|
})
|
||||||
}
|
}
|
||||||
const getTokens = async (payload: any) => {
|
const getTokens = async (payload: any) => {
|
||||||
return fetch(`${baseUrl}/SASjsApi/auth/token`, {
|
return fetch(`${baseUrl}/SASjsApi/auth/token`, {
|
||||||
@@ -28,26 +34,62 @@ const getTokens = async (payload: any) => {
|
|||||||
}).then((data) => data.json())
|
}).then((data) => data.json())
|
||||||
}
|
}
|
||||||
|
|
||||||
const Login = ({ setTokens }: any) => {
|
const Login = ({ setTokens, getCodeOnly }: any) => {
|
||||||
const [username, setUserName] = useState()
|
const location = useLocation()
|
||||||
const [password, setPassword] = useState()
|
const [username, setUserName] = useState('')
|
||||||
|
const [password, setPassword] = useState('')
|
||||||
|
const [errorMessage, setErrorMessage] = useState('')
|
||||||
|
let error: boolean
|
||||||
|
const [displayCode, setDisplayCode] = useState(null)
|
||||||
|
|
||||||
const handleSubmit = async (e: any) => {
|
const handleSubmit = async (e: any) => {
|
||||||
|
error = false
|
||||||
|
setErrorMessage('')
|
||||||
e.preventDefault()
|
e.preventDefault()
|
||||||
const { REACT_APP_CLIENT_ID: clientId } = process.env
|
let { REACT_APP_CLIENT_ID: clientId } = process.env
|
||||||
|
|
||||||
|
if (getCodeOnly) {
|
||||||
|
const params = new URLSearchParams(location.search)
|
||||||
|
const responseType = params.get('response_type')
|
||||||
|
if (responseType === 'code')
|
||||||
|
clientId = params.get('client_id') ?? undefined
|
||||||
|
}
|
||||||
|
|
||||||
const { code } = await getAuthCode({
|
const { code } = await getAuthCode({
|
||||||
clientId,
|
clientId,
|
||||||
username,
|
username,
|
||||||
password
|
password
|
||||||
|
}).catch((err: string) => {
|
||||||
|
error = true
|
||||||
|
setErrorMessage(err)
|
||||||
|
return {}
|
||||||
})
|
})
|
||||||
|
|
||||||
const { accessToken, refreshToken } = await getTokens({
|
if (!error) {
|
||||||
clientId,
|
if (getCodeOnly) return setDisplayCode(code)
|
||||||
code
|
|
||||||
})
|
|
||||||
|
|
||||||
setTokens(accessToken, refreshToken)
|
const { accessToken, refreshToken } = await getTokens({
|
||||||
|
clientId,
|
||||||
|
code
|
||||||
|
})
|
||||||
|
|
||||||
|
setTokens(accessToken, refreshToken)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (displayCode) {
|
||||||
|
return (
|
||||||
|
<Box className="main">
|
||||||
|
<CssBaseline />
|
||||||
|
<br />
|
||||||
|
<h2>Authorization Code</h2>
|
||||||
|
<Typography m={2} p={3} style={{ overflowWrap: 'anywhere' }}>
|
||||||
|
{displayCode}
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
</Box>
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -61,7 +103,12 @@ const Login = ({ setTokens }: any) => {
|
|||||||
>
|
>
|
||||||
<CssBaseline />
|
<CssBaseline />
|
||||||
<br />
|
<br />
|
||||||
<h2>Welcome to SASjs Server!</h2>
|
<h2 style={{ width: 'auto' }}>Welcome to SASjs Server!</h2>
|
||||||
|
{getCodeOnly && (
|
||||||
|
<p style={{ width: 'auto' }}>
|
||||||
|
Provide credentials to get authorization code.
|
||||||
|
</p>
|
||||||
|
)}
|
||||||
<br />
|
<br />
|
||||||
|
|
||||||
<TextField
|
<TextField
|
||||||
@@ -80,6 +127,7 @@ const Login = ({ setTokens }: any) => {
|
|||||||
onChange={(e: any) => setPassword(e.target.value)}
|
onChange={(e: any) => setPassword(e.target.value)}
|
||||||
required
|
required
|
||||||
/>
|
/>
|
||||||
|
{errorMessage && <span>{errorMessage}</span>}
|
||||||
<Button type="submit" variant="outlined">
|
<Button type="submit" variant="outlined">
|
||||||
Submit
|
Submit
|
||||||
</Button>
|
</Button>
|
||||||
@@ -88,7 +136,8 @@ const Login = ({ setTokens }: any) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Login.propTypes = {
|
Login.propTypes = {
|
||||||
setTokens: PropTypes.func.isRequired
|
setTokens: PropTypes.func,
|
||||||
|
getCodeOnly: PropTypes.bool
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Login
|
export default Login
|
||||||
|
|||||||
@@ -3,15 +3,8 @@ import { useEffect, useState } from 'react'
|
|||||||
|
|
||||||
export default function useTokens() {
|
export default function useTokens() {
|
||||||
const getTokens = () => {
|
const getTokens = () => {
|
||||||
const accessTokenString = localStorage.getItem('accessToken')
|
const accessToken = localStorage.getItem('accessToken')
|
||||||
const accessToken: string = accessTokenString
|
const refreshToken = localStorage.getItem('refreshToken')
|
||||||
? JSON.parse(accessTokenString)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
const refreshTokenString = localStorage.getItem('refreshToken')
|
|
||||||
const refreshToken: string = refreshTokenString
|
|
||||||
? JSON.parse(refreshTokenString)
|
|
||||||
: undefined
|
|
||||||
|
|
||||||
if (accessToken && refreshToken) {
|
if (accessToken && refreshToken) {
|
||||||
setAxiosRequestHeader(accessToken)
|
setAxiosRequestHeader(accessToken)
|
||||||
@@ -31,8 +24,8 @@ export default function useTokens() {
|
|||||||
setAxiosResponse(setTokens)
|
setAxiosResponse(setTokens)
|
||||||
|
|
||||||
const saveTokens = (accessToken: string, refreshToken: string) => {
|
const saveTokens = (accessToken: string, refreshToken: string) => {
|
||||||
localStorage.setItem('accessToken', JSON.stringify(accessToken))
|
localStorage.setItem('accessToken', accessToken)
|
||||||
localStorage.setItem('refreshToken', JSON.stringify(refreshToken))
|
localStorage.setItem('refreshToken', refreshToken)
|
||||||
setAxiosRequestHeader(accessToken)
|
setAxiosRequestHeader(accessToken)
|
||||||
setTokens({ accessToken, refreshToken })
|
setTokens({ accessToken, refreshToken })
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user