@mcp-abap-adt/auth-providers 4.1.3 → 4.2.0
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/CHANGELOG.md +51 -0
- package/README.md +36 -16
- package/dist/providers/BaseTokenProvider.d.ts +11 -2
- package/dist/providers/BaseTokenProvider.d.ts.map +1 -1
- package/dist/providers/BaseTokenProvider.js +14 -7
- package/dist/providers/index.d.ts +1 -1
- package/dist/providers/index.js +1 -1
- package/dist/sso/SsoProviderFactory.d.ts +2 -2
- package/dist/sso/SsoProviderFactory.d.ts.map +1 -1
- package/dist/sso/types.d.ts +2 -2
- package/dist/sso/types.d.ts.map +1 -1
- package/dist/strategies/BrowserCallbackStrategy.d.ts.map +1 -1
- package/dist/strategies/BrowserCallbackStrategy.js +14 -0
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [4.2.0] - 2026-09-26
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **`refreshTokens()`** on every provider, through `BaseTokenProvider`: a new
|
|
15
|
+
token, never the cached one — the refresh token when there is one, the login
|
|
16
|
+
flow when there is none or the refresh is refused — and it replaces the
|
|
17
|
+
cache. `getTokens()` answers the cache while the token looks valid, so a
|
|
18
|
+
caller holding a 401 had no way to ask for another; auth-broker's
|
|
19
|
+
`refreshToken()` got the refused token back. Every provider now implements
|
|
20
|
+
`IRefreshableTokenProvider` from `@mcp-abap-adt/interfaces-auth` 2.1.0
|
|
21
|
+
(decision 39 there). `getTokens()` is unchanged: it now calls
|
|
22
|
+
`refreshTokens()` once the cache is not valid, which is the same path it
|
|
23
|
+
took inline before.
|
|
24
|
+
- **`SsoProviderFactory.create()` answers `IRefreshableTokenProvider`**, and
|
|
25
|
+
`SsoProviderInstance` is that type, so a provider from the factory can be
|
|
26
|
+
handed to anything requiring the refreshable contract without a cast. Every
|
|
27
|
+
provider it builds already was one.
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- **A failed browser login is a `BrowserAuthError`.** The class was exported
|
|
32
|
+
and documented as "browser auth failed", and thrown nowhere: a timeout, the
|
|
33
|
+
identity provider's refusal (`OAuth2 authentication failed: …`), a busy
|
|
34
|
+
callback port, a browser that would not open and an abort all reached the
|
|
35
|
+
caller as a plain `Error`, so the one type a caller could catch for them
|
|
36
|
+
never arrived. `BrowserCallbackStrategy` — and so `browserCallbackStrategy`,
|
|
37
|
+
`oidcCallbackStrategy` and `samlCallbackStrategy` — now throws it, with the
|
|
38
|
+
original message and the original error as `cause`; an error that already
|
|
39
|
+
has a type (a `ValidationError` from building the URL) passes unchanged.
|
|
40
|
+
Found by auth-broker, whose migration note had nothing to point at.
|
|
41
|
+
- The README's error-handling example caught `RefreshError` as "browser auth
|
|
42
|
+
failed". No provider throws `RefreshError`, `SessionDataError` or
|
|
43
|
+
`ServiceKeyError`; the README now says so.
|
|
44
|
+
|
|
45
|
+
### Changed
|
|
46
|
+
|
|
47
|
+
- `@mcp-abap-adt/interfaces-auth` `^2.1.0` (was `^2.0.1`), which declares
|
|
48
|
+
`IRefreshableTokenProvider`.
|
|
49
|
+
|
|
50
|
+
### Documentation
|
|
51
|
+
|
|
52
|
+
- `docs/btp-setup.md`: what each provider needs on the SAP side — XSUAA
|
|
53
|
+
client, trust, user, ABAP mapping — and whether ADT accepts its token, with
|
|
54
|
+
every claim tagged by source (SAP, Community, Measured, Inference) and the
|
|
55
|
+
open questions still to be settled live.
|
|
56
|
+
- `docs/passwordless-sso.md`: SAP GUI's passwordless SNC login, why Eclipse
|
|
57
|
+
ADT's on-premise SSO runs over RFC, the HTTP equivalents (X.509 client
|
|
58
|
+
certificates, SPNego, IAS), what a Node.js client can reach, and options for
|
|
59
|
+
this package.
|
|
60
|
+
|
|
10
61
|
## [4.1.3] - 2026-09-26
|
|
11
62
|
|
|
12
63
|
### Changed
|
package/README.md
CHANGED
|
@@ -13,7 +13,7 @@ npm install @mcp-abap-adt/auth-providers
|
|
|
13
13
|
|
|
14
14
|
## Overview
|
|
15
15
|
|
|
16
|
-
This package implements
|
|
16
|
+
This package implements `IRefreshableTokenProvider` — `ITokenProvider` plus `refreshTokens()` — from `@mcp-abap-adt/interfaces-auth`:
|
|
17
17
|
|
|
18
18
|
- **ClientCredentialsProvider** — `client_credentials`, no user interaction
|
|
19
19
|
- **AuthorizationCodeProvider** — UAA/XSUAA authorization code, through a browser
|
|
@@ -27,7 +27,15 @@ This package implements the `ITokenProvider` interface from `@mcp-abap-adt/inter
|
|
|
27
27
|
(RFC 7522)
|
|
28
28
|
- **Saml2PureProvider** — a SAML assertion exchanged for session cookies
|
|
29
29
|
|
|
30
|
-
Providers are configured via constructor; `getTokens()` takes no parameters and handles refresh/login internally.
|
|
30
|
+
Providers are configured via constructor; `getTokens()` takes no parameters and handles refresh/login internally. `refreshTokens()` obtains a new token even while the cached one looks valid — what a caller holding a 401 needs.
|
|
31
|
+
|
|
32
|
+
A token is only half of it: whether ADT accepts it depends on the XSUAA client,
|
|
33
|
+
the trust and the user configured on the SAP side. What each provider needs
|
|
34
|
+
there, and which are usable for ADT at all, is in
|
|
35
|
+
[docs/btp-setup.md](docs/btp-setup.md).
|
|
36
|
+
Logging on without a password — SAP GUI's SNC single sign-on and its HTTP
|
|
37
|
+
equivalents, X.509 client certificates and SPNego — is in
|
|
38
|
+
[docs/passwordless-sso.md](docs/passwordless-sso.md).
|
|
31
39
|
|
|
32
40
|
Since 2.0.0 an interactive login is conducted by an **authorization strategy**
|
|
33
41
|
(`IAuthorizationStrategy` from `@mcp-abap-adt/interfaces-auth`) passed as
|
|
@@ -1204,7 +1212,22 @@ This approach prevents unnecessary token refresh and browser authentication when
|
|
|
1204
1212
|
|
|
1205
1213
|
### Token Refresh
|
|
1206
1214
|
|
|
1207
|
-
Providers handle refresh automatically inside `getTokens()
|
|
1215
|
+
Providers handle refresh automatically inside `getTokens()`: while the cached token is valid it
|
|
1216
|
+
is returned, once it expires the refresh token is used, and a login follows when there is none or
|
|
1217
|
+
the refresh is refused.
|
|
1218
|
+
|
|
1219
|
+
The clock is not the only judge, though. When the server refuses a token the cache still
|
|
1220
|
+
considers valid — a 401 — ask for a new one with `refreshTokens()`. It skips the cache, takes the
|
|
1221
|
+
same refresh-then-login path, and replaces the cache with what it obtains:
|
|
1222
|
+
|
|
1223
|
+
```typescript
|
|
1224
|
+
let { authorizationToken } = await provider.getTokens();
|
|
1225
|
+
let response = await call(authorizationToken);
|
|
1226
|
+
if (response.status === 401) {
|
|
1227
|
+
({ authorizationToken } = await provider.refreshTokens());
|
|
1228
|
+
response = await call(authorizationToken);
|
|
1229
|
+
}
|
|
1230
|
+
```
|
|
1208
1231
|
|
|
1209
1232
|
```typescript
|
|
1210
1233
|
try {
|
|
@@ -1213,8 +1236,9 @@ try {
|
|
|
1213
1236
|
} catch (error) {
|
|
1214
1237
|
if (error instanceof ValidationError) {
|
|
1215
1238
|
console.error('Missing fields:', error.missingFields);
|
|
1216
|
-
} else if (error instanceof
|
|
1217
|
-
|
|
1239
|
+
} else if (error instanceof BrowserAuthError) {
|
|
1240
|
+
// the login timed out, the IdP refused, the port was taken, ...
|
|
1241
|
+
console.error('Browser auth failed:', error.message, error.cause);
|
|
1218
1242
|
}
|
|
1219
1243
|
}
|
|
1220
1244
|
```
|
|
@@ -1245,14 +1269,12 @@ try {
|
|
|
1245
1269
|
// provider config validation failed
|
|
1246
1270
|
console.error('Missing required fields:', error.missingFields);
|
|
1247
1271
|
console.error('Error code:', error.code); // 'VALIDATION_ERROR'
|
|
1248
|
-
} else if (error instanceof RefreshError) {
|
|
1249
|
-
// Token refresh operation failed
|
|
1250
|
-
console.error('Refresh failed:', error.message);
|
|
1251
|
-
console.error('Original error:', error.cause);
|
|
1252
|
-
console.error('Error code:', error.code); // 'REFRESH_ERROR'
|
|
1253
1272
|
} else if (error instanceof BrowserAuthError) {
|
|
1254
|
-
//
|
|
1255
|
-
|
|
1273
|
+
// A browser login failed: timeout, the IdP's refusal, a busy callback
|
|
1274
|
+
// port, a browser that would not open, an abort. The message is the
|
|
1275
|
+
// original's, and the original is `cause`.
|
|
1276
|
+
console.error('Browser auth failed:', error.message);
|
|
1277
|
+
console.error('Error code:', error.code); // 'BROWSER_AUTH_ERROR'
|
|
1256
1278
|
}
|
|
1257
1279
|
}
|
|
1258
1280
|
```
|
|
@@ -1260,10 +1282,8 @@ try {
|
|
|
1260
1282
|
**Error Types**:
|
|
1261
1283
|
- `TokenProviderError` - Base class with `code: string` property
|
|
1262
1284
|
- `ValidationError` - provider config validation failed, includes `missingFields: string[]`
|
|
1263
|
-
- `
|
|
1264
|
-
- `SessionDataError` -
|
|
1265
|
-
- `ServiceKeyError` - Service key data invalid, includes `missingFields: string[]`
|
|
1266
|
-
- `BrowserAuthError` - Browser auth failed, includes `cause?: Error`
|
|
1285
|
+
- `BrowserAuthError` - a browser login failed (timeout, the identity provider's refusal, a busy callback port, a browser that would not open, an abort), includes `cause?: Error`; thrown by every browser strategy (`browserCallbackStrategy`, `oidcCallbackStrategy`, `samlCallbackStrategy`)
|
|
1286
|
+
- `RefreshError`, `SessionDataError`, `ServiceKeyError` - exported, but no provider throws them: a refused refresh falls back to a login inside `getTokens()`/`refreshTokens()`, and sessions and service keys are read by `@mcp-abap-adt/auth-stores`, not here
|
|
1267
1287
|
- `AssertionValidationError` - a SAML assertion was refused, includes `check: AssertionCheck` naming the check that failed — see [SAML assertion validation](#errors)
|
|
1268
1288
|
|
|
1269
1289
|
All error codes are defined in `@mcp-abap-adt/interfaces-auth` package as `TOKEN_PROVIDER_ERROR_CODES`, and `AssertionValidationError`'s as `ASSERTION_ERROR_CODES`.
|
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* - Expiration checking
|
|
8
8
|
* - Automatic refresh/relogin
|
|
9
9
|
*/
|
|
10
|
-
import type {
|
|
10
|
+
import type { IRefreshableTokenProvider, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces-auth';
|
|
11
11
|
import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
|
|
12
12
|
/**
|
|
13
13
|
* Abstract base class for token providers
|
|
@@ -18,7 +18,7 @@ import type { ILogger } from '@mcp-abap-adt/interfaces-utils';
|
|
|
18
18
|
* - Automatically refreshes expired tokens
|
|
19
19
|
* - Falls back to login if refresh fails
|
|
20
20
|
*/
|
|
21
|
-
export declare abstract class BaseTokenProvider implements
|
|
21
|
+
export declare abstract class BaseTokenProvider implements IRefreshableTokenProvider {
|
|
22
22
|
protected authorizationToken?: string;
|
|
23
23
|
protected refreshToken?: string;
|
|
24
24
|
protected expiresAt?: number;
|
|
@@ -66,6 +66,15 @@ export declare abstract class BaseTokenProvider implements ITokenProvider {
|
|
|
66
66
|
* @returns Promise that resolves to token result
|
|
67
67
|
*/
|
|
68
68
|
getTokens(): Promise<ITokenResult>;
|
|
69
|
+
/**
|
|
70
|
+
* A new token, never the cached one: the refresh token when there is one,
|
|
71
|
+
* the login flow when there is none or the refresh is refused.
|
|
72
|
+
*
|
|
73
|
+
* `getTokens()` answers the cache while the token looks valid, so a caller
|
|
74
|
+
* holding a 401 — the server refused a token the clock still accepts — has
|
|
75
|
+
* no other way to get a different one. What this obtains replaces the cache.
|
|
76
|
+
*/
|
|
77
|
+
refreshTokens(): Promise<ITokenResult>;
|
|
69
78
|
validateToken(_token: string, _serviceUrl?: string): Promise<boolean>;
|
|
70
79
|
/**
|
|
71
80
|
* Update internal token cache from result
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BaseTokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/BaseTokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,
|
|
1
|
+
{"version":3,"file":"BaseTokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/BaseTokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,KAAK,EACV,yBAAyB,EACzB,YAAY,EACZ,eAAe,EAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D;;;;;;;;GAQG;AACH,8BAAsB,iBAAkB,YAAW,yBAAyB;IAC1E,SAAS,CAAC,kBAAkB,CAAC,EAAE,MAAM,CAAC;IACtC,SAAS,CAAC,YAAY,CAAC,EAAE,MAAM,CAAC;IAChC,SAAS,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC;IAC7B,SAAS,CAAC,SAAS,CAAC,EAAE,KAAK,GAAG,MAAM,GAAG,QAAQ,CAAC;IAChD,SAAS,CAAC,MAAM,CAAC,EAAE,OAAO,CAAC;IAE3B;;;;OAIG;IACH,SAAS,CAAC,oBAAoB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM;IAWzD;;;;OAIG;IACH,SAAS,CAAC,WAAW,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IAKzD;;;OAGG;IACH,SAAS,CAAC,YAAY,IAAI,OAAO;IAyBjC;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,YAAY,IAAI,OAAO,CAAC,YAAY,CAAC;IAExD;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,cAAc,IAAI,OAAO,CAAC,YAAY,CAAC;IAE1D;;;OAGG;IACH,SAAS,CAAC,QAAQ,CAAC,WAAW,IAAI,eAAe;IAEjD;;;;;;;;OAQG;IACG,SAAS,IAAI,OAAO,CAAC,YAAY,CAAC;IAmCxC;;;;;;;OAOG;IACG,aAAa,IAAI,OAAO,CAAC,YAAY,CAAC;IAsCtC,aAAa,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC;IAoC3E;;;OAGG;IACH,SAAS,CAAC,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,IAAI;IA0BlD;;;;OAIG;IACH,SAAS,CAAC,sBAAsB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;IA0BnE;;;;OAIG;IACH,SAAS,CAAC,kBAAkB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS;CAShE"}
|
|
@@ -115,9 +115,19 @@ class BaseTokenProvider {
|
|
|
115
115
|
: undefined,
|
|
116
116
|
};
|
|
117
117
|
}
|
|
118
|
-
|
|
118
|
+
return this.refreshTokens();
|
|
119
|
+
}
|
|
120
|
+
/**
|
|
121
|
+
* A new token, never the cached one: the refresh token when there is one,
|
|
122
|
+
* the login flow when there is none or the refresh is refused.
|
|
123
|
+
*
|
|
124
|
+
* `getTokens()` answers the cache while the token looks valid, so a caller
|
|
125
|
+
* holding a 401 — the server refused a token the clock still accepts — has
|
|
126
|
+
* no other way to get a different one. What this obtains replaces the cache.
|
|
127
|
+
*/
|
|
128
|
+
async refreshTokens() {
|
|
119
129
|
if (this.refreshToken) {
|
|
120
|
-
this.logger?.info('[BaseTokenProvider]
|
|
130
|
+
this.logger?.info('[BaseTokenProvider] Obtaining a new token by refresh', {
|
|
121
131
|
oldToken: this.formatToken(this.authorizationToken),
|
|
122
132
|
refreshToken: this.formatToken(this.refreshToken),
|
|
123
133
|
});
|
|
@@ -134,14 +144,11 @@ class BaseTokenProvider {
|
|
|
134
144
|
this.logger?.warn('[BaseTokenProvider] Refresh failed', {
|
|
135
145
|
error: error instanceof Error ? error.message : String(error),
|
|
136
146
|
});
|
|
137
|
-
//
|
|
138
|
-
// Clear refresh token as it's invalid
|
|
147
|
+
// The refresh token was refused: it is spent, so a login follows.
|
|
139
148
|
this.refreshToken = undefined;
|
|
140
|
-
// Fall through to login
|
|
141
149
|
}
|
|
142
150
|
}
|
|
143
|
-
|
|
144
|
-
this.logger?.info('[BaseTokenProvider] Token invalid and no refresh token, performing login');
|
|
151
|
+
this.logger?.info('[BaseTokenProvider] No usable refresh token, performing login');
|
|
145
152
|
const result = await this.performLogin();
|
|
146
153
|
this.updateTokens(result);
|
|
147
154
|
this.logger?.info('[BaseTokenProvider] Login completed', {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* Token Providers
|
|
3
3
|
*
|
|
4
4
|
* Stateful token providers with automatic token lifecycle management.
|
|
5
|
-
* All providers extend BaseTokenProvider and implement
|
|
5
|
+
* All providers extend BaseTokenProvider and implement IRefreshableTokenProvider.
|
|
6
6
|
*/
|
|
7
7
|
export type { AuthorizationCodeProviderConfig } from './AuthorizationCodeProvider';
|
|
8
8
|
export { AuthorizationCodeProvider } from './AuthorizationCodeProvider';
|
package/dist/providers/index.js
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Token Providers
|
|
4
4
|
*
|
|
5
5
|
* Stateful token providers with automatic token lifecycle management.
|
|
6
|
-
* All providers extend BaseTokenProvider and implement
|
|
6
|
+
* All providers extend BaseTokenProvider and implement IRefreshableTokenProvider.
|
|
7
7
|
*/
|
|
8
8
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
9
|
exports.UaaPasscodeProvider = exports.Saml2PureProvider = exports.Saml2BearerProvider = exports.OidcTokenExchangeProvider = exports.OidcPasswordProvider = exports.OidcDeviceFlowProvider = exports.OidcBrowserProvider = exports.ClientCredentialsProvider = exports.BaseTokenProvider = exports.AuthorizationCodeProvider = void 0;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IRefreshableTokenProvider } from '@mcp-abap-adt/interfaces-auth';
|
|
2
2
|
import type { SsoProviderConfig } from './types';
|
|
3
3
|
export declare class SsoProviderFactory {
|
|
4
|
-
static create(config: SsoProviderConfig):
|
|
4
|
+
static create(config: SsoProviderConfig): IRefreshableTokenProvider;
|
|
5
5
|
}
|
|
6
6
|
//# sourceMappingURL=SsoProviderFactory.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"SsoProviderFactory.d.ts","sourceRoot":"","sources":["../../src/sso/SsoProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"SsoProviderFactory.d.ts","sourceRoot":"","sources":["../../src/sso/SsoProviderFactory.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAO/E,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,yBAAyB;CA6BpE"}
|
package/dist/sso/types.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { IRefreshableTokenProvider } from '@mcp-abap-adt/interfaces-auth';
|
|
2
2
|
import type { OidcBrowserProviderConfig } from '../providers/OidcBrowserProvider';
|
|
3
3
|
import type { OidcDeviceFlowProviderConfig } from '../providers/OidcDeviceFlowProvider';
|
|
4
4
|
import type { OidcPasswordProviderConfig } from '../providers/OidcPasswordProvider';
|
|
5
5
|
import type { OidcTokenExchangeProviderConfig } from '../providers/OidcTokenExchangeProvider';
|
|
6
6
|
import type { Saml2BearerProviderConfig } from '../providers/Saml2BearerProvider';
|
|
7
7
|
import type { Saml2PureProviderConfig } from '../providers/Saml2PureProvider';
|
|
8
|
-
export type SsoProviderInstance =
|
|
8
|
+
export type SsoProviderInstance = IRefreshableTokenProvider;
|
|
9
9
|
export type SsoProviderConfig = {
|
|
10
10
|
protocol: 'oidc';
|
|
11
11
|
flow: 'browser';
|
package/dist/sso/types.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sso/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sso/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,+BAA+B,CAAC;AAC/E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAClF,OAAO,KAAK,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AACxF,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,mCAAmC,CAAC;AACpF,OAAO,KAAK,EAAE,+BAA+B,EAAE,MAAM,wCAAwC,CAAC;AAC9F,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,kCAAkC,CAAC;AAClF,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,gCAAgC,CAAC;AAE9E,MAAM,MAAM,mBAAmB,GAAG,yBAAyB,CAAC;AAE5D,MAAM,MAAM,iBAAiB,GACzB;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,SAAS,CAAC;IAChB,MAAM,EAAE,yBAAyB,CAAC;CACnC,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,4BAA4B,CAAC;CACtC,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,UAAU,CAAC;IACjB,MAAM,EAAE,0BAA0B,CAAC;CACpC,GACD;IACE,QAAQ,EAAE,MAAM,CAAC;IACjB,IAAI,EAAE,gBAAgB,CAAC;IACvB,MAAM,EAAE,+BAA+B,CAAC;CACzC,GACD;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,QAAQ,CAAC;IACf,MAAM,EAAE,yBAAyB,CAAC;CACnC,GACD;IACE,QAAQ,EAAE,OAAO,CAAC;IAClB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,uBAAuB,CAAC;CACjC,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"BrowserCallbackStrategy.d.ts","sourceRoot":"","sources":["../../src/strategies/BrowserCallbackStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"BrowserCallbackStrategy.d.ts","sourceRoot":"","sources":["../../src/strategies/BrowserCallbackStrategy.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,oBAAoB,EACpB,oBAAoB,EACpB,qBAAqB,EACrB,sBAAsB,EACvB,MAAM,+BAA+B,CAAC;AAIvC,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,yBAAyB,CAAC;AAQlE;;;GAGG;AACH,eAAO,MAAM,qBAAqB,QAAQ,CAAC;AAE3C,+DAA+D;AAC/D,eAAO,MAAM,wBAAwB,QAAS,CAAC;AAE/C,MAAM,WAAW,uBAAuB,CAAC,OAAO,GAAG,MAAM;IACvD,gFAAgF;IAChF,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,mFAAmF;IACnF,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB;;;;;OAKG;IACH,cAAc,CAAC,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC;IAChD,wFAAwF;IACxF,OAAO,CAAC,EAAE,CACR,GAAG,EAAE,MAAM,EACX,OAAO,EAAE,MAAM,EACf,WAAW,EAAE,MAAM,KAChB,OAAO,CAAC,IAAI,CAAC,CAAC;IACnB;;;;;;;;OAQG;IACH,UAAU,CAAC,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,MAAM,CAAC;IAC7C,MAAM,CAAC,EAAE,WAAW,CAAC;CACtB;AAED,MAAM,WAAW,8BAA8B,CAAC,OAAO,CACrD,SAAQ,uBAAuB,CAAC,OAAO,CAAC;IACxC,cAAc,EAAE,qBAAqB,CAAC,OAAO,CAAC,CAAC;CAChD;AAwBD,qBAAa,uBAAuB,CAAC,OAAO,CAC1C,YAAW,sBAAsB,CAAC,OAAO,CAAC;IAOxC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAuD;IACvE,OAAO,CAAC,UAAU,CAAgC;IAClD,OAAO,CAAC,QAAQ,CAAS;gBAGN,OAAO,EAAE,8BAA8B,CAAC,OAAO,CAAC;IAG7D,SAAS,CACb,OAAO,EAAE,oBAAoB,GAC5B,OAAO,CAAC,oBAAoB,CAAC,OAAO,CAAC,CAAC;IAyGzC;;;OAGG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAM/B;AAyBD,wBAAgB,uBAAuB,CACrC,OAAO,GAAE,uBAAuB,CAAC,MAAM,CAAM,GAC5C,sBAAsB,CAAC,MAAM,CAAC,CAWhC;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,uBAAuB,CAAC,kBAAkB,CAAM,GACxD,sBAAsB,CAAC,kBAAkB,CAAC,CAK5C;AAED,wBAAgB,oBAAoB,CAClC,OAAO,GAAE,uBAAuB,CAAC,MAAM,CAAM,GAC5C,sBAAsB,CAAC,MAAM,CAAC,CAKhC"}
|
|
@@ -51,6 +51,7 @@ const browserAuth_1 = require("../auth/browserAuth");
|
|
|
51
51
|
const callbackServer_1 = require("../auth/callbackServer");
|
|
52
52
|
const oidcBrowserAuth_1 = require("../auth/oidcBrowserAuth");
|
|
53
53
|
const saml2Auth_1 = require("../auth/saml2Auth");
|
|
54
|
+
const TokenProviderErrors_1 = require("../errors/TokenProviderErrors");
|
|
54
55
|
/**
|
|
55
56
|
* Above Linux's `ip_local_port_range` (32768–60999), so an outbound connection
|
|
56
57
|
* never squats on it, and far from the 3001/3333 range application servers use.
|
|
@@ -146,6 +147,19 @@ class BrowserCallbackStrategy {
|
|
|
146
147
|
try {
|
|
147
148
|
return await run;
|
|
148
149
|
}
|
|
150
|
+
catch (error) {
|
|
151
|
+
// Everything that ends a browser login here — the timeout, the identity
|
|
152
|
+
// provider's own refusal, a port in use, a browser that would not open,
|
|
153
|
+
// an abort — is a browser authentication failure, and the one type a
|
|
154
|
+
// caller can catch for it. It was exported and thrown nowhere: each of
|
|
155
|
+
// these reached the caller as a plain Error. The text is kept, and the
|
|
156
|
+
// original is the cause. An error that already has a type (a
|
|
157
|
+
// ValidationError from building the URL) is not one of these.
|
|
158
|
+
if (error instanceof TokenProviderErrors_1.TokenProviderError)
|
|
159
|
+
throw error;
|
|
160
|
+
const cause = error instanceof Error ? error : new Error(String(error));
|
|
161
|
+
throw new TokenProviderErrors_1.BrowserAuthError(cause.message, cause);
|
|
162
|
+
}
|
|
149
163
|
finally {
|
|
150
164
|
this.options.signal?.removeEventListener('abort', relay);
|
|
151
165
|
this.controller = null;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@mcp-abap-adt/auth-providers",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.2.0",
|
|
4
4
|
"description": "Token providers for MCP ABAP ADT auth-broker",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -57,7 +57,7 @@
|
|
|
57
57
|
"node": "^22 || ^24"
|
|
58
58
|
},
|
|
59
59
|
"dependencies": {
|
|
60
|
-
"@mcp-abap-adt/interfaces-auth": "^2.0
|
|
60
|
+
"@mcp-abap-adt/interfaces-auth": "^2.1.0",
|
|
61
61
|
"@mcp-abap-adt/interfaces-auth-sap": "^1.0.1",
|
|
62
62
|
"@mcp-abap-adt/interfaces-utils": "^1.1.0",
|
|
63
63
|
"@xmldom/xmldom": "^0.9.12",
|