@enfuce/nextgen-sdk 0.0.7 → 0.0.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +19 -6
- package/dist/authorisation-control/client.d.ts +2 -2
- package/dist/card/client.d.ts +2 -2
- package/dist/cardholder/client.d.ts +2 -2
- package/dist/cards/client.d.ts +2 -2
- package/dist/esm/authorisation-control/client.d.ts +2 -2
- package/dist/esm/card/client.d.ts +2 -2
- package/dist/esm/cardholder/client.d.ts +2 -2
- package/dist/esm/cards/client.d.ts +2 -2
- package/dist/esm/exchange-rate/client.d.ts +2 -2
- package/dist/esm/issuer-events/client.d.ts +2 -2
- package/dist/esm/oauth/axios.d.ts +3 -3
- package/dist/esm/oauth/axios.js +1 -1
- package/dist/esm/oauth/clientCredentials.d.ts +17 -3
- package/dist/esm/oauth/clientCredentials.js +20 -4
- package/dist/esm/oauth/index.d.ts +2 -2
- package/dist/esm/oauth/index.js +1 -1
- package/dist/esm/oauth/tokenManager.d.ts +2 -2
- package/dist/esm/oauth/tokenManager.js +2 -2
- package/dist/esm/pin/client.d.ts +2 -2
- package/dist/esm/threeds/client.d.ts +2 -2
- package/dist/esm/threeds-oob/client.d.ts +2 -2
- package/dist/esm/wallet/client.d.ts +2 -2
- package/dist/exchange-rate/client.d.ts +2 -2
- package/dist/issuer-events/client.d.ts +2 -2
- package/dist/oauth/axios.d.ts +3 -3
- package/dist/oauth/axios.js +1 -1
- package/dist/oauth/clientCredentials.d.ts +17 -3
- package/dist/oauth/clientCredentials.js +19 -3
- package/dist/oauth/index.d.ts +2 -2
- package/dist/oauth/index.js +2 -2
- package/dist/oauth/tokenManager.d.ts +2 -2
- package/dist/oauth/tokenManager.js +4 -4
- package/dist/pin/client.d.ts +2 -2
- package/dist/threeds/client.d.ts +2 -2
- package/dist/threeds-oob/client.d.ts +2 -2
- package/dist/wallet/client.d.ts +2 -2
- package/package.json +1 -1
- package/src/authorisation-control/client.ts +3 -3
- package/src/card/client.ts +3 -3
- package/src/cardholder/client.ts +3 -3
- package/src/cards/client.ts +3 -3
- package/src/exchange-rate/client.ts +3 -3
- package/src/issuer-events/client.ts +3 -3
- package/src/oauth/axios.ts +3 -3
- package/src/oauth/clientCredentials.ts +43 -4
- package/src/oauth/index.ts +2 -2
- package/src/oauth/tokenManager.ts +2 -2
- package/src/pin/client.ts +3 -3
- package/src/threeds/client.ts +3 -3
- package/src/threeds-oob/client.ts +3 -3
- package/src/wallet/client.ts +3 -3
- package/test/clientCredentials.test.ts +20 -0
- package/test/oauth.test.ts +12 -12
|
@@ -16,20 +16,20 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
16
16
|
});
|
|
17
17
|
};
|
|
18
18
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
19
|
-
exports.
|
|
19
|
+
exports.OAuthClientCredentialsManager = void 0;
|
|
20
20
|
/**
|
|
21
21
|
* Caches an access token and refreshes it before expiry. Plug it into a generated
|
|
22
22
|
* client's `Configuration`:
|
|
23
23
|
*
|
|
24
24
|
* ```ts
|
|
25
|
-
* const tokens = new
|
|
25
|
+
* const tokens = new OAuthClientCredentialsManager(fetcher);
|
|
26
26
|
* const config = new card.Configuration({ accessToken: () => tokens.getToken() });
|
|
27
27
|
* ```
|
|
28
28
|
*
|
|
29
29
|
* A token is considered expired once the clock passes `expiry - skew`. Concurrent
|
|
30
30
|
* refreshes share a single in-flight promise, so the token endpoint is hit once.
|
|
31
31
|
*/
|
|
32
|
-
class
|
|
32
|
+
class OAuthClientCredentialsManager {
|
|
33
33
|
constructor(fetcher, skewSeconds = 60, now = Date.now) {
|
|
34
34
|
this.fetcher = fetcher;
|
|
35
35
|
this.skewSeconds = skewSeconds;
|
|
@@ -78,4 +78,4 @@ class TokenManager {
|
|
|
78
78
|
return this.inflight;
|
|
79
79
|
}
|
|
80
80
|
}
|
|
81
|
-
exports.
|
|
81
|
+
exports.OAuthClientCredentialsManager = OAuthClientCredentialsManager;
|
package/dist/pin/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AxiosInstance } from 'axios';
|
|
2
|
-
import { type
|
|
2
|
+
import { type OAuthClientCredentialsManager } from '../oauth';
|
|
3
3
|
import { Configuration } from './configuration';
|
|
4
4
|
import { PINOperationsUsingPKIApi, PINOperationsWithPreSharedKeyApi } from './api';
|
|
5
5
|
/**
|
|
@@ -38,7 +38,7 @@ export declare class PinClientBuilder {
|
|
|
38
38
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
39
39
|
baseUrl(baseUrl: string): this;
|
|
40
40
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
41
|
-
oauth(tokens:
|
|
41
|
+
oauth(tokens: OAuthClientCredentialsManager): this;
|
|
42
42
|
/**
|
|
43
43
|
* Customize the underlying axios instance — set a timeout (`http.defaults.timeout = ...`), a proxy,
|
|
44
44
|
* default headers, extra interceptors, etc. Runs after OAuth is installed. Composable: multiple
|
package/dist/threeds/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AxiosInstance } from 'axios';
|
|
2
|
-
import { type
|
|
2
|
+
import { type OAuthClientCredentialsManager } from '../oauth';
|
|
3
3
|
import { Configuration } from './configuration';
|
|
4
4
|
import { ThreeDSApi } from './api';
|
|
5
5
|
/**
|
|
@@ -36,7 +36,7 @@ export declare class ThreedsClientBuilder {
|
|
|
36
36
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
37
37
|
baseUrl(baseUrl: string): this;
|
|
38
38
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
39
|
-
oauth(tokens:
|
|
39
|
+
oauth(tokens: OAuthClientCredentialsManager): this;
|
|
40
40
|
/**
|
|
41
41
|
* Customize the underlying axios instance — set a timeout (`http.defaults.timeout = ...`), a proxy,
|
|
42
42
|
* default headers, extra interceptors, etc. Runs after OAuth is installed. Composable: multiple
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AxiosInstance } from 'axios';
|
|
2
|
-
import { type
|
|
2
|
+
import { type OAuthClientCredentialsManager } from '../oauth';
|
|
3
3
|
import { Configuration } from './configuration';
|
|
4
4
|
import { OOBAuthenticationChallengeWebhookNotificationApi } from './api';
|
|
5
5
|
/**
|
|
@@ -36,7 +36,7 @@ export declare class ThreedsOobClientBuilder {
|
|
|
36
36
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
37
37
|
baseUrl(baseUrl: string): this;
|
|
38
38
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
39
|
-
oauth(tokens:
|
|
39
|
+
oauth(tokens: OAuthClientCredentialsManager): this;
|
|
40
40
|
/**
|
|
41
41
|
* Customize the underlying axios instance — set a timeout (`http.defaults.timeout = ...`), a proxy,
|
|
42
42
|
* default headers, extra interceptors, etc. Runs after OAuth is installed. Composable: multiple
|
package/dist/wallet/client.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { type AxiosInstance } from 'axios';
|
|
2
|
-
import { type
|
|
2
|
+
import { type OAuthClientCredentialsManager } from '../oauth';
|
|
3
3
|
import { Configuration } from './configuration';
|
|
4
4
|
import { ActivateTokenApi, DeactivateTokenApi, EligibleTokenRequestorsApi, GetTokensApi, PushCardsApi, PushProvisionApi, SuspendTokenApi, UnsuspendTokenApi } from './api';
|
|
5
5
|
/**
|
|
@@ -50,7 +50,7 @@ export declare class WalletClientBuilder {
|
|
|
50
50
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
51
51
|
baseUrl(baseUrl: string): this;
|
|
52
52
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
53
|
-
oauth(tokens:
|
|
53
|
+
oauth(tokens: OAuthClientCredentialsManager): this;
|
|
54
54
|
/**
|
|
55
55
|
* Customize the underlying axios instance — set a timeout (`http.defaults.timeout = ...`), a proxy,
|
|
56
56
|
* default headers, extra interceptors, etc. Runs after OAuth is installed. Composable: multiple
|
package/package.json
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
AuthorisationRequestAPIApi,
|
|
@@ -50,7 +50,7 @@ export class AuthorisationControlClient {
|
|
|
50
50
|
export class AuthorisationControlClientBuilder {
|
|
51
51
|
|
|
52
52
|
private _baseUrl?: string;
|
|
53
|
-
private _tokens?:
|
|
53
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
54
54
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
55
55
|
|
|
56
56
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -60,7 +60,7 @@ export class AuthorisationControlClientBuilder {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
63
|
-
oauth(tokens:
|
|
63
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
64
64
|
this._tokens = tokens;
|
|
65
65
|
return this;
|
|
66
66
|
}
|
package/src/card/client.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
CreateCardApi,
|
|
@@ -80,7 +80,7 @@ export class CardClient {
|
|
|
80
80
|
export class CardClientBuilder {
|
|
81
81
|
|
|
82
82
|
private _baseUrl?: string;
|
|
83
|
-
private _tokens?:
|
|
83
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
84
84
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
85
85
|
|
|
86
86
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -90,7 +90,7 @@ export class CardClientBuilder {
|
|
|
90
90
|
}
|
|
91
91
|
|
|
92
92
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
93
|
-
oauth(tokens:
|
|
93
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
94
94
|
this._tokens = tokens;
|
|
95
95
|
return this;
|
|
96
96
|
}
|
package/src/cardholder/client.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
CreateCardholderApi,
|
|
@@ -68,7 +68,7 @@ export class CardholderClient {
|
|
|
68
68
|
export class CardholderClientBuilder {
|
|
69
69
|
|
|
70
70
|
private _baseUrl?: string;
|
|
71
|
-
private _tokens?:
|
|
71
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
72
72
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
73
73
|
|
|
74
74
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -78,7 +78,7 @@ export class CardholderClientBuilder {
|
|
|
78
78
|
}
|
|
79
79
|
|
|
80
80
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
81
|
-
oauth(tokens:
|
|
81
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
82
82
|
this._tokens = tokens;
|
|
83
83
|
return this;
|
|
84
84
|
}
|
package/src/cards/client.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
CardsApi,
|
|
@@ -50,7 +50,7 @@ export class CardsClient {
|
|
|
50
50
|
export class CardsClientBuilder {
|
|
51
51
|
|
|
52
52
|
private _baseUrl?: string;
|
|
53
|
-
private _tokens?:
|
|
53
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
54
54
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
55
55
|
|
|
56
56
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -60,7 +60,7 @@ export class CardsClientBuilder {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
63
|
-
oauth(tokens:
|
|
63
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
64
64
|
this._tokens = tokens;
|
|
65
65
|
return this;
|
|
66
66
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
GetECBExchangeRateApi,
|
|
@@ -62,7 +62,7 @@ export class ExchangeRateClient {
|
|
|
62
62
|
export class ExchangeRateClientBuilder {
|
|
63
63
|
|
|
64
64
|
private _baseUrl?: string;
|
|
65
|
-
private _tokens?:
|
|
65
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
66
66
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
67
67
|
|
|
68
68
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -72,7 +72,7 @@ export class ExchangeRateClientBuilder {
|
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
75
|
-
oauth(tokens:
|
|
75
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
76
76
|
this._tokens = tokens;
|
|
77
77
|
return this;
|
|
78
78
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
IssuerEventWebhooksApi,
|
|
@@ -50,7 +50,7 @@ export class IssuerEventsClient {
|
|
|
50
50
|
export class IssuerEventsClientBuilder {
|
|
51
51
|
|
|
52
52
|
private _baseUrl?: string;
|
|
53
|
-
private _tokens?:
|
|
53
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
54
54
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
55
55
|
|
|
56
56
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -60,7 +60,7 @@ export class IssuerEventsClientBuilder {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
63
|
-
oauth(tokens:
|
|
63
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
64
64
|
this._tokens = tokens;
|
|
65
65
|
return this;
|
|
66
66
|
}
|
package/src/oauth/axios.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* OpenAPI Generator to (re)produce it.
|
|
7
7
|
*/
|
|
8
8
|
import axios, { AxiosError, AxiosInstance, InternalAxiosRequestConfig } from 'axios';
|
|
9
|
-
import {
|
|
9
|
+
import { OAuthClientCredentialsManager } from './tokenManager';
|
|
10
10
|
|
|
11
11
|
const BEARER_PREFIX = 'Bearer ';
|
|
12
12
|
|
|
@@ -22,7 +22,7 @@ function stripBearer(header: string): string {
|
|
|
22
22
|
* Returns an {@link AxiosInstance} with OAuth bearer-token handling built in:
|
|
23
23
|
*
|
|
24
24
|
* - **Proactive:** a request interceptor attaches a fresh bearer token (from the
|
|
25
|
-
* {@link
|
|
25
|
+
* {@link OAuthClientCredentialsManager}) to *every* request. This authenticates API modules whose spec declares no
|
|
26
26
|
* security scheme (e.g. exchange-rate), so no `accessToken` on the `Configuration` is required.
|
|
27
27
|
* - **Reactive:** a response interceptor, on a `401`, forces a token refresh and retries the request
|
|
28
28
|
* exactly once with the new bearer token.
|
|
@@ -35,7 +35,7 @@ function stripBearer(header: string): string {
|
|
|
35
35
|
* const api = new card.CardApi(config, undefined, http);
|
|
36
36
|
* ```
|
|
37
37
|
*/
|
|
38
|
-
export function createOAuthAxios(manager:
|
|
38
|
+
export function createOAuthAxios(manager: OAuthClientCredentialsManager, instance?: AxiosInstance): AxiosInstance {
|
|
39
39
|
const http = instance ?? axios.create();
|
|
40
40
|
|
|
41
41
|
// Proactive: attach a fresh bearer token to every outgoing request.
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* OpenAPI Generator to (re)produce it.
|
|
7
7
|
*/
|
|
8
8
|
import axios, { AxiosInstance, AxiosRequestConfig } from 'axios';
|
|
9
|
-
import { AccessToken, TokenFetcher,
|
|
9
|
+
import { AccessToken, TokenFetcher, OAuthClientCredentialsManager } from './tokenManager';
|
|
10
10
|
|
|
11
11
|
/** Configuration for the OAuth2 `client_credentials` grant. */
|
|
12
12
|
export interface OAuthConfig {
|
|
@@ -23,6 +23,19 @@ export interface OAuthConfig {
|
|
|
23
23
|
axiosInstance?: AxiosInstance;
|
|
24
24
|
}
|
|
25
25
|
|
|
26
|
+
/**
|
|
27
|
+
* The OAuth2 client-credentials identity — the client id/secret and requested scopes. It carries
|
|
28
|
+
* no URL: pair it with a token URL (e.g. derived from a `TenantEnvironment`) via the
|
|
29
|
+
* `clientCredentials(tokenUrl, credentials)` overload below.
|
|
30
|
+
*/
|
|
31
|
+
export interface ClientCredentials {
|
|
32
|
+
clientId: string;
|
|
33
|
+
/** May be omitted for a public client. */
|
|
34
|
+
clientSecret?: string;
|
|
35
|
+
/** Requested scopes; space-joined into a `scope` parameter. */
|
|
36
|
+
scopes?: string[];
|
|
37
|
+
}
|
|
38
|
+
|
|
26
39
|
interface TokenResponse {
|
|
27
40
|
access_token?: string;
|
|
28
41
|
expires_in?: number;
|
|
@@ -66,7 +79,33 @@ export function clientCredentialsFetcher(config: OAuthConfig): TokenFetcher {
|
|
|
66
79
|
};
|
|
67
80
|
}
|
|
68
81
|
|
|
69
|
-
/** Convenience: a caching {@link
|
|
70
|
-
export function clientCredentials(config: OAuthConfig, skewSeconds
|
|
71
|
-
|
|
82
|
+
/** Convenience: a caching {@link OAuthClientCredentialsManager} backed by the client-credentials grant. */
|
|
83
|
+
export function clientCredentials(config: OAuthConfig, skewSeconds?: number): OAuthClientCredentialsManager;
|
|
84
|
+
/** Convenience overload pairing a token URL with a {@link ClientCredentials} identity. */
|
|
85
|
+
export function clientCredentials(
|
|
86
|
+
tokenUrl: string,
|
|
87
|
+
credentials: ClientCredentials,
|
|
88
|
+
skewSeconds?: number,
|
|
89
|
+
): OAuthClientCredentialsManager;
|
|
90
|
+
export function clientCredentials(
|
|
91
|
+
configOrUrl: OAuthConfig | string,
|
|
92
|
+
credsOrSkew?: ClientCredentials | number,
|
|
93
|
+
skewSeconds = 60,
|
|
94
|
+
): OAuthClientCredentialsManager {
|
|
95
|
+
let config: OAuthConfig;
|
|
96
|
+
let skew: number;
|
|
97
|
+
if (typeof configOrUrl === 'string') {
|
|
98
|
+
const creds = credsOrSkew as ClientCredentials;
|
|
99
|
+
config = {
|
|
100
|
+
tokenUrl: configOrUrl,
|
|
101
|
+
clientId: creds.clientId,
|
|
102
|
+
clientSecret: creds.clientSecret ?? '',
|
|
103
|
+
scopes: creds.scopes,
|
|
104
|
+
};
|
|
105
|
+
skew = skewSeconds;
|
|
106
|
+
} else {
|
|
107
|
+
config = configOrUrl;
|
|
108
|
+
skew = typeof credsOrSkew === 'number' ? credsOrSkew : 60;
|
|
109
|
+
}
|
|
110
|
+
return new OAuthClientCredentialsManager(clientCredentialsFetcher(config), skew);
|
|
72
111
|
}
|
package/src/oauth/index.ts
CHANGED
|
@@ -22,6 +22,6 @@
|
|
|
22
22
|
*
|
|
23
23
|
* Server-side only — a client secret must never ship to a browser.
|
|
24
24
|
*/
|
|
25
|
-
export { AccessToken, TokenFetcher,
|
|
26
|
-
export { OAuthConfig, clientCredentials, clientCredentialsFetcher } from './clientCredentials';
|
|
25
|
+
export { AccessToken, TokenFetcher, OAuthClientCredentialsManager } from './tokenManager';
|
|
26
|
+
export { ClientCredentials, OAuthConfig, clientCredentials, clientCredentialsFetcher } from './clientCredentials';
|
|
27
27
|
export { createOAuthAxios } from './axios';
|
|
@@ -21,14 +21,14 @@ export type TokenFetcher = () => Promise<AccessToken>;
|
|
|
21
21
|
* client's `Configuration`:
|
|
22
22
|
*
|
|
23
23
|
* ```ts
|
|
24
|
-
* const tokens = new
|
|
24
|
+
* const tokens = new OAuthClientCredentialsManager(fetcher);
|
|
25
25
|
* const config = new card.Configuration({ accessToken: () => tokens.getToken() });
|
|
26
26
|
* ```
|
|
27
27
|
*
|
|
28
28
|
* A token is considered expired once the clock passes `expiry - skew`. Concurrent
|
|
29
29
|
* refreshes share a single in-flight promise, so the token endpoint is hit once.
|
|
30
30
|
*/
|
|
31
|
-
export class
|
|
31
|
+
export class OAuthClientCredentialsManager {
|
|
32
32
|
private token?: string;
|
|
33
33
|
private expiresAtMs?: number;
|
|
34
34
|
private inflight?: Promise<string>;
|
package/src/pin/client.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
PINOperationsUsingPKIApi,
|
|
@@ -56,7 +56,7 @@ export class PinClient {
|
|
|
56
56
|
export class PinClientBuilder {
|
|
57
57
|
|
|
58
58
|
private _baseUrl?: string;
|
|
59
|
-
private _tokens?:
|
|
59
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
60
60
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
61
61
|
|
|
62
62
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -66,7 +66,7 @@ export class PinClientBuilder {
|
|
|
66
66
|
}
|
|
67
67
|
|
|
68
68
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
69
|
-
oauth(tokens:
|
|
69
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
70
70
|
this._tokens = tokens;
|
|
71
71
|
return this;
|
|
72
72
|
}
|
package/src/threeds/client.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
ThreeDSApi,
|
|
@@ -50,7 +50,7 @@ export class ThreedsClient {
|
|
|
50
50
|
export class ThreedsClientBuilder {
|
|
51
51
|
|
|
52
52
|
private _baseUrl?: string;
|
|
53
|
-
private _tokens?:
|
|
53
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
54
54
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
55
55
|
|
|
56
56
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -60,7 +60,7 @@ export class ThreedsClientBuilder {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
63
|
-
oauth(tokens:
|
|
63
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
64
64
|
this._tokens = tokens;
|
|
65
65
|
return this;
|
|
66
66
|
}
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
OOBAuthenticationChallengeWebhookNotificationApi,
|
|
@@ -50,7 +50,7 @@ export class ThreedsOobClient {
|
|
|
50
50
|
export class ThreedsOobClientBuilder {
|
|
51
51
|
|
|
52
52
|
private _baseUrl?: string;
|
|
53
|
-
private _tokens?:
|
|
53
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
54
54
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
55
55
|
|
|
56
56
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -60,7 +60,7 @@ export class ThreedsOobClientBuilder {
|
|
|
60
60
|
}
|
|
61
61
|
|
|
62
62
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
63
|
-
oauth(tokens:
|
|
63
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
64
64
|
this._tokens = tokens;
|
|
65
65
|
return this;
|
|
66
66
|
}
|
package/src/wallet/client.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* retry) to the module's Configuration and exposes each API. Nothing is shared across modules.
|
|
7
7
|
*/
|
|
8
8
|
import globalAxios, { type AxiosInstance } from 'axios';
|
|
9
|
-
import { createOAuthAxios, type
|
|
9
|
+
import { createOAuthAxios, type OAuthClientCredentialsManager } from '../oauth';
|
|
10
10
|
import { Configuration } from './configuration';
|
|
11
11
|
import {
|
|
12
12
|
ActivateTokenApi,
|
|
@@ -92,7 +92,7 @@ export class WalletClient {
|
|
|
92
92
|
export class WalletClientBuilder {
|
|
93
93
|
|
|
94
94
|
private _baseUrl?: string;
|
|
95
|
-
private _tokens?:
|
|
95
|
+
private _tokens?: OAuthClientCredentialsManager;
|
|
96
96
|
private _customizers: Array<(http: AxiosInstance) => void> = [];
|
|
97
97
|
|
|
98
98
|
/** The module's host, e.g. `https://api.<tenant>.../issuer`. */
|
|
@@ -102,7 +102,7 @@ export class WalletClientBuilder {
|
|
|
102
102
|
}
|
|
103
103
|
|
|
104
104
|
/** Install OAuth: a fresh bearer token on every request + reactive 401 retry. */
|
|
105
|
-
oauth(tokens:
|
|
105
|
+
oauth(tokens: OAuthClientCredentialsManager): this {
|
|
106
106
|
this._tokens = tokens;
|
|
107
107
|
return this;
|
|
108
108
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/** Tests for the ClientCredentials identity object and the clientCredentials overloads. */
|
|
2
|
+
import { describe, expect, it } from 'vitest';
|
|
3
|
+
import { oauth } from '../src';
|
|
4
|
+
|
|
5
|
+
describe('ClientCredentials pairing', () => {
|
|
6
|
+
it('clientCredentials(tokenUrl, credentials) builds an OAuthClientCredentialsManager', () => {
|
|
7
|
+
const creds: oauth.ClientCredentials = { clientId: 'id', clientSecret: 'secret', scopes: ['s.read'] };
|
|
8
|
+
const mgr = oauth.clientCredentials('https://auth.example.com/oauth2/token', creds);
|
|
9
|
+
expect(mgr).toBeInstanceOf(oauth.OAuthClientCredentialsManager);
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
it('still accepts a full OAuthConfig', () => {
|
|
13
|
+
const mgr = oauth.clientCredentials({
|
|
14
|
+
tokenUrl: 'https://auth.example.com/oauth2/token',
|
|
15
|
+
clientId: 'id',
|
|
16
|
+
clientSecret: 'secret',
|
|
17
|
+
});
|
|
18
|
+
expect(mgr).toBeInstanceOf(oauth.OAuthClientCredentialsManager);
|
|
19
|
+
});
|
|
20
|
+
});
|
package/test/oauth.test.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Tests for the hand-written OAuth token-management helper (src/oauth).
|
|
3
3
|
*
|
|
4
|
-
* Covers the
|
|
4
|
+
* Covers the OAuthClientCredentialsManager cache/refresh/single-flight logic, the client-credentials
|
|
5
5
|
* fetcher and the reactive 401-retry axios instance, using capturing axios adapters
|
|
6
6
|
* so nothing hits the network.
|
|
7
7
|
*/
|
|
@@ -9,7 +9,7 @@ import axios, { type AxiosAdapter, type InternalAxiosRequestConfig } from 'axios
|
|
|
9
9
|
import { describe, expect, it } from 'vitest';
|
|
10
10
|
import { oauth } from '../src';
|
|
11
11
|
|
|
12
|
-
const {
|
|
12
|
+
const { OAuthClientCredentialsManager, clientCredentialsFetcher, clientCredentials, createOAuthAxios } = oauth;
|
|
13
13
|
|
|
14
14
|
function makeClock(start = 1000) {
|
|
15
15
|
const state = { t: start };
|
|
@@ -25,11 +25,11 @@ function countingFetcher(expiresInSeconds = 300) {
|
|
|
25
25
|
return { fetch, calls };
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
-
describe('
|
|
28
|
+
describe('OAuthClientCredentialsManager', () => {
|
|
29
29
|
it('caches the token within its validity window', async () => {
|
|
30
30
|
const { fetch, calls } = countingFetcher();
|
|
31
31
|
const clock = makeClock();
|
|
32
|
-
const mgr = new
|
|
32
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, clock.now);
|
|
33
33
|
|
|
34
34
|
expect(await mgr.getToken()).toBe('token-1');
|
|
35
35
|
expect(await mgr.getToken()).toBe('token-1');
|
|
@@ -39,7 +39,7 @@ describe('TokenManager', () => {
|
|
|
39
39
|
it('refreshes once past expiry minus skew', async () => {
|
|
40
40
|
const { fetch, calls } = countingFetcher(300);
|
|
41
41
|
const clock = makeClock();
|
|
42
|
-
const mgr = new
|
|
42
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, clock.now);
|
|
43
43
|
|
|
44
44
|
expect(await mgr.getToken()).toBe('token-1');
|
|
45
45
|
clock.advance(239_000);
|
|
@@ -53,7 +53,7 @@ describe('TokenManager', () => {
|
|
|
53
53
|
|
|
54
54
|
it('refetches every call when lifetime is unknown', async () => {
|
|
55
55
|
const { fetch, calls } = countingFetcher(0);
|
|
56
|
-
const mgr = new
|
|
56
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, makeClock().now);
|
|
57
57
|
await mgr.getToken();
|
|
58
58
|
await mgr.getToken();
|
|
59
59
|
expect(calls.n).toBe(2);
|
|
@@ -61,7 +61,7 @@ describe('TokenManager', () => {
|
|
|
61
61
|
|
|
62
62
|
it('shares a single in-flight fetch across concurrent callers', async () => {
|
|
63
63
|
const { fetch, calls } = countingFetcher();
|
|
64
|
-
const mgr = new
|
|
64
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, makeClock().now);
|
|
65
65
|
|
|
66
66
|
const results = await Promise.all([mgr.getToken(), mgr.getToken(), mgr.getToken()]);
|
|
67
67
|
|
|
@@ -71,7 +71,7 @@ describe('TokenManager', () => {
|
|
|
71
71
|
|
|
72
72
|
it('forceRefresh is a no-op when the cached token already moved on', async () => {
|
|
73
73
|
const { fetch, calls } = countingFetcher();
|
|
74
|
-
const mgr = new
|
|
74
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, makeClock().now);
|
|
75
75
|
|
|
76
76
|
expect(await mgr.getToken()).toBe('token-1');
|
|
77
77
|
expect(await mgr.forceRefresh('an-older-token')).toBe('token-1');
|
|
@@ -129,7 +129,7 @@ describe('clientCredentialsFetcher', () => {
|
|
|
129
129
|
expect(captured.config?.auth).toBeUndefined();
|
|
130
130
|
});
|
|
131
131
|
|
|
132
|
-
it('clientCredentials returns a caching
|
|
132
|
+
it('clientCredentials returns a caching OAuthClientCredentialsManager', async () => {
|
|
133
133
|
const { instance } = capturingAxios({ access_token: 'abc', expires_in: 120 });
|
|
134
134
|
const mgr = clientCredentials(
|
|
135
135
|
{ tokenUrl: 'https://auth.example.com/oauth/token', clientId: 'id', clientSecret: 's', axiosInstance: instance },
|
|
@@ -141,7 +141,7 @@ describe('clientCredentialsFetcher', () => {
|
|
|
141
141
|
describe('createOAuthAxios', () => {
|
|
142
142
|
it('proactively attaches a bearer token to a request that has no Authorization header', async () => {
|
|
143
143
|
const { fetch, calls } = countingFetcher();
|
|
144
|
-
const mgr = new
|
|
144
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, makeClock().now);
|
|
145
145
|
|
|
146
146
|
const seenAuth: (string | undefined)[] = [];
|
|
147
147
|
const adapter: AxiosAdapter = async (config) => {
|
|
@@ -160,7 +160,7 @@ describe('createOAuthAxios', () => {
|
|
|
160
160
|
|
|
161
161
|
it('retries the request with a refreshed token when the first call rejects with 401', async () => {
|
|
162
162
|
const { fetch } = countingFetcher();
|
|
163
|
-
const mgr = new
|
|
163
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, makeClock().now);
|
|
164
164
|
await mgr.getToken(); // seed token-1
|
|
165
165
|
|
|
166
166
|
const seenAuth: (string | undefined)[] = [];
|
|
@@ -196,7 +196,7 @@ describe('createOAuthAxios', () => {
|
|
|
196
196
|
|
|
197
197
|
it('surfaces the error after a single retry (no infinite loop)', async () => {
|
|
198
198
|
const { fetch } = countingFetcher();
|
|
199
|
-
const mgr = new
|
|
199
|
+
const mgr = new OAuthClientCredentialsManager(fetch, 60, makeClock().now);
|
|
200
200
|
await mgr.getToken();
|
|
201
201
|
|
|
202
202
|
let calls = 0;
|