@enfuce/nextgen-sdk 0.0.6 → 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/config/enfuceUrls.d.ts +20 -0
- package/dist/config/enfuceUrls.js +47 -0
- package/dist/config/index.d.ts +1 -0
- package/dist/config/index.js +22 -0
- 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/config/enfuceUrls.d.ts +20 -0
- package/dist/esm/config/enfuceUrls.js +40 -0
- package/dist/esm/config/index.d.ts +1 -0
- package/dist/esm/config/index.js +14 -0
- package/dist/esm/exchange-rate/client.d.ts +2 -2
- package/dist/esm/index.d.ts +1 -0
- package/dist/esm/index.js +1 -0
- 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/index.d.ts +1 -0
- package/dist/index.js +2 -1
- 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/config/enfuceUrls.ts +57 -0
- package/src/config/index.ts +21 -0
- package/src/exchange-rate/client.ts +3 -3
- package/src/index.ts +1 -0
- 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/config.test.ts +33 -0
- package/test/oauth.test.ts +12 -12
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
|
+
});
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tests for the hand-written tenant/environment URL helper (src/config).
|
|
3
|
+
*/
|
|
4
|
+
import { describe, expect, it } from 'vitest';
|
|
5
|
+
import { config } from '../src';
|
|
6
|
+
|
|
7
|
+
const sandbox: config.TenantEnvironment = { tenant: 'swile', environment: 'ext-uat1-sandbox' };
|
|
8
|
+
|
|
9
|
+
describe('config URL helpers', () => {
|
|
10
|
+
it('derives per-domain base URLs as api.<tenant>.<environment>.mycore.enfuce.com/<domain>', () => {
|
|
11
|
+
expect(config.apiBaseUrl(sandbox)).toBe('https://api.swile.ext-uat1-sandbox.mycore.enfuce.com');
|
|
12
|
+
expect(config.issuerBaseUrl(sandbox)).toBe('https://api.swile.ext-uat1-sandbox.mycore.enfuce.com/issuer');
|
|
13
|
+
expect(config.processorBaseUrl(sandbox)).toBe('https://api.swile.ext-uat1-sandbox.mycore.enfuce.com/processor');
|
|
14
|
+
expect(config.exchangeRateBaseUrl(sandbox)).toBe(
|
|
15
|
+
'https://api.swile.ext-uat1-sandbox.mycore.enfuce.com/exchange-rates-api',
|
|
16
|
+
);
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('derives the token URL from the auth host', () => {
|
|
20
|
+
expect(config.tokenUrl(sandbox)).toBe('https://auth.swile.ext-uat1-sandbox.mycore.enfuce.com/oauth2/token');
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
it('places a multi-segment environment verbatim', () => {
|
|
24
|
+
expect(config.issuerBaseUrl({ tenant: 'swile', environment: 'eu.live.prod' })).toBe(
|
|
25
|
+
'https://api.swile.eu.live.prod.mycore.enfuce.com/issuer',
|
|
26
|
+
);
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
it('rejects an empty tenant or environment', () => {
|
|
30
|
+
expect(() => config.issuerBaseUrl({ tenant: ' ', environment: 'env' })).toThrow();
|
|
31
|
+
expect(() => config.tokenUrl({ tenant: 'swile', environment: '' })).toThrow();
|
|
32
|
+
});
|
|
33
|
+
});
|
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;
|