@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 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 the `ITokenProvider` interface from `@mcp-abap-adt/interfaces-auth`:
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()`. No separate refresh methods are needed.
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 RefreshError) {
1217
- console.error('Browser auth failed:', error.cause);
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
- // Browser authentication failed
1255
- console.error('Browser auth failed:', error.cause);
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
- - `RefreshError` - Token refresh failed, includes `cause?: Error`
1264
- - `SessionDataError` - Session data invalid, includes `missingFields: string[]`
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 { ITokenProvider, ITokenResult, OAuth2GrantType } from '@mcp-abap-adt/interfaces-auth';
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 ITokenProvider {
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,cAAc,EACd,YAAY,EACZ,eAAe,EAChB,MAAM,+BAA+B,CAAC;AACvC,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,gCAAgC,CAAC;AAE9D;;;;;;;;GAQG;AACH,8BAAsB,iBAAkB,YAAW,cAAc;IAC/D,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;IAyElC,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"}
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
- // Try refresh if we have refresh token
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] Token invalid, attempting refresh', {
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
- // Refresh failed - need to login
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
- // Perform login
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 ITokenProvider.
5
+ * All providers extend BaseTokenProvider and implement IRefreshableTokenProvider.
6
6
  */
7
7
  export type { AuthorizationCodeProviderConfig } from './AuthorizationCodeProvider';
8
8
  export { AuthorizationCodeProvider } from './AuthorizationCodeProvider';
@@ -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 ITokenProvider.
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 { ITokenProvider } from '@mcp-abap-adt/interfaces-auth';
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): ITokenProvider;
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,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAOpE,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,SAAS,CAAC;AAEjD,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,MAAM,CAAC,MAAM,EAAE,iBAAiB,GAAG,cAAc;CA6BzD"}
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"}
@@ -1,11 +1,11 @@
1
- import type { ITokenProvider } from '@mcp-abap-adt/interfaces-auth';
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 = ITokenProvider;
8
+ export type SsoProviderInstance = IRefreshableTokenProvider;
9
9
  export type SsoProviderConfig = {
10
10
  protocol: 'oidc';
11
11
  flow: 'browser';
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/sso/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AACpE,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,cAAc,CAAC;AAEjD,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
+ {"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;AAIlE;;;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;IA8FzC;;;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"}
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.1.3",
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.1",
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",