1
0
mirror of https://github.com/sasjs/server.git synced 2025-12-10 19:34:34 +00:00

Merge pull request #166 from sasjs/deprecate-get-auth-code-api

Deprecate get auth code api
This commit is contained in:
Muhammad Saad
2022-05-12 08:47:40 -07:00
committed by GitHub
14 changed files with 494 additions and 423 deletions

View File

@@ -54,6 +54,7 @@ jobs:
ACCESS_TOKEN_SECRET: ${{secrets.ACCESS_TOKEN_SECRET}}
REFRESH_TOKEN_SECRET: ${{secrets.REFRESH_TOKEN_SECRET}}
AUTH_CODE_SECRET: ${{secrets.AUTH_CODE_SECRET}}
SESSION_SECRET: ${{secrets.SESSION_SECRET}}
- name: Build Package
working-directory: ./api

View File

@@ -5,51 +5,6 @@ components:
requestBodies: {}
responses: {}
schemas:
LoginPayload:
properties:
username:
type: string
description: 'Username for user'
example: secretuser
password:
type: string
description: 'Password for user'
example: secretpassword
required:
- username
- password
type: object
additionalProperties: false
AuthorizeResponse:
properties:
code:
type: string
description: 'Authorization code'
example: someRandomCryptoString
required:
- code
type: object
additionalProperties: false
AuthorizePayload:
properties:
username:
type: string
description: 'Username for user'
example: secretuser
password:
type: string
description: 'Password for user'
example: secretpassword
clientId:
type: string
description: 'Client ID'
example: clientID1
required:
- username
- password
- clientId
type: object
additionalProperties: false
TokenResponse:
properties:
accessToken:
@@ -92,6 +47,41 @@ components:
- userId
type: object
additionalProperties: false
LoginPayload:
properties:
username:
type: string
description: 'Username for user'
example: secretuser
password:
type: string
description: 'Password for user'
example: secretpassword
required:
- username
- password
type: object
additionalProperties: false
AuthorizeResponse:
properties:
code:
type: string
description: 'Authorization code'
example: someRandomCryptoString
required:
- code
type: object
additionalProperties: false
AuthorizePayload:
properties:
clientId:
type: string
description: 'Client ID'
example: clientID1
required:
- clientId
type: object
additionalProperties: false
ClientPayload:
properties:
clientId:
@@ -425,14 +415,6 @@ components:
- description
type: object
additionalProperties: false
ExecuteReturnJsonPayload:
properties:
_program:
type: string
description: 'Location of SAS program'
example: /Public/somefolder/some.file
type: object
additionalProperties: false
InfoResponse:
properties:
mode:
@@ -452,6 +434,14 @@ components:
- protocol
type: object
additionalProperties: false
ExecuteReturnJsonPayload:
properties:
_program:
type: string
description: 'Location of SAS program'
example: /Public/somefolder/some.file
type: object
additionalProperties: false
securitySchemes:
bearerAuth:
type: http
@@ -465,86 +455,6 @@ info:
name: '4GL Ltd'
openapi: 3.0.0
paths:
/:
get:
operationId: Home
responses:
'200':
description: Ok
content:
application/json:
schema:
type: string
summary: 'Render index.html'
tags:
- Web
security: []
parameters: []
/login:
post:
operationId: Login
responses:
'200':
description: Ok
content:
application/json:
schema:
properties:
user: {properties: {displayName: {type: string}, username: {type: string}}, required: [displayName, username], type: object}
loggedIn: {type: boolean}
required:
- user
- loggedIn
type: object
summary: 'Accept a valid username/password'
tags:
- Web
security: []
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginPayload'
/logout:
get:
operationId: Logout
responses:
'200':
description: Ok
content:
application/json:
schema: {}
summary: 'Accept a valid username/password'
tags:
- Web
security: []
parameters: []
/SASjsApi/auth/authorize:
post:
operationId: Authorize
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/AuthorizeResponse'
examples:
'Example 1':
value: {code: someRandomCryptoString}
summary: 'Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE'
tags:
- Auth
security: []
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AuthorizePayload'
/SASjsApi/auth/token:
post:
operationId: Token
@@ -602,6 +512,86 @@ paths:
-
bearerAuth: []
parameters: []
/:
get:
operationId: Home
responses:
'200':
description: Ok
content:
application/json:
schema:
type: string
summary: 'Render index.html'
tags:
- Web
security: []
parameters: []
/SASLogon/login:
post:
operationId: Login
responses:
'200':
description: Ok
content:
application/json:
schema:
properties:
user: {properties: {displayName: {type: string}, username: {type: string}}, required: [displayName, username], type: object}
loggedIn: {type: boolean}
required:
- user
- loggedIn
type: object
summary: 'Accept a valid username/password'
tags:
- Web
security: []
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/LoginPayload'
/SASLogon/authorize:
post:
operationId: Authorize
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/AuthorizeResponse'
examples:
'Example 1':
value: {code: someRandomCryptoString}
summary: 'Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE'
tags:
- Web
security: []
parameters: []
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/AuthorizePayload'
/logout:
get:
operationId: Logout
responses:
'200':
description: Ok
content:
application/json:
schema: {}
summary: 'Accept a valid username/password'
tags:
- Web
security: []
parameters: []
/SASjsApi/client:
post:
operationId: CreateClient
@@ -1248,6 +1238,24 @@ paths:
format: double
type: number
example: '6789'
/SASjsApi/info:
get:
operationId: Info
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/InfoResponse'
examples:
'Example 1':
value: {mode: desktop, cors: enable, whiteList: ['http://example.com', 'http://example2.com'], protocol: http}
summary: 'Get server info (mode, cors, whiteList, protocol).'
tags:
- Info
security: []
parameters: []
/SASjsApi/session:
get:
operationId: Session
@@ -1330,24 +1338,6 @@ paths:
application/json:
schema:
$ref: '#/components/schemas/ExecuteReturnJsonPayload'
/SASjsApi/info:
get:
operationId: Info
responses:
'200':
description: Ok
content:
application/json:
schema:
$ref: '#/components/schemas/InfoResponse'
examples:
'Example 1':
value: {mode: desktop, cors: enable, whiteList: ['http://example.com', 'http://example2.com'], protocol: http}
summary: 'Get server info (mode, cors, whiteList, protocol).'
tags:
- Info
security: []
parameters: []
servers:
-
url: /

View File

@@ -85,21 +85,25 @@ if (MODE?.trim() !== 'server' || CORS?.trim() === 'enable') {
* With Mongo Store *
***********************************/
if (MODE?.trim() === 'server') {
let store: MongoStore | undefined
// NOTE: when exporting app.js as agent for supertest
// we should exclude connecting to the real database
if (process.env.NODE_ENV !== 'test') {
const clientPromise = connectDB().then((conn) => conn!.getClient() as any)
app.use(
session({
secret: process.env.SESSION_SECRET as string,
saveUninitialized: false, // don't create session until something stored
resave: false, //don't save session if unmodified
store: MongoStore.create({ clientPromise, collectionName: 'sessions' }),
cookie: cookieOptions
})
)
store = MongoStore.create({ clientPromise, collectionName: 'sessions' })
}
app.use(
session({
secret: process.env.SESSION_SECRET as string,
saveUninitialized: false, // don't create session until something stored
resave: false, //don't save session if unmodified
store,
cookie: cookieOptions
})
)
}
app.use(express.json({ limit: '100mb' }))
app.use(express.static(path.join(__dirname, '../public')))

View File

@@ -1,11 +1,8 @@
import { Security, Route, Tags, Example, Post, Body, Query, Hidden } from 'tsoa'
import jwt from 'jsonwebtoken'
import User from '../model/User'
import Client from '../model/Client'
import { InfoJWT } from '../types'
import {
generateAccessToken,
generateAuthCode,
generateRefreshToken,
removeTokensInDB,
saveTokensInDB
@@ -25,20 +22,6 @@ export class AuthController {
static deleteCode = (userId: number, clientId: string) =>
delete AuthController.authCodes[userId][clientId]
/**
* @summary Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE
*
*/
@Example<AuthorizeResponse>({
code: 'someRandomCryptoString'
})
@Post('/authorize')
public async authorize(
@Body() body: AuthorizePayload
): Promise<AuthorizeResponse> {
return authorize(body)
}
/**
* @summary Accepts client/auth code and returns access/refresh tokens
*
@@ -79,33 +62,6 @@ export class AuthController {
}
}
const authorize = async (data: any): Promise<AuthorizeResponse> => {
const { username, password, clientId } = data
const client = await Client.findOne({ clientId })
if (!client) throw new Error('Invalid clientId.')
// Authenticate User
const user = await User.findOne({ username })
if (!user) throw new Error('Username is not found.')
const validPass = user.comparePassword(password)
if (!validPass) throw new Error('Invalid password.')
// generate authorization code against clientId
const userInfo: InfoJWT = {
clientId,
userId: user.id
}
const code = AuthController.saveCode(
user.id,
clientId,
generateAuthCode(userInfo)
)
return { code }
}
const token = async (data: any): Promise<TokenResponse> => {
const { clientId, code } = data
@@ -143,32 +99,6 @@ const logout = async (userInfo: InfoJWT) => {
await removeTokensInDB(userInfo.userId, userInfo.clientId)
}
interface AuthorizePayload {
/**
* Username for user
* @example "secretuser"
*/
username: string
/**
* Password for user
* @example "secretpassword"
*/
password: string
/**
* Client ID
* @example "clientID1"
*/
clientId: string
}
interface AuthorizeResponse {
/**
* Authorization code
* @example "someRandomCryptoString"
*/
code: string
}
interface TokenPayload {
/**
* Client ID

View File

@@ -3,7 +3,8 @@ export * from './client'
export * from './code'
export * from './drive'
export * from './group'
export * from './info'
export * from './session'
export * from './stp'
export * from './user'
export * from './info'
export * from './web'

View File

@@ -1,10 +1,13 @@
import path from 'path'
import express from 'express'
import { Request, Route, Tags, Post, Body, Get } from 'tsoa'
import { Request, Route, Tags, Post, Body, Get, Example } from 'tsoa'
import { readFile } from '@sasjs/utils'
import User from '../model/User'
import { getWebBuildFolderPath } from '../utils'
import Client from '../model/Client'
import { getWebBuildFolderPath, generateAuthCode } from '../utils'
import { InfoJWT } from '../types'
import { AuthController } from './auth'
@Route('/')
@Tags('Web')
@@ -22,7 +25,7 @@ export class WebController {
* @summary Accept a valid username/password
*
*/
@Post('/login')
@Post('/SASLogon/login')
public async login(
@Request() req: express.Request,
@Body() body: LoginPayload
@@ -30,6 +33,21 @@ export class WebController {
return login(req, body)
}
/**
* @summary Accept a valid username/password, plus a CLIENT_ID, and return an AUTH_CODE
*
*/
@Example<AuthorizeResponse>({
code: 'someRandomCryptoString'
})
@Post('/SASLogon/authorize')
public async authorize(
@Request() req: express.Request,
@Body() body: AuthorizePayload
): Promise<AuthorizeResponse> {
return authorize(req, body.clientId)
}
/**
* @summary Accept a valid username/password
*
@@ -84,6 +102,30 @@ const login = async (
}
}
const authorize = async (
req: express.Request,
clientId: string
): Promise<AuthorizeResponse> => {
const userId = req.session.user?.userId
if (!userId) throw new Error('Invalid userId.')
const client = await Client.findOne({ clientId })
if (!client) throw new Error('Invalid clientId.')
// generate authorization code against clientId
const userInfo: InfoJWT = {
clientId,
userId
}
const code = AuthController.saveCode(
userId,
clientId,
generateAuthCode(userInfo)
)
return { code }
}
interface LoginPayload {
/**
* Username for user
@@ -96,3 +138,19 @@ interface LoginPayload {
*/
password: string
}
interface AuthorizePayload {
/**
* Client ID
* @example "clientID1"
*/
clientId: string
}
interface AuthorizeResponse {
/**
* Authorization code
* @example "someRandomCryptoString"
*/
code: string
}

View File

@@ -13,19 +13,6 @@ import { InfoJWT } from '../../types'
const authRouter = express.Router()
const controller = new AuthController()
authRouter.post('/authorize', async (req, res) => {
const { error, value: body } = authorizeValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
try {
const response = await controller.authorize(body)
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
})
authRouter.post('/token', async (req, res) => {
const { error, value: body } = tokenValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)

View File

@@ -49,114 +49,6 @@ describe('auth', () => {
await mongoServer.stop()
})
describe('authorize', () => {
afterEach(async () => {
const collections = mongoose.connection.collections
const collection = collections['users']
await collection.deleteMany({})
})
it('should respond with authorization code', async () => {
await userController.createUser(user)
const res = await request(app)
.post('/SASjsApi/auth/authorize')
.send({
username: user.username,
password: user.password,
clientId
})
.expect(200)
expect(res.body).toHaveProperty('code')
})
it('should respond with Bad Request if username is missing', async () => {
const res = await request(app)
.post('/SASjsApi/auth/authorize')
.send({
password: user.password,
clientId
})
.expect(400)
expect(res.text).toEqual(`"username" is required`)
expect(res.body).toEqual({})
})
it('should respond with Bad Request if password is missing', async () => {
const res = await request(app)
.post('/SASjsApi/auth/authorize')
.send({
username: user.username,
clientId
})
.expect(400)
expect(res.text).toEqual(`"password" is required`)
expect(res.body).toEqual({})
})
it('should respond with Bad Request if clientId is missing', async () => {
const res = await request(app)
.post('/SASjsApi/auth/authorize')
.send({
username: user.username,
password: user.password
})
.expect(400)
expect(res.text).toEqual(`"clientId" is required`)
expect(res.body).toEqual({})
})
it('should respond with Forbidden if username is incorrect', async () => {
const res = await request(app)
.post('/SASjsApi/auth/authorize')
.send({
username: user.username,
password: user.password,
clientId
})
.expect(403)
expect(res.text).toEqual('Error: Username is not found.')
expect(res.body).toEqual({})
})
it('should respond with Forbidden if password is incorrect', async () => {
await userController.createUser(user)
const res = await request(app)
.post('/SASjsApi/auth/authorize')
.send({
username: user.username,
password: 'WrongPassword',
clientId
})
.expect(403)
expect(res.text).toEqual('Error: Invalid password.')
expect(res.body).toEqual({})
})
it('should respond with Forbidden if clientId is incorrect', async () => {
await userController.createUser(user)
const res = await request(app)
.post('/SASjsApi/auth/authorize')
.send({
username: user.username,
password: user.password,
clientId: 'WrongClientID'
})
.expect(403)
expect(res.text).toEqual('Error: Invalid clientId.')
expect(res.body).toEqual({})
})
})
describe('token', () => {
const userInfo: InfoJWT = {
clientId,

View File

@@ -0,0 +1,182 @@
import { Express } from 'express'
import mongoose, { Mongoose } from 'mongoose'
import { MongoMemoryServer } from 'mongodb-memory-server'
import request from 'supertest'
import appPromise from '../../../app'
import { UserController, ClientController } from '../../../controllers/'
const clientId = 'someclientID'
const clientSecret = 'someclientSecret'
const user = {
id: 1234,
displayName: 'Test User',
username: 'testUsername',
password: '87654321',
isAdmin: false,
isActive: true
}
describe('web', () => {
let app: Express
let con: Mongoose
let mongoServer: MongoMemoryServer
const userController = new UserController()
const clientController = new ClientController()
beforeAll(async () => {
app = await appPromise
mongoServer = await MongoMemoryServer.create()
con = await mongoose.connect(mongoServer.getUri())
await clientController.createClient({ clientId, clientSecret })
})
afterAll(async () => {
await con.connection.dropDatabase()
await con.connection.close()
await mongoServer.stop()
})
describe('home', () => {
it('should respond with CSRF Token', async () => {
await request(app)
.get('/')
.expect(
'set-cookie',
/_csrf=.*; Max-Age=86400000; Path=\/; HttpOnly,XSRF-TOKEN=.*; Path=\//
)
})
})
describe('SASLogon/login', () => {
let csrfToken: string
let cookies: string
beforeAll(async () => {
;({ csrfToken, cookies } = await getCSRF(app))
})
afterEach(async () => {
const collections = mongoose.connection.collections
const collection = collections['users']
await collection.deleteMany({})
})
it('should respond with successful login', async () => {
await userController.createUser(user)
const res = await request(app)
.post('/SASLogon/login')
.set('Cookie', cookies)
.set('x-xsrf-token', csrfToken)
.send({
username: user.username,
password: user.password
})
.expect(200)
expect(res.body.loggedIn).toBeTruthy()
expect(res.body.user).toEqual({
username: user.username,
displayName: user.displayName
})
})
})
describe('SASLogon/authorize', () => {
let csrfToken: string
let cookies: string
let authCookies: string
beforeAll(async () => {
;({ csrfToken, cookies } = await getCSRF(app))
await userController.createUser(user)
const credentials = {
username: user.username,
password: user.password
}
;({ cookies: authCookies } = await performLogin(
app,
credentials,
cookies,
csrfToken
))
})
afterAll(async () => {
const collections = mongoose.connection.collections
const collection = collections['users']
await collection.deleteMany({})
})
it('should respond with authorization code', async () => {
const res = await request(app)
.post('/SASLogon/authorize')
.set('Cookie', [authCookies, cookies].join('; '))
.set('x-xsrf-token', csrfToken)
.send({ clientId })
expect(res.body).toHaveProperty('code')
})
it('should respond with Bad Request if clientId is missing', async () => {
const res = await request(app)
.post('/SASLogon/authorize')
.set('Cookie', [authCookies, cookies].join('; '))
.set('x-xsrf-token', csrfToken)
.send({})
.expect(400)
expect(res.text).toEqual(`"clientId" is required`)
expect(res.body).toEqual({})
})
it('should respond with Forbidden if clientId is incorrect', async () => {
const res = await request(app)
.post('/SASLogon/authorize')
.set('Cookie', [authCookies, cookies].join('; '))
.set('x-xsrf-token', csrfToken)
.send({
clientId: 'WrongClientID'
})
.expect(403)
expect(res.text).toEqual('Error: Invalid clientId.')
expect(res.body).toEqual({})
})
})
})
const getCSRF = async (app: Express) => {
// make request to get CSRF
const { header } = await request(app).get('/')
const cookies = header['set-cookie'].join()
console.log('cookies', cookies)
const csrfToken = extractCSRF(cookies)
return { csrfToken, cookies }
}
const performLogin = async (
app: Express,
credentials: { username: string; password: string },
cookies: string,
csrfToken: string
) => {
const { header } = await request(app)
.post('/SASLogon/login')
.set('Cookie', cookies)
.set('x-xsrf-token', csrfToken)
.send(credentials)
const newCookies: string = header['set-cookie'].join()
return { cookies: newCookies }
}
const extractCSRF = (cookies: string) =>
/_csrf=(.*); Max-Age=86400000; Path=\/; HttpOnly,XSRF-TOKEN=(.*); Path=\//.exec(
cookies
)![2]

View File

@@ -1,23 +1,25 @@
import express from 'express'
import { WebController } from '../../controllers/web'
import { loginWebValidation } from '../../utils'
import { authenticateAccessToken } from '../../middlewares'
import { authorizeValidation, loginWebValidation } from '../../utils'
const webRouter = express.Router()
const controller = new WebController()
webRouter.get('/', async (req, res) => {
let response
try {
const response = await controller.home()
response = await controller.home()
} catch (_) {
response = 'Web Build is not present'
} finally {
res.cookie('XSRF-TOKEN', req.csrfToken())
return res.send(response)
} catch (_) {
return res.send('Web Build is not present')
}
})
webRouter.post('/login', async (req, res) => {
webRouter.post('/SASLogon/login', async (req, res) => {
const { error, value: body } = loginWebValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
@@ -25,16 +27,32 @@ webRouter.post('/login', async (req, res) => {
const response = await controller.login(req, body)
res.send(response)
} catch (err: any) {
res.status(400).send(err.toString())
res.status(403).send(err.toString())
}
})
webRouter.post(
'/SASLogon/authorize',
authenticateAccessToken,
async (req, res) => {
const { error, value: body } = authorizeValidation(req.body)
if (error) return res.status(400).send(error.details[0].message)
try {
const response = await controller.authorize(req, body)
res.send(response)
} catch (err: any) {
res.status(403).send(err.toString())
}
}
)
webRouter.get('/logout', async (req, res) => {
try {
await controller.logout(req)
res.status(200).send('OK!')
} catch (err: any) {
res.status(400).send(err.toString())
res.status(403).send(err.toString())
}
})

View File

@@ -13,8 +13,6 @@ export const loginWebValidation = (data: any): Joi.ValidationResult =>
export const authorizeValidation = (data: any): Joi.ValidationResult =>
Joi.object({
username: usernameSchema.required(),
password: passwordSchema.required(),
clientId: Joi.string().required()
}).validate(data)

View File

@@ -10,6 +10,7 @@ import Drive from './containers/Drive'
import Studio from './containers/Studio'
import { AppContext } from './context/appContext'
import AuthCode from './containers/AuthCode'
function App() {
const appContext = useContext(AppContext)
@@ -20,9 +21,6 @@ function App() {
<HashRouter>
<Header />
<Switch>
<Route exact path="/SASjsLogon">
<Login getCodeOnly />
</Route>
<Route path="/">
<Login />
</Route>
@@ -47,7 +45,7 @@ function App() {
<Studio />
</Route>
<Route exact path="/SASjsLogon">
<Login getCodeOnly />
<AuthCode />
</Route>
</Switch>
</HashRouter>

View File

@@ -1,56 +1,27 @@
import axios from 'axios'
import React, { useState, useContext } from 'react'
import { useLocation } from 'react-router-dom'
import PropTypes from 'prop-types'
import { CssBaseline, Box, TextField, Button, Typography } from '@mui/material'
import { CssBaseline, Box, TextField, Button } from '@mui/material'
import { AppContext } from '../context/appContext'
const getAuthCode = async (credentials: any) =>
axios.post('/SASjsApi/auth/authorize', credentials).then((res) => res.data)
const login = async (payload: { username: string; password: string }) =>
axios.post('/login', payload).then((res) => res.data)
axios.post('/SASLogon/login', payload).then((res) => res.data)
const Login = ({ getCodeOnly }: any) => {
const location = useLocation()
const Login = () => {
const appContext = useContext(AppContext)
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) => {
error = false
setErrorMessage('')
e.preventDefault()
if (getCodeOnly) {
const params = new URLSearchParams(location.search)
const responseType = params.get('response_type')
if (responseType === 'code') {
const clientId = params.get('client_id')
const { code } = await getAuthCode({
clientId,
username,
password
}).catch((err: any) => {
error = true
setErrorMessage(err.response.data)
return {}
})
if (!error) return setDisplayCode(code)
return
}
}
const { loggedIn, user } = await login({
username,
password
}).catch((err: any) => {
error = true
setErrorMessage(err.response.data)
return {}
})
@@ -62,21 +33,6 @@ const Login = ({ getCodeOnly }: any) => {
}
}
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 (
<Box
className="main"
@@ -89,13 +45,6 @@ const Login = ({ getCodeOnly }: any) => {
<CssBaseline />
<br />
<h2 style={{ width: 'auto' }}>Welcome to SASjs Server!</h2>
{getCodeOnly && (
<p style={{ width: 'auto' }}>
Provide credentials to get authorization code.
</p>
)}
<br />
<TextField
id="username"
label="Username"

View File

@@ -0,0 +1,63 @@
import axios from 'axios'
import React, { useEffect, useState } from 'react'
import { useLocation } from 'react-router-dom'
import { CssBaseline, Box, Typography } from '@mui/material'
const getAuthCode = async (credentials: any) =>
axios.post('/SASLogon/authorize', credentials).then((res) => res.data)
const AuthCode = () => {
const location = useLocation()
const [displayCode, setDisplayCode] = useState(null)
const [errorMessage, setErrorMessage] = useState('')
useEffect(() => {
requestAuthCode()
}, [])
const requestAuthCode = async () => {
setErrorMessage('')
const params = new URLSearchParams(location.search)
const responseType = params.get('response_type')
if (responseType !== 'code')
return setErrorMessage('response type is not support')
const clientId = params.get('client_id')
if (!clientId) return setErrorMessage('clientId is not provided')
setErrorMessage('Fetching auth code... ')
const { code } = await getAuthCode({
clientId
})
.then((res) => {
setErrorMessage('')
return res
})
.catch((err: any) => {
setErrorMessage(err.response.data)
return { code: null }
})
return setDisplayCode(code)
}
return (
<Box className="main">
<CssBaseline />
<br />
<h2>Authorization Code</h2>
{displayCode && (
<Typography m={2} p={3} style={{ overflowWrap: 'anywhere' }}>
{displayCode}
</Typography>
)}
{errorMessage && <Typography>{errorMessage}</Typography>}
<br />
</Box>
)
}
export default AuthCode