@mcp-abap-adt/auth-broker 0.2.11 → 0.2.13

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
@@ -11,6 +11,26 @@ Thank you to all contributors! See [CONTRIBUTORS.md](CONTRIBUTORS.md) for the co
11
11
 
12
12
  ## [Unreleased]
13
13
 
14
+ ## [0.2.13] - 2025-12-26
15
+
16
+ ### Changed
17
+ - **Token lifecycle management**: Broker now always calls `provider.getTokens()` instead of validating tokens itself. Provider handles all token lifecycle operations internally (validation, refresh, login). Consumer doesn't need to know about token issues - provider manages everything automatically.
18
+ - **Removed token validation step**: Removed Step 1 (Token Validation) from broker flow. Broker no longer checks token validity before calling provider - provider decides what to do based on token state.
19
+ - **Simplified broker logic**: Broker is now a thin wrapper that always delegates to provider. Provider is responsible for token lifecycle management.
20
+
21
+ ### Removed
22
+ - **`validateExistingToken()` method**: Removed internal token validation method. Broker no longer validates tokens - provider handles this internally via `getTokens()`.
23
+
24
+ ### Fixed
25
+ - **Compatibility with auth-providers 0.2.8**: Removed dependency on deprecated `refreshTokenFromServiceKey()` method from token providers. Broker now exclusively uses `getTokens()` method which handles all token lifecycle operations internally. This ensures compatibility with stateful token providers that manage refresh/re-auth internally.
26
+
27
+ ## [0.2.12] - 2025-12-25
28
+
29
+ ### Changed
30
+ - **Auth flow**: Broker now relies on `ITokenProvider.getTokens()` with no parameters and expects providers to manage refresh/re-auth internally.
31
+ - **Constructor**: `tokenProvider` is required and `allowBrowserAuth` is supported for non-interactive flows.
32
+ - **Docs**: Updated usage/architecture/export docs to reflect provider injection, new flow, and CLI usage.
33
+
14
34
  ## [0.2.11] - 2025-12-23
15
35
 
16
36
  ### Changed
package/README.md CHANGED
@@ -7,7 +7,7 @@ JWT authentication broker for MCP ABAP ADT server. Manages authentication tokens
7
7
  - 🔐 **Destination-based Authentication**: Load tokens based on `x-mcp-destination` header
8
8
  - 📁 **Environment File Support**: Automatically loads tokens from `{destination}.env` files
9
9
  - 🔄 **Automatic Token Refresh**: Refreshes expired tokens using service keys from `{destination}.json` files
10
- - ✅ **Token Validation**: Validates tokens by testing connection to SAP system
10
+ - ✅ **Token Validation**: Validates tokens via provider (if `validateToken` is implemented)
11
11
  - 💾 **Token Caching**: In-memory caching for improved performance
12
12
  - 🔧 **Configurable Base Path**: Customize where `.env` and `.json` files are stored
13
13
 
@@ -19,19 +19,26 @@ npm install @mcp-abap-adt/auth-broker
19
19
 
20
20
  ## Usage
21
21
 
22
- ### Basic Usage (Session Only)
22
+ ### Basic Usage (Provider Required)
23
23
 
24
- If your sessionStore contains valid UAA credentials, you only need to provide `sessionStore`:
24
+ AuthBroker requires a token provider configured for the destination:
25
25
 
26
26
  ```typescript
27
27
  import { AuthBroker, AbapSessionStore } from '@mcp-abap-adt/auth-broker';
28
+ import { AuthorizationCodeProvider } from '@mcp-abap-adt/auth-providers';
29
+
30
+ const tokenProvider = new AuthorizationCodeProvider({
31
+ uaaUrl: 'https://...authentication...hana.ondemand.com',
32
+ clientId: '...',
33
+ clientSecret: '...',
34
+ browser: 'system',
35
+ });
28
36
 
29
- // Session-only mode - works if session has UAA credentials
30
37
  const broker = new AuthBroker({
31
38
  sessionStore: new AbapSessionStore('/path/to/destinations'),
39
+ tokenProvider,
32
40
  });
33
41
 
34
- // Get token - uses direct UAA HTTP requests automatically
35
42
  const token = await broker.getToken('TRIAL');
36
43
  ```
37
44
 
@@ -40,37 +47,52 @@ const token = await broker.getToken('TRIAL');
40
47
  For maximum flexibility, provide all three dependencies:
41
48
 
42
49
  ```typescript
43
- import {
44
- AuthBroker,
45
- AbapServiceKeyStore,
46
- AbapSessionStore,
47
- BtpTokenProvider
50
+ import {
51
+ AuthBroker,
52
+ AbapServiceKeyStore,
53
+ AbapSessionStore,
48
54
  } from '@mcp-abap-adt/auth-broker';
55
+ import { AuthorizationCodeProvider } from '@mcp-abap-adt/auth-providers';
49
56
 
50
57
  const broker = new AuthBroker({
51
58
  sessionStore: new AbapSessionStore('/path/to/destinations'),
52
59
  serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'), // optional
53
- tokenProvider: new BtpTokenProvider(), // optional
60
+ tokenProvider: new AuthorizationCodeProvider({
61
+ uaaUrl: 'https://...authentication...hana.ondemand.com',
62
+ clientId: '...',
63
+ clientSecret: '...',
64
+ browser: 'system',
65
+ }),
54
66
  }, 'chrome', logger);
55
67
 
56
68
  // Disable browser authentication for headless/stdio environments (e.g., MCP with Cline)
57
69
  const brokerNoBrowser = new AuthBroker({
58
70
  sessionStore: new AbapSessionStore('/path/to/destinations'),
59
71
  serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
60
- tokenProvider: new BtpTokenProvider(),
72
+ tokenProvider: new AuthorizationCodeProvider({
73
+ uaaUrl: 'https://...authentication...hana.ondemand.com',
74
+ clientId: '...',
75
+ clientSecret: '...',
76
+ browser: 'none',
77
+ }),
61
78
  allowBrowserAuth: false, // Throws BROWSER_AUTH_REQUIRED if browser auth needed
62
79
  }, 'chrome', logger);
63
80
  ```
64
81
 
65
82
  ### Session + Service Key (For Initialization)
66
83
 
67
- If you need to initialize sessions from service keys:
84
+ If you need to initialize sessions from service keys, create the provider from service key auth config:
68
85
 
69
86
  ```typescript
70
87
  const broker = new AuthBroker({
71
88
  sessionStore: new AbapSessionStore('/path/to/destinations'),
72
89
  serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
73
- // tokenProvider optional - direct UAA requests will be used from service key
90
+ tokenProvider: new AuthorizationCodeProvider({
91
+ uaaUrl: 'https://...authentication...hana.ondemand.com',
92
+ clientId: '...',
93
+ clientSecret: '...',
94
+ browser: 'system',
95
+ }),
74
96
  });
75
97
  ```
76
98
 
@@ -94,16 +116,19 @@ To avoid port conflicts with browser authentication:
94
116
  const broker = new AuthBroker({
95
117
  sessionStore: new AbapSessionStore('/path/to/destinations'),
96
118
  serviceKeyStore: new AbapServiceKeyStore('/path/to/destinations'),
97
- tokenProvider: new BtpTokenProvider(4001), // Custom port for OAuth callback server
119
+ tokenProvider: new AuthorizationCodeProvider({
120
+ uaaUrl: 'https://...authentication...hana.ondemand.com',
121
+ clientId: '...',
122
+ clientSecret: '...',
123
+ browser: 'system',
124
+ redirectPort: 4001,
125
+ }),
98
126
  }, 'chrome');
99
127
  ```
100
128
 
101
- **Note**: The `BtpTokenProvider` automatically finds an available port if the requested port is in use. This prevents `EADDRINUSE` errors when multiple stdio servers run simultaneously. The server properly closes all connections and frees the port after authentication completes, ensuring no lingering port occupation.
102
-
103
129
  ### Getting Tokens
104
130
 
105
131
  ```typescript
106
- // Get token - automatically uses direct UAA requests if UAA credentials available
107
132
  const token = await broker.getToken('TRIAL');
108
133
 
109
134
  // Force refresh token
@@ -282,7 +307,7 @@ This package supports two types of BTP authentication:
282
307
  **Interface-Only Communication**: This package follows a fundamental development principle: **all interactions with external dependencies happen ONLY through interfaces**. The code knows **NOTHING beyond what is defined in the interfaces**.
283
308
 
284
309
  This means:
285
- - Does not know about concrete implementation classes (e.g., `AbapSessionStore`, `BtpTokenProvider`)
310
+ - Does not know about concrete implementation classes (e.g., `AbapSessionStore`, `AuthorizationCodeProvider`)
286
311
  - Does not know about internal data structures or methods not defined in interfaces
287
312
  - Does not make assumptions about implementation behavior beyond interface contracts
288
313
  - Does not access properties or methods not explicitly defined in interfaces
@@ -302,13 +327,11 @@ The `@mcp-abap-adt/auth-broker` package defines **interfaces** and provides **or
302
327
  - **Orchestrates authentication flows**: Coordinates token retrieval, validation, and refresh using provided stores and providers
303
328
  - **Manages token lifecycle**: Handles token caching, validation, and automatic refresh
304
329
  - **Works with interfaces only**: Uses `IServiceKeyStore`, `ISessionStore`, and `ITokenProvider` interfaces without knowing concrete implementations
305
- - **Delegates to providers**: Calls `tokenProvider.getConnectionConfig()` to obtain tokens and connection configuration
306
- - **Delegates to stores**: Uses `sessionStore.setConnectionConfig()` to save tokens and connection configuration
330
+ - **Delegates to providers**: Calls `tokenProvider.getTokens()` to obtain tokens
331
+ - **Delegates to stores**: Saves tokens and connection configuration to `sessionStore`
307
332
 
308
333
  #### What AuthBroker Does NOT Do
309
334
 
310
- - **Does NOT know about `serviceUrl`**: `AuthBroker` does not know whether a specific `ISessionStore` implementation requires `serviceUrl` or not. It simply passes the `IConnectionConfig` returned by `tokenProvider` to `sessionStore.setConnectionConfig()`
311
- - **Does NOT merge configurations**: `AuthBroker` does not merge `serviceUrl` from service keys with connection config from token providers. This is the responsibility of the consumer or the session store implementation
312
335
  - **Does NOT implement storage**: File I/O, parsing, and storage logic are handled by concrete store implementations from `@mcp-abap-adt/auth-stores`
313
336
  - **Does NOT implement token acquisition**: OAuth2 flows, refresh token logic, and client credentials are handled by concrete provider implementations from `@mcp-abap-adt/auth-providers`
314
337
 
@@ -317,9 +340,9 @@ The `@mcp-abap-adt/auth-broker` package defines **interfaces** and provides **or
317
340
  The **consumer** (application using `AuthBroker`) is responsible for:
318
341
 
319
342
  1. **Selecting appropriate implementations**: Choose the correct `IServiceKeyStore`, `ISessionStore`, and `ITokenProvider` implementations based on the use case:
320
- - **ABAP systems**: Use `AbapServiceKeyStore`, `AbapSessionStore` (or `SafeAbapSessionStore`), and `BtpTokenProvider`
321
- - **BTP systems**: Use `AbapServiceKeyStore`, `BtpSessionStore` (or `SafeBtpSessionStore`), and `BtpTokenProvider`
322
- - **XSUAA services**: Use `XsuaaServiceKeyStore`, `XsuaaSessionStore` (or `SafeXsuaaSessionStore`), and `XsuaaTokenProvider`
343
+ - **ABAP systems**: Use `AbapServiceKeyStore`, `AbapSessionStore` (or `SafeAbapSessionStore`), and `AuthorizationCodeProvider`
344
+ - **BTP systems**: Use `AbapServiceKeyStore`, `BtpSessionStore` (or `SafeBtpSessionStore`), and `AuthorizationCodeProvider`
345
+ - **XSUAA services**: Use `XsuaaServiceKeyStore`, `XsuaaSessionStore` (or `SafeXsuaaSessionStore`), and `ClientCredentialsProvider`
323
346
 
324
347
  2. **Ensuring complete configuration**: If a session store requires `serviceUrl` (e.g., `AbapSessionStore` requires `sapUrl`), the consumer must ensure that:
325
348
  - The session is created with `serviceUrl` before calling `AuthBroker.getToken()`, OR
@@ -346,8 +369,7 @@ Concrete `ISessionStore` implementations are responsible for:
346
369
  Concrete `ITokenProvider` implementations are responsible for:
347
370
 
348
371
  - **Obtaining tokens**: Using OAuth2 flows, refresh tokens, or client credentials to obtain JWT tokens
349
- - **Returning connection config**: Returning `IConnectionConfig` with `authorizationToken` and optionally `serviceUrl` (if known)
350
- - **Not returning `serviceUrl` if unknown**: Providers like `BtpTokenProvider` may not return `serviceUrl` because they only handle token acquisition, not connection configuration
372
+ - **Managing token lifecycle**: Caching, validating, refreshing, and re-authenticating as needed
351
373
 
352
374
  ### Design Principles
353
375
 
@@ -361,22 +383,6 @@ Concrete `ITokenProvider` implementations are responsible for:
361
383
  4. **Interface Segregation**: Interfaces are focused and minimal, containing only what's necessary for their specific purpose
362
384
  5. **Open/Closed Principle**: New store and provider implementations can be added without modifying `AuthBroker`
363
385
 
364
- ### Example: Why AuthBroker Doesn't Handle `serviceUrl`
365
-
366
- Consider this scenario:
367
- - `BtpTokenProvider.getConnectionConfig()` returns `IConnectionConfig` with `authorizationToken` but **without** `serviceUrl` (because it only handles token acquisition)
368
- - `AbapSessionStore.setConnectionConfig()` requires `sapUrl` (which maps to `serviceUrl`)
369
-
370
- If `AuthBroker` tried to merge `serviceUrl` from `serviceKeyStore`, it would:
371
- 1. Violate the DIP by knowing about specific store requirements
372
- 2. Break the abstraction - `AuthBroker` shouldn't know that `AbapSessionStore` needs `serviceUrl`
373
- 3. Create coupling between `AuthBroker` and concrete implementations
374
-
375
- Instead, the consumer or `AbapSessionStore` itself should handle this:
376
- - **Option 1**: Consumer retrieves `serviceUrl` from `serviceKeyStore` and ensures it's in the session before calling `AuthBroker.getToken()`
377
- - **Option 2**: `AbapSessionStore.setConnectionConfig()` retrieves `serviceUrl` from `serviceKeyStore` internally if not provided
378
- - **Option 3**: `AbapSessionStore.setConnectionConfig()` uses existing `sapUrl` from current session if available
379
-
380
386
  ## API
381
387
 
382
388
  ### `AuthBroker`
@@ -388,7 +394,8 @@ new AuthBroker(
388
394
  config: {
389
395
  sessionStore: ISessionStore; // required
390
396
  serviceKeyStore?: IServiceKeyStore; // optional
391
- tokenProvider?: ITokenProvider; // optional
397
+ tokenProvider: ITokenProvider; // required
398
+ allowBrowserAuth?: boolean; // optional
392
399
  },
393
400
  browser?: string,
394
401
  logger?: ILogger
@@ -399,7 +406,8 @@ new AuthBroker(
399
406
  - `config` - Configuration object:
400
407
  - `sessionStore` - **Required** - Store for session data. Must contain initial session with `serviceUrl`
401
408
  - `serviceKeyStore` - **Optional** - Store for service keys. Only needed for initializing sessions from service keys
402
- - `tokenProvider` - **Optional** - Token provider for token acquisition. Only needed for browser authentication or when direct UAA requests fail
409
+ - `tokenProvider` - **Required** - Token provider for token acquisition and refresh
410
+ - `allowBrowserAuth` - **Optional** - When `false`, throws `BROWSER_AUTH_REQUIRED` instead of launching browser auth
403
411
  - `browser` - Optional browser name for authentication (`chrome`, `edge`, `firefox`, `system`, `headless`, `none`). Default: `system`
404
412
  - Use `'headless'` for SSH/remote sessions - logs URL and waits for manual callback
405
413
  - Use `'none'` for automated tests - logs URL and rejects immediately
@@ -411,16 +419,15 @@ new AuthBroker(
411
419
  - **`sessionStore` (required)**: Always required. Must contain initial session with `serviceUrl`
412
420
  - **`serviceKeyStore` (optional)**:
413
421
  - Required if you need to initialize sessions from service keys (Step 0)
414
- - Not needed if session already contains UAA credentials
415
- - **`tokenProvider` (optional)**:
416
- - Required for browser authentication when initializing from service key (Step 0)
417
- - Optional but recommended as fallback when direct UAA requests fail
418
- - Not needed if session contains valid UAA credentials (direct UAA HTTP requests will be used)
422
+ - Not needed if session already contains authorization config and tokens
423
+ - **`tokenProvider` (required)**:
424
+ - Used for all token acquisition and refresh flows
425
+ - Must be configured with the destination's auth parameters (e.g., UAA credentials)
419
426
 
420
427
  **Available Implementations:**
421
- - **ABAP**: `AbapServiceKeyStore(directory, defaultServiceUrl?, logger?)`, `AbapSessionStore(directory, defaultServiceUrl?, logger?)`, `SafeAbapSessionStore(defaultServiceUrl?, logger?)`, `BtpTokenProvider(browserAuthPort?)`
422
- - **XSUAA** (reduced scope): `XsuaaServiceKeyStore(directory, logger?)`, `XsuaaSessionStore(directory, defaultServiceUrl, logger?)`, `SafeXsuaaSessionStore(defaultServiceUrl, logger?)`, `XsuaaTokenProvider()`
423
- - **BTP** (full scope for ABAP): `AbapServiceKeyStore(directory, defaultServiceUrl?, logger?)`, `BtpSessionStore(directory, defaultServiceUrl, logger?)`, `SafeBtpSessionStore(defaultServiceUrl, logger?)`, `BtpTokenProvider(browserAuthPort?)`
428
+ - **ABAP**: `AbapServiceKeyStore(directory, defaultServiceUrl?, logger?)`, `AbapSessionStore(directory, defaultServiceUrl?, logger?)`, `SafeAbapSessionStore(defaultServiceUrl?, logger?)`, `AuthorizationCodeProvider(...)`
429
+ - **XSUAA** (reduced scope): `XsuaaServiceKeyStore(directory, logger?)`, `XsuaaSessionStore(directory, defaultServiceUrl, logger?)`, `SafeXsuaaSessionStore(defaultServiceUrl, logger?)`, `ClientCredentialsProvider(...)`
430
+ - **BTP** (full scope for ABAP): `AbapServiceKeyStore(directory, defaultServiceUrl?, logger?)`, `BtpSessionStore(directory, defaultServiceUrl, logger?)`, `SafeBtpSessionStore(defaultServiceUrl, logger?)`, `AuthorizationCodeProvider(...)`
424
431
 
425
432
  #### Methods
426
433
 
@@ -429,31 +436,29 @@ new AuthBroker(
429
436
  Gets authentication token for destination. Implements a three-step flow:
430
437
 
431
438
  **Step 0: Initialize Session with Token (if needed)**
432
- - Checks if session has `authorizationToken` and UAA credentials
433
- - If both are empty and `serviceKeyStore` is available:
434
- - Tries direct UAA request from service key (if UAA credentials available)
435
- - If failed and `tokenProvider` available uses provider for authentication
436
- - If session has token OR UAA credentials → proceeds to Step 1
437
-
438
- **Step 1: Refresh Token Flow**
439
- - Checks if refresh token exists in session
440
- - If refresh token exists:
441
- - Tries direct UAA refresh (if UAA credentials in session)
442
- - If failed and `tokenProvider` available → uses provider
443
- - If successful → returns new token
444
- - Otherwise proceeds to Step 2
445
-
446
- **Step 2: UAA Credentials Flow**
447
- - Checks if UAA credentials exist in session or service key
448
- - Tries direct UAA client_credentials request (if UAA credentials available)
449
- - If failed and `tokenProvider` available → uses provider
450
- - If successful → returns new token
439
+ - Checks if session has `authorizationToken` and authorization config
440
+ - If both are missing and `serviceKeyStore` is available:
441
+ - Loads authorization config from service key
442
+ - Uses `tokenProvider.getTokens()` to obtain tokens
443
+ - Persists tokens to session
444
+ - Otherwise → proceeds to Step 1
445
+
446
+ **Step 1: Token Refresh / Re-Auth**
447
+ - If session has authorization config:
448
+ - Uses `tokenProvider.getTokens()` to refresh or re-authenticate
449
+ - Persists tokens to session
450
+ - Returns new token
451
+ - If that fails (or no session auth config) and `serviceKeyStore` is available:
452
+ - Loads authorization config from service key
453
+ - Uses `tokenProvider.getTokens()` to obtain tokens
454
+ - Persists tokens to session
451
455
  - If all failed → throws error
452
456
 
453
457
  **Important Notes:**
454
- - If `sessionStore` contains valid UAA credentials, neither `serviceKeyStore` nor `tokenProvider` are required. Direct UAA HTTP requests will be used automatically.
455
- - `tokenProvider` is only needed for browser authentication or when direct UAA requests fail.
456
- - Token validation is performed only when checking existing session. Tokens obtained through refresh/UAA/browser authentication are not validated before being saved.
458
+ - All authentication is handled by the injected provider (authorization_code or client_credentials).
459
+ - `tokenProvider` is required for all token acquisition and refresh flows.
460
+ - **Broker always calls `provider.getTokens()`** - provider handles token lifecycle internally (validation, refresh, login). Consumer doesn't need to know about token issues.
461
+ - Provider decides whether to return cached token, refresh, or perform login based on token state.
457
462
  - **Store errors are handled gracefully**: If service key files are missing or malformed, the broker logs the error and continues with fallback mechanisms (session store data or provider-based auth)
458
463
 
459
464
  ##### Error Handling
@@ -512,13 +517,7 @@ Example error scenarios handled:
512
517
 
513
518
  ##### `refreshToken(destination: string): Promise<string>`
514
519
 
515
- Force refresh token for destination. Uses refresh token from session if available, otherwise uses UAA credentials from session or service key.
516
-
517
- **Flow:**
518
- - If refresh token exists and UAA credentials available → tries direct UAA refresh
519
- - If direct UAA fails and `tokenProvider` available → uses provider
520
- - If no refresh token but UAA credentials available → tries direct UAA client_credentials
521
- - If all failed → throws error
520
+ Force refresh token for destination. Calls `getToken()` to run the full refresh flow and persist updated tokens.
522
521
 
523
522
  ##### `clearCache(destination: string): void`
524
523
 
@@ -530,14 +529,14 @@ Clear all cached tokens.
530
529
 
531
530
  ### Token Providers
532
531
 
533
- The package uses `ITokenProvider` interface for token acquisition. Two implementations are available:
532
+ The package uses the `ITokenProvider` interface for token acquisition. Provider implementations live in `@mcp-abap-adt/auth-providers`:
534
533
 
535
- - **`XsuaaTokenProvider`** - For XSUAA authentication (reduced scope)
534
+ - **`ClientCredentialsProvider`** - For XSUAA authentication (reduced scope)
536
535
  - Uses client_credentials grant type
537
536
  - No browser interaction required
538
537
  - No refresh token provided
539
538
 
540
- - **`BtpTokenProvider`** - For BTP/ABAP authentication (full scope)
539
+ - **`AuthorizationCodeProvider`** - For BTP/ABAP authentication (full scope)
541
540
  - Constructor accepts optional `browserAuthPort?: number` parameter (default: 3001)
542
541
  - Automatically finds an available port if the requested port is in use (prevents `EADDRINUSE` errors)
543
542
  - Server properly closes all connections and frees the port after authentication completes
@@ -553,55 +552,59 @@ import {
553
552
  AuthBroker,
554
553
  XsuaaServiceKeyStore,
555
554
  XsuaaSessionStore,
556
- XsuaaTokenProvider,
557
- BtpTokenProvider,
558
555
  AbapServiceKeyStore,
559
556
  BtpSessionStore
560
557
  } from '@mcp-abap-adt/auth-broker';
558
+ import {
559
+ ClientCredentialsProvider,
560
+ AuthorizationCodeProvider,
561
+ } from '@mcp-abap-adt/auth-providers';
561
562
 
562
- // XSUAA authentication - session only (direct UAA requests)
563
+ // XSUAA authentication
563
564
  const xsuaaBroker = new AuthBroker({
564
565
  sessionStore: new XsuaaSessionStore('/path/to/sessions', 'https://mcp.example.com'),
565
- // serviceKeyStore and tokenProvider not needed if session has UAA credentials
566
+ tokenProvider: new ClientCredentialsProvider({
567
+ uaaUrl: 'https://auth.example.com',
568
+ clientId: '...',
569
+ clientSecret: '...',
570
+ }),
566
571
  });
567
572
 
568
573
  // XSUAA authentication - with service key initialization
569
574
  const xsuaaBrokerWithServiceKey = new AuthBroker({
570
575
  sessionStore: new XsuaaSessionStore('/path/to/sessions', 'https://mcp.example.com'),
571
576
  serviceKeyStore: new XsuaaServiceKeyStore('/path/to/keys'),
572
- // tokenProvider optional - direct UAA requests will be used
577
+ tokenProvider: new ClientCredentialsProvider({
578
+ uaaUrl: 'https://auth.example.com',
579
+ clientId: '...',
580
+ clientSecret: '...',
581
+ }),
573
582
  }, 'none');
574
583
 
575
- // BTP authentication - session only (direct UAA requests)
584
+ // BTP authentication
576
585
  const btpBroker = new AuthBroker({
577
586
  sessionStore: new BtpSessionStore('/path/to/sessions', 'https://abap.example.com'),
578
- // serviceKeyStore and tokenProvider not needed if session has UAA credentials
587
+ tokenProvider: new AuthorizationCodeProvider({
588
+ uaaUrl: 'https://auth.example.com',
589
+ clientId: '...',
590
+ clientSecret: '...',
591
+ browser: 'system',
592
+ }),
579
593
  });
580
594
 
581
595
  // BTP authentication - with service key and provider (for browser auth)
582
596
  const btpBrokerFull = new AuthBroker({
583
597
  sessionStore: new BtpSessionStore('/path/to/sessions', 'https://abap.example.com'),
584
598
  serviceKeyStore: new AbapServiceKeyStore('/path/to/keys'),
585
- tokenProvider: new BtpTokenProvider(), // needed for browser authentication
599
+ tokenProvider: new AuthorizationCodeProvider({
600
+ uaaUrl: 'https://auth.example.com',
601
+ clientId: '...',
602
+ clientSecret: '...',
603
+ browser: 'system',
604
+ }),
586
605
  });
587
606
  ```
588
607
 
589
- ### Direct UAA HTTP Requests
590
-
591
- When UAA credentials are available in session, `AuthBroker` automatically uses direct HTTP requests to UAA without requiring `tokenProvider`:
592
-
593
- - **Refresh Token Grant**: Direct HTTP POST to `{uaaUrl}/oauth/token` with `grant_type=refresh_token`
594
- - **Client Credentials Grant**: Direct HTTP POST to `{uaaUrl}/oauth/token` with `grant_type=client_credentials`
595
-
596
- **Benefits:**
597
- - No dependency on `tokenProvider` when session has UAA credentials
598
- - Faster token refresh (no provider overhead)
599
- - Simpler configuration (only `sessionStore` needed)
600
-
601
- **Fallback to Provider:**
602
- - If direct UAA request fails and `tokenProvider` is available, broker automatically falls back to provider
603
- - Provider is useful for browser authentication or alternative authentication flows
604
-
605
608
  ### CLI: mcp-auth
606
609
 
607
610
  Generate or refresh `.env`/JSON output using AuthBroker + stores:
@@ -16,8 +16,16 @@
16
16
  import * as path from 'path';
17
17
  import * as fs from 'fs';
18
18
  import { AuthBroker } from '../src/AuthBroker';
19
- import { AbapServiceKeyStore, AbapSessionStore, XsuaaServiceKeyStore, XsuaaSessionStore } from '@mcp-abap-adt/auth-stores';
20
- import { BtpTokenProvider, XsuaaTokenProvider } from '@mcp-abap-adt/auth-providers';
19
+ import {
20
+ AbapServiceKeyStore,
21
+ AbapSessionStore,
22
+ XsuaaServiceKeyStore,
23
+ XsuaaSessionStore,
24
+ } from '@mcp-abap-adt/auth-stores';
25
+ import {
26
+ AuthorizationCodeProvider,
27
+ ClientCredentialsProvider,
28
+ } from '@mcp-abap-adt/auth-providers';
21
29
 
22
30
  async function main() {
23
31
  const args = process.argv.slice(2);
@@ -65,10 +73,24 @@ async function main() {
65
73
  ? new XsuaaSessionStore(sessionDir)
66
74
  : new AbapSessionStore(sessionDir);
67
75
 
76
+ const authConfig = await serviceKeyStore.getAuthorizationConfig(destination);
77
+ if (!authConfig) {
78
+ throw new Error(`Missing authorization config for ${destination}`);
79
+ }
80
+
68
81
  // Create token provider
69
82
  const tokenProvider = isXsuaa
70
- ? new XsuaaTokenProvider()
71
- : new BtpTokenProvider();
83
+ ? new ClientCredentialsProvider({
84
+ uaaUrl: authConfig.uaaUrl,
85
+ clientId: authConfig.uaaClientId,
86
+ clientSecret: authConfig.uaaClientSecret,
87
+ })
88
+ : new AuthorizationCodeProvider({
89
+ uaaUrl: authConfig.uaaUrl,
90
+ clientId: authConfig.uaaClientId,
91
+ clientSecret: authConfig.uaaClientSecret,
92
+ browser: 'system',
93
+ });
72
94
 
73
95
  // Create AuthBroker
74
96
  // For ABAP, use 'system' browser (will open browser for auth)
@@ -124,4 +146,3 @@ main().catch((error) => {
124
146
  console.error('Fatal error:', error);
125
147
  process.exit(1);
126
148
  });
127
-
package/bin/mcp-auth.ts CHANGED
@@ -32,12 +32,6 @@ import {
32
32
  AuthorizationCodeProvider,
33
33
  ClientCredentialsProvider,
34
34
  } from '@mcp-abap-adt/auth-providers';
35
- import type {
36
- IAuthorizationConfig,
37
- ITokenProvider,
38
- ITokenProviderOptions,
39
- ITokenProviderResult,
40
- } from '@mcp-abap-adt/interfaces';
41
35
  import {
42
36
  ABAP_CONNECTION_VARS,
43
37
  ABAP_AUTHORIZATION_VARS,
@@ -241,86 +235,6 @@ function parseArgs(): McpAuthOptions | null {
241
235
  };
242
236
  }
243
237
 
244
- type ProviderMode = 'authorization_code' | 'client_credentials';
245
-
246
- class BrokerTokenProvider implements ITokenProvider {
247
- private mode: ProviderMode;
248
- private browser?: string;
249
- private redirectPort?: number;
250
-
251
- constructor(mode: ProviderMode, browser?: string, redirectPort?: number) {
252
- this.mode = mode;
253
- this.browser = browser;
254
- this.redirectPort = redirectPort;
255
- }
256
-
257
- async getConnectionConfig(
258
- authConfig: IAuthorizationConfig,
259
- options?: ITokenProviderOptions,
260
- ): Promise<ITokenProviderResult> {
261
- return this.getTokenResult(authConfig, options);
262
- }
263
-
264
- async refreshTokenFromSession(
265
- authConfig: IAuthorizationConfig,
266
- options?: ITokenProviderOptions,
267
- ): Promise<ITokenProviderResult> {
268
- return this.getTokenResult(authConfig, options);
269
- }
270
-
271
- async refreshTokenFromServiceKey(
272
- authConfig: IAuthorizationConfig,
273
- options?: ITokenProviderOptions,
274
- ): Promise<ITokenProviderResult> {
275
- return this.getTokenResult(authConfig, options);
276
- }
277
-
278
- private async getTokenResult(
279
- authConfig: IAuthorizationConfig,
280
- options?: ITokenProviderOptions,
281
- ): Promise<ITokenProviderResult> {
282
- const uaaUrl = authConfig.uaaUrl;
283
- const uaaClientId = authConfig.uaaClientId;
284
- const uaaClientSecret = authConfig.uaaClientSecret;
285
-
286
- if (!uaaUrl || !uaaClientId || !uaaClientSecret) {
287
- throw new Error('Auth config missing required UAA credentials');
288
- }
289
-
290
- if (this.mode === 'client_credentials') {
291
- const provider = new ClientCredentialsProvider({
292
- uaaUrl,
293
- clientId: uaaClientId,
294
- clientSecret: uaaClientSecret,
295
- });
296
- const result = await provider.getTokens();
297
- return {
298
- connectionConfig: {
299
- authorizationToken: result.authorizationToken,
300
- },
301
- refreshToken: result.refreshToken,
302
- };
303
- }
304
-
305
- const browserValue = options?.browser ?? this.browser ?? 'system';
306
- const provider = new AuthorizationCodeProvider({
307
- uaaUrl,
308
- clientId: uaaClientId,
309
- clientSecret: uaaClientSecret,
310
- refreshToken: authConfig.refreshToken,
311
- browser: browserValue,
312
- redirectPort: this.redirectPort,
313
- });
314
- const result = await provider.getTokens();
315
- return {
316
- connectionConfig: {
317
- authorizationToken: result.authorizationToken,
318
- },
319
- refreshToken: result.refreshToken,
320
- };
321
- }
322
- }
323
-
324
238
  function writeEnvFile(
325
239
  outputPath: string,
326
240
  authType: 'abap' | 'xsuaa',
@@ -531,15 +445,32 @@ async function main() {
531
445
  ? new XsuaaSessionStore(tempSessionDir, brokerServiceUrl)
532
446
  : new AbapSessionStore(tempSessionDir);
533
447
 
448
+ const sessionAuthConfig =
449
+ await sessionStore.getAuthorizationConfig(destination);
450
+ const serviceKeyAuthConfig =
451
+ serviceKeyStore?.getAuthorizationConfig
452
+ ? await serviceKeyStore.getAuthorizationConfig(destination)
453
+ : null;
454
+ const authConfig = sessionAuthConfig || serviceKeyAuthConfig;
455
+ if (!authConfig) {
456
+ throw new Error(`Authorization config not found for ${destination}`);
457
+ }
458
+
534
459
  const useBrowserAuth = options.browser !== undefined;
535
- const providerMode: ProviderMode = useBrowserAuth
536
- ? 'authorization_code'
537
- : 'client_credentials';
538
- const tokenProvider = new BrokerTokenProvider(
539
- providerMode,
540
- options.browser,
541
- options.redirectPort,
542
- );
460
+ const tokenProvider = useBrowserAuth
461
+ ? new AuthorizationCodeProvider({
462
+ uaaUrl: authConfig.uaaUrl,
463
+ clientId: authConfig.uaaClientId,
464
+ clientSecret: authConfig.uaaClientSecret,
465
+ refreshToken: authConfig.refreshToken,
466
+ browser: options.browser,
467
+ redirectPort: options.redirectPort,
468
+ })
469
+ : new ClientCredentialsProvider({
470
+ uaaUrl: authConfig.uaaUrl,
471
+ clientId: authConfig.uaaClientId,
472
+ clientSecret: authConfig.uaaClientSecret,
473
+ });
543
474
 
544
475
  const broker = new AuthBroker(
545
476
  {
@@ -55,27 +55,13 @@ export declare class AuthBroker {
55
55
  /**
56
56
  * Get UAA credentials from session or service key
57
57
  */
58
- private getUaaCredentials;
58
+ private getAuthorizationConfigFromServiceKey;
59
59
  /**
60
60
  * Save token and config to session
61
61
  */
62
62
  private saveTokenToSession;
63
- /**
64
- * Initialize session from service key (Step 0)
65
- */
66
- private initializeSessionFromServiceKey;
67
- /**
68
- * Validate existing token (Step 1)
69
- */
70
- private validateExistingToken;
71
- /**
72
- * Refresh token from session (Step 2a)
73
- */
74
- private refreshTokenFromSession;
75
- /**
76
- * Refresh token from service key (Step 2b)
77
- */
78
- private refreshTokenFromServiceKey;
63
+ private requestTokens;
64
+ private persistTokenResult;
79
65
  /**
80
66
  * Get authentication token for destination.
81
67
  * Uses tokenProvider for all authentication operations (browser-based authorization).
@@ -1 +1 @@
1
- {"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EAErB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACd,MAAM,qBAAqB,CAAC;AA4C7B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,YAAY,EAAE,aAAa,CAAC;IAC5B,uEAAuE;IACvE,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,4IAA4I;IAC5I,aAAa,EAAE,cAAc,CAAC;IAC9B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,gBAAgB,CAAU;IAElC;;;;;;;;;;;OAWG;gBACS,MAAM,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO;IAoFxE;;OAEG;YACW,eAAe;IA0D7B;;OAEG;YACW,aAAa;IAoD3B;;OAEG;YACW,iBAAiB;IA2D/B;;OAEG;YACW,kBAAkB;IAkChC;;OAEG;YACW,+BAA+B;IA4G7C;;OAEG;YACW,qBAAqB;IA8BnC;;OAEG;YACW,uBAAuB;IAyFrC;;OAEG;YACW,0BAA0B;IAgHxC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IA8GpD;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IASxD;;;;OAIG;IACG,sBAAsB,CAC1B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAoEvC;;;;OAIG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA8DpC;;;;;;;;;;;;;;;;OAgBG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe;CAqB3D"}
1
+ {"version":3,"file":"AuthBroker.d.ts","sourceRoot":"","sources":["../src/AuthBroker.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,EACL,KAAK,OAAO,EACZ,KAAK,eAAe,EAGrB,MAAM,0BAA0B,CAAC;AAClC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAClD,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,EACd,MAAM,qBAAqB,CAAC;AA4C7B;;GAEG;AACH,MAAM,WAAW,gBAAgB;IAC/B,mEAAmE;IACnE,YAAY,EAAE,aAAa,CAAC;IAC5B,uEAAuE;IACvE,eAAe,CAAC,EAAE,gBAAgB,CAAC;IACnC,4IAA4I;IAC5I,aAAa,EAAE,cAAc,CAAC;IAC9B;;;;OAIG;IACH,gBAAgB,CAAC,EAAE,OAAO,CAAC;CAC5B;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,OAAO,CAAqB;IACpC,OAAO,CAAC,MAAM,CAAU;IACxB,OAAO,CAAC,eAAe,CAA+B;IACtD,OAAO,CAAC,YAAY,CAAgB;IACpC,OAAO,CAAC,aAAa,CAAiB;IACtC,OAAO,CAAC,gBAAgB,CAAU;IAElC;;;;;;;;;;;OAWG;gBACS,MAAM,EAAE,gBAAgB,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,OAAO;IAkFxE;;OAEG;YACW,eAAe;IA0D7B;;OAEG;YACW,aAAa;IAoD3B;;OAEG;YACW,oCAAoC;IA4ClD;;OAEG;YACW,kBAAkB;YAkClB,aAAa;YA8Cb,kBAAkB;IAiChC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;OAuCG;IACG,QAAQ,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IAsHpD;;;;;OAKG;IACG,YAAY,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;IASxD;;;;OAIG;IACG,sBAAsB,CAC1B,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,oBAAoB,GAAG,IAAI,CAAC;IAoEvC;;;;OAIG;IACG,mBAAmB,CACvB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,iBAAiB,GAAG,IAAI,CAAC;IA8DpC;;;;;;;;;;;;;;;;OAgBG;IACH,oBAAoB,CAAC,WAAW,EAAE,MAAM,GAAG,eAAe;CAqB3D"}
@@ -84,8 +84,8 @@ class AuthBroker {
84
84
  throw new Error('AuthBroker: sessionStore.setConnectionConfig must be a function');
85
85
  }
86
86
  // Check tokenProvider methods (required)
87
- if (typeof tokenProvider.getConnectionConfig !== 'function') {
88
- throw new Error('AuthBroker: tokenProvider.getConnectionConfig must be a function');
87
+ if (typeof tokenProvider.getTokens !== 'function') {
88
+ throw new Error('AuthBroker: tokenProvider.getTokens must be a function');
89
89
  }
90
90
  // validateToken is optional, so we don't check it
91
91
  // Check serviceKeyStore methods (if provided)
@@ -196,14 +196,9 @@ class AuthBroker {
196
196
  /**
197
197
  * Get UAA credentials from session or service key
198
198
  */
199
- async getUaaCredentials(destination, authConfig) {
200
- if (authConfig?.uaaUrl &&
201
- authConfig?.uaaClientId &&
202
- authConfig?.uaaClientSecret) {
203
- return authConfig;
204
- }
199
+ async getAuthorizationConfigFromServiceKey(destination) {
205
200
  if (!this.serviceKeyStore) {
206
- throw new Error(`UAA credentials not found for ${destination}. Session has no UAA credentials and serviceKeyStore is not available.`);
201
+ throw new Error(`Authorization config not found for ${destination}. Session has no auth config and serviceKeyStore is not available.`);
207
202
  }
208
203
  let serviceKeyAuthConfig = null;
209
204
  try {
@@ -219,21 +214,17 @@ class AuthBroker {
219
214
  this.logger?.warn(`Failed to parse service key for ${destination}: ${error.filePath || 'unknown path'} - ${getErrorMessage(error)}`);
220
215
  }
221
216
  else {
222
- this.logger?.warn(`Failed to get UAA credentials from service key store for ${destination}: ${getErrorMessage(error)}`);
217
+ this.logger?.warn(`Failed to get authorization config from service key store for ${destination}: ${getErrorMessage(error)}`);
223
218
  }
224
219
  }
225
220
  else {
226
- this.logger?.warn(`Failed to get UAA credentials from service key store for ${destination}: ${getErrorMessage(error)}`);
221
+ this.logger?.warn(`Failed to get authorization config from service key store for ${destination}: ${getErrorMessage(error)}`);
227
222
  }
228
223
  }
229
- const uaaCredentials = authConfig || serviceKeyAuthConfig;
230
- if (!uaaCredentials ||
231
- !uaaCredentials.uaaUrl ||
232
- !uaaCredentials.uaaClientId ||
233
- !uaaCredentials.uaaClientSecret) {
234
- throw new Error(`UAA credentials not found for ${destination}. Session has no UAA credentials${this.serviceKeyStore ? ' and serviceKeyStore has no UAA credentials' : ' and serviceKeyStore is not available'}.`);
224
+ if (!serviceKeyAuthConfig) {
225
+ throw new Error(`Authorization config not found for ${destination}. Session has no auth config${this.serviceKeyStore ? ' and serviceKeyStore has no auth config' : ' and serviceKeyStore is not available'}.`);
235
226
  }
236
- return uaaCredentials;
227
+ return serviceKeyAuthConfig;
237
228
  }
238
229
  /**
239
230
  * Save token and config to session
@@ -254,228 +245,51 @@ class AuthBroker {
254
245
  throw new Error(`Failed to save authorization config for destination "${destination}": ${getErrorMessage(error)}`);
255
246
  }
256
247
  }
257
- /**
258
- * Initialize session from service key (Step 0)
259
- */
260
- async initializeSessionFromServiceKey(destination, serviceUrl) {
261
- if (!this.serviceKeyStore) {
262
- throw new Error(`Cannot initialize session for destination "${destination}": authorizationToken is empty, UAA credentials are empty, and serviceKeyStore is not available. Provide serviceKeyStore to initialize from service key.`);
263
- }
264
- const serviceKeyAuthConfig = await this.serviceKeyStore.getAuthorizationConfig(destination);
265
- if (!serviceKeyAuthConfig ||
266
- !serviceKeyAuthConfig.uaaUrl ||
267
- !serviceKeyAuthConfig.uaaClientId ||
268
- !serviceKeyAuthConfig.uaaClientSecret) {
269
- throw new Error(`Service key for destination "${destination}" does not contain UAA credentials`);
270
- }
271
- if (!this.allowBrowserAuth) {
272
- const error = new Error(`Browser authentication required for destination "${destination}" but allowBrowserAuth is disabled. Either enable browser auth or provide a valid session with token.`);
273
- error.code = 'BROWSER_AUTH_REQUIRED';
274
- error.destination = destination;
275
- this.logger?.error(`Step 0: Browser auth required but disabled for ${destination}`);
276
- throw error;
277
- }
278
- this.logger?.debug(`Step 0: Authenticating via provider (browser) for ${destination} using service key UAA credentials`);
279
- const getConnectionConfig = this.tokenProvider.getConnectionConfig;
280
- if (!getConnectionConfig) {
281
- throw new Error('AuthBroker: tokenProvider.getConnectionConfig is required');
282
- }
283
- let tokenResult;
284
- try {
285
- tokenResult = await getConnectionConfig(serviceKeyAuthConfig, {
286
- browser: this.browser,
287
- logger: this.logger,
288
- });
289
- }
290
- catch (error) {
291
- if (hasErrorCode(error)) {
292
- if (error.code === 'VALIDATION_ERROR') {
293
- throw new Error(`Cannot initialize session for destination "${destination}": provider validation failed - missing ${error.missingFields?.join(', ') || 'required fields'}`);
294
- }
295
- else if (error.code === 'BROWSER_AUTH_ERROR') {
296
- throw new Error(`Cannot initialize session for destination "${destination}": browser authentication failed - ${getErrorMessage(error)}`);
297
- }
298
- else if (error.code === 'ECONNREFUSED' ||
299
- error.code === 'ETIMEDOUT' ||
300
- error.code === 'ENOTFOUND') {
301
- throw new Error(`Cannot initialize session for destination "${destination}": network error - cannot reach authentication server (${error.code})`);
302
- }
303
- }
304
- throw new Error(`Cannot initialize session for destination "${destination}": provider error - ${getErrorMessage(error)}`);
305
- }
306
- const token = tokenResult.connectionConfig.authorizationToken;
307
- if (!token) {
308
- throw new Error(`Token provider did not return authorization token for destination "${destination}"`);
309
- }
310
- const tokenLength = token.length;
311
- this.logger?.info(`Step 0: Token initialized for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
312
- // Get serviceUrl from service key store if not in connectionConfig
313
- const serviceKeyConnConfig = await this.serviceKeyStore.getConnectionConfig(destination);
314
- const connectionConfigWithServiceUrl = {
315
- ...tokenResult.connectionConfig,
316
- serviceUrl: tokenResult.connectionConfig.serviceUrl ||
317
- serviceKeyConnConfig?.serviceUrl ||
318
- serviceUrl,
319
- };
320
- await this.saveTokenToSession(destination, connectionConfigWithServiceUrl, {
321
- ...serviceKeyAuthConfig,
322
- refreshToken: tokenResult.refreshToken || serviceKeyAuthConfig.refreshToken,
323
- });
324
- return token;
325
- }
326
- /**
327
- * Validate existing token (Step 1)
328
- */
329
- async validateExistingToken(destination, token, serviceUrl) {
330
- if (!this.tokenProvider?.validateToken) {
331
- return false;
332
- }
248
+ async requestTokens(destination, sourceLabel) {
249
+ this.logger?.debug(`Requesting tokens for ${destination} via ${sourceLabel}`);
333
250
  try {
334
- const isValid = await this.tokenProvider.validateToken(token, serviceUrl);
335
- if (isValid) {
336
- this.logger?.info(`Step 1: Token valid for ${destination}: token(${token.length} chars)`);
337
- return true;
338
- }
339
- this.logger?.debug(`Step 1: Token invalid for ${destination}, continuing to refresh`);
340
- return false;
341
- }
342
- catch (error) {
343
- // Validation failed due to network/server error - log and continue to refresh
344
- this.logger?.warn(`Step 1: Token validation failed for ${destination} (network error): ${getErrorMessage(error)}. Continuing to refresh.`);
345
- return false;
346
- }
347
- }
348
- /**
349
- * Refresh token from session (Step 2a)
350
- */
351
- async refreshTokenFromSession(destination, uaaCredentials, refreshToken, serviceUrl) {
352
- this.logger?.debug(`Step 2a: Trying refreshTokenFromSession for ${destination}`);
353
- const authConfigWithRefresh = { ...uaaCredentials, refreshToken };
354
- const refreshTokenFromSession = this.tokenProvider.refreshTokenFromSession;
355
- if (!refreshTokenFromSession) {
356
- throw new Error('AuthBroker: tokenProvider.refreshTokenFromSession is required');
357
- }
358
- let tokenResult;
359
- try {
360
- tokenResult = await refreshTokenFromSession(authConfigWithRefresh, {
361
- browser: this.browser,
362
- logger: this.logger,
363
- });
364
- }
365
- catch (error) {
366
- if (hasErrorCode(error)) {
367
- if (error.code === 'ECONNREFUSED' ||
368
- error.code === 'ETIMEDOUT' ||
369
- error.code === 'ENOTFOUND') {
370
- this.logger?.debug(`Step 2a: Network error during refreshTokenFromSession for ${destination}: ${error.code}. Trying refreshTokenFromServiceKey`);
371
- throw error; // Re-throw to trigger fallback to Step 2b
372
- }
373
- }
374
- throw error; // Re-throw other errors
375
- }
376
- const token = tokenResult.connectionConfig.authorizationToken;
377
- if (!token) {
378
- throw new Error(`Token provider did not return authorization token for destination "${destination}"`);
379
- }
380
- const tokenLength = token.length;
381
- this.logger?.info(`Step 2a: Token refreshed from session for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
382
- // Get serviceUrl from session or service key
383
- let serviceKeyServiceUrl;
384
- if (this.serviceKeyStore) {
385
- try {
386
- const serviceKeyConn = await this.serviceKeyStore.getConnectionConfig(destination);
387
- serviceKeyServiceUrl = serviceKeyConn?.serviceUrl;
388
- }
389
- catch (error) {
390
- this.logger?.debug(`Could not get serviceUrl from service key store: ${getErrorMessage(error)}`);
251
+ const getTokens = this.tokenProvider.getTokens;
252
+ if (!getTokens) {
253
+ throw new Error('AuthBroker: tokenProvider.getTokens is required');
391
254
  }
392
- }
393
- const finalServiceUrl = tokenResult.connectionConfig.serviceUrl ||
394
- serviceUrl ||
395
- serviceKeyServiceUrl;
396
- const connectionConfigWithServiceUrl = {
397
- ...tokenResult.connectionConfig,
398
- serviceUrl: finalServiceUrl,
399
- };
400
- const authorizationConfig = {
401
- ...uaaCredentials,
402
- refreshToken: tokenResult.refreshToken || refreshToken,
403
- };
404
- await this.saveTokenToSession(destination, connectionConfigWithServiceUrl, authorizationConfig);
405
- return token;
406
- }
407
- /**
408
- * Refresh token from service key (Step 2b)
409
- */
410
- async refreshTokenFromServiceKey(destination, uaaCredentials, serviceUrl) {
411
- if (!this.allowBrowserAuth) {
412
- const error = new Error(`Browser authentication required for destination "${destination}" but allowBrowserAuth is disabled. Token refresh via session failed and browser auth is not allowed. Either enable browser auth or ensure a valid refresh token exists in session.`);
413
- error.code = 'BROWSER_AUTH_REQUIRED';
414
- error.destination = destination;
415
- this.logger?.error(`Step 2b: Browser auth required but disabled for ${destination}`);
416
- throw error;
417
- }
418
- this.logger?.debug(`Step 2b: Trying refreshTokenFromServiceKey for ${destination}`);
419
- const refreshTokenFromServiceKey = this.tokenProvider.refreshTokenFromServiceKey;
420
- if (!refreshTokenFromServiceKey) {
421
- throw new Error('AuthBroker: tokenProvider.refreshTokenFromServiceKey is required');
422
- }
423
- let tokenResult;
424
- try {
425
- tokenResult = await refreshTokenFromServiceKey(uaaCredentials, {
426
- browser: this.browser,
427
- logger: this.logger,
428
- });
255
+ return await getTokens.call(this.tokenProvider);
429
256
  }
430
257
  catch (error) {
431
258
  if (hasErrorCode(error)) {
432
259
  if (error.code === 'VALIDATION_ERROR') {
433
- throw new Error(`Token refresh failed: Missing required fields in authConfig - ${error.missingFields?.join(', ')}`);
260
+ throw new Error(`Token provider validation failed for ${destination}: missing ${error.missingFields?.join(', ') || 'required fields'}`);
434
261
  }
435
- else if (error.code === 'BROWSER_AUTH_ERROR') {
436
- throw new Error(`Token refresh failed: Browser authentication failed or was cancelled - ${getErrorMessage(error)}`);
262
+ if (error.code === 'BROWSER_AUTH_ERROR') {
263
+ throw new Error(`Token provider browser authentication failed for ${destination}: ${getErrorMessage(error)}`);
437
264
  }
438
- else if (error.code === 'ECONNREFUSED' ||
265
+ if (error.code === 'ECONNREFUSED' ||
439
266
  error.code === 'ETIMEDOUT' ||
440
267
  error.code === 'ENOTFOUND') {
441
- throw new Error(`Token refresh failed: Network error - ${error.code}: Cannot reach authentication server`);
268
+ throw new Error(`Token provider network error for ${destination}: ${error.code}`);
442
269
  }
443
- else if (error.code === 'SERVICE_KEY_ERROR') {
444
- throw new Error(`Token refresh failed: Service key not found or invalid for ${destination}`);
270
+ if (error.code === 'SERVICE_KEY_ERROR') {
271
+ throw new Error(`Token provider service key error for ${destination}: ${getErrorMessage(error)}`);
445
272
  }
446
273
  }
447
- throw new Error(`Token refresh failed for ${destination}: ${getErrorMessage(error)}`);
274
+ throw new Error(`Token provider error for ${destination}: ${getErrorMessage(error)}`);
448
275
  }
449
- const token = tokenResult.connectionConfig.authorizationToken;
276
+ }
277
+ async persistTokenResult(destination, serviceUrl, baseConnConfig, authConfig, tokenResult) {
278
+ const token = tokenResult.authorizationToken;
450
279
  if (!token) {
451
280
  throw new Error(`Token provider did not return authorization token for destination "${destination}"`);
452
281
  }
453
- const tokenLength = token.length;
454
- this.logger?.info(`Step 2b: Token refreshed from service key for ${destination}: token(${tokenLength} chars), hasRefreshToken(${!!tokenResult.refreshToken})`);
455
- // Get serviceUrl from session or service key
456
- let serviceKeyServiceUrl;
457
- if (this.serviceKeyStore) {
458
- try {
459
- const serviceKeyConn = await this.serviceKeyStore.getConnectionConfig(destination);
460
- serviceKeyServiceUrl = serviceKeyConn?.serviceUrl;
461
- }
462
- catch (error) {
463
- this.logger?.debug(`Could not get serviceUrl from service key store: ${getErrorMessage(error)}`);
464
- }
465
- }
466
- const finalServiceUrl = tokenResult.connectionConfig.serviceUrl ||
467
- serviceUrl ||
468
- serviceKeyServiceUrl;
469
282
  const connectionConfigWithServiceUrl = {
470
- ...tokenResult.connectionConfig,
471
- serviceUrl: finalServiceUrl,
283
+ ...baseConnConfig,
284
+ serviceUrl,
285
+ authorizationToken: token,
286
+ authType: 'jwt',
472
287
  };
473
288
  const authorizationConfig = {
474
- ...uaaCredentials,
475
- refreshToken: tokenResult.refreshToken || uaaCredentials.refreshToken,
289
+ ...authConfig,
290
+ refreshToken: tokenResult.refreshToken ?? authConfig.refreshToken,
476
291
  };
477
292
  await this.saveTokenToSession(destination, connectionConfigWithServiceUrl, authorizationConfig);
478
- return token;
479
293
  }
480
294
  /**
481
295
  * Get authentication token for destination.
@@ -525,66 +339,62 @@ class AuthBroker {
525
339
  const serviceUrl = await this.getServiceUrl(destination, connConfig);
526
340
  // Check if we have token or UAA credentials
527
341
  const hasToken = !!connConfig?.authorizationToken;
528
- const hasUaaCredentials = !!(authConfig?.uaaUrl &&
529
- authConfig?.uaaClientId &&
530
- authConfig?.uaaClientSecret);
531
- this.logger?.debug(`Session check for ${destination}: hasToken(${hasToken}), hasUaaCredentials(${hasUaaCredentials}), serviceUrl(${serviceUrl ? 'yes' : 'no'})`);
342
+ const hasAuthConfig = !!authConfig;
343
+ this.logger?.debug(`Session check for ${destination}: hasToken(${hasToken}), hasAuthConfig(${hasAuthConfig}), serviceUrl(${serviceUrl ? 'yes' : 'no'})`);
532
344
  // Step 0: Initialize Session with Token (if needed)
533
- if (!hasToken && !hasUaaCredentials) {
534
- try {
535
- return await this.initializeSessionFromServiceKey(destination, serviceUrl);
536
- }
537
- catch (error) {
538
- // Re-throw BROWSER_AUTH_REQUIRED error without wrapping
539
- if (hasErrorCode(error) && error.code === 'BROWSER_AUTH_REQUIRED') {
540
- throw error;
541
- }
542
- // Handle typed store errors
543
- if (hasErrorCode(error)) {
544
- if (error.code === interfaces_1.STORE_ERROR_CODES.FILE_NOT_FOUND) {
545
- throw new Error(`Cannot initialize session for destination "${destination}": service key file not found`);
546
- }
547
- else if (error.code === interfaces_1.STORE_ERROR_CODES.PARSE_ERROR) {
548
- throw new Error(`Cannot initialize session for destination "${destination}": service key parsing failed - ${getErrorMessage(error)}`);
549
- }
550
- else if (error.code === interfaces_1.STORE_ERROR_CODES.INVALID_CONFIG) {
551
- throw new Error(`Cannot initialize session for destination "${destination}": invalid service key - missing ${error.missingFields?.join(', ') || 'required fields'}`);
552
- }
553
- }
345
+ if (!hasToken && !hasAuthConfig) {
346
+ if (!this.allowBrowserAuth) {
347
+ const error = new Error(`Browser authentication required for destination "${destination}" but allowBrowserAuth is disabled. Either enable browser auth or provide a valid session with token.`);
348
+ error.code = 'BROWSER_AUTH_REQUIRED';
349
+ error.destination = destination;
350
+ this.logger?.error(`Step 0: Browser auth required but disabled for ${destination}`);
554
351
  throw error;
555
352
  }
556
- }
557
- // Step 1: Validate existing token
558
- if (hasToken && connConfig?.authorizationToken) {
559
- const isValid = await this.validateExistingToken(destination, connConfig.authorizationToken, serviceUrl);
560
- if (isValid) {
561
- return connConfig.authorizationToken;
562
- }
563
- // If no validation or validation failed, continue to refresh
564
- if (!this.tokenProvider?.validateToken) {
565
- this.logger?.info(`Token found for ${destination} (no validation): token(${connConfig.authorizationToken.length} chars)`);
566
- return connConfig.authorizationToken;
353
+ const serviceKeyAuthConfig = await this.getAuthorizationConfigFromServiceKey(destination);
354
+ const tokenResult = await this.requestTokens(destination, 'serviceKey');
355
+ await this.persistTokenResult(destination, serviceUrl, connConfig, serviceKeyAuthConfig, tokenResult);
356
+ return tokenResult.authorizationToken;
357
+ }
358
+ // Step 1: Request tokens via provider (provider handles token lifecycle internally)
359
+ // Broker always calls provider.getTokens() - provider decides whether to return cached token,
360
+ // refresh, or perform login. Consumer doesn't need to know about token issues.
361
+ this.logger?.debug(`Step 1: Requesting tokens via provider for ${destination}`);
362
+ let lastError = null;
363
+ if (authConfig) {
364
+ if (!this.allowBrowserAuth && !authConfig.refreshToken) {
365
+ const error = new Error(`Browser authentication required for destination "${destination}" but allowBrowserAuth is disabled. Session has no refresh token.`);
366
+ error.code = 'BROWSER_AUTH_REQUIRED';
367
+ error.destination = destination;
368
+ this.logger?.error(`Step 2: Browser auth required but disabled for ${destination}`);
369
+ throw error;
567
370
  }
568
- }
569
- // Step 2: Refresh Token Flow
570
- this.logger?.debug(`Step 2: Attempting token refresh for ${destination}`);
571
- const uaaCredentials = await this.getUaaCredentials(destination, authConfig);
572
- // Step 2a: Try refresh from session (if refresh token exists)
573
- const refreshToken = authConfig?.refreshToken;
574
- if (refreshToken) {
575
371
  try {
576
- return await this.refreshTokenFromSession(destination, uaaCredentials, refreshToken, serviceUrl);
372
+ const tokenResult = await this.requestTokens(destination, 'session');
373
+ await this.persistTokenResult(destination, serviceUrl, connConfig, authConfig, tokenResult);
374
+ return tokenResult.authorizationToken;
577
375
  }
578
376
  catch (error) {
579
- this.logger?.debug(`Step 2a: refreshTokenFromSession failed for ${destination}: ${getErrorMessage(error)}, trying refreshTokenFromServiceKey`);
580
- // Continue to try service key refresh
377
+ lastError = error instanceof Error ? error : new Error(String(error));
378
+ this.logger?.debug(`Step 2: Token request via session failed for ${destination}: ${getErrorMessage(error)}, trying service key`);
581
379
  }
582
380
  }
583
- else {
584
- this.logger?.debug(`Step 2a: No refresh token in session for ${destination}, skipping to service key refresh`);
381
+ if (!this.allowBrowserAuth) {
382
+ const error = new Error(`Browser authentication required for destination "${destination}" but allowBrowserAuth is disabled. Token refresh via session failed and browser auth is not allowed. Either enable browser auth or ensure a valid refresh token exists in session.`);
383
+ error.code = 'BROWSER_AUTH_REQUIRED';
384
+ error.destination = destination;
385
+ this.logger?.error(`Step 2: Browser auth required but disabled for ${destination}`);
386
+ throw error;
387
+ }
388
+ if (!this.serviceKeyStore) {
389
+ if (lastError) {
390
+ throw lastError;
391
+ }
392
+ throw new Error(`Authorization config not found for ${destination}. Session has no auth config and serviceKeyStore is not available.`);
585
393
  }
586
- // Step 2b: Try refresh from service key (browser authentication)
587
- return await this.refreshTokenFromServiceKey(destination, uaaCredentials, serviceUrl);
394
+ const serviceKeyAuthConfig = await this.getAuthorizationConfigFromServiceKey(destination);
395
+ const tokenResult = await this.requestTokens(destination, 'serviceKey');
396
+ await this.persistTokenResult(destination, serviceUrl, connConfig, serviceKeyAuthConfig, tokenResult);
397
+ return tokenResult.authorizationToken;
588
398
  }
589
399
  /**
590
400
  * Force refresh token for destination.
@@ -10,6 +10,8 @@ export interface TestConfig {
10
10
  };
11
11
  abap?: {
12
12
  destination?: string;
13
+ expired_token?: string;
14
+ refresh_token?: string;
13
15
  };
14
16
  xsuaa?: {
15
17
  btp_destination?: string;
@@ -46,4 +48,12 @@ export declare function getServiceKeysDir(config?: TestConfig): string | null;
46
48
  * Get sessions directory from config
47
49
  */
48
50
  export declare function getSessionsDir(config?: TestConfig): string | null;
51
+ /**
52
+ * Get expired token from config (real expired token from YAML)
53
+ */
54
+ export declare function getExpiredToken(config?: TestConfig): string | null;
55
+ /**
56
+ * Get refresh token from config (real refresh token from YAML)
57
+ */
58
+ export declare function getRefreshToken(config?: TestConfig): string | null;
49
59
  //# sourceMappingURL=configHelpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"configHelpers.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/configHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE;YACN,gBAAgB,CAAC,EAAE,MAAM,CAAC;YAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC;QACF,IAAI,CAAC,EAAE;YACL,WAAW,CAAC,EAAE,MAAM,CAAC;SACtB,CAAC;QACF,KAAK,CAAC,EAAE;YACN,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,OAAO,CAAC,EAAE,MAAM,CAAC;SAClB,CAAC;KACH,CAAC;CACH;AAkBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAyD3C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,GAAG,OAAO,GACxB,OAAO,CAwBT;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGrE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG;IACzD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAOA;AAaD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CASpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CASjE"}
1
+ {"version":3,"file":"configHelpers.d.ts","sourceRoot":"","sources":["../../../src/__tests__/helpers/configHelpers.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAQH,MAAM,WAAW,UAAU;IACzB,WAAW,CAAC,EAAE;QACZ,KAAK,CAAC,EAAE;YACN,gBAAgB,CAAC,EAAE,MAAM,CAAC;YAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;SACvB,CAAC;QACF,IAAI,CAAC,EAAE;YACL,WAAW,CAAC,EAAE,MAAM,CAAC;YACrB,aAAa,CAAC,EAAE,MAAM,CAAC;YACvB,aAAa,CAAC,EAAE,MAAM,CAAC;SACxB,CAAC;QACF,KAAK,CAAC,EAAE;YACN,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,eAAe,CAAC,EAAE,MAAM,CAAC;YACzB,OAAO,CAAC,EAAE,MAAM,CAAC;SAClB,CAAC;KACH,CAAC;CACH;AAkBD;;;GAGG;AACH,wBAAgB,cAAc,IAAI,UAAU,CAyD3C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,EAClB,OAAO,EAAE,MAAM,GAAG,OAAO,GACxB,OAAO,CAwBT;AAED;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGrE;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG;IACzD,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;CACxB,CAOA;AAaD;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CASpE;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CASjE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGlE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,MAAM,CAAC,EAAE,UAAU,GAAG,MAAM,GAAG,IAAI,CAGlE"}
@@ -43,6 +43,8 @@ exports.getAbapDestination = getAbapDestination;
43
43
  exports.getXsuaaDestinations = getXsuaaDestinations;
44
44
  exports.getServiceKeysDir = getServiceKeysDir;
45
45
  exports.getSessionsDir = getSessionsDir;
46
+ exports.getExpiredToken = getExpiredToken;
47
+ exports.getRefreshToken = getRefreshToken;
46
48
  const fs = __importStar(require("node:fs"));
47
49
  const path = __importStar(require("node:path"));
48
50
  const yaml = __importStar(require("js-yaml"));
@@ -188,3 +190,17 @@ function getSessionsDir(config) {
188
190
  const expanded = expandTilde(dir);
189
191
  return path.resolve(expanded);
190
192
  }
193
+ /**
194
+ * Get expired token from config (real expired token from YAML)
195
+ */
196
+ function getExpiredToken(config) {
197
+ const cfg = config || loadTestConfig();
198
+ return cfg.auth_broker?.abap?.expired_token || null;
199
+ }
200
+ /**
201
+ * Get refresh token from config (real refresh token from YAML)
202
+ */
203
+ function getRefreshToken(config) {
204
+ const cfg = config || loadTestConfig();
205
+ return cfg.auth_broker?.abap?.refresh_token || null;
206
+ }
package/dist/index.d.ts CHANGED
@@ -4,7 +4,7 @@
4
4
  */
5
5
  export type { AuthType, ILogger, ITokenRefresher, } from '@mcp-abap-adt/interfaces';
6
6
  export { AuthBroker, type AuthBrokerConfig } from './AuthBroker';
7
- export type { ITokenProvider, TokenProviderOptions, TokenProviderResult, } from './providers';
7
+ export type { ITokenProvider, ITokenResult, TokenProviderOptions, } from './providers';
8
8
  export type { IAuthorizationConfig, IConnectionConfig, IServiceKeyStore, ISessionStore, } from './stores/interfaces';
9
9
  export type { IConfig } from './types';
10
10
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,YAAY,EACV,QAAQ,EACR,OAAO,EACP,eAAe,GAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEjE,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAKH,YAAY,EACV,QAAQ,EACR,OAAO,EACP,eAAe,GAChB,MAAM,0BAA0B,CAAC;AAClC,OAAO,EAAE,UAAU,EAAE,KAAK,gBAAgB,EAAE,MAAM,cAAc,CAAC;AAEjE,YAAY,EACV,cAAc,EACd,YAAY,EACZ,oBAAoB,GACrB,MAAM,aAAa,CAAC;AAGrB,YAAY,EACV,oBAAoB,EACpB,iBAAiB,EACjB,gBAAgB,EAChB,aAAa,GACd,MAAM,qBAAqB,CAAC;AAC7B,YAAY,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC"}
@@ -1,13 +1,9 @@
1
1
  /**
2
2
  * Token Provider interface
3
3
  *
4
- * Converts IAuthorizationConfig to IConnectionConfig by obtaining tokens.
5
- * Different implementations handle different authentication flows:
6
- * - XSUAA: client_credentials grant type (no browser)
7
- * - BTP/ABAP: browser-based OAuth2 or refresh token
4
+ * Stateful providers handle token lifecycle internally (refresh/relogin).
8
5
  */
9
- import type { IAuthorizationConfig, IConnectionConfig, ITokenProvider, ITokenProviderOptions, ITokenProviderResult } from '@mcp-abap-adt/interfaces';
10
- export type { ITokenProvider, IAuthorizationConfig, IConnectionConfig };
11
- export type TokenProviderResult = ITokenProviderResult;
6
+ import type { IAuthorizationConfig, IConnectionConfig, ITokenProvider, ITokenProviderOptions, ITokenResult } from '@mcp-abap-adt/interfaces';
7
+ export type { ITokenProvider, IAuthorizationConfig, IConnectionConfig, ITokenResult, };
12
8
  export type TokenProviderOptions = ITokenProviderOptions;
13
9
  //# sourceMappingURL=ITokenProvider.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ITokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/ITokenProvider.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,oBAAoB,EACrB,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EAAE,cAAc,EAAE,oBAAoB,EAAE,iBAAiB,EAAE,CAAC;AACxE,MAAM,MAAM,mBAAmB,GAAG,oBAAoB,CAAC;AACvD,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC"}
1
+ {"version":3,"file":"ITokenProvider.d.ts","sourceRoot":"","sources":["../../src/providers/ITokenProvider.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,EACV,oBAAoB,EACpB,iBAAiB,EACjB,cAAc,EACd,qBAAqB,EACrB,YAAY,EACb,MAAM,0BAA0B,CAAC;AAGlC,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,iBAAiB,EACjB,YAAY,GACb,CAAC;AACF,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC"}
@@ -2,9 +2,6 @@
2
2
  /**
3
3
  * Token Provider interface
4
4
  *
5
- * Converts IAuthorizationConfig to IConnectionConfig by obtaining tokens.
6
- * Different implementations handle different authentication flows:
7
- * - XSUAA: client_credentials grant type (no browser)
8
- * - BTP/ABAP: browser-based OAuth2 or refresh token
5
+ * Stateful providers handle token lifecycle internally (refresh/relogin).
9
6
  */
10
7
  Object.defineProperty(exports, "__esModule", { value: true });
@@ -4,5 +4,5 @@
4
4
  * Provider implementations are in separate packages:
5
5
  * - @mcp-abap-adt/auth-providers - XSUAA and BTP providers
6
6
  */
7
- export type { ITokenProvider, TokenProviderOptions, TokenProviderResult, } from './ITokenProvider';
7
+ export type { ITokenProvider, ITokenResult, TokenProviderOptions, } from './ITokenProvider';
8
8
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,cAAc,EACd,oBAAoB,EACpB,mBAAmB,GACpB,MAAM,kBAAkB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/providers/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,YAAY,EACV,cAAc,EACd,YAAY,EACZ,oBAAoB,GACrB,MAAM,kBAAkB,CAAC"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mcp-abap-adt/auth-broker",
3
- "version": "0.2.11",
3
+ "version": "0.2.13",
4
4
  "description": "JWT authentication broker for MCP ABAP ADT - manages tokens based on destination headers",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",
@@ -56,9 +56,9 @@
56
56
  "node": ">=18.0.0"
57
57
  },
58
58
  "dependencies": {
59
- "@mcp-abap-adt/auth-providers": "^0.2.6",
60
- "@mcp-abap-adt/auth-stores": "^0.2.8",
61
- "@mcp-abap-adt/interfaces": "^0.2.9",
59
+ "@mcp-abap-adt/auth-providers": "^0.2.8",
60
+ "@mcp-abap-adt/auth-stores": "^0.2.9",
61
+ "@mcp-abap-adt/interfaces": "^0.2.14",
62
62
  "axios": "^1.13.2",
63
63
  "tsx": "^4.21.0"
64
64
  },