Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 0 additions & 13 deletions packages/apps/human-app/frontend/src/api/api-client.ts

This file was deleted.

102 changes: 0 additions & 102 deletions packages/apps/human-app/frontend/src/api/api-paths.ts

This file was deleted.

117 changes: 117 additions & 0 deletions packages/apps/human-app/frontend/src/api/auth-service.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
import { jwtDecode } from 'jwt-decode';
import { type SignInDto } from '@/modules/signin/worker/schemas';
import { browserAuthProvider } from '@/shared/contexts/browser-auth-provider';
import {
type AuthTokensSuccessResponse,
authTokensSuccessResponseSchema,
} from '@/shared/schemas';
import { type BrowserAuthProvider } from '@/shared/types/browser-auth-provider';
import { type HttpApiClient } from './http-api-client';
import { commonApiPaths } from './common-api-paths';

export interface AuthProvider {
getAccessToken: () => Promise<string | null>;
refreshAccessToken: () => Promise<void>;
}

const apiPaths = {
worker: {
signIn: {
path: '/auth/signin',
},
},
} as const;

export class AuthService implements AuthProvider {
private readonly browserAuthProvider: BrowserAuthProvider =
browserAuthProvider;

private static refreshPromise: Promise<AuthTokensSuccessResponse | null> | null =
null;

constructor(private readonly httpClient: HttpApiClient) {}

async signIn(data: SignInDto): Promise<void> {
const res = await this.httpClient.post<AuthTokensSuccessResponse>(
apiPaths.worker.signIn.path,
{
successSchema: authTokensSuccessResponseSchema,
body: data,
}
);

this.browserAuthProvider.signIn(res, 'web2');
}

async getAccessToken(): Promise<string | null> {
const accessToken = this.browserAuthProvider.getAccessToken();

if (!accessToken) {
return null;
}

const decodedToken = jwtDecode<{ exp: number }>(accessToken);
const currentTime = Math.floor(Date.now() / 1000);
const tokenExpiration = decodedToken.exp;

if (tokenExpiration && tokenExpiration - currentTime < 30) {
await this.refreshAccessToken();

const newAccessToken = this.browserAuthProvider.getAccessToken();

if (!newAccessToken) {
return null;
}

return newAccessToken;
}

return accessToken;
}

async refreshAccessToken(): Promise<void> {
const authType = this.browserAuthProvider.getAuthType();

if (!authType) {
throw new Error('Auth type not found');
}

if (!AuthService.refreshPromise) {
AuthService.refreshPromise = this.fetchTokenRefresh();
}

const tokens = await AuthService.refreshPromise;

AuthService.refreshPromise = null;

if (tokens === null) {
throw new Error('Failed to refresh access token');
}

browserAuthProvider.signIn(tokens, authType);
}

private async fetchTokenRefresh(): Promise<AuthTokensSuccessResponse | null> {
const refreshToken = this.browserAuthProvider.getRefreshToken();

if (!refreshToken) {
return null;
}

try {
const response = await this.httpClient.post<AuthTokensSuccessResponse>(
commonApiPaths.auth.refresh.path,
{
body: {
// eslint-disable-next-line camelcase
refresh_token: refreshToken,
},
}
);

return response;
} catch (error) {
return null;
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { env } from '@/shared/env';
import {
HttpApiClient,
humanAppApiClient,
type RequestConfig,
} from './http-api-client';
import { AuthService, type AuthProvider } from './auth-service';

export class AuthorizedHttpApiClient extends HttpApiClient {
constructor(
baseUrl: string,
private readonly authProvider: AuthProvider
) {
super(baseUrl);
}

protected async makeRequest<T = unknown>(
method: string,
path: string,
config: RequestConfig
): Promise<T> {
const token = await this.authProvider.getAccessToken();

const _config = {
...config,
headers: {
...config.headers,
Authorization: `Bearer ${token}`,
},
};

return super.makeRequest(method, path, _config);
}
}

export const authService = new AuthService(humanAppApiClient);

export const authorizedHumanAppApiClient = new AuthorizedHttpApiClient(
env.VITE_API_URL,
authService
);
7 changes: 7 additions & 0 deletions packages/apps/human-app/frontend/src/api/common-api-paths.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const commonApiPaths = {
auth: {
refresh: {
path: '/auth/refresh',
},
},
} as const;
29 changes: 0 additions & 29 deletions packages/apps/human-app/frontend/src/api/fetch-refresh-token.ts

This file was deleted.

Loading