Feature/Auth: implement user authentication #3
@@ -2,7 +2,7 @@
|
||||
// service to interact with the api/auth endpoints
|
||||
// handles user registration, user logins, tokens, password reset, etc.
|
||||
|
||||
import api from "./axios.ts"
|
||||
import { api, authStorage } from "./axios.ts"
|
||||
import type { User, RegisterDto, LoginDto } from "../models/User.ts";
|
||||
|
||||
const API_URL: string = "/auth";
|
||||
@@ -10,9 +10,8 @@ const API_URL: string = "/auth";
|
||||
export const register = async (user: RegisterDto) => {
|
||||
|
||||
try {
|
||||
console.log(user);
|
||||
|
||||
// TODO: if valid
|
||||
const response = await api.post(`${API_URL}/register`, user);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -28,9 +27,7 @@ export const login = async (user: LoginDto ) => {
|
||||
|
||||
try {
|
||||
const response = await api.post(`${API_URL}/login`, user);
|
||||
const token = response.data.token;
|
||||
|
||||
localStorage.setItem("token", token);
|
||||
localStorage.setTokens(response.data);
|
||||
|
||||
return true;
|
||||
|
||||
@@ -41,9 +38,9 @@ export const login = async (user: LoginDto ) => {
|
||||
}
|
||||
|
||||
export const logout = () => {
|
||||
localStorage.removeItem("token");
|
||||
authStorage.clear();
|
||||
}
|
||||
|
||||
export const getToken = () => {
|
||||
return localStorage.getItem("token");
|
||||
authStorage.getAccessToken();
|
||||
}
|
||||
|
||||
@@ -5,13 +5,41 @@
|
||||
import axios from "axios";
|
||||
|
||||
const baseUrl: string = import.meta.env.DEV ? import.meta.env.VITE_DEV_API_URL : "https://app.vxbard.net/api"
|
||||
const api = axios.create({
|
||||
export const api = axios.create({
|
||||
baseURL: baseUrl
|
||||
});
|
||||
|
||||
api.interceptors.request.use(config => {
|
||||
type FailedRequest = { resolve: (token: string) => void, reject: (error: unknown) => void}
|
||||
let isRefreshing: boolean = false;
|
||||
let failedQueue: FailedRequest[] = [];
|
||||
|
||||
const token = localStorage.getItem("token");
|
||||
export const authStorage = {
|
||||
getAccessToken: () => localStorage.getItem("accessToken"),
|
||||
getRefreshToken: () => localStorage.getItem("refreshToken"),
|
||||
|
||||
setTokens: ({ accessToken, refreshToken } : { accessToken: string, refreshToken: string }) => {
|
||||
localStorage.setItem("accessToken", accessToken)
|
||||
localStorage.setItem("refreshToken", refreshToken)
|
||||
},
|
||||
|
||||
clear: () => {
|
||||
localStorage.removeItem("accessToken")
|
||||
localStorage.removeItem("refreshToken")
|
||||
}
|
||||
}
|
||||
|
||||
const processQueue = (error: unknown, token: string | null = null): void => {
|
||||
failedQueue.forEach(prom => {
|
||||
if (error) prom.reject(error);
|
||||
else prom.resolve(token as string);
|
||||
})
|
||||
failedQueue = [];
|
||||
}
|
||||
|
||||
// intercept on each request
|
||||
api.interceptors.request.use(config => { // add access token to request headers
|
||||
|
||||
const token = localStorage.getAccessToken();
|
||||
|
||||
if (token) {
|
||||
config.headers.Authorization = `Bearer ${token}`;
|
||||
@@ -21,4 +49,42 @@ api.interceptors.request.use(config => {
|
||||
|
||||
});
|
||||
|
||||
// intercept on each response
|
||||
api.interceptors.response.use(response => response, async error => { // mainly for authentication refreshTokens
|
||||
const originalRequest = error.config;
|
||||
|
||||
// if un authorized then refresh the token
|
||||
if(error.response?.status === 401 && !originalRequest._retry) {
|
||||
if(isRefreshing) {
|
||||
return new Promise((resolve, reject) => {
|
||||
failedQueue.push({ resolve, reject })
|
||||
}).then(token => {
|
||||
originalRequest.headers.Authorization = `Bearer ${token}`;
|
||||
return api(originalRequest);
|
||||
}).catch(err => Promise.reject(err));
|
||||
}
|
||||
|
||||
originalRequest._retry = true;
|
||||
isRefreshing = true;
|
||||
const refreshToken = authStorage.getRefreshToken();
|
||||
try {
|
||||
// request refresh endpoint get back a new accessToken
|
||||
const res = await axios.post(`${baseUrl}/auth/refresh`, { refreshToken });
|
||||
const { accessToken, refreshToken: newRefresh } = res.data;
|
||||
authStorage.setTokens({ accessToken, refreshToken: newRefresh });
|
||||
processQueue(null, accessToken);
|
||||
originalRequest.headers.Authorization = `Bearer ${accessToken}`;
|
||||
return api(originalRequest);
|
||||
} catch (err) {
|
||||
processQueue(err, null);
|
||||
authStorage.clear()
|
||||
window.location.href = "/login";
|
||||
return Promise.reject(err);
|
||||
} finally {
|
||||
isRefreshing = false;
|
||||
}
|
||||
}
|
||||
return Promise.reject(error);
|
||||
})
|
||||
|
||||
export default api;
|
||||
|
||||
Reference in New Issue
Block a user