Skip to content

Commit

Permalink
fix(cache): separate failed test reloging from unauthenticated
Browse files Browse the repository at this point in the history
  • Loading branch information
JGiter committed Nov 18, 2021
1 parent dec430f commit d07a084
Show file tree
Hide file tree
Showing 7 changed files with 102 additions and 91 deletions.
23 changes: 9 additions & 14 deletions .eslintrc.json
Expand Up @@ -4,31 +4,26 @@
"es2021": true,
"node": true
},
"extends": [
"eslint:recommended",
"plugin:@typescript-eslint/recommended",
"plugin:prettier/recommended"
],
"extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended", "plugin:prettier/recommended"],
"overrides": [
{
"files": ["*.js"],
"rules": {
"@typescript-eslint/no-var-requires": "off"
{
"files": ["*.js"],
"rules": {
"@typescript-eslint/no-var-requires": "off"
}
}
}
],
"parser": "@typescript-eslint/parser",
"parserOptions": {
"ecmaVersion": 12,
"sourceType": "module"
},
"plugins": [
"@typescript-eslint"
],
"plugins": ["@typescript-eslint"],
"rules": {
"semi": "error",
"@typescript-eslint/no-explicit-any": "off",
"@typescript-eslint/explicit-module-boundary-types": "off",
"quotes": ["warn", "double"]
"quotes": ["warn", "double"],
"no-empty": ["error", { "allowEmptyCatch": true }]
}
}
Expand Up @@ -46,8 +46,8 @@
- [getRolesByOwner](modules_cacheClient_cacheClient_service.CacheClient.md#getrolesbyowner)
- [getRolesDefinition](modules_cacheClient_cacheClient_service.CacheClient.md#getrolesdefinition)
- [getSubOrganizationsByOrganization](modules_cacheClient_cacheClient_service.CacheClient.md#getsuborganizationsbyorganization)
- [handleRefreshToken](modules_cacheClient_cacheClient_service.CacheClient.md#handlerefreshtoken)
- [handleUnauthorized](modules_cacheClient_cacheClient_service.CacheClient.md#handleunauthorized)
- [handleError](modules_cacheClient_cacheClient_service.CacheClient.md#handleerror)
- [handleUnauthenticated](modules_cacheClient_cacheClient_service.CacheClient.md#handleunauthenticated)
- [init](modules_cacheClient_cacheClient_service.CacheClient.md#init)
- [isAuthEnabled](modules_cacheClient_cacheClient_service.CacheClient.md#isauthenabled)
- [issueClaim](modules_cacheClient_cacheClient_service.CacheClient.md#issueclaim)
Expand Down Expand Up @@ -107,7 +107,7 @@ ___

| Name | Type |
| :------ | :------ |
| `callback` | (`token?`: `string`) => `void` |
| `callback` | () => `void` |

#### Returns

Expand Down Expand Up @@ -639,19 +639,11 @@ ___

___

### handleRefreshToken
### handleError

**handleRefreshToken**(): `Promise`<`void`\>
**handleError**(`error`): `Promise`<`unknown`\>

#### Returns

`Promise`<`void`\>

___

### handleUnauthorized

**handleUnauthorized**(`error`): `Promise`<`unknown`\>
**`description`** if error was returned not from test loging endpoint, then first tries to refresh auth token and if not helps then asks for new

#### Parameters

Expand All @@ -665,6 +657,18 @@ ___

___

### handleUnauthenticated

**handleUnauthenticated**(): `Promise`<`void`\>

Refreshes access token. If login still fails then signs new identity token and requests access token

#### Returns

`Promise`<`void`\>

___

### init

**init**(): `Promise`<`void`\>
Expand Down
6 changes: 3 additions & 3 deletions docs/api/modules/index.md
Expand Up @@ -61,7 +61,7 @@
- [StakeStatus](index.md#stakestatus)
- [StakingPool](index.md#stakingpool)
- [StakingService](index.md#stakingservice)
- [TEST\_LOGIN\_NAMESPACE](index.md#test_login_namespace)
- [TEST\_LOGIN\_ENDPOINT](index.md#test_login_endpoint)
- [VOLTA\_CHAIN\_ID](index.md#volta_chain_id)
- [addSupportedDID](index.md#addsupporteddid)
- [agreement\_type\_hash](index.md#agreement_type_hash)
Expand Down Expand Up @@ -442,9 +442,9 @@ Re-exports: [StakingService](../classes/modules_staking_staking_service.StakingS

___

### TEST\_LOGIN\_NAMESPACE
### TEST\_LOGIN\_ENDPOINT

Re-exports: [TEST\_LOGIN\_NAMESPACE](modules_cacheClient_cacheClient_types.md#test_login_namespace)
Re-exports: [TEST\_LOGIN\_ENDPOINT](modules_cacheClient_cacheClient_types.md#test_login_endpoint)

___

Expand Down
6 changes: 3 additions & 3 deletions docs/api/modules/modules_cacheClient.md
Expand Up @@ -11,7 +11,7 @@
- [ICacheClient](modules_cacheClient.md#icacheclient)
- [Order](modules_cacheClient.md#order)
- [SearchType](modules_cacheClient.md#searchtype)
- [TEST\_LOGIN\_NAMESPACE](modules_cacheClient.md#test_login_namespace)
- [TEST\_LOGIN\_ENDPOINT](modules_cacheClient.md#test_login_endpoint)

## References

Expand Down Expand Up @@ -57,6 +57,6 @@ Re-exports: [SearchType](../enums/modules_cacheClient_cacheClient_types.SearchTy

___

### TEST\_LOGIN\_NAMESPACE
### TEST\_LOGIN\_ENDPOINT

Re-exports: [TEST\_LOGIN\_NAMESPACE](modules_cacheClient_cacheClient_types.md#test_login_namespace)
Re-exports: [TEST\_LOGIN\_ENDPOINT](modules_cacheClient_cacheClient_types.md#test_login_endpoint)
6 changes: 3 additions & 3 deletions docs/api/modules/modules_cacheClient_cacheClient_types.md
Expand Up @@ -18,7 +18,7 @@

### Variables

- [TEST\_LOGIN\_NAMESPACE](modules_cacheClient_cacheClient_types.md#test_login_namespace)
- [TEST\_LOGIN\_ENDPOINT](modules_cacheClient_cacheClient_types.md#test_login_endpoint)

## Type aliases

Expand Down Expand Up @@ -50,6 +50,6 @@ ___

## Variables

### TEST\_LOGIN\_NAMESPACE
### TEST\_LOGIN\_ENDPOINT

`Const` **TEST\_LOGIN\_NAMESPACE**: ``"testing.if.logged.in"``
`Const` **TEST\_LOGIN\_ENDPOINT**: ``"/assets/owner/"``
118 changes: 65 additions & 53 deletions src/modules/cacheClient/cacheClient.service.ts
Expand Up @@ -11,17 +11,18 @@ import { SignerService } from "../signer/signer.service";
import { IPubKeyAndIdentityToken } from "../signer/signer.types";
import { cacheConfigs } from "../../config/cache.config";
import { ICacheClient } from "./ICacheClient";
import { AssetsFilter, ClaimsFilter } from "./cacheClient.types";
import { SearchType, TEST_LOGIN_NAMESPACE } from ".";
import { AssetsFilter, ClaimsFilter, TEST_LOGIN_ENDPOINT } from "./cacheClient.types";
import { SearchType } from ".";

export class CacheClient implements ICacheClient {
public pubKeyAndIdentityToken: IPubKeyAndIdentityToken | undefined;
private httpClient: AxiosInstance;
private isAlreadyFetchingAccessToken = false;
private failedRequests: Array<(token?: string) => void> = [];
private isAuthenticating = false;
private failedRequests: Array<() => void> = [];
private authEnabled: boolean;
private isBrowser: boolean;
private refresh_token: string | undefined;
private token: string | undefined;

constructor(private _signerService: SignerService) {
this._signerService.onInit(this.init.bind(this));
Expand All @@ -35,60 +36,84 @@ export class CacheClient implements ICacheClient {
});
this.httpClient.interceptors.response.use((response: AxiosResponse) => {
return response;
}, this.handleUnauthorized.bind(this));
}, this.handleError.bind(this));
this.authEnabled = cacheServerSupportsAuth;
this.isBrowser = executionEnvironment() === ExecutionEnvironment.BROWSER;
if (!this.isBrowser) {
this.httpClient.defaults.headers.common["Authorization"] = `Bearer ${this.token}`;
}
}

isAuthEnabled() {
return this.authEnabled;
}

async handleRefreshToken() {
const { refreshToken, token } = await this.refreshToken();
/**
* Refreshes access token. If login still fails then signs new identity token and requests access token
*/
async handleUnauthenticated() {
try {
const { refreshToken, token } = await this.refreshToken();
if (await this.isLoggedIn()) {
this.refresh_token = refreshToken;
this.token = token;
return;
}
} catch {}

const pubKeyAndIdentityToken = await this._signerService.publicKeyAndIdentityToken();
const {
data: { refreshToken, token },
} = await this.httpClient.post<{ token: string; refreshToken: string }>("/login", {
identityToken: pubKeyAndIdentityToken.identityToken,
});
this.refresh_token = refreshToken;
this.failedRequests = this.failedRequests.filter((callback) => callback(this.isBrowser ? undefined : token));
if (!this.isBrowser) {
this.httpClient.defaults.headers.common["Authorization"] = `Bearer ${token}`;
this.refresh_token = refreshToken;
}
this.token = token;
this.pubKeyAndIdentityToken = pubKeyAndIdentityToken;

this.failedRequests = this.failedRequests.filter((callback) => callback());
}

addFailedRequest(callback: (token?: string) => void) {
addFailedRequest(callback: () => void) {
this.failedRequests.push(callback);
}

async handleUnauthorized(error: AxiosError) {
/**
* @description if error was returned not from test loging endpoint, then first tries to refresh auth token and if not helps then asks for new
* @param error
* @returns
*/
async handleError(error: AxiosError) {
const { config, response } = error;
const originalRequest = config;
if (
this.authEnabled &&
response &&
response.status === 401 &&
(response.status === 401 || response.status === 403) &&
config &&
config.url?.indexOf("/login") === -1 &&
config.url?.indexOf("/refresh_token") === -1
config.url?.indexOf("/refresh_token") === -1 &&
config.url?.indexOf(TEST_LOGIN_ENDPOINT) === -1
) {
const retryOriginalRequest = new Promise((resolve) => {
this.addFailedRequest((token?: string) => {
if (token) {
originalRequest.headers.Authorization = "Bearer " + token;
}
this.addFailedRequest(() => {
resolve(axios(originalRequest));
});
});
if (!this.isAlreadyFetchingAccessToken) {
this.isAlreadyFetchingAccessToken = true;
await this.handleRefreshToken();
this.isAlreadyFetchingAccessToken = false;
if (!this.isAuthenticating) {
this.isAuthenticating = true;
await this.handleUnauthenticated();
this.isAuthenticating = false;
}
return retryOriginalRequest;
}
return Promise.reject(error);
}

async login() {
await this.testLogin();
if (!(await this.isLoggedIn())) {
await this.handleUnauthenticated();
}
}

async getRoleDefinition(namespace: string) {
Expand Down Expand Up @@ -271,35 +296,22 @@ export class CacheClient implements ICacheClient {
}

private async refreshToken(): Promise<{ token: string; refreshToken: string }> {
try {
const { data } = await this.httpClient.get<{ token: string; refreshToken: string }>(
`/refresh_token${
executionEnvironment() === ExecutionEnvironment.BROWSER
? ""
: `?refresh_token=${this.refresh_token}`
}`,
);
return data;
} catch {
const pubKeyAndIdentityToken = await this._signerService.publicKeyAndIdentityToken();
const {
data: { refreshToken, token },
} = await this.httpClient.post<{ token: string; refreshToken: string }>("/login", {
identityToken: pubKeyAndIdentityToken.identityToken,
});

if (!this.isBrowser) {
this.httpClient.defaults.headers.common["Authorization"] = `Bearer ${token}`;
this.refresh_token = refreshToken;
}
this.pubKeyAndIdentityToken = pubKeyAndIdentityToken;
return { token, refreshToken };
}
const { data } = await this.httpClient.get<{ token: string; refreshToken: string }>(
`/refresh_token${this.isBrowser ? "" : `?refresh_token=${this.refresh_token}`}`,
);
return data;
}

private async testLogin(): Promise<void> {
// Simple test to check if logged in or no. TODO: have dedicated endpoint on the cache-server
// If receive unauthorized response, expect that refreshToken() will be called
await this.getRoleDefinition(TEST_LOGIN_NAMESPACE);
/**
* @description Checks that auth token has been created, has not expired and corresponds to logged in user
* @todo specific endpoint on cache server to return login info instead of error
*/
private async isLoggedIn(): Promise<boolean> {
try {
await this.getOwnedAssets(this._signerService.did);
return true;
} catch (_) {
return false;
}
}
}
2 changes: 1 addition & 1 deletion src/modules/cacheClient/cacheClient.types.ts
Expand Up @@ -28,4 +28,4 @@ export enum SearchType {
Role = "Role",
}

export const TEST_LOGIN_NAMESPACE = "testing.if.logged.in";
export const TEST_LOGIN_ENDPOINT = "/assets/owner/";

0 comments on commit d07a084

Please sign in to comment.