@equinor/fusion-framework-module-msal-node 0.1.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.
Files changed (62) hide show
  1. package/CHANGELOG.md +99 -0
  2. package/LICENSE +21 -0
  3. package/README.md +125 -0
  4. package/dist/esm/AuthConfigurator.interface.js +2 -0
  5. package/dist/esm/AuthConfigurator.interface.js.map +1 -0
  6. package/dist/esm/AuthConfigurator.js +112 -0
  7. package/dist/esm/AuthConfigurator.js.map +1 -0
  8. package/dist/esm/AuthProvider.interface.js +2 -0
  9. package/dist/esm/AuthProvider.interface.js.map +1 -0
  10. package/dist/esm/AuthProvider.js +109 -0
  11. package/dist/esm/AuthProvider.js.map +1 -0
  12. package/dist/esm/AuthProviderInteractive.js +88 -0
  13. package/dist/esm/AuthProviderInteractive.js.map +1 -0
  14. package/dist/esm/AuthTokenProvider.js +50 -0
  15. package/dist/esm/AuthTokenProvider.js.map +1 -0
  16. package/dist/esm/create-auth-cache.js +67 -0
  17. package/dist/esm/create-auth-cache.js.map +1 -0
  18. package/dist/esm/create-auth-client.js +35 -0
  19. package/dist/esm/create-auth-client.js.map +1 -0
  20. package/dist/esm/create-auth-server.js +81 -0
  21. package/dist/esm/create-auth-server.js.map +1 -0
  22. package/dist/esm/enable-module.js +31 -0
  23. package/dist/esm/enable-module.js.map +1 -0
  24. package/dist/esm/error.js +64 -0
  25. package/dist/esm/error.js.map +1 -0
  26. package/dist/esm/index.js +4 -0
  27. package/dist/esm/index.js.map +1 -0
  28. package/dist/esm/module.js +26 -0
  29. package/dist/esm/module.js.map +1 -0
  30. package/dist/esm/version.js +3 -0
  31. package/dist/esm/version.js.map +1 -0
  32. package/dist/tsconfig.tsbuildinfo +1 -0
  33. package/dist/types/AuthConfigurator.d.ts +55 -0
  34. package/dist/types/AuthConfigurator.interface.d.ts +153 -0
  35. package/dist/types/AuthProvider.d.ts +81 -0
  36. package/dist/types/AuthProvider.interface.d.ts +55 -0
  37. package/dist/types/AuthProviderInteractive.d.ts +73 -0
  38. package/dist/types/AuthTokenProvider.d.ts +43 -0
  39. package/dist/types/create-auth-cache.d.ts +32 -0
  40. package/dist/types/create-auth-client.d.ts +24 -0
  41. package/dist/types/create-auth-server.d.ts +34 -0
  42. package/dist/types/enable-module.d.ts +24 -0
  43. package/dist/types/error.d.ts +59 -0
  44. package/dist/types/index.d.ts +5 -0
  45. package/dist/types/module.d.ts +24 -0
  46. package/dist/types/version.d.ts +1 -0
  47. package/package.json +46 -0
  48. package/src/AuthConfigurator.interface.ts +163 -0
  49. package/src/AuthConfigurator.ts +131 -0
  50. package/src/AuthProvider.interface.ts +53 -0
  51. package/src/AuthProvider.ts +119 -0
  52. package/src/AuthProviderInteractive.ts +117 -0
  53. package/src/AuthTokenProvider.ts +56 -0
  54. package/src/create-auth-cache.ts +85 -0
  55. package/src/create-auth-client.ts +40 -0
  56. package/src/create-auth-server.ts +93 -0
  57. package/src/enable-module.ts +35 -0
  58. package/src/error.ts +66 -0
  59. package/src/index.ts +9 -0
  60. package/src/module.ts +52 -0
  61. package/src/version.ts +2 -0
  62. package/tsconfig.json +15 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,99 @@
1
+ # @equinor/fusion-framework-module-msal-node
2
+
3
+ ## 0.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#3056](https://github.com/equinor/fusion-framework/pull/3056) [`378bade`](https://github.com/equinor/fusion-framework/commit/378bade86c38e1057afe125fffc0bb06d6927deb) Thanks [@odinr](https://github.com/odinr)! - Easily add secure Azure AD authentication to your Node.js Fusion Framework apps with the new `@equinor/fusion-framework-module-msal-node` package. This module is designed for developers who need to authenticate services, CLI tools, or background jobs against Microsoft Azure using familiar, robust MSAL flows—without the hassle of manual token management or insecure storage.
8
+
9
+ **Why use this module?**
10
+
11
+ - **Simple integration:** Plug into the Fusion Framework with minimal setup.
12
+ - **Multiple auth flows:** Choose the right authentication mode for your use case—CI/CD, background jobs, or interactive CLI tools.
13
+ - **Secure by default:** Tokens are encrypted and stored safely using `@azure/msal-node-extensions`.
14
+ - **Consistent API:** Acquire tokens the same way across all your Node.js Fusion projects.
15
+
16
+ **How to use:**
17
+
18
+ 1. Install the package in your Fusion Framework Node.js project.
19
+ 2. Enable the module and pick your authentication mode (`token_only`, `silent`, or `interactive`).
20
+ 3. Use the provided API to acquire tokens for Azure resources—no need to handle refresh logic or storage yourself.
21
+
22
+ - Supports token_only, silent, and interactive authentication modes
23
+ - Provides secure, encrypted token storage using `@azure/msal-node-extensions`
24
+ - Integrates with Fusion Framework for seamless authentication
25
+ - Includes comprehensive documentation and usage examples
26
+
27
+ ## Example Setups
28
+
29
+ ### `token_only` Mode
30
+
31
+ ```ts
32
+ import {
33
+ enableAuthModule,
34
+ type MsalNodeModule,
35
+ } from "@equinor/fusion-framework-msal-node";
36
+ import { ModulesConfigurator } from "@equinor/fusion-framework-module";
37
+
38
+ const configurator = new ModulesConfigurator<[MsalNodeModule]>();
39
+
40
+ enableAuthModule(configurator, (builder) => {
41
+ builder.setMode("token_only");
42
+ builder.setAccessToken("your-access-token"); // Provide your token
43
+ });
44
+
45
+ const instance = await initialize();
46
+ console.log(
47
+ await instance.auth.acquireAccessToken({ scopes: ["user.read"] })
48
+ );
49
+ ```
50
+
51
+ ### `silent` Mode
52
+
53
+ ```ts
54
+ import {
55
+ enableAuthModule,
56
+ type MsalNodeModule,
57
+ } from "@equinor/fusion-framework-msal-node";
58
+ import { ModulesConfigurator } from "@equinor/fusion-framework-module";
59
+
60
+ const configurator = new ModulesConfigurator<[MsalNodeModule]>();
61
+
62
+ enableAuthModule(configurator, (builder) => {
63
+ builder.setMode("silent");
64
+ builder.setClientId("your-client-id");
65
+ builder.setTenantId("your-tenant-id");
66
+ });
67
+
68
+ const instance = await initialize();
69
+ console.log(
70
+ await instance.auth.acquireAccessToken({ scopes: ["user.read"] })
71
+ );
72
+ ```
73
+
74
+ ### `interactive` Mode
75
+
76
+ ```ts
77
+ import {
78
+ enableAuthModule,
79
+ type MsalNodeModule,
80
+ } from "@equinor/fusion-framework-msal-node";
81
+ import { ModulesConfigurator } from "@equinor/fusion-framework-module";
82
+
83
+ const configurator = new ModulesConfigurator<[MsalNodeModule]>();
84
+
85
+ enableAuthModule(configurator, (builder) => {
86
+ builder.setMode("interactive");
87
+ builder.setClientId("your-client-id");
88
+ builder.setTenantId("your-tenant-id");
89
+ builder.setServerPort(3000); // Local server port for auth callback
90
+ builder.setServerOnOpen((url) => {
91
+ console.log(`Please navigate to: ${url}`);
92
+ });
93
+ });
94
+
95
+ const instance = await initialize();
96
+ console.log(await instance.auth.login({ scopes: ["user.read"] }));
97
+ ```
98
+
99
+ See README.md for details.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2022 Equinor
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,125 @@
1
+ # Fusion MSAL Node Module
2
+
3
+ `@equinor/fusion-framework-msal-node` enables secure authentication for the Fusion Framework in Node.js environments using Microsoft's MSAL (Microsoft Authentication Library) to integrate with Azure Active Directory (Azure AD).
4
+
5
+ ## Features
6
+
7
+ - **Simple Token Acquisition**: Easy-to-use API for acquiring authentication tokens.
8
+ - **Multiple Auth Flows**: Supports client credentials and other Azure AD authentication flows.
9
+ - **Token Caching**: Built-in caching for improved performance and security.
10
+ - **Fusion Framework Integration**: Seamless authentication across Fusion Framework applications.
11
+ - **Authentication Modes**:
12
+ - `token_only`: Uses a pre-provided token for authentication (e.g., CI/CD, automation).
13
+ - `silent`: Acquires tokens silently using cached or refresh tokens (background services, scripts).
14
+ - `interactive`: Prompts users for authentication via a local HTTP server (CLI tools, development).
15
+
16
+ ## Authentication Modes
17
+
18
+ The module supports three authentication modes to suit different use cases:
19
+
20
+ | Mode | Description | Use Case |
21
+ | ------------- | ---------------------------------------------------------------------------- | ------------------------------------------ |
22
+ | `token_only` | Uses a pre-obtained token, typically from environment variables. | CI/CD pipelines, automated processes. |
23
+ | `silent` | Acquires tokens silently using cached or refresh tokens, without user input. | Background services, pre-seeding commands. |
24
+ | `interactive` | Prompts user login via a browser, using a local HTTP server for callbacks. | CLI tools, development, manual operations. |
25
+
26
+ ### `token_only`
27
+ Ideal for scenarios where a token is already available (e.g., via CI/CD). No interaction with Azure AD is required.
28
+
29
+ ### `silent`
30
+ Uses cached or refresh tokens to authenticate without user interaction. Perfect for automated or background tasks.
31
+
32
+ ### `interactive`
33
+ Requires user interaction, launching a local HTTP server to handle browser-based authentication. Suitable for CLI or development workflows.
34
+
35
+ ## Secure Token Storage
36
+
37
+ The module leverages `@azure/msal-node-extensions` for secure, encrypted token storage:
38
+
39
+ - **Encryption**: Tokens are encrypted at rest using platform-specific mechanisms (e.g., DPAPI on Windows, Keychain on macOS).
40
+ - **Cross-Platform**: Supports Windows, macOS, and Linux.
41
+ - **Persistence**: Tokens are stored securely for reuse across sessions, minimizing re-authentication.
42
+
43
+ This ensures sensitive token data is protected, reducing the risk of unauthorized access.
44
+
45
+ ## Usage
46
+
47
+ Below are examples for enabling the module in each authentication mode.
48
+
49
+ ### `token_only` Mode
50
+
51
+ Use a pre-obtained token for authentication.
52
+
53
+ ```ts
54
+ import { enableAuthModule, type MsalNodeModule } from '@equinor/fusion-framework-msal-node';
55
+ import { ModulesConfigurator } from '@equinor/fusion-framework-module';
56
+
57
+ const configurator = new ModulesConfigurator<[MsalNodeModule]>();
58
+
59
+ enableAuthModule(configurator, (builder) => {
60
+ builder.setMode('token_only');
61
+ builder.setAccessToken('your-access-token'); // Provide your token
62
+ });
63
+
64
+ const instance = await initialize();
65
+ console.log(typeof instance.auth); // AuthTokenProvider
66
+ console.log(await instance.auth.acquireAccessToken({ scopes: ['user.read'] }));
67
+ ```
68
+
69
+ ### `silent` Mode
70
+
71
+ Authenticate silently using cached or refresh tokens.
72
+
73
+ ```ts
74
+ import { enableAuthModule, type MsalNodeModule } from '@equinor/fusion-framework-msal-node';
75
+ import { ModulesConfigurator } from '@equinor/fusion-framework-module';
76
+
77
+ const configurator = new ModulesConfigurator<[MsalNodeModule]>();
78
+
79
+ enableAuthModule(configurator, (builder) => {
80
+ builder.setMode('silent');
81
+ builder.setClientId('your-client-id'); // Azure AD client ID
82
+ builder.setTenantId('your-tenant-id'); // Azure AD tenant ID
83
+ });
84
+
85
+ const instance = await initialize();
86
+ console.log(typeof instance.auth); // AuthTokenProvider
87
+ console.log(await instance.auth.acquireAccessToken({ scopes: ['user.read'] }));
88
+ ```
89
+
90
+ ### `interactive` Mode
91
+
92
+ Prompt the user for browser-based authentication.
93
+
94
+ ```ts
95
+ import { enableAuthModule, type MsalNodeModule } from '@equinor/fusion-framework-msal-node';
96
+ import { ModulesConfigurator } from '@equinor/fusion-framework-module';
97
+
98
+ const configurator = new ModulesConfigurator<[MsalNodeModule]>();
99
+
100
+ enableAuthModule(configurator, (builder) => {
101
+ builder.setMode('interactive');
102
+ builder.setClientId('your-client-id'); // Azure AD client ID
103
+ builder.setTenantId('your-tenant-id'); // Azure AD tenant ID
104
+ builder.setServerPort(3000); // Local server port for auth callback
105
+ builder.setServerOnOpen((url) => {
106
+ console.log(`Please navigate to: ${url}`);
107
+ });
108
+ });
109
+
110
+ const instance = await initialize();
111
+ console.log(typeof instance.auth); // AuthProviderInteractive
112
+ console.log(await instance.auth.login({ scopes: ['user.read'] }));
113
+ ```
114
+
115
+ ## Configuration
116
+
117
+ - **Client ID and Tenant ID**: Obtain these from your Azure AD application registration.
118
+ - **Scopes**: Specify required permissions (e.g., `['user.read']`) when acquiring tokens.
119
+ - **Server Port**: For `interactive` mode, ensure the port is available and not blocked by firewalls.
120
+
121
+ ## Troubleshooting
122
+
123
+ - **Token Issues**: Verify your token, client ID, or tenant ID are correct.
124
+ - **Interactive Mode Fails**: Check if the specified port is free and accessible.
125
+ - **Silent Mode Fails**: Ensure cached tokens or refresh tokens are valid.
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AuthConfigurator.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthConfigurator.interface.js","sourceRoot":"","sources":["../../src/AuthConfigurator.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,112 @@
1
+ import { PublicClientApplication } from '@azure/msal-node';
2
+ import { BaseConfigBuilder, } from '@equinor/fusion-framework-module';
3
+ import { createAuthClient } from './create-auth-client';
4
+ /**
5
+ * Internal builder for MSAL Node authentication configuration.
6
+ *
7
+ * This class provides the implementation for the fluent API exposed via the public interface.
8
+ * Most consumer-facing documentation is in the interface; see {@link IAuthConfigurator} for usage details.
9
+ *
10
+ * @see IAuthConfigurator
11
+ * @extends BaseConfigBuilder
12
+ *
13
+ * Maintainers: Extend or refactor this class to support new authentication modes or configuration options.
14
+ * Ensure changes are reflected in the interface and validated in `_processConfig`.
15
+ */
16
+ export class AuthConfigurator extends BaseConfigBuilder {
17
+ constructor() {
18
+ super();
19
+ this.setMode('interactive');
20
+ }
21
+ setMode(mode) {
22
+ this._set('mode', mode);
23
+ }
24
+ setClient(client) {
25
+ this._set('client', client);
26
+ }
27
+ setClientConfig(tenantId, clientId) {
28
+ this._set('client', () => createAuthClient(tenantId, clientId));
29
+ }
30
+ setServerPort(port) {
31
+ this._set('server.port', port);
32
+ }
33
+ setServerOnOpen(onOpen) {
34
+ this._set('server.onOpen', onOpen);
35
+ }
36
+ setAccessToken(token) {
37
+ this._set('accessToken', token);
38
+ }
39
+ /**
40
+ * Prepares and finalizes the authentication configuration before validation and use.
41
+ *
42
+ * This method injects the parent authentication provider reference (if available)
43
+ * into the configuration. It is called before `_processConfig` and allows for
44
+ * dynamic or contextual configuration adjustments based on the current module instance.
45
+ *
46
+ * Future maintainers: If additional contextual setup is needed (e.g., injecting
47
+ * dependencies, environment-specific values, or chaining providers), extend this method.
48
+ *
49
+ * @inheritdoc
50
+ * @param init - Initialization arguments, including module references.
51
+ * @param initial - Optional initial configuration values.
52
+ * @returns The prepared configuration object, ready for validation.
53
+ */
54
+ _buildConfig(init, initial) {
55
+ // Inject the parent auth provider from the current module instance, if present
56
+ this._set('parent', init.ref?.auth);
57
+ // Call the base builder to finalize the config
58
+ return super._buildConfig(init, initial);
59
+ }
60
+ /**
61
+ * Validates and processes the authentication configuration before use.
62
+ *
63
+ * This method ensures that all required properties are present and correctly typed
64
+ * for the selected authentication mode. Throws descriptive errors if configuration
65
+ * is incomplete or invalid, helping catch misconfigurations early.
66
+ *
67
+ * Future maintainers: Update this logic if new authentication modes or required
68
+ * properties are introduced. Keep error messages clear to aid debugging.
69
+ *
70
+ * @inheritdoc
71
+ * @param config - The authentication configuration object to validate.
72
+ * @returns The validated configuration object.
73
+ * @throws Error if required properties are missing or invalid for the selected mode.
74
+ */
75
+ async _processConfig(config) {
76
+ switch (config.mode) {
77
+ case 'interactive': {
78
+ // Interactive mode requires a valid MSAL client instance
79
+ if (config.client instanceof PublicClientApplication === false) {
80
+ throw new Error('Client is required when mode is interactive');
81
+ }
82
+ // Server configuration must be present
83
+ if (!config.server) {
84
+ throw new Error('Server is required when mode is interactive');
85
+ }
86
+ // Server port must be a number
87
+ if (typeof config.server.port !== 'number') {
88
+ throw new Error('Server port must be a number when mode is interactive');
89
+ }
90
+ break;
91
+ }
92
+ case 'silent': {
93
+ // Silent mode requires a valid MSAL client instance
94
+ if (config.client instanceof PublicClientApplication === false) {
95
+ throw new Error('Client is required when mode is silent');
96
+ }
97
+ break;
98
+ }
99
+ case 'token_only': {
100
+ // Token only mode requires a string access token
101
+ if (typeof config.accessToken !== 'string') {
102
+ throw new Error('Access token is required when mode is token_only');
103
+ }
104
+ break;
105
+ }
106
+ // If new modes are added, ensure validation is implemented here
107
+ }
108
+ // Return the validated config for use by the module
109
+ return config;
110
+ }
111
+ }
112
+ //# sourceMappingURL=AuthConfigurator.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthConfigurator.js","sourceRoot":"","sources":["../../src/AuthConfigurator.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EACL,iBAAiB,GAGlB,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,gBAAgB,EAAE,MAAM,sBAAsB,CAAC;AAKxD;;;;;;;;;;;GAWG;AACH,MAAM,OAAO,gBAAiB,SAAQ,iBAA6B;IACjE;QACE,KAAK,EAAE,CAAC;QACR,IAAI,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,CAAC,IAAwB;QAC9B,IAAI,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;IAC1B,CAAC;IAED,SAAS,CAAC,MAA4B;QACpC,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC9B,CAAC;IAED,eAAe,CAAC,QAAgB,EAAE,QAAgB;QAChD,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,EAAE,CAAC,gBAAgB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC,CAAC;IAClE,CAAC;IAED,aAAa,CAAC,IAAY;QACxB,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,IAAI,CAAC,CAAC;IACjC,CAAC;IAED,eAAe,CAAC,MAA2C;QACzD,IAAI,CAAC,IAAI,CAAC,eAAe,EAAE,MAAM,CAAC,CAAC;IACrC,CAAC;IAED,cAAc,CAAC,KAAa;QAC1B,IAAI,CAAC,IAAI,CAAC,aAAa,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACO,YAAY,CACpB,IAA+B,EAC/B,OAAyC;QAEzC,+EAA+E;QAC/E,IAAI,CAAC,IAAI,CAAC,QAAQ,EAAG,IAAI,CAAC,GAAyC,EAAE,IAAI,CAAC,CAAC;QAC3E,+CAA+C;QAC/C,OAAO,KAAK,CAAC,YAAY,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;IAC3C,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACH,KAAK,CAAC,cAAc,CAAC,MAAkB;QACrC,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;YACpB,KAAK,aAAa,CAAC,CAAC,CAAC;gBACnB,yDAAyD;gBACzD,IAAI,MAAM,CAAC,MAAM,YAAY,uBAAuB,KAAK,KAAK,EAAE,CAAC;oBAC/D,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;gBACjE,CAAC;gBACD,uCAAuC;gBACvC,IAAI,CAAC,MAAM,CAAC,MAAM,EAAE,CAAC;oBACnB,MAAM,IAAI,KAAK,CAAC,6CAA6C,CAAC,CAAC;gBACjE,CAAC;gBACD,+BAA+B;gBAC/B,IAAI,OAAO,MAAM,CAAC,MAAM,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;oBAC3C,MAAM,IAAI,KAAK,CAAC,uDAAuD,CAAC,CAAC;gBAC3E,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,QAAQ,CAAC,CAAC,CAAC;gBACd,oDAAoD;gBACpD,IAAI,MAAM,CAAC,MAAM,YAAY,uBAAuB,KAAK,KAAK,EAAE,CAAC;oBAC/D,MAAM,IAAI,KAAK,CAAC,wCAAwC,CAAC,CAAC;gBAC5D,CAAC;gBACD,MAAM;YACR,CAAC;YACD,KAAK,YAAY,CAAC,CAAC,CAAC;gBAClB,iDAAiD;gBACjD,IAAI,OAAO,MAAM,CAAC,WAAW,KAAK,QAAQ,EAAE,CAAC;oBAC3C,MAAM,IAAI,KAAK,CAAC,kDAAkD,CAAC,CAAC;gBACtE,CAAC;gBACD,MAAM;YACR,CAAC;YACD,gEAAgE;QAClE,CAAC;QACD,oDAAoD;QACpD,OAAO,MAAM,CAAC;IAChB,CAAC;CACF"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=AuthProvider.interface.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.interface.js","sourceRoot":"","sources":["../../src/AuthProvider.interface.ts"],"names":[],"mappings":""}
@@ -0,0 +1,109 @@
1
+ import { AuthServerError, NoAccountsError, SilentTokenAcquisitionError } from './error.js';
2
+ /**
3
+ * Implementation of the authentication provider for the Fusion MSAL Node module.
4
+ *
5
+ * Implements {@link IAuthProvider} and provides methods for managing authentication
6
+ * and token acquisition using the MSAL (Microsoft Authentication Library) for Node.js.
7
+ *
8
+ * This implementation assumes the user is already logged in and does not support
9
+ * triggering interactive login or logout flows. The `login` method will always throw, and `logout`
10
+ * only clears the token cache.
11
+ *
12
+ * @see AuthProviderInteractive For interactive login/logout support (user-driven authentication flows).
13
+ * @see AuthProviderTokenOnly For scenarios where a pre-obtained token is used (automation, CI/CD, etc).
14
+ *
15
+ * Developers extending this class can add support for additional authentication flows or modify token
16
+ * acquisition logic. Ensure that any changes remain consistent with the interface contract.
17
+ */
18
+ export class AuthProvider {
19
+ _client;
20
+ constructor(_client) {
21
+ this._client = _client;
22
+ }
23
+ /**
24
+ * Retrieves the first account from the list of all accounts available in the MSAL client.
25
+ *
26
+ * @returns A promise that resolves to the first `AccountInfo` object if available, or `null` if no accounts exist.
27
+ */
28
+ async getAccount() {
29
+ const accounts = await this._client.getAllAccounts();
30
+ return accounts[0] ?? null;
31
+ }
32
+ /**
33
+ * Acquires an access token for the specified scopes.
34
+ *
35
+ * @param options - An object containing the options for acquiring the token.
36
+ * @param options.scopes - An array of strings representing the scopes for which the access token is requested.
37
+ * @returns A promise that resolves to the acquired access token as a string.
38
+ * @throws An error if the token acquisition process fails.
39
+ */
40
+ async acquireAccessToken(options) {
41
+ const { accessToken } = await this.acquireToken(options);
42
+ return accessToken;
43
+ }
44
+ /**
45
+ * Initiates the login process with the specified options.
46
+ *
47
+ * @param _options - An object containing the scopes required for authentication.
48
+ * @returns A promise that resolves to an `AuthenticationResult` upon successful login.
49
+ * @throws `AuthServerError` - Always throws this error as login is not supported in this implementation.
50
+ *
51
+ * @remarks
52
+ * This method is not supported and is intended to be overridden by `AuthProviderInteractive`.
53
+ */
54
+ async login(_options) {
55
+ throw new AuthServerError('Login not supported, use AuthProviderInteractive instead');
56
+ }
57
+ /**
58
+ * Logs out the user by clearing the token cache and removing all accounts.
59
+ *
60
+ * This method retrieves all accounts from the token cache and removes them
61
+ * individually. Afterward, it clears the entire cache to ensure no residual
62
+ * authentication data remains.
63
+ *
64
+ * @returns A promise that resolves when the logout process is complete.
65
+ */
66
+ async logout() {
67
+ const cache = this._client.getTokenCache();
68
+ const accounts = await cache.getAllAccounts();
69
+ for (const account of accounts) {
70
+ await cache.removeAccount(account);
71
+ }
72
+ this._client.clearCache();
73
+ }
74
+ /**
75
+ * Acquires an authentication token for the specified scopes.
76
+ *
77
+ * This method first attempts to acquire a token silently using the accounts
78
+ * available in the token cache. If no accounts are found and interactive login
79
+ * is allowed, it initiates an interactive login flow. If interactive login is
80
+ * not allowed and no accounts are found, an error is thrown.
81
+ *
82
+ * @param scopes - An array of strings representing the scopes for which the token is requested.
83
+ * @param options - Optional parameters for token acquisition.
84
+ * @param options.interactive - A boolean indicating whether interactive login is allowed
85
+ * if no accounts are found in the cache. Defaults to `false`.
86
+ * @returns A promise that resolves to an `AuthenticationResult` containing the acquired token.
87
+ * @throws {@link NoAccountsError} If no accounts are found in the cache and interactive login is not allowed.
88
+ * @throws {@link SilentTokenAcquisitionError} If an error occurs during silent token acquisition.
89
+ */
90
+ async acquireToken(options) {
91
+ const account = await this.getAccount();
92
+ if (!account) {
93
+ throw new NoAccountsError('No accounts found in cache');
94
+ }
95
+ try {
96
+ const tokenResponse = await this._client.acquireTokenSilent({
97
+ scopes: options.scopes,
98
+ account,
99
+ });
100
+ return tokenResponse;
101
+ }
102
+ catch (error) {
103
+ throw new SilentTokenAcquisitionError('Error acquiring token', {
104
+ cause: error,
105
+ });
106
+ }
107
+ }
108
+ }
109
+ //# sourceMappingURL=AuthProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProvider.js","sourceRoot":"","sources":["../../src/AuthProvider.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,eAAe,EAAE,2BAA2B,EAAE,MAAM,YAAY,CAAC;AAI3F;;;;;;;;;;;;;;;GAeG;AACH,MAAM,OAAO,YAAY;IACD;IAAtB,YAAsB,OAAgC;QAAhC,YAAO,GAAP,OAAO,CAAyB;IAAG,CAAC;IAE1D;;;;OAIG;IACI,KAAK,CAAC,UAAU;QACrB,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,EAAE,CAAC;QACrD,OAAO,QAAQ,CAAC,CAAC,CAAC,IAAI,IAAI,CAAC;IAC7B,CAAC;IAED;;;;;;;OAOG;IACI,KAAK,CAAC,kBAAkB,CAAC,OAE/B;QACC,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,IAAI,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;QACzD,OAAO,WAAW,CAAC;IACrB,CAAC;IAED;;;;;;;;;OASG;IACI,KAAK,CAAC,KAAK,CAAC,QAA8B;QAC/C,MAAM,IAAI,eAAe,CAAC,0DAA0D,CAAC,CAAC;IACxF,CAAC;IAED;;;;;;;;OAQG;IACI,KAAK,CAAC,MAAM;QACjB,MAAM,KAAK,GAAG,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;QAC3C,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,cAAc,EAAE,CAAC;QAC9C,KAAK,MAAM,OAAO,IAAI,QAAQ,EAAE,CAAC;YAC/B,MAAM,KAAK,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;QACrC,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC;IAC5B,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACI,KAAK,CAAC,YAAY,CAAC,OAEzB;QACC,MAAM,OAAO,GAAG,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC;QACxC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,eAAe,CAAC,4BAA4B,CAAC,CAAC;QAC1D,CAAC;QAED,IAAI,CAAC;YACH,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,kBAAkB,CAAC;gBAC1D,MAAM,EAAE,OAAO,CAAC,MAAM;gBACtB,OAAO;aACR,CAAC,CAAC;YACH,OAAO,aAAa,CAAC;QACvB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,MAAM,IAAI,2BAA2B,CAAC,uBAAuB,EAAE;gBAC7D,KAAK,EAAE,KAAK;aACb,CAAC,CAAC;QACL,CAAC;IACH,CAAC;CACF"}
@@ -0,0 +1,88 @@
1
+ import { CryptoProvider, } from '@azure/msal-node';
2
+ import openBrowser from 'open';
3
+ import { createAuthServer } from './create-auth-server.js';
4
+ import { AuthProvider } from './AuthProvider.js';
5
+ /**
6
+ * Implementation of an interactive authentication provider for the Fusion MSAL Node module.
7
+ *
8
+ * Extends {@link AuthProvider} to support user-driven authentication flows using the authorization code flow with PKCE.
9
+ * This class opens the user's default browser for authentication and handles the response via a local server.
10
+ *
11
+ * This implementation is intended for scenarios where interactive login is required, such as CLI tools or development utilities.
12
+ *
13
+ * Developers extending this provider can customize the interactive flow, server handling, or PKCE logic as needed.
14
+ * Ensure that any changes remain consistent with the expected interface and security best practices.
15
+ *
16
+ * @see AuthProvider for non-interactive (silent) authentication flows.
17
+ * @see AuthProviderTokenOnly for token-only scenarios.
18
+ */
19
+ export class AuthProviderInteractive extends AuthProvider {
20
+ #options;
21
+ constructor(client, options) {
22
+ super(client);
23
+ this.#options = options;
24
+ }
25
+ /**
26
+ * Initiates the login process using the authorization code flow with PKCE.
27
+ *
28
+ * This method generates a PKCE code verifier and challenge to enhance security
29
+ * and prevent authorization code interception attacks. It constructs an
30
+ * authorization code URL, opens the default browser for user authentication,
31
+ * and starts a local server to handle the authentication response.
32
+ *
33
+ * @param scopes - An array of scopes that specify the permissions being requested.
34
+ * @returns A promise that resolves to an `AuthenticationResult` containing the
35
+ * authentication details upon successful login.
36
+ *
37
+ * @throws Will throw an error if the PKCE code generation, browser opening, or
38
+ * authentication server setup fails.
39
+ */
40
+ async login(options) {
41
+ const { scopes } = options;
42
+ const { port, onOpen } = this.#options.server;
43
+ // Generate a new PKCE code verifier and challenge
44
+ // This is used to enhance security in the authorization code flow
45
+ // by preventing authorization code interception attacks.
46
+ const cryptoProvider = new CryptoProvider();
47
+ const { verifier, challenge } = await cryptoProvider.generatePkceCodes();
48
+ const authCodeUrl = await this._client.getAuthCodeUrl({
49
+ scopes,
50
+ redirectUri: `http://localhost:${port}`,
51
+ codeChallenge: challenge,
52
+ codeChallengeMethod: 'S256',
53
+ });
54
+ // open default browser to authenticate
55
+ await openBrowser(authCodeUrl);
56
+ // callback to open the auth code url
57
+ if (onOpen)
58
+ onOpen(authCodeUrl);
59
+ return createAuthServer(this._client, scopes, {
60
+ codeVerifier: verifier,
61
+ port,
62
+ });
63
+ }
64
+ /**
65
+ * Acquires an authentication token for the specified scopes.
66
+ *
67
+ * This method first attempts to acquire a token silently using the accounts
68
+ * available in the token cache. If no accounts are found and interactive login
69
+ * is allowed, it initiates an interactive login flow. If interactive login is
70
+ * not allowed and no accounts are found, an error is thrown.
71
+ *
72
+ * @param scopes - An array of strings representing the scopes for which the token is requested.
73
+ * @param options - Optional parameters for token acquisition.
74
+ * @param options.interactive - A boolean indicating whether interactive login is allowed
75
+ * if no accounts are found in the cache. Defaults to `false`.
76
+ * @returns A promise that resolves to an `AuthenticationResult` containing the acquired token.
77
+ * @throws {@link NoAccountsError} If no accounts are found in the cache and interactive login is not allowed.
78
+ * @throws {@link SilentTokenAcquisitionError} If an error occurs during silent token acquisition.
79
+ */
80
+ async acquireToken(options) {
81
+ const { scopes } = options ?? { scopes: [] };
82
+ if ((await this.getAccount()) === null) {
83
+ return this.login({ scopes });
84
+ }
85
+ return super.acquireToken(options);
86
+ }
87
+ }
88
+ //# sourceMappingURL=AuthProviderInteractive.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthProviderInteractive.js","sourceRoot":"","sources":["../../src/AuthProviderInteractive.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,cAAc,GAGf,MAAM,kBAAkB,CAAC;AAE1B,OAAO,WAAW,MAAM,MAAM,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAC3D,OAAO,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAkBjD;;;;;;;;;;;;;GAaG;AACH,MAAM,OAAO,uBAAwB,SAAQ,YAAY;IACvD,QAAQ,CAAsB;IAE9B,YAAY,MAA+B,EAAE,OAA4B;QACvE,KAAK,CAAC,MAAM,CAAC,CAAC;QACd,IAAI,CAAC,QAAQ,GAAG,OAAO,CAAC;IAC1B,CAAC;IAED;;;;;;;;;;;;;;OAcG;IACI,KAAK,CAAC,KAAK,CAAC,OAA6B;QAC9C,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC;QAC3B,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC;QAE9C,kDAAkD;QAClD,kEAAkE;QAClE,yDAAyD;QACzD,MAAM,cAAc,GAAG,IAAI,cAAc,EAAE,CAAC;QAC5C,MAAM,EAAE,QAAQ,EAAE,SAAS,EAAE,GAAG,MAAM,cAAc,CAAC,iBAAiB,EAAE,CAAC;QACzE,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC;YACpD,MAAM;YACN,WAAW,EAAE,oBAAoB,IAAI,EAAE;YACvC,aAAa,EAAE,SAAS;YACxB,mBAAmB,EAAE,MAAM;SAC5B,CAAC,CAAC;QAEH,uCAAuC;QACvC,MAAM,WAAW,CAAC,WAAW,CAAC,CAAC;QAE/B,qCAAqC;QACrC,IAAI,MAAM;YAAE,MAAM,CAAC,WAAW,CAAC,CAAC;QAEhC,OAAO,gBAAgB,CAAC,IAAI,CAAC,OAAO,EAAE,MAAM,EAAE;YAC5C,YAAY,EAAE,QAAQ;YACtB,IAAI;SACL,CAAC,CAAC;IACL,CAAC;IAED;;;;;;;;;;;;;;;OAeG;IACI,KAAK,CAAC,YAAY,CAAC,OAEzB;QACC,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,CAAC;QAC7C,IAAI,CAAC,MAAM,IAAI,CAAC,UAAU,EAAE,CAAC,KAAK,IAAI,EAAE,CAAC;YACvC,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;QAChC,CAAC;QACD,OAAO,KAAK,CAAC,YAAY,CAAC,OAAO,CAAC,CAAC;IACrC,CAAC;CACF"}
@@ -0,0 +1,50 @@
1
+ /**
2
+ * Implementation of an authentication provider that supplies a static, pre-obtained access token.
3
+ *
4
+ * This class implements {@link IAuthProvider} and is intended for scenarios where authentication
5
+ * is handled externally and a token is provided directly (e.g., CI/CD pipelines, automation, or service accounts).
6
+ *
7
+ * Login and logout operations are not supported and will always throw errors if called.
8
+ *
9
+ * @see AuthProvider for silent authentication using cached accounts and MSAL flows.
10
+ * @see AuthProviderInteractive for interactive, user-driven authentication flows.
11
+ */
12
+ export class AuthTokenProvider {
13
+ #accessToken;
14
+ constructor(token) {
15
+ this.#accessToken = token;
16
+ }
17
+ /**
18
+ * Not supported in token-only mode. Always throws an error if called.
19
+ *
20
+ * This provider is designed for scenarios where authentication is handled externally
21
+ * and a static token is supplied. Login flows are not possible in this context.
22
+ *
23
+ * @throws Error Always throws to indicate login is not supported.
24
+ */
25
+ login() {
26
+ throw new Error('Method not supported in token mode');
27
+ }
28
+ /**
29
+ * Not supported in token-only mode. Always throws an error if called.
30
+ *
31
+ * Since this provider does not manage user sessions or accounts, logout is not applicable.
32
+ *
33
+ * @throws Error Always throws to indicate logout is not supported.
34
+ */
35
+ logout() {
36
+ throw new Error('Method not supported in token mode');
37
+ }
38
+ /**
39
+ * Returns the pre-obtained access token supplied to the provider.
40
+ *
41
+ * This is the only supported operation for this provider. No token refresh or acquisition logic is performed.
42
+ *
43
+ * @returns The static access token as a string.
44
+ */
45
+ async acquireAccessToken() {
46
+ return this.#accessToken;
47
+ }
48
+ }
49
+ export default AuthTokenProvider;
50
+ //# sourceMappingURL=AuthTokenProvider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AuthTokenProvider.js","sourceRoot":"","sources":["../../src/AuthTokenProvider.ts"],"names":[],"mappings":"AAGA;;;;;;;;;;GAUG;AACH,MAAM,OAAO,iBAAiB;IAC5B,YAAY,CAAS;IACrB,YAAY,KAAa;QACvB,IAAI,CAAC,YAAY,GAAG,KAAK,CAAC;IAC5B,CAAC;IAED;;;;;;;OAOG;IACH,KAAK;QACH,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;OAMG;IACH,MAAM;QACJ,MAAM,IAAI,KAAK,CAAC,oCAAoC,CAAC,CAAC;IACxD,CAAC;IAED;;;;;;OAMG;IACH,KAAK,CAAC,kBAAkB;QACtB,OAAO,IAAI,CAAC,YAAY,CAAC;IAC3B,CAAC;CACF;AAED,eAAe,iBAAiB,CAAC"}