@equinor/fusion-framework-module-msal-node 4.0.2 → 4.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.
- package/CHANGELOG.md +10 -0
- package/dist/esm/AuthConfigurator.js +17 -0
- package/dist/esm/AuthConfigurator.js.map +1 -1
- package/dist/esm/AuthProviderDeviceCode.js +94 -0
- package/dist/esm/AuthProviderDeviceCode.js.map +1 -0
- package/dist/esm/AuthProviderInteractive.js +9 -1
- package/dist/esm/AuthProviderInteractive.js.map +1 -1
- package/dist/esm/index.js +3 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/module.js +5 -0
- package/dist/esm/module.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/AuthConfigurator.d.ts +9 -0
- package/dist/types/AuthConfigurator.interface.d.ts +61 -7
- package/dist/types/AuthProviderDeviceCode.d.ts +68 -0
- package/dist/types/index.d.ts +3 -1
- package/dist/types/module.d.ts +1 -0
- package/dist/types/version.d.ts +1 -1
- package/package.json +1 -1
- package/src/AuthConfigurator.interface.ts +67 -7
- package/src/AuthConfigurator.ts +19 -0
- package/src/AuthProviderDeviceCode.ts +109 -0
- package/src/AuthProviderInteractive.ts +8 -1
- package/src/index.ts +4 -1
- package/src/module.ts +7 -0
- package/src/version.ts +1 -1
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DeviceCodeRequest } from '@azure/msal-node';
|
|
1
2
|
import { BaseConfigBuilder, type ConfigBuilderCallbackArgs } from '@equinor/fusion-framework-module';
|
|
2
3
|
import type { AuthConfig } from './AuthConfigurator.interface.js';
|
|
3
4
|
/**
|
|
@@ -59,6 +60,14 @@ export declare class AuthConfigurator extends BaseConfigBuilder<AuthConfig> {
|
|
|
59
60
|
* @param token - The static access token string.
|
|
60
61
|
*/
|
|
61
62
|
setAccessToken(token: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Sets the callback invoked with the device code response during `device_code` authentication.
|
|
65
|
+
*
|
|
66
|
+
* If not set, the default behaviour is `console.log(response.message)`.
|
|
67
|
+
*
|
|
68
|
+
* @param callback - The callback, or `undefined` to restore the default handler.
|
|
69
|
+
*/
|
|
70
|
+
setDeviceCodeCallback(callback: DeviceCodeRequest['deviceCodeCallback'] | undefined): void;
|
|
62
71
|
/**
|
|
63
72
|
* Prepares and finalizes the authentication configuration before validation and use.
|
|
64
73
|
*
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PublicClientApplication } from '@azure/msal-node';
|
|
1
|
+
import type { DeviceCodeRequest, PublicClientApplication } from '@azure/msal-node';
|
|
2
2
|
import type { IAuthProvider } from './AuthProvider.interface.js';
|
|
3
3
|
/**
|
|
4
4
|
* Represents the configuration for authentication in "token only" mode.
|
|
@@ -48,15 +48,39 @@ type AuthConfigInteractiveMode = {
|
|
|
48
48
|
accessToken?: never;
|
|
49
49
|
parent?: IAuthProvider;
|
|
50
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Configuration type for the device code authentication mode.
|
|
53
|
+
*
|
|
54
|
+
* In this mode the user is shown a short code and a URL (`https://microsoft.com/devicelogin`).
|
|
55
|
+
* They open the URL on any device, enter the code, and authenticate there.
|
|
56
|
+
* No local HTTP server is required, making this the recommended mode for CLI tools.
|
|
57
|
+
*
|
|
58
|
+
* @property mode - Specifies the authentication mode as `'device_code'`.
|
|
59
|
+
* @property client - An instance of `PublicClientApplication` used for authentication.
|
|
60
|
+
* @property deviceCodeCallback - Optional callback invoked with the device code response.
|
|
61
|
+
* Receives the full {@link DeviceCodeRequest.deviceCodeCallback} response containing
|
|
62
|
+
* `userCode`, `verificationUri`, `message`, and expiry details.
|
|
63
|
+
* Defaults to printing `response.message` to `console.log`.
|
|
64
|
+
* @property parent - An optional parent `IAuthProvider` instance for delegation.
|
|
65
|
+
*/
|
|
66
|
+
type AuthConfigDeviceCodeMode = {
|
|
67
|
+
mode: 'device_code';
|
|
68
|
+
client: PublicClientApplication;
|
|
69
|
+
deviceCodeCallback?: DeviceCodeRequest['deviceCodeCallback'];
|
|
70
|
+
server?: never;
|
|
71
|
+
accessToken?: never;
|
|
72
|
+
parent?: IAuthProvider;
|
|
73
|
+
};
|
|
51
74
|
/**
|
|
52
75
|
* Represents the configuration options for authentication.
|
|
53
76
|
*
|
|
54
|
-
* This type is a union of
|
|
55
|
-
* - `AuthConfigInteractiveMode`:
|
|
56
|
-
* - `AuthConfigSilentMode`:
|
|
57
|
-
* - `AuthConfigTokenMode`:
|
|
77
|
+
* This type is a union of four different authentication modes:
|
|
78
|
+
* - `AuthConfigInteractiveMode`: Browser-based login with a local callback server.
|
|
79
|
+
* - `AuthConfigSilentMode`: Silent authentication using cached/refreshed tokens.
|
|
80
|
+
* - `AuthConfigTokenMode`: Static pre-obtained token passthrough (CI/CD, automation).
|
|
81
|
+
* - `AuthConfigDeviceCodeMode`: Device code flow — prints a code for the user to enter at a URL. Recommended for CLI tools.
|
|
58
82
|
*/
|
|
59
|
-
export type AuthConfig = AuthConfigInteractiveMode | AuthConfigSilentMode | AuthConfigTokenMode;
|
|
83
|
+
export type AuthConfig = AuthConfigInteractiveMode | AuthConfigSilentMode | AuthConfigTokenMode | AuthConfigDeviceCodeMode;
|
|
60
84
|
/**
|
|
61
85
|
* Interface for configuring authentication settings for the MSAL Node module.
|
|
62
86
|
*
|
|
@@ -67,11 +91,20 @@ export type AuthConfig = AuthConfigInteractiveMode | AuthConfigSilentMode | Auth
|
|
|
67
91
|
* - `token_only`: Use a pre-obtained access token (e.g., for CI/CD or automation).
|
|
68
92
|
* - `silent`: Use MSAL's silent authentication with a configured client (for background services or cached tokens).
|
|
69
93
|
* - `interactive`: Use MSAL's interactive authentication, typically for CLI tools or development, with a local server for browser-based login.
|
|
94
|
+
* - `device_code`: Use MSAL's device code flow — prints a code for the user to enter at `https://microsoft.com/devicelogin`. No local server required. **Recommended for CLI tools.**
|
|
70
95
|
*
|
|
71
96
|
* Consumers should use the provided methods to configure the module according to their use case.
|
|
72
97
|
* Maintainers should ensure that new authentication flows or configuration options are exposed via this interface for consistency.
|
|
73
98
|
*
|
|
74
99
|
* @example
|
|
100
|
+
* // --- Device code mode (recommended for CLI tools) ---
|
|
101
|
+
* ```ts
|
|
102
|
+
* builder.setMode('device_code');
|
|
103
|
+
* builder.setClientConfig('your-tenant-id', 'your-client-id');
|
|
104
|
+
* // Optional: customise the output shown to the user
|
|
105
|
+
* builder.setDeviceCodeCallback((response) => console.log(response.message));
|
|
106
|
+
* ```
|
|
107
|
+
*
|
|
75
108
|
* // --- Interactive mode (browser login, local server) ---
|
|
76
109
|
* ```ts
|
|
77
110
|
* builder.setMode('interactive');
|
|
@@ -97,7 +130,7 @@ export interface IAuthConfigurator {
|
|
|
97
130
|
/**
|
|
98
131
|
* Sets the authentication mode for the module.
|
|
99
132
|
*
|
|
100
|
-
* @param mode - The authentication mode to use: 'token_only'
|
|
133
|
+
* @param mode - The authentication mode to use: `'token_only'`, `'silent'`, `'interactive'`, or `'device_code'`.
|
|
101
134
|
*
|
|
102
135
|
* Consumers: Call this first to define the overall authentication strategy.
|
|
103
136
|
* Maintainers: Add new modes here if supporting additional auth flows.
|
|
@@ -149,5 +182,26 @@ export interface IAuthConfigurator {
|
|
|
149
182
|
* Maintainers: Ensure this is securely handled and not mixed with other modes.
|
|
150
183
|
*/
|
|
151
184
|
setAccessToken(token: string): void;
|
|
185
|
+
/**
|
|
186
|
+
* Sets a callback invoked with the device code response during `device_code` authentication.
|
|
187
|
+
*
|
|
188
|
+
* The callback receives a `DeviceCodeResponse` containing `userCode`, `verificationUri`,
|
|
189
|
+
* `message`, and expiry information. Typically used to display the code and URL to the user.
|
|
190
|
+
*
|
|
191
|
+
* If not set, the default behaviour is to print `response.message` to `console.log`.
|
|
192
|
+
*
|
|
193
|
+
* @param callback - The callback function, or `undefined` to restore the default.
|
|
194
|
+
*
|
|
195
|
+
* @example
|
|
196
|
+
* ```ts
|
|
197
|
+
* builder.setMode('device_code');
|
|
198
|
+
* builder.setClientConfig('your-tenant-id', 'your-client-id');
|
|
199
|
+
* builder.setDeviceCodeCallback((response) => {
|
|
200
|
+
* console.log(`\nAuthenticate at: ${response.verificationUri}`);
|
|
201
|
+
* console.log(`Enter code: ${response.userCode}\n`);
|
|
202
|
+
* });
|
|
203
|
+
* ```
|
|
204
|
+
*/
|
|
205
|
+
setDeviceCodeCallback(callback: DeviceCodeRequest['deviceCodeCallback'] | undefined): void;
|
|
152
206
|
}
|
|
153
207
|
export {};
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
import type { DeviceCodeRequest } from '@azure/msal-node';
|
|
2
|
+
import type { AuthenticationResult, PublicClientApplication } from '@azure/msal-node';
|
|
3
|
+
import { AuthProvider } from './AuthProvider.js';
|
|
4
|
+
/**
|
|
5
|
+
* Authentication provider that uses the OAuth 2.0 device code flow.
|
|
6
|
+
*
|
|
7
|
+
* When an access token cannot be acquired silently, the provider calls
|
|
8
|
+
* `acquireTokenByDeviceCode` and invokes `deviceCodeCallback` with the
|
|
9
|
+
* response containing `userCode`, `verificationUri`, and `message`.
|
|
10
|
+
* The user opens the URL on any device, enters the code, and authenticates.
|
|
11
|
+
* No local HTTP server is required, making this the recommended mode for CLI tools.
|
|
12
|
+
*
|
|
13
|
+
* @example
|
|
14
|
+
* ```ts
|
|
15
|
+
* const provider = new AuthProviderDeviceCode(msalClient, {
|
|
16
|
+
* deviceCodeCallback: (response) => console.log(response.message),
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @see AuthProviderInteractive - Browser-based login with a local callback server.
|
|
21
|
+
* @see AuthProvider - Silent-only provider (base class).
|
|
22
|
+
*/
|
|
23
|
+
export declare class AuthProviderDeviceCode extends AuthProvider {
|
|
24
|
+
#private;
|
|
25
|
+
/**
|
|
26
|
+
* Creates an instance of `AuthProviderDeviceCode`.
|
|
27
|
+
*
|
|
28
|
+
* @param client - The MSAL `PublicClientApplication` to use for token acquisition.
|
|
29
|
+
* @param options - Configuration options.
|
|
30
|
+
* @param options.deviceCodeCallback - Callback invoked with the device code response.
|
|
31
|
+
* Defaults to printing `response.message` to `console.log`.
|
|
32
|
+
*/
|
|
33
|
+
constructor(client: PublicClientApplication, options?: {
|
|
34
|
+
deviceCodeCallback?: DeviceCodeRequest['deviceCodeCallback'];
|
|
35
|
+
});
|
|
36
|
+
/**
|
|
37
|
+
* Acquires an access token for the specified scopes.
|
|
38
|
+
*
|
|
39
|
+
* First attempts silent acquisition using the cached account.
|
|
40
|
+
* If that fails (e.g. no account or new resource requiring consent),
|
|
41
|
+
* falls back to the device code flow — invoking `deviceCodeCallback` so
|
|
42
|
+
* the user can authenticate on any device.
|
|
43
|
+
*
|
|
44
|
+
* @param options - Token request options.
|
|
45
|
+
* @param options.request.scopes - OAuth 2.0 scopes to request.
|
|
46
|
+
* @returns A promise resolving to an `AuthenticationResult`.
|
|
47
|
+
* @throws {@link SilentTokenAcquisitionError} If device code acquisition also fails.
|
|
48
|
+
*/
|
|
49
|
+
acquireToken(options: {
|
|
50
|
+
request: {
|
|
51
|
+
scopes: string[];
|
|
52
|
+
};
|
|
53
|
+
}): Promise<AuthenticationResult>;
|
|
54
|
+
/**
|
|
55
|
+
* Initiates the device code login flow explicitly.
|
|
56
|
+
*
|
|
57
|
+
* This is equivalent to calling `acquireToken` and is provided to satisfy
|
|
58
|
+
* the `IAuthProvider` contract.
|
|
59
|
+
*
|
|
60
|
+
* @param options - Login options containing the requested scopes.
|
|
61
|
+
* @returns A promise resolving to an `AuthenticationResult`.
|
|
62
|
+
*/
|
|
63
|
+
login(options: {
|
|
64
|
+
request: {
|
|
65
|
+
scopes: string[];
|
|
66
|
+
};
|
|
67
|
+
}): Promise<AuthenticationResult>;
|
|
68
|
+
}
|
package/dist/types/index.d.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* `@equinor/fusion-framework-module-msal-node` provides Azure AD authentication
|
|
3
3
|
* for Node.js applications using Microsoft's MSAL library.
|
|
4
4
|
*
|
|
5
|
-
* Supports
|
|
5
|
+
* Supports four authentication modes:
|
|
6
6
|
* - **interactive** — browser-based login with a local callback server (CLI tools, development)
|
|
7
|
+
* - **device_code** — prints a short code for the user to enter at a URL; no server needed (**recommended for CLI tools**)
|
|
7
8
|
* - **silent** — cached credential reuse without user interaction (background services)
|
|
8
9
|
* - **token_only** — static pre-obtained token passthrough (CI/CD, automation)
|
|
9
10
|
*
|
|
@@ -20,6 +21,7 @@
|
|
|
20
21
|
* @packageDocumentation
|
|
21
22
|
*/
|
|
22
23
|
export { AuthProvider } from './AuthProvider.js';
|
|
24
|
+
export { AuthProviderDeviceCode } from './AuthProviderDeviceCode.js';
|
|
23
25
|
export type { IAuthProvider } from './AuthProvider.interface.js';
|
|
24
26
|
export type { IAuthConfigurator, AuthConfig } from './AuthConfigurator.interface.js';
|
|
25
27
|
export { module as authModule, type MsalNodeModule } from './module.js';
|
package/dist/types/module.d.ts
CHANGED
|
@@ -11,6 +11,7 @@ import type { IAuthProvider } from './AuthProvider.interface.js';
|
|
|
11
11
|
*
|
|
12
12
|
* - In `token_only` mode, a static access token is used (see {@link AuthTokenProvider}).
|
|
13
13
|
* - In `interactive` mode, the user is prompted via a local server and browser (see {@link AuthProviderInteractive}).
|
|
14
|
+
* - In `device_code` mode, the user authenticates by entering a code at a URL on any device (see {@link AuthProviderDeviceCode}). Recommended for CLI tools.
|
|
14
15
|
* - In all other cases, silent authentication is attempted using cached credentials (see {@link AuthProvider}).
|
|
15
16
|
*
|
|
16
17
|
* @see AuthProvider
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "4.0
|
|
1
|
+
export declare const version = "4.1.0";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@equinor/fusion-framework-module-msal-node",
|
|
3
|
-
"version": "4.0
|
|
3
|
+
"version": "4.1.0",
|
|
4
4
|
"description": "Fusion Framework module for secure Azure AD authentication in Node.js using MSAL. Supports interactive, silent, and token-only authentication modes with encrypted token storage.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/esm/index.js",
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { PublicClientApplication } from '@azure/msal-node';
|
|
1
|
+
import type { DeviceCodeRequest, PublicClientApplication } from '@azure/msal-node';
|
|
2
2
|
|
|
3
3
|
import type { IAuthProvider } from './AuthProvider.interface.js';
|
|
4
4
|
|
|
@@ -53,15 +53,44 @@ type AuthConfigInteractiveMode = {
|
|
|
53
53
|
parent?: IAuthProvider;
|
|
54
54
|
};
|
|
55
55
|
|
|
56
|
+
/**
|
|
57
|
+
* Configuration type for the device code authentication mode.
|
|
58
|
+
*
|
|
59
|
+
* In this mode the user is shown a short code and a URL (`https://microsoft.com/devicelogin`).
|
|
60
|
+
* They open the URL on any device, enter the code, and authenticate there.
|
|
61
|
+
* No local HTTP server is required, making this the recommended mode for CLI tools.
|
|
62
|
+
*
|
|
63
|
+
* @property mode - Specifies the authentication mode as `'device_code'`.
|
|
64
|
+
* @property client - An instance of `PublicClientApplication` used for authentication.
|
|
65
|
+
* @property deviceCodeCallback - Optional callback invoked with the device code response.
|
|
66
|
+
* Receives the full {@link DeviceCodeRequest.deviceCodeCallback} response containing
|
|
67
|
+
* `userCode`, `verificationUri`, `message`, and expiry details.
|
|
68
|
+
* Defaults to printing `response.message` to `console.log`.
|
|
69
|
+
* @property parent - An optional parent `IAuthProvider` instance for delegation.
|
|
70
|
+
*/
|
|
71
|
+
type AuthConfigDeviceCodeMode = {
|
|
72
|
+
mode: 'device_code';
|
|
73
|
+
client: PublicClientApplication;
|
|
74
|
+
deviceCodeCallback?: DeviceCodeRequest['deviceCodeCallback'];
|
|
75
|
+
server?: never;
|
|
76
|
+
accessToken?: never;
|
|
77
|
+
parent?: IAuthProvider;
|
|
78
|
+
};
|
|
79
|
+
|
|
56
80
|
/**
|
|
57
81
|
* Represents the configuration options for authentication.
|
|
58
82
|
*
|
|
59
|
-
* This type is a union of
|
|
60
|
-
* - `AuthConfigInteractiveMode`:
|
|
61
|
-
* - `AuthConfigSilentMode`:
|
|
62
|
-
* - `AuthConfigTokenMode`:
|
|
83
|
+
* This type is a union of four different authentication modes:
|
|
84
|
+
* - `AuthConfigInteractiveMode`: Browser-based login with a local callback server.
|
|
85
|
+
* - `AuthConfigSilentMode`: Silent authentication using cached/refreshed tokens.
|
|
86
|
+
* - `AuthConfigTokenMode`: Static pre-obtained token passthrough (CI/CD, automation).
|
|
87
|
+
* - `AuthConfigDeviceCodeMode`: Device code flow — prints a code for the user to enter at a URL. Recommended for CLI tools.
|
|
63
88
|
*/
|
|
64
|
-
export type AuthConfig =
|
|
89
|
+
export type AuthConfig =
|
|
90
|
+
| AuthConfigInteractiveMode
|
|
91
|
+
| AuthConfigSilentMode
|
|
92
|
+
| AuthConfigTokenMode
|
|
93
|
+
| AuthConfigDeviceCodeMode;
|
|
65
94
|
|
|
66
95
|
/**
|
|
67
96
|
* Interface for configuring authentication settings for the MSAL Node module.
|
|
@@ -73,11 +102,20 @@ export type AuthConfig = AuthConfigInteractiveMode | AuthConfigSilentMode | Auth
|
|
|
73
102
|
* - `token_only`: Use a pre-obtained access token (e.g., for CI/CD or automation).
|
|
74
103
|
* - `silent`: Use MSAL's silent authentication with a configured client (for background services or cached tokens).
|
|
75
104
|
* - `interactive`: Use MSAL's interactive authentication, typically for CLI tools or development, with a local server for browser-based login.
|
|
105
|
+
* - `device_code`: Use MSAL's device code flow — prints a code for the user to enter at `https://microsoft.com/devicelogin`. No local server required. **Recommended for CLI tools.**
|
|
76
106
|
*
|
|
77
107
|
* Consumers should use the provided methods to configure the module according to their use case.
|
|
78
108
|
* Maintainers should ensure that new authentication flows or configuration options are exposed via this interface for consistency.
|
|
79
109
|
*
|
|
80
110
|
* @example
|
|
111
|
+
* // --- Device code mode (recommended for CLI tools) ---
|
|
112
|
+
* ```ts
|
|
113
|
+
* builder.setMode('device_code');
|
|
114
|
+
* builder.setClientConfig('your-tenant-id', 'your-client-id');
|
|
115
|
+
* // Optional: customise the output shown to the user
|
|
116
|
+
* builder.setDeviceCodeCallback((response) => console.log(response.message));
|
|
117
|
+
* ```
|
|
118
|
+
*
|
|
81
119
|
* // --- Interactive mode (browser login, local server) ---
|
|
82
120
|
* ```ts
|
|
83
121
|
* builder.setMode('interactive');
|
|
@@ -103,7 +141,7 @@ export interface IAuthConfigurator {
|
|
|
103
141
|
/**
|
|
104
142
|
* Sets the authentication mode for the module.
|
|
105
143
|
*
|
|
106
|
-
* @param mode - The authentication mode to use: 'token_only'
|
|
144
|
+
* @param mode - The authentication mode to use: `'token_only'`, `'silent'`, `'interactive'`, or `'device_code'`.
|
|
107
145
|
*
|
|
108
146
|
* Consumers: Call this first to define the overall authentication strategy.
|
|
109
147
|
* Maintainers: Add new modes here if supporting additional auth flows.
|
|
@@ -160,4 +198,26 @@ export interface IAuthConfigurator {
|
|
|
160
198
|
* Maintainers: Ensure this is securely handled and not mixed with other modes.
|
|
161
199
|
*/
|
|
162
200
|
setAccessToken(token: string): void;
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Sets a callback invoked with the device code response during `device_code` authentication.
|
|
204
|
+
*
|
|
205
|
+
* The callback receives a `DeviceCodeResponse` containing `userCode`, `verificationUri`,
|
|
206
|
+
* `message`, and expiry information. Typically used to display the code and URL to the user.
|
|
207
|
+
*
|
|
208
|
+
* If not set, the default behaviour is to print `response.message` to `console.log`.
|
|
209
|
+
*
|
|
210
|
+
* @param callback - The callback function, or `undefined` to restore the default.
|
|
211
|
+
*
|
|
212
|
+
* @example
|
|
213
|
+
* ```ts
|
|
214
|
+
* builder.setMode('device_code');
|
|
215
|
+
* builder.setClientConfig('your-tenant-id', 'your-client-id');
|
|
216
|
+
* builder.setDeviceCodeCallback((response) => {
|
|
217
|
+
* console.log(`\nAuthenticate at: ${response.verificationUri}`);
|
|
218
|
+
* console.log(`Enter code: ${response.userCode}\n`);
|
|
219
|
+
* });
|
|
220
|
+
* ```
|
|
221
|
+
*/
|
|
222
|
+
setDeviceCodeCallback(callback: DeviceCodeRequest['deviceCodeCallback'] | undefined): void;
|
|
163
223
|
}
|
package/src/AuthConfigurator.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { PublicClientApplication } from '@azure/msal-node';
|
|
2
|
+
import type { DeviceCodeRequest } from '@azure/msal-node';
|
|
2
3
|
import {
|
|
3
4
|
BaseConfigBuilder,
|
|
4
5
|
type ConfigBuilderCallbackArgs,
|
|
@@ -95,6 +96,17 @@ export class AuthConfigurator extends BaseConfigBuilder<AuthConfig> {
|
|
|
95
96
|
this._set('accessToken', token);
|
|
96
97
|
}
|
|
97
98
|
|
|
99
|
+
/**
|
|
100
|
+
* Sets the callback invoked with the device code response during `device_code` authentication.
|
|
101
|
+
*
|
|
102
|
+
* If not set, the default behaviour is `console.log(response.message)`.
|
|
103
|
+
*
|
|
104
|
+
* @param callback - The callback, or `undefined` to restore the default handler.
|
|
105
|
+
*/
|
|
106
|
+
setDeviceCodeCallback(callback: DeviceCodeRequest['deviceCodeCallback'] | undefined) {
|
|
107
|
+
this._set('deviceCodeCallback', callback ?? undefined);
|
|
108
|
+
}
|
|
109
|
+
|
|
98
110
|
/**
|
|
99
111
|
* Prepares and finalizes the authentication configuration before validation and use.
|
|
100
112
|
*
|
|
@@ -159,6 +171,13 @@ export class AuthConfigurator extends BaseConfigBuilder<AuthConfig> {
|
|
|
159
171
|
}
|
|
160
172
|
break;
|
|
161
173
|
}
|
|
174
|
+
case 'device_code': {
|
|
175
|
+
// Device code mode requires a valid MSAL client instance
|
|
176
|
+
if (config.client instanceof PublicClientApplication === false) {
|
|
177
|
+
throw new Error('Client is required when mode is device_code');
|
|
178
|
+
}
|
|
179
|
+
break;
|
|
180
|
+
}
|
|
162
181
|
case 'token_only': {
|
|
163
182
|
// Token only mode requires a string access token
|
|
164
183
|
if (typeof config.accessToken !== 'string') {
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import type { DeviceCodeRequest } from '@azure/msal-node';
|
|
2
|
+
|
|
3
|
+
import type { AuthenticationResult, PublicClientApplication } from '@azure/msal-node';
|
|
4
|
+
|
|
5
|
+
import { AuthProvider } from './AuthProvider.js';
|
|
6
|
+
import { SilentTokenAcquisitionError } from './error.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Authentication provider that uses the OAuth 2.0 device code flow.
|
|
10
|
+
*
|
|
11
|
+
* When an access token cannot be acquired silently, the provider calls
|
|
12
|
+
* `acquireTokenByDeviceCode` and invokes `deviceCodeCallback` with the
|
|
13
|
+
* response containing `userCode`, `verificationUri`, and `message`.
|
|
14
|
+
* The user opens the URL on any device, enters the code, and authenticates.
|
|
15
|
+
* No local HTTP server is required, making this the recommended mode for CLI tools.
|
|
16
|
+
*
|
|
17
|
+
* @example
|
|
18
|
+
* ```ts
|
|
19
|
+
* const provider = new AuthProviderDeviceCode(msalClient, {
|
|
20
|
+
* deviceCodeCallback: (response) => console.log(response.message),
|
|
21
|
+
* });
|
|
22
|
+
* ```
|
|
23
|
+
*
|
|
24
|
+
* @see AuthProviderInteractive - Browser-based login with a local callback server.
|
|
25
|
+
* @see AuthProvider - Silent-only provider (base class).
|
|
26
|
+
*/
|
|
27
|
+
export class AuthProviderDeviceCode extends AuthProvider {
|
|
28
|
+
readonly #deviceCodeCallback: DeviceCodeRequest['deviceCodeCallback'];
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Creates an instance of `AuthProviderDeviceCode`.
|
|
32
|
+
*
|
|
33
|
+
* @param client - The MSAL `PublicClientApplication` to use for token acquisition.
|
|
34
|
+
* @param options - Configuration options.
|
|
35
|
+
* @param options.deviceCodeCallback - Callback invoked with the device code response.
|
|
36
|
+
* Defaults to printing `response.message` to `console.log`.
|
|
37
|
+
*/
|
|
38
|
+
constructor(
|
|
39
|
+
client: PublicClientApplication,
|
|
40
|
+
options?: {
|
|
41
|
+
deviceCodeCallback?: DeviceCodeRequest['deviceCodeCallback'];
|
|
42
|
+
},
|
|
43
|
+
) {
|
|
44
|
+
super(client);
|
|
45
|
+
this.#deviceCodeCallback =
|
|
46
|
+
options?.deviceCodeCallback ?? ((response) => console.log(response.message));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Acquires an access token for the specified scopes.
|
|
51
|
+
*
|
|
52
|
+
* First attempts silent acquisition using the cached account.
|
|
53
|
+
* If that fails (e.g. no account or new resource requiring consent),
|
|
54
|
+
* falls back to the device code flow — invoking `deviceCodeCallback` so
|
|
55
|
+
* the user can authenticate on any device.
|
|
56
|
+
*
|
|
57
|
+
* @param options - Token request options.
|
|
58
|
+
* @param options.request.scopes - OAuth 2.0 scopes to request.
|
|
59
|
+
* @returns A promise resolving to an `AuthenticationResult`.
|
|
60
|
+
* @throws {@link SilentTokenAcquisitionError} If device code acquisition also fails.
|
|
61
|
+
*/
|
|
62
|
+
public override async acquireToken(options: {
|
|
63
|
+
request: { scopes: string[] };
|
|
64
|
+
}): Promise<AuthenticationResult> {
|
|
65
|
+
// Attempt silent acquisition first (uses cached account / refresh token)
|
|
66
|
+
const account = await this.getAccount();
|
|
67
|
+
if (account) {
|
|
68
|
+
try {
|
|
69
|
+
return await this._client.acquireTokenSilent({
|
|
70
|
+
scopes: options.request.scopes,
|
|
71
|
+
account,
|
|
72
|
+
});
|
|
73
|
+
} catch {
|
|
74
|
+
// Silent failed — fall through to device code flow below
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Fall back to device code flow
|
|
79
|
+
try {
|
|
80
|
+
const result = await this._client.acquireTokenByDeviceCode({
|
|
81
|
+
scopes: options.request.scopes,
|
|
82
|
+
deviceCodeCallback: this.#deviceCodeCallback,
|
|
83
|
+
});
|
|
84
|
+
if (!result) {
|
|
85
|
+
throw new SilentTokenAcquisitionError('Device code flow returned no result');
|
|
86
|
+
}
|
|
87
|
+
return result;
|
|
88
|
+
} catch (error) {
|
|
89
|
+
throw new SilentTokenAcquisitionError('Device code token acquisition failed', {
|
|
90
|
+
cause: error,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Initiates the device code login flow explicitly.
|
|
97
|
+
*
|
|
98
|
+
* This is equivalent to calling `acquireToken` and is provided to satisfy
|
|
99
|
+
* the `IAuthProvider` contract.
|
|
100
|
+
*
|
|
101
|
+
* @param options - Login options containing the requested scopes.
|
|
102
|
+
* @returns A promise resolving to an `AuthenticationResult`.
|
|
103
|
+
*/
|
|
104
|
+
public override async login(options: {
|
|
105
|
+
request: { scopes: string[] };
|
|
106
|
+
}): Promise<AuthenticationResult> {
|
|
107
|
+
return this.acquireToken(options);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -134,6 +134,13 @@ export class AuthProviderInteractive extends AuthProvider {
|
|
|
134
134
|
if ((await this.getAccount()) === null) {
|
|
135
135
|
return this.login({ request: { scopes } });
|
|
136
136
|
}
|
|
137
|
-
|
|
137
|
+
try {
|
|
138
|
+
return await super.acquireToken(options);
|
|
139
|
+
} catch {
|
|
140
|
+
// Silent acquisition failed (e.g. no cached token for this resource/audience).
|
|
141
|
+
// Fall back to interactive login so the user only needs one browser session
|
|
142
|
+
// rather than seeing an unhandled error or a separate prompted login elsewhere.
|
|
143
|
+
return this.login({ request: { scopes } });
|
|
144
|
+
}
|
|
138
145
|
}
|
|
139
146
|
}
|
package/src/index.ts
CHANGED
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
* `@equinor/fusion-framework-module-msal-node` provides Azure AD authentication
|
|
3
3
|
* for Node.js applications using Microsoft's MSAL library.
|
|
4
4
|
*
|
|
5
|
-
* Supports
|
|
5
|
+
* Supports four authentication modes:
|
|
6
6
|
* - **interactive** — browser-based login with a local callback server (CLI tools, development)
|
|
7
|
+
* - **device_code** — prints a short code for the user to enter at a URL; no server needed (**recommended for CLI tools**)
|
|
7
8
|
* - **silent** — cached credential reuse without user interaction (background services)
|
|
8
9
|
* - **token_only** — static pre-obtained token passthrough (CI/CD, automation)
|
|
9
10
|
*
|
|
@@ -22,6 +23,8 @@
|
|
|
22
23
|
|
|
23
24
|
export { AuthProvider } from './AuthProvider.js';
|
|
24
25
|
|
|
26
|
+
export { AuthProviderDeviceCode } from './AuthProviderDeviceCode.js';
|
|
27
|
+
|
|
25
28
|
export type { IAuthProvider } from './AuthProvider.interface.js';
|
|
26
29
|
|
|
27
30
|
export type { IAuthConfigurator, AuthConfig } from './AuthConfigurator.interface.js';
|
package/src/module.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { AuthConfigurator } from './AuthConfigurator.js';
|
|
|
3
3
|
import { AuthProvider } from './AuthProvider.js';
|
|
4
4
|
import { AuthTokenProvider } from './AuthTokenProvider.js';
|
|
5
5
|
import { AuthProviderInteractive } from './AuthProviderInteractive.js';
|
|
6
|
+
import { AuthProviderDeviceCode } from './AuthProviderDeviceCode.js';
|
|
6
7
|
import type { IAuthProvider } from './AuthProvider.interface.js';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -15,6 +16,7 @@ import type { IAuthProvider } from './AuthProvider.interface.js';
|
|
|
15
16
|
*
|
|
16
17
|
* - In `token_only` mode, a static access token is used (see {@link AuthTokenProvider}).
|
|
17
18
|
* - In `interactive` mode, the user is prompted via a local server and browser (see {@link AuthProviderInteractive}).
|
|
19
|
+
* - In `device_code` mode, the user authenticates by entering a code at a URL on any device (see {@link AuthProviderDeviceCode}). Recommended for CLI tools.
|
|
18
20
|
* - In all other cases, silent authentication is attempted using cached credentials (see {@link AuthProvider}).
|
|
19
21
|
*
|
|
20
22
|
* @see AuthProvider
|
|
@@ -43,6 +45,11 @@ export const module: MsalNodeModule = {
|
|
|
43
45
|
return new AuthProviderInteractive(client, { server });
|
|
44
46
|
}
|
|
45
47
|
|
|
48
|
+
case 'device_code': {
|
|
49
|
+
const { client, deviceCodeCallback } = config;
|
|
50
|
+
return new AuthProviderDeviceCode(client, { deviceCodeCallback });
|
|
51
|
+
}
|
|
52
|
+
|
|
46
53
|
default:
|
|
47
54
|
return new AuthProvider(config.client);
|
|
48
55
|
}
|
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '4.0
|
|
2
|
+
export const version = '4.1.0';
|