feat: refresh token added
This commit is contained in:
2
.env
2
.env
@@ -1,5 +1,5 @@
|
|||||||
VITE_GOOGLE_CLIENT_ID=https://272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com/
|
VITE_GOOGLE_CLIENT_ID=https://272098283932-bft2gvlgjn8edopg0lnqjq1i9ekdmipt.apps.googleusercontent.com/
|
||||||
VITE_DEFUALT_AUTH_RETURN_URL=/setting/profile
|
VITE_DEFUALT_AUTH_RETURN_URL=/signup
|
||||||
VITE_API_URL=https://accounts.business-harmony.com/api/
|
VITE_API_URL=https://accounts.business-harmony.com/api/
|
||||||
VITE_IDENTITY_URL=https://accounts.business-harmony.com/connect/token
|
VITE_IDENTITY_URL=https://accounts.business-harmony.com/connect/token
|
||||||
VITE_IDENTITY_CLIENT_ID=harmony_identity
|
VITE_IDENTITY_CLIENT_ID=harmony_identity
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import { getAccessToken } from '@/lib/apiClient';
|
import { getAccessToken } from '@/features/authorization/api/identityAPI';
|
||||||
import { type PropsWithChildren } from 'react';
|
import { type PropsWithChildren } from 'react';
|
||||||
import { Navigate } from 'react-router-dom';
|
import { Navigate } from 'react-router-dom';
|
||||||
|
|
||||||
|
|||||||
109
src/features/authorization/api/identityAPI.ts
Normal file
109
src/features/authorization/api/identityAPI.ts
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
import apiClient from '@/lib/apiClient';
|
||||||
|
import axios from 'axios';
|
||||||
|
|
||||||
|
export interface GenerateToken {
|
||||||
|
username: string;
|
||||||
|
password: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface GenerateTokenResponse {
|
||||||
|
access_token: string;
|
||||||
|
expires_in: number;
|
||||||
|
token_type: 'Bearer';
|
||||||
|
refresh_token: string;
|
||||||
|
scope: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ACCESS_TOKEN_KEY: 'access_token' = 'access_token' as const;
|
||||||
|
const REFRESH_TOKEN_KEY: 'refresh_token' = 'refresh_token' as const;
|
||||||
|
const EXPIRES_IN_KEY: 'expires_in' = 'expires_in' as const;
|
||||||
|
|
||||||
|
export const generateToken = async (request: GenerateToken) => {
|
||||||
|
const body = new URLSearchParams();
|
||||||
|
body.set('grant_type', 'password');
|
||||||
|
body.set('client_id', import.meta.env.VITE_IDENTITY_CLIENT_ID);
|
||||||
|
body.set('scope', import.meta.env.VITE_IDENTITY_SCOPE);
|
||||||
|
body.set('username', request.username);
|
||||||
|
body.set('password', request.password);
|
||||||
|
|
||||||
|
const result = await apiClient.post<GenerateTokenResponse>(
|
||||||
|
import.meta.env.VITE_IDENTITY_URL,
|
||||||
|
body.toString(),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
sessionStorage.setItem(ACCESS_TOKEN_KEY, result.data.access_token);
|
||||||
|
sessionStorage.setItem(REFRESH_TOKEN_KEY, result.data.refresh_token);
|
||||||
|
sessionStorage.setItem(
|
||||||
|
EXPIRES_IN_KEY,
|
||||||
|
String(Date.now() + result.data.expires_in * 1000),
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const getAccessToken = () => {
|
||||||
|
const sessionToken = sessionStorage.getItem(ACCESS_TOKEN_KEY);
|
||||||
|
|
||||||
|
if (sessionToken) return null;
|
||||||
|
|
||||||
|
const expiresAt = Number(sessionStorage.getItem(EXPIRES_IN_KEY));
|
||||||
|
const now = Date.now();
|
||||||
|
|
||||||
|
if (now < expiresAt) {
|
||||||
|
return sessionToken;
|
||||||
|
} else {
|
||||||
|
return refreshAccessToken();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const refreshAccessToken = async () => {
|
||||||
|
const refreshToken = sessionStorage.getItem(REFRESH_TOKEN_KEY);
|
||||||
|
if (!refreshToken) return null;
|
||||||
|
|
||||||
|
const result = await axios.post<GenerateTokenResponse>(
|
||||||
|
import.meta.env.VITE_IDENTITY_URL,
|
||||||
|
new URLSearchParams({
|
||||||
|
grant_type: 'refresh_token',
|
||||||
|
refresh_token: refreshToken,
|
||||||
|
client_id: 'harmony_identity', // from your token payload
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
if (result.data.access_token) {
|
||||||
|
sessionStorage.setItem(ACCESS_TOKEN_KEY, result.data.access_token);
|
||||||
|
sessionStorage.setItem(
|
||||||
|
EXPIRES_IN_KEY,
|
||||||
|
String(Date.now() + result.data.expires_in * 1000),
|
||||||
|
);
|
||||||
|
return result.data.access_token;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
};
|
||||||
|
|
||||||
|
// export const generateToken = (request: any) => {
|
||||||
|
// const body = new URLSearchParams();
|
||||||
|
// body.set('grant_type', request.password ? 'password' : 'otp');
|
||||||
|
// body.set('client_id', import.meta.env.VITE_IDENTITY_CLIENT_ID);
|
||||||
|
// body.set('scope', import.meta.env.VITE_IDENTITY_SCOPE);
|
||||||
|
// if (request.phoneNumber) body.set('phonenumber', request.phoneNumber);
|
||||||
|
// if (request.email) body.set('email', request.email);
|
||||||
|
// if (request.password) body.set('password', request.password);
|
||||||
|
// if (request.otp) body.set('otp', request.otp);
|
||||||
|
|
||||||
|
// return axios
|
||||||
|
// .post(import.meta.env.VITE_IDENTITY_URL, body.toString(), {
|
||||||
|
// headers: {
|
||||||
|
// 'Content-Type': 'application/x-www-form-urlencoded',
|
||||||
|
// },
|
||||||
|
// })
|
||||||
|
// .then((result) => console.log(result));
|
||||||
|
// };
|
||||||
@@ -21,6 +21,7 @@ import {
|
|||||||
import type { PasswordLoginRequest } from '../../types/userTypes';
|
import type { PasswordLoginRequest } from '../../types/userTypes';
|
||||||
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
import { Icon, useToast } from '@rkheftan/harmony-ui';
|
||||||
import { useApi } from '@/hooks/useApi';
|
import { useApi } from '@/hooks/useApi';
|
||||||
|
import { generateToken } from '../../api/identityAPI';
|
||||||
|
|
||||||
export interface EnterPasswordFormProps {
|
export interface EnterPasswordFormProps {
|
||||||
onEditValue: () => void;
|
onEditValue: () => void;
|
||||||
@@ -79,6 +80,13 @@ export const EnterPasswordForm = ({
|
|||||||
if (!loginWithPassResult) return;
|
if (!loginWithPassResult) return;
|
||||||
|
|
||||||
if (loginWithPassResult.success) {
|
if (loginWithPassResult.success) {
|
||||||
|
console.log('logged in');
|
||||||
|
|
||||||
|
await generateToken({
|
||||||
|
username: apiRequest.email ?? (apiRequest.phoneNumber as string),
|
||||||
|
password: apiRequest.password,
|
||||||
|
});
|
||||||
|
|
||||||
onLoggedIn(loginWithPassResult.userId);
|
onLoggedIn(loginWithPassResult.userId);
|
||||||
|
|
||||||
toast({
|
toast({
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
|
import { getAccessToken } from '@/features/authorization/api/identityAPI';
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
|
|
||||||
// Function to get the token from local storage or state management
|
|
||||||
export const ACCESS_TOKEN_KEY: 'access_token' = 'access_token' as const;
|
|
||||||
export const getAccessToken = () => sessionStorage.getItem(ACCESS_TOKEN_KEY);
|
|
||||||
|
|
||||||
const apiClient = axios.create({
|
const apiClient = axios.create({
|
||||||
// Define the base URL for all API requests
|
// Define the base URL for all API requests
|
||||||
baseURL: import.meta.env.VITE_API_URL,
|
baseURL: import.meta.env.VITE_API_URL,
|
||||||
@@ -21,8 +18,8 @@ const apiClient = axios.create({
|
|||||||
// --- Request Interceptor ---
|
// --- Request Interceptor ---
|
||||||
// This runs BEFORE each request is sent
|
// This runs BEFORE each request is sent
|
||||||
apiClient.interceptors.request.use(
|
apiClient.interceptors.request.use(
|
||||||
(config) => {
|
async (config) => {
|
||||||
const token = getAccessToken();
|
const token = await getAccessToken();
|
||||||
if (token) {
|
if (token) {
|
||||||
// Add the authorization token to the headers
|
// Add the authorization token to the headers
|
||||||
config.headers.Authorization = `Bearer ${token}`;
|
config.headers.Authorization = `Bearer ${token}`;
|
||||||
|
|||||||
Reference in New Issue
Block a user