some frontend code for requests and authentication

This commit is contained in:
overflowerror 2021-08-15 12:36:54 +02:00
parent 5ba50cb2f6
commit b253feda0c
10 changed files with 187 additions and 9 deletions

View file

@ -10,6 +10,7 @@
"@types/node": "^12.0.0",
"@types/react": "^17.0.0",
"@types/react-dom": "^17.0.0",
"axios": "^0.21.1",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-scripts": "4.0.3",

View file

@ -1,6 +1,6 @@
import React from 'react';
import './App.css';
import {AuthProvider} from "./auth/AuthProvider";
import AuthProvider from "./auth/AuthProvider";
function App() {
return (

View file

@ -0,0 +1,26 @@
import axios, {AxiosInstance} from "axios";
const AUTHORIZATION_TYPE = "Bearer"
export type Client = {
axios: AxiosInstance
authorized: boolean
}
export const getClient = (authToken?: string): Client => {
if (authToken) {
return {
axios: axios.create({
headers: {
"Authorization": AUTHORIZATION_TYPE + " " + authToken
}
}),
authorized: true
}
} else {
return {
axios: axios,
authorized: false
}
}
}

View file

@ -0,0 +1,24 @@
import Endpoint from "./Endpoint";
import {Client} from "../client";
import {LoginParams, LoginResponse} from "../entities/login";
import User from "../entities/User";
const API_PREFIX = "http://localhost:8080/authentication/"
class AuthenticationEndpoint extends Endpoint {
constructor(client: Client) {
super(client);
}
public async login(params: LoginParams): Promise<LoginResponse> {
return this.post<LoginResponse>(API_PREFIX, {
data: params
})
}
public async getUser(): Promise<User> {
return this.get<User>(API_PREFIX)
}
}
export default AuthenticationEndpoint

View file

@ -0,0 +1,62 @@
import {Client} from "../client";
import {AxiosRequestConfig} from "axios";
import ErrorStatus, {ErrorStatusGuard} from "../entities/ErrorStatus";
abstract class Endpoint {
protected client: Client
protected constructor(client: Client) {
this.client = client
}
protected requireAuthenticated() {
if (!this.client.authorized)
throw "this endpoint needs an authorized client"
}
private static handleResponse<T>(response: any): T {
if (ErrorStatusGuard(response)) {
throw (response as ErrorStatus)
}
return response as T
}
protected async get<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
return Endpoint.handleResponse<T>(
this.client.axios.get(path, config)
)
}
protected async post<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
return Endpoint.handleResponse<T>(
this.client.axios.post(path, config)
)
}
protected async options<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
return Endpoint.handleResponse<T>(
this.client.axios.options(path, config)
)
}
protected async head<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
return Endpoint.handleResponse<T>(
this.client.axios.head(path, config)
)
}
protected async put<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
return Endpoint.handleResponse<T>(
this.client.axios.put(path, config)
)
}
protected async delete<T>(path: string, config?: AxiosRequestConfig): Promise<T> {
return Endpoint.handleResponse<T>(
this.client.axios.delete(path, config)
)
}
}
export default Endpoint

View file

@ -0,0 +1,17 @@
type ErrorStatus = {
status: number,
message: string,
details: string,
time: Date
}
export const ErrorStatusGuard = (value: any): value is ErrorStatus => {
return value.status !== undefined &&
typeof value.status == "number" &&
value.message !== undefined &&
value.details !== undefined &&
value.time !== undefined;
}
export default ErrorStatus

View file

@ -0,0 +1,6 @@
type Group = {
// TODO
}
export default Group

View file

@ -0,0 +1,9 @@
import Group from "./Group";
type User = {
id: string
username: string
groups: Group[]
}
export default User

View file

@ -0,0 +1,9 @@
export type LoginParams = {
username: string
password: string
}
export type LoginResponse = {
token: string
}

View file

@ -1,32 +1,54 @@
import React, {FunctionComponent, useContext, useState} from "react";
import AuthenticationEndpoint from "../api/endpoints/AuthenticationEndpoint";
import {Client, getClient} from "../api/client";
import User from "../api/entities/User";
type AuthState = {
loggedIn: boolean
login: (username: string, password: string) => Promise<string>
login: (username: string, password: string) => Promise<User>
}
const emptyAuthState = {
loggedIn: false,
login: async () => "not implemented"
login: async () => {
throw "not implemented"
}
}
const AuthContext = React.createContext<AuthState>(emptyAuthState)
export const useAuth = () => {
export const useAuth = (): AuthState => {
return useContext(AuthContext)
}
type AuthProviderProps = {
}
export const AuthProvider: FunctionComponent<AuthProviderProps> = ({children}) => {
const defaultAuthState = {
...emptyAuthState
}
const [authState, setAuthState] = useState<AuthState>(defaultAuthState)
const AuthProvider: FunctionComponent<AuthProviderProps> = ({children}) => {
const [client, setClient] = useState<Client>(getClient())
const authenticationEndpoint = new AuthenticationEndpoint(client)
const [authState, setAuthState] = useState<AuthState>({
...emptyAuthState,
login: async (username: string, password: string): Promise<User> => {
const response = await authenticationEndpoint.login({
username: username,
password: password
})
// local new client
const client = getClient(response.token)
setClient(client)
// local new authenticationEndpoint
const tmpAuthenticationEndpoint = new AuthenticationEndpoint(client)
return await tmpAuthenticationEndpoint.getUser()
}
})
return (
<AuthContext.Provider value={authState}>
@ -34,3 +56,5 @@ export const AuthProvider: FunctionComponent<AuthProviderProps> = ({children}) =
</AuthContext.Provider>
)
}
export default AuthProvider