This repository has been archived on 2024-10-29. You can view files and clone it, but cannot push or open issues or pull requests.
website/tools/authentication/Authentication.ts

132 lines
3.8 KiB
TypeScript
Raw Permalink Normal View History

2022-08-31 21:44:33 +02:00
import type { AxiosInstance } from 'axios'
import axios from 'axios'
import type { Socket } from 'socket.io-client'
import { io } from 'socket.io-client'
2021-10-26 16:38:55 +02:00
import { API_URL } from '../api'
import { cookies } from '../cookies'
2022-08-31 21:44:33 +02:00
import type { Tokens } from '.'
import { fetchRefreshToken } from './authenticationFromServerSide'
2022-08-30 18:42:21 +02:00
import { clearCache } from '../cache'
export class Authentication {
public tokens: Tokens
public accessTokenAge: number
public socket?: Socket
public api: AxiosInstance
constructor(tokens: Tokens, disableSocketIO: boolean = false) {
this.tokens = tokens
this.accessTokenAge = Date.now()
2022-08-30 21:30:06 +02:00
if (typeof window === 'undefined' || disableSocketIO) {
this.socket = undefined
} else {
this.socket = io(API_URL, {
auth: { token: `Bearer ${tokens.accessToken}` }
})
this.socket.on('connect', () => {
console.log(
`Connected to socket with clientId: ${this.socket?.id ?? 'undefined'}`
)
})
this.socket.on('connect_error', (error) => {
if (error.message.startsWith('Unauthorized')) {
fetchRefreshToken(this.tokens.refreshToken)
.then(({ accessToken }) => {
this.setAccessToken(accessToken)
})
.catch(async () => {
this.signout()
return await Promise.reject(error)
})
}
})
}
this.api = axios.create({
baseURL: API_URL,
headers: {
'Content-Type': 'application/json'
}
})
this.api.interceptors.request.use(
async (config) => {
const isValidAccessToken =
this.accessTokenAge + tokens.expiresIn > Date.now()
if (!isValidAccessToken) {
const { accessToken } = await fetchRefreshToken(
this.tokens.refreshToken
)
this.setAccessToken(accessToken)
}
2021-10-26 16:38:55 +02:00
config.headers = config.headers == null ? {} : config.headers
2023-01-11 17:39:09 +01:00
config.headers[
'Authorization'
] = `${this.tokens.type} ${this.tokens.accessToken}`
return config
},
async (error) => {
this.signout()
return await Promise.reject(error)
}
)
this.api.interceptors.response.use(
(response) => {
return response
},
async (error) => {
if (error.response.status !== 403 || (error.config._retry as boolean)) {
return await Promise.reject(error)
}
error.config._retry = true
try {
const { accessToken } = await fetchRefreshToken(
this.tokens.refreshToken
)
this.setAccessToken(accessToken)
error.response.config.headers.Authorization = `${this.tokens.type} ${this.tokens.accessToken}`
return await this.api.request(error.response.config)
} catch {
this.signout()
return await Promise.reject(error)
}
}
)
}
public setAccessToken(accessToken: string): void {
this.tokens.accessToken = accessToken
this.accessTokenAge = Date.now()
const token = `${this.tokens.type} ${this.tokens.accessToken}`
if (typeof this?.socket?.auth !== 'function' && this.socket != null) {
2023-01-11 17:39:09 +01:00
this.socket.auth['token'] = token
}
}
public signout(): void {
cookies.remove('refreshToken')
2022-08-30 18:42:21 +02:00
clearCache()
window.location.href = '/authentication/signin'
}
public async signoutServerSide(): Promise<void> {
try {
await this.api.post('/users/signout', {
refreshToken: this.tokens.refreshToken
})
} catch {}
this.signout()
}
public async signoutAllDevicesServerSide(): Promise<void> {
try {
await this.api.delete('/users/signout')
} catch {}
this.signout()
}
public signin(): void {
2022-08-30 18:42:21 +02:00
clearCache()
cookies.set('refreshToken', this.tokens.refreshToken)
}
}