@equinor/fusion-framework-module-msal 10.0.1 → 11.0.0-next.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 +173 -0
- package/README.md +17 -368
- package/dist/esm/MsalClient.js +9 -0
- package/dist/esm/MsalClient.js.map +1 -1
- package/dist/esm/MsalConfigurator.js +178 -88
- package/dist/esm/MsalConfigurator.js.map +1 -1
- package/dist/esm/MsalProvider.js +20 -1
- package/dist/esm/MsalProvider.js.map +1 -1
- package/dist/esm/__tests__/MsalConfigurator.test.js +75 -0
- package/dist/esm/__tests__/MsalConfigurator.test.js.map +1 -1
- package/dist/esm/__tests__/create-proxy-provider.test.js +53 -0
- package/dist/esm/__tests__/create-proxy-provider.test.js.map +1 -0
- package/dist/esm/__tests__/mock/msal-mock.test.js +399 -0
- package/dist/esm/__tests__/mock/msal-mock.test.js.map +1 -0
- package/dist/esm/create-client-log-callback.js +1 -0
- package/dist/esm/create-client-log-callback.js.map +1 -1
- package/dist/esm/create-proxy-provider.js +5 -0
- package/dist/esm/create-proxy-provider.js.map +1 -1
- package/dist/esm/index.js +5 -0
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/mock/MsalMockClient.js +467 -0
- package/dist/esm/mock/MsalMockClient.js.map +1 -0
- package/dist/esm/mock/MsalMockConfigurator.js +187 -0
- package/dist/esm/mock/MsalMockConfigurator.js.map +1 -0
- package/dist/esm/mock/create-mock-token.js +60 -0
- package/dist/esm/mock/create-mock-token.js.map +1 -0
- package/dist/esm/mock/create-msal-mock-client.js +20 -0
- package/dist/esm/mock/create-msal-mock-client.js.map +1 -0
- package/dist/esm/mock/decode-jwt-segment.js +22 -0
- package/dist/esm/mock/decode-jwt-segment.js.map +1 -0
- package/dist/esm/mock/index.js +30 -0
- package/dist/esm/mock/index.js.map +1 -0
- package/dist/esm/mock/module.js +38 -0
- package/dist/esm/mock/module.js.map +1 -0
- package/dist/esm/module.js +3 -2
- package/dist/esm/module.js.map +1 -1
- package/dist/esm/msal-config-schema.js +37 -0
- package/dist/esm/msal-config-schema.js.map +1 -0
- package/dist/esm/telemetry-config-schema.js +16 -0
- package/dist/esm/telemetry-config-schema.js.map +1 -0
- package/dist/esm/util/normalize-uri.js.map +1 -1
- package/dist/esm/v2/Logger.js +173 -0
- package/dist/esm/v2/Logger.js.map +1 -0
- package/dist/esm/v2/create-proxy-client.js +6 -0
- package/dist/esm/v2/create-proxy-client.js.map +1 -1
- package/dist/esm/v2/create-proxy-provider.js +4 -0
- package/dist/esm/v2/create-proxy-provider.js.map +1 -1
- package/dist/esm/v2/types.js +3 -124
- package/dist/esm/v2/types.js.map +1 -1
- package/dist/esm/v4/create-proxy-provider.js +1 -0
- package/dist/esm/v4/create-proxy-provider.js.map +1 -1
- package/dist/esm/v4/types.js.map +1 -1
- package/dist/esm/version.js +1 -1
- package/dist/esm/version.js.map +1 -1
- package/dist/esm/versioning/VersionError.js.map +1 -1
- package/dist/esm/versioning/resolve-version.js +4 -1
- package/dist/esm/versioning/resolve-version.js.map +1 -1
- package/dist/tsconfig.tsbuildinfo +1 -1
- package/dist/types/MsalClient.d.ts +4 -0
- package/dist/types/MsalConfigurator.d.ts +97 -20
- package/dist/types/MsalProvider.d.ts +13 -0
- package/dist/types/__tests__/create-proxy-provider.test.d.ts +1 -0
- package/dist/types/__tests__/mock/msal-mock.test.d.ts +1 -0
- package/dist/types/create-proxy-provider.d.ts +3 -0
- package/dist/types/index.d.ts +5 -0
- package/dist/types/mock/MsalMockClient.d.ts +270 -0
- package/dist/types/mock/MsalMockConfigurator.d.ts +156 -0
- package/dist/types/mock/create-mock-token.d.ts +54 -0
- package/dist/types/mock/create-msal-mock-client.d.ts +14 -0
- package/dist/types/mock/decode-jwt-segment.d.ts +13 -0
- package/dist/types/mock/index.d.ts +29 -0
- package/dist/types/mock/module.d.ts +35 -0
- package/dist/types/msal-config-schema.d.ts +64 -0
- package/dist/types/static.d.ts +1 -1
- package/dist/types/telemetry-config-schema.d.ts +8 -0
- package/dist/types/v2/IAuthClient.interface.d.ts +2 -2
- package/dist/types/v2/Logger.d.ts +92 -0
- package/dist/types/v2/MsalProvider.interface.d.ts +1 -1
- package/dist/types/v2/types.d.ts +3 -49
- package/dist/types/v4/types.d.ts +1 -3
- package/dist/types/version.d.ts +1 -1
- package/docs/api-reference.md +85 -0
- package/docs/auth-code-flow.md +86 -0
- package/docs/migration-v2-to-v4.md +115 -0
- package/docs/testing.md +167 -0
- package/docs/troubleshooting.md +17 -0
- package/docs/version-management.md +67 -0
- package/package.json +14 -7
- package/src/MsalClient.ts +9 -0
- package/src/MsalConfigurator.ts +204 -115
- package/src/MsalProvider.ts +20 -1
- package/src/__tests__/MsalConfigurator.test.ts +106 -0
- package/src/__tests__/create-proxy-provider.test.ts +77 -0
- package/src/__tests__/mock/msal-mock.test.ts +544 -0
- package/src/create-client-log-callback.ts +1 -0
- package/src/create-proxy-provider.ts +5 -0
- package/src/index.ts +6 -0
- package/src/mock/MsalMockClient.ts +599 -0
- package/src/mock/MsalMockConfigurator.ts +241 -0
- package/src/mock/create-mock-token.ts +92 -0
- package/src/mock/create-msal-mock-client.ts +25 -0
- package/src/mock/decode-jwt-segment.ts +22 -0
- package/src/mock/index.ts +29 -0
- package/src/mock/module.ts +54 -0
- package/src/module.ts +3 -2
- package/src/msal-config-schema.ts +81 -0
- package/src/telemetry-config-schema.ts +25 -0
- package/src/v2/IAuthClient.interface.ts +2 -2
- package/src/v2/Logger.ts +204 -0
- package/src/v2/MsalProvider.interface.ts +1 -1
- package/src/v2/create-proxy-client.ts +6 -0
- package/src/v2/create-proxy-provider.ts +4 -0
- package/src/v2/types.ts +8 -158
- package/src/v4/create-proxy-provider.ts +1 -0
- package/src/v4/types.ts +3 -3
- package/src/version.ts +1 -1
- package/src/versioning/resolve-version.ts +4 -1
- package/vitest.config.ts +1 -1
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Claims that can be set on a generated mock token.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Mirrors the subset of Entra ID claims that Fusion applications read. Any
|
|
6
|
+
* additional claims are passed through unchanged.
|
|
7
|
+
*/
|
|
8
|
+
export interface MockTokenClaims {
|
|
9
|
+
/** Object ID of the signed-in user. */
|
|
10
|
+
oid?: string;
|
|
11
|
+
/** Display name of the signed-in user. */
|
|
12
|
+
name?: string;
|
|
13
|
+
/** UPN / email of the signed-in user. */
|
|
14
|
+
preferred_username?: string;
|
|
15
|
+
/** Tenant the token was issued for. */
|
|
16
|
+
tid?: string;
|
|
17
|
+
/** Audience — normally the client or resource the token is intended for. */
|
|
18
|
+
aud?: string;
|
|
19
|
+
/** Issuer. */
|
|
20
|
+
iss?: string;
|
|
21
|
+
/** Scopes granted, space-separated as in a real Entra ID token. */
|
|
22
|
+
scp?: string;
|
|
23
|
+
/** Issued-at, seconds since epoch. */
|
|
24
|
+
iat?: number;
|
|
25
|
+
/** Not-before, seconds since epoch. */
|
|
26
|
+
nbf?: number;
|
|
27
|
+
/** Expiry, seconds since epoch. */
|
|
28
|
+
exp?: number;
|
|
29
|
+
[claim: string]: unknown;
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Creates a structurally valid, unsigned JWT for use in tests.
|
|
33
|
+
*
|
|
34
|
+
* The token has three base64url segments and decodes to the supplied claims, so
|
|
35
|
+
* code that splits, decodes, or inspects token claims behaves exactly as it does
|
|
36
|
+
* in production. The signature segment is a fixed placeholder — the token is
|
|
37
|
+
* **not** cryptographically valid and will be rejected by any real service.
|
|
38
|
+
*
|
|
39
|
+
* @remarks
|
|
40
|
+
* Timestamps default to a fixed issue time and a one-hour lifetime so repeated
|
|
41
|
+
* runs produce byte-identical tokens. A test that needs an expired token can set
|
|
42
|
+
* `exp` in the past.
|
|
43
|
+
*
|
|
44
|
+
* @param claims - Claims to embed in the token payload.
|
|
45
|
+
* @returns An unsigned JWT string in `header.payload.signature` form.
|
|
46
|
+
*
|
|
47
|
+
* @example
|
|
48
|
+
* ```typescript
|
|
49
|
+
* const token = createMockToken({ name: 'Test User', scp: 'Files.Read' });
|
|
50
|
+
* const [, payload] = token.split('.');
|
|
51
|
+
* JSON.parse(decodeJwtSegment(payload)).name; // 'Test User'
|
|
52
|
+
* ```
|
|
53
|
+
*/
|
|
54
|
+
export declare const createMockToken: (claims?: MockTokenClaims) => string;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { IMsalClient } from '../MsalClient.interface';
|
|
2
|
+
import type { MsalClientConfig } from '../MsalClient';
|
|
3
|
+
import { type MsalMockUser } from './MsalMockClient';
|
|
4
|
+
/**
|
|
5
|
+
* Convenience helper that creates a mock client instance.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* The class form is preferred for a more familiar configuration pattern.
|
|
9
|
+
*
|
|
10
|
+
* @param config - The same client configuration the real client is built from.
|
|
11
|
+
* @param user - Optional user to sign in, applied after construction.
|
|
12
|
+
* @returns A client that resolves tokens in-process.
|
|
13
|
+
*/
|
|
14
|
+
export declare const createMsalMockClient: (config: MsalClientConfig, user?: MsalMockUser) => IMsalClient;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Decodes a base64url segment of a {@link createMockToken} JWT back to its JSON string.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Plain `atob` alone mangles non-ASCII claims: it treats its output as latin1,
|
|
6
|
+
* while segments are UTF-8 encoded. This reverses that encoding and also
|
|
7
|
+
* restores the standard base64 alphabet/padding `atob` expects.
|
|
8
|
+
*
|
|
9
|
+
* @param segment - A base64url segment, e.g. from splitting a JWT on `.`.
|
|
10
|
+
* @returns The decoded UTF-8 string.
|
|
11
|
+
*/
|
|
12
|
+
export declare function decodeJwtSegment(segment: string): string;
|
|
13
|
+
export default decodeJwtSegment;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Mock MSAL for tests: real provider, real configurator, fake client.
|
|
3
|
+
*
|
|
4
|
+
* @remarks
|
|
5
|
+
* Substituting the client is the smallest change that removes Entra ID from a test.
|
|
6
|
+
* Everything above it — scope resolution, silent-first token acquisition, account
|
|
7
|
+
* handling, proxy providers, telemetry — is the production code path.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { enableMsalMock } from '@equinor/fusion-framework-module-msal/mock';
|
|
12
|
+
*
|
|
13
|
+
* // default mock user
|
|
14
|
+
* enableMsalMock(configurator);
|
|
15
|
+
*
|
|
16
|
+
* // or a specific one
|
|
17
|
+
* enableMsalMock(configurator, (builder) => {
|
|
18
|
+
* builder.setAccount({ name: 'Ada Lovelace', signedOut: true });
|
|
19
|
+
* });
|
|
20
|
+
* ```
|
|
21
|
+
*
|
|
22
|
+
* @packageDocumentation
|
|
23
|
+
*/
|
|
24
|
+
export { MsalMockClient, type MsalMockUser } from './MsalMockClient';
|
|
25
|
+
export { createMsalMockClient } from './create-msal-mock-client';
|
|
26
|
+
export { MsalMockConfigurator } from './MsalMockConfigurator';
|
|
27
|
+
export { enableMsalMock, msalMockModule, type AuthConfigMockFn } from './module';
|
|
28
|
+
export { createMockToken, type MockTokenClaims } from './create-mock-token';
|
|
29
|
+
export { decodeJwtSegment } from './decode-jwt-segment';
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import type { IModulesConfigurator } from '@equinor/fusion-framework-module';
|
|
2
|
+
import { type MsalModule } from '../module';
|
|
3
|
+
import { MsalMockConfigurator } from './MsalMockConfigurator';
|
|
4
|
+
/**
|
|
5
|
+
* The MSAL module with a mock client instead of a live connection to Entra ID.
|
|
6
|
+
*
|
|
7
|
+
* @remarks
|
|
8
|
+
* Only `configure` differs from the real module. `initialize` is the production
|
|
9
|
+
* one, untouched, so proxy providers, host-provider hoisting and provider
|
|
10
|
+
* initialization all behave exactly as they do in production — and a test
|
|
11
|
+
* observes the real start-up path rather than a rehearsal of it.
|
|
12
|
+
*/
|
|
13
|
+
export declare const msalMockModule: MsalModule;
|
|
14
|
+
/**
|
|
15
|
+
* Configuration callback for {@link enableMsalMock}.
|
|
16
|
+
*/
|
|
17
|
+
export type AuthConfigMockFn<TRef = unknown> = (configurator: MsalMockConfigurator, ref?: TRef) => void;
|
|
18
|
+
/**
|
|
19
|
+
* Enables MSAL against a mock client, so a test needs no credentials and no network.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* Registered last, this replaces whichever auth module the configurator already
|
|
23
|
+
* carries, so it works on a `FrameworkConfigurator` that pre-registers the real one.
|
|
24
|
+
*
|
|
25
|
+
* @param configurator - The modules configurator to register on.
|
|
26
|
+
* @param configure - Optional callback to override the default mock client.
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* enableMsalMock(configurator, (builder) => {
|
|
31
|
+
* builder.setAccount({ name: 'Ada Lovelace' });
|
|
32
|
+
* });
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export declare const enableMsalMock: (configurator: IModulesConfigurator<any, any>, configure?: AuthConfigMockFn) => void;
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
import { CacheLookupPolicy } from '@azure/msal-browser';
|
|
3
|
+
import type { IMsalClient } from './MsalClient.interface';
|
|
4
|
+
import type { IMsalProvider } from './MsalProvider.interface';
|
|
5
|
+
export type { TelemetryConfig } from './telemetry-config-schema';
|
|
6
|
+
/**
|
|
7
|
+
* Zod schema for MSAL module configuration validation.
|
|
8
|
+
*
|
|
9
|
+
* @remarks
|
|
10
|
+
* Kept in its own module so the configuration can be extended at its source.
|
|
11
|
+
* The schema itself describes what reaches `MsalProvider` and strips anything
|
|
12
|
+
* else; keys a variant of this module needs only while the configuration is
|
|
13
|
+
* being built are declared on {@link MsalConfigExtension} instead.
|
|
14
|
+
*/
|
|
15
|
+
export declare const MsalConfigSchema: z.ZodObject<{
|
|
16
|
+
client: z.ZodOptional<z.ZodCustom<IMsalClient, IMsalClient>>;
|
|
17
|
+
provider: z.ZodOptional<z.ZodCustom<IMsalProvider, IMsalProvider>>;
|
|
18
|
+
requiresAuth: z.ZodOptional<z.ZodBoolean>;
|
|
19
|
+
redirectUri: z.ZodOptional<z.ZodString>;
|
|
20
|
+
loginHint: z.ZodOptional<z.ZodString>;
|
|
21
|
+
authCode: z.ZodOptional<z.ZodString>;
|
|
22
|
+
cacheLookupPolicy: z.ZodOptional<z.ZodCustom<CacheLookupPolicy, CacheLookupPolicy>>;
|
|
23
|
+
version: z.ZodPipe<z.ZodString, z.ZodTransform<string, string>>;
|
|
24
|
+
telemetry: z.ZodObject<{
|
|
25
|
+
provider: z.ZodOptional<z.ZodCustom<import("@equinor/fusion-framework-module-telemetry").ITelemetryProvider, import("@equinor/fusion-framework-module-telemetry").ITelemetryProvider>>;
|
|
26
|
+
metadata: z.ZodDefault<z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodUnknown>>>;
|
|
27
|
+
scope: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
28
|
+
}, z.core.$strip>;
|
|
29
|
+
}, z.core.$strip>;
|
|
30
|
+
/**
|
|
31
|
+
* Configuration a variant of this module adds to {@link MsalConfig}.
|
|
32
|
+
*
|
|
33
|
+
* @remarks
|
|
34
|
+
* Empty by design: production MSAL configuration is exactly the schema. This
|
|
35
|
+
* exists so a variant — the test double in `./mock`, for instance — can declare
|
|
36
|
+
* its own branch of the configuration through declaration merging:
|
|
37
|
+
*
|
|
38
|
+
* ```typescript
|
|
39
|
+
* declare module '@equinor/fusion-framework-module-msal' {
|
|
40
|
+
* interface MsalConfigExtension {
|
|
41
|
+
* mock?: { account?: MsalMockUser };
|
|
42
|
+
* }
|
|
43
|
+
* }
|
|
44
|
+
* ```
|
|
45
|
+
*
|
|
46
|
+
* That is what keeps `BaseConfigBuilder._set` honest about the added key. Its
|
|
47
|
+
* target is a dot-path union derived from {@link MsalConfig}, so a key the type
|
|
48
|
+
* does not know about can only be set by casting past the builder — and a
|
|
49
|
+
* generic configurator cannot help, because a dot-path union over an unresolved
|
|
50
|
+
* type parameter defers, taking every existing literal path down with it.
|
|
51
|
+
*
|
|
52
|
+
* The key exists to carry a declaration across the builder, not to reach the
|
|
53
|
+
* provider: the schema strips it during validation, so it is readable from the
|
|
54
|
+
* raw configuration and absent from the validated one.
|
|
55
|
+
*/
|
|
56
|
+
export interface MsalConfigExtension {
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Complete configuration object for MSAL authentication module.
|
|
60
|
+
*
|
|
61
|
+
* This type represents the full configuration including client setup, authentication
|
|
62
|
+
* requirements, telemetry, and version information.
|
|
63
|
+
*/
|
|
64
|
+
export type MsalConfig = z.infer<typeof MsalConfigSchema> & MsalConfigExtension;
|
package/dist/types/static.d.ts
CHANGED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import z from 'zod';
|
|
2
|
+
/**
|
|
3
|
+
* Telemetry configuration for MSAL module.
|
|
4
|
+
*
|
|
5
|
+
* This configuration controls how authentication events are tracked and logged
|
|
6
|
+
* through the framework's telemetry system.
|
|
7
|
+
*/
|
|
8
|
+
export type TelemetryConfig = z.infer<typeof TelemetryConfigSchema>;
|
|
@@ -79,7 +79,7 @@ export interface IAuthClient extends IPublicClientApplication {
|
|
|
79
79
|
* @param silent - Whether to attempt silent authentication first
|
|
80
80
|
* @returns Promise resolving to authentication result or void
|
|
81
81
|
*/
|
|
82
|
-
login(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult |
|
|
82
|
+
login(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult | undefined>;
|
|
83
83
|
/**
|
|
84
84
|
* Acquire access token with optional silent authentication fallback
|
|
85
85
|
* @param options - Authentication request options
|
|
@@ -87,5 +87,5 @@ export interface IAuthClient extends IPublicClientApplication {
|
|
|
87
87
|
* @param silent - Whether to attempt silent authentication first
|
|
88
88
|
* @returns Promise resolving to authentication result or void
|
|
89
89
|
*/
|
|
90
|
-
acquireToken(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult |
|
|
90
|
+
acquireToken(options?: AuthRequest, behavior?: AuthBehavior, silent?: boolean): Promise<AuthenticationResult | undefined>;
|
|
91
91
|
}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import { LogLevel, type LoggerOptions } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Class which facilitates logging of messages to a specific place
|
|
4
|
+
*/
|
|
5
|
+
export declare class Logger {
|
|
6
|
+
private correlationId;
|
|
7
|
+
private level;
|
|
8
|
+
private piiLoggingEnabled;
|
|
9
|
+
private localCallback;
|
|
10
|
+
private packageName;
|
|
11
|
+
private packageVersion;
|
|
12
|
+
/**
|
|
13
|
+
* Creates a new `Logger`.
|
|
14
|
+
*
|
|
15
|
+
* @param loggerOptions - Logger configuration, including callback, PII, and log level settings.
|
|
16
|
+
* @param packageName - Name of the package implementing this logger.
|
|
17
|
+
* @param packageVersion - Version of the package implementing this logger.
|
|
18
|
+
*/
|
|
19
|
+
constructor(loggerOptions: LoggerOptions, packageName?: string, packageVersion?: string);
|
|
20
|
+
/**
|
|
21
|
+
* Creates the default logger options used when none are provided.
|
|
22
|
+
*
|
|
23
|
+
* @returns Default logger options with a no-op callback, PII logging disabled, and Info level.
|
|
24
|
+
*/
|
|
25
|
+
private static createDefaultLoggerOptions;
|
|
26
|
+
/**
|
|
27
|
+
* Create new Logger with existing configurations.
|
|
28
|
+
*
|
|
29
|
+
* @param packageName - Name of the package implementing this logger.
|
|
30
|
+
* @param packageVersion - Version of the package implementing this logger.
|
|
31
|
+
* @param correlationId - Correlation ID to use, falling back to this logger's own if omitted.
|
|
32
|
+
* @returns A new `Logger` instance cloned from this one.
|
|
33
|
+
*/
|
|
34
|
+
clone(packageName: string, packageVersion: string, correlationId?: string): Logger;
|
|
35
|
+
/**
|
|
36
|
+
* Logs error messages.
|
|
37
|
+
*
|
|
38
|
+
* @param message - The message to log.
|
|
39
|
+
* @param correlationId - Correlation ID to attach to the log entry, if any.
|
|
40
|
+
*/
|
|
41
|
+
error(message: string, correlationId?: string): void;
|
|
42
|
+
/**
|
|
43
|
+
* Logs warning messages.
|
|
44
|
+
*
|
|
45
|
+
* @param message - The message to log.
|
|
46
|
+
* @param correlationId - Correlation ID to attach to the log entry, if any.
|
|
47
|
+
*/
|
|
48
|
+
warning(message: string, correlationId?: string): void;
|
|
49
|
+
/**
|
|
50
|
+
* Logs info messages.
|
|
51
|
+
*
|
|
52
|
+
* @param message - The message to log.
|
|
53
|
+
* @param correlationId - Correlation ID to attach to the log entry, if any.
|
|
54
|
+
*/
|
|
55
|
+
info(message: string, correlationId?: string): void;
|
|
56
|
+
/**
|
|
57
|
+
* Logs verbose messages.
|
|
58
|
+
*
|
|
59
|
+
* @param message - The message to log.
|
|
60
|
+
* @param correlationId - Correlation ID to attach to the log entry, if any.
|
|
61
|
+
*/
|
|
62
|
+
verbose(message: string, correlationId?: string): void;
|
|
63
|
+
/**
|
|
64
|
+
* Logs trace messages.
|
|
65
|
+
*
|
|
66
|
+
* @param message - The message to log.
|
|
67
|
+
* @param correlationId - Correlation ID to attach to the log entry, if any.
|
|
68
|
+
*/
|
|
69
|
+
trace(message: string, correlationId?: string): void;
|
|
70
|
+
/**
|
|
71
|
+
* Returns whether PII Logging is enabled or not.
|
|
72
|
+
*
|
|
73
|
+
* @returns `true` when PII logging is enabled, otherwise `false`.
|
|
74
|
+
*/
|
|
75
|
+
isPiiLoggingEnabled(): boolean;
|
|
76
|
+
/**
|
|
77
|
+
* Formats and dispatches a log message to the configured callback, unless it
|
|
78
|
+
* is below the configured log level or contains PII while PII logging is disabled.
|
|
79
|
+
*
|
|
80
|
+
* @param logMessage - The raw message to log.
|
|
81
|
+
* @param options - Log level, PII flag, and correlation ID for this message.
|
|
82
|
+
*/
|
|
83
|
+
private logMessage;
|
|
84
|
+
/**
|
|
85
|
+
* Execute callback with message.
|
|
86
|
+
*
|
|
87
|
+
* @param level - The log level of the message.
|
|
88
|
+
* @param message - The formatted message to send to the callback.
|
|
89
|
+
* @param containsPii - Whether the message contains personally identifiable information.
|
|
90
|
+
*/
|
|
91
|
+
executeCallback(level: LogLevel, message: string, containsPii: boolean): void;
|
|
92
|
+
}
|
|
@@ -81,7 +81,7 @@ export interface IMsalProvider {
|
|
|
81
81
|
/**
|
|
82
82
|
* Handle authentication redirect
|
|
83
83
|
*/
|
|
84
|
-
handleRedirect(): Promise<
|
|
84
|
+
handleRedirect(): Promise<undefined | null>;
|
|
85
85
|
/**
|
|
86
86
|
* Create a proxy provider for version compatibility
|
|
87
87
|
* @param version - Version string
|
package/dist/types/v2/types.d.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
* This file contains all types required to type IPublicClientApplication
|
|
5
5
|
* from MSAL v2.38.4 to maintain backward compatibility while using MSAL v4 implementation.
|
|
6
6
|
*/
|
|
7
|
+
import type { Logger } from './Logger.js';
|
|
7
8
|
/**
|
|
8
9
|
* Key-Value type to support queryParams, extraQueryParams and claims
|
|
9
10
|
*/
|
|
@@ -291,9 +292,7 @@ export declare enum LogLevel {
|
|
|
291
292
|
/**
|
|
292
293
|
* Callback to send the messages to
|
|
293
294
|
*/
|
|
294
|
-
export
|
|
295
|
-
(level: LogLevel, message: string, containsPii: boolean): void;
|
|
296
|
-
}
|
|
295
|
+
export type ILoggerCallback = (level: LogLevel, message: string, containsPii: boolean) => void;
|
|
297
296
|
/**
|
|
298
297
|
* Options for logger messages
|
|
299
298
|
*/
|
|
@@ -303,52 +302,7 @@ export type LoggerOptions = {
|
|
|
303
302
|
logLevel?: LogLevel;
|
|
304
303
|
correlationId?: string;
|
|
305
304
|
};
|
|
306
|
-
|
|
307
|
-
* Class which facilitates logging of messages to a specific place
|
|
308
|
-
*/
|
|
309
|
-
export declare class Logger {
|
|
310
|
-
private correlationId;
|
|
311
|
-
private level;
|
|
312
|
-
private piiLoggingEnabled;
|
|
313
|
-
private localCallback;
|
|
314
|
-
private packageName;
|
|
315
|
-
private packageVersion;
|
|
316
|
-
constructor(loggerOptions: LoggerOptions, packageName?: string, packageVersion?: string);
|
|
317
|
-
private static createDefaultLoggerOptions;
|
|
318
|
-
/**
|
|
319
|
-
* Create new Logger with existing configurations.
|
|
320
|
-
*/
|
|
321
|
-
clone(packageName: string, packageVersion: string, correlationId?: string): Logger;
|
|
322
|
-
/**
|
|
323
|
-
* Logs error messages.
|
|
324
|
-
*/
|
|
325
|
-
error(message: string, correlationId?: string): void;
|
|
326
|
-
/**
|
|
327
|
-
* Logs warning messages.
|
|
328
|
-
*/
|
|
329
|
-
warning(message: string, correlationId?: string): void;
|
|
330
|
-
/**
|
|
331
|
-
* Logs info messages.
|
|
332
|
-
*/
|
|
333
|
-
info(message: string, correlationId?: string): void;
|
|
334
|
-
/**
|
|
335
|
-
* Logs verbose messages.
|
|
336
|
-
*/
|
|
337
|
-
verbose(message: string, correlationId?: string): void;
|
|
338
|
-
/**
|
|
339
|
-
* Logs trace messages.
|
|
340
|
-
*/
|
|
341
|
-
trace(message: string, correlationId?: string): void;
|
|
342
|
-
/**
|
|
343
|
-
* Returns whether PII Logging is enabled or not.
|
|
344
|
-
*/
|
|
345
|
-
isPiiLoggingEnabled(): boolean;
|
|
346
|
-
private logMessage;
|
|
347
|
-
/**
|
|
348
|
-
* Execute callback with message.
|
|
349
|
-
*/
|
|
350
|
-
executeCallback(level: LogLevel, message: string, containsPii: boolean): void;
|
|
351
|
-
}
|
|
305
|
+
export { Logger } from './Logger.js';
|
|
352
306
|
/**
|
|
353
307
|
* Performance event type
|
|
354
308
|
*/
|
package/dist/types/v4/types.d.ts
CHANGED
|
@@ -395,9 +395,7 @@ export declare enum LogLevel {
|
|
|
395
395
|
/**
|
|
396
396
|
* Callback to send the messages to
|
|
397
397
|
*/
|
|
398
|
-
export
|
|
399
|
-
(level: LogLevel, message: string, containsPii: boolean): void;
|
|
400
|
-
}
|
|
398
|
+
export type ILoggerCallback = (level: LogLevel, message: string, containsPii: boolean) => void;
|
|
401
399
|
/**
|
|
402
400
|
* Event callback function type
|
|
403
401
|
*/
|
package/dist/types/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const version = "
|
|
1
|
+
export declare const version = "11.0.0-next.0";
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# MSAL API Reference
|
|
2
|
+
|
|
3
|
+
## `enableMSAL(configurator, configure?)`
|
|
4
|
+
|
|
5
|
+
Enables the MSAL module in your Fusion Framework application.
|
|
6
|
+
|
|
7
|
+
**Parameters:**
|
|
8
|
+
- `configurator`: `IModulesConfigurator` - The modules configurator instance
|
|
9
|
+
- `configure?`: `(builder: { setClientConfig, setRequiresAuth }) => void` - Optional configuration function
|
|
10
|
+
|
|
11
|
+
**Returns:** `void`
|
|
12
|
+
|
|
13
|
+
**Example:**
|
|
14
|
+
```typescript
|
|
15
|
+
enableMSAL(configurator, (builder) => {
|
|
16
|
+
builder.setClientConfig({ auth: { clientId: '...', tenantId: '...' } });
|
|
17
|
+
builder.setRequiresAuth(true);
|
|
18
|
+
});
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
## Type Definitions
|
|
22
|
+
|
|
23
|
+
### `LoginOptions`
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
type LoginOptions = {
|
|
27
|
+
request: PopupRequest | RedirectRequest; // MSAL request object
|
|
28
|
+
behavior?: 'popup' | 'redirect'; // Auth method (default: 'redirect')
|
|
29
|
+
silent?: boolean; // Attempt silent auth first (default: true)
|
|
30
|
+
};
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### `LogoutOptions`
|
|
34
|
+
|
|
35
|
+
```typescript
|
|
36
|
+
type LogoutOptions = {
|
|
37
|
+
redirectUri?: string; // Redirect after logout
|
|
38
|
+
account?: AccountInfo; // Account to logout (defaults to active)
|
|
39
|
+
};
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### `AcquireTokenOptions`
|
|
43
|
+
|
|
44
|
+
```typescript
|
|
45
|
+
type AcquireTokenOptions = {
|
|
46
|
+
request: PopupRequest | RedirectRequest; // MSAL request with scopes
|
|
47
|
+
behavior?: 'popup' | 'redirect'; // Auth method (default: 'redirect')
|
|
48
|
+
silent?: boolean; // Attempt silent first (default: true if account available)
|
|
49
|
+
};
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## `IMsalProvider`
|
|
53
|
+
|
|
54
|
+
The authentication provider interface available at `framework.auth`:
|
|
55
|
+
|
|
56
|
+
```typescript
|
|
57
|
+
interface IMsalProvider {
|
|
58
|
+
// The MSAL PublicClientApplication instance
|
|
59
|
+
readonly client: IMsalClient;
|
|
60
|
+
|
|
61
|
+
// Current user account information
|
|
62
|
+
readonly account: AccountInfo | null;
|
|
63
|
+
|
|
64
|
+
// Initialize the MSAL provider
|
|
65
|
+
initialize(): Promise<void>;
|
|
66
|
+
|
|
67
|
+
// Acquire an access token for the specified scopes
|
|
68
|
+
acquireAccessToken(options: AcquireTokenOptionsLegacy): Promise<string | undefined>;
|
|
69
|
+
|
|
70
|
+
// Acquire full authentication result
|
|
71
|
+
acquireToken(options: AcquireTokenOptionsLegacy): Promise<AcquireTokenResult>;
|
|
72
|
+
|
|
73
|
+
// Login user interactively
|
|
74
|
+
login(options: LoginOptions): Promise<LoginResult>;
|
|
75
|
+
|
|
76
|
+
// Logout user (returns boolean)
|
|
77
|
+
logout(options?: LogoutOptions): Promise<boolean>;
|
|
78
|
+
|
|
79
|
+
// Handle authentication redirect (returns AuthenticationResult | null)
|
|
80
|
+
handleRedirect(): Promise<AuthenticationResult | null>;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Note: defaultAccount and other deprecated v2 properties are available only
|
|
84
|
+
// when using a v2-compatible proxy via createProxyProvider()
|
|
85
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Backend-Issued Auth Code Flow
|
|
2
|
+
|
|
3
|
+
Enable automatic sign-in using a backend-issued authorization code without interactive login prompts.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
When your backend authenticates a user and generates a short-lived SPA auth code, the MSAL module can exchange it for tokens during initialization, eliminating double-login issues and providing seamless authentication.
|
|
8
|
+
|
|
9
|
+
## Usage
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { enableMSAL } from '@equinor/fusion-framework-module-msal';
|
|
13
|
+
|
|
14
|
+
enableMSAL(configurator, (builder) => {
|
|
15
|
+
builder.setClientConfig({
|
|
16
|
+
auth: {
|
|
17
|
+
clientId: 'your-client-id',
|
|
18
|
+
tenantId: 'your-tenant-id'
|
|
19
|
+
}
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
// Backend injects auth code as window.MSAL_AUTH_CODE during initial first page load
|
|
23
|
+
// This is the most secure approach - only available on first render, cleared after use
|
|
24
|
+
if (typeof window !== 'undefined' && window.MSAL_AUTH_CODE) {
|
|
25
|
+
builder.setAuthCode(window.MSAL_AUTH_CODE);
|
|
26
|
+
delete (window as any).MSAL_AUTH_CODE; // Clear after consuming
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
builder.setRequiresAuth(true);
|
|
30
|
+
});
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How It Works
|
|
34
|
+
|
|
35
|
+
1. Backend authenticates user and generates short-lived auth code
|
|
36
|
+
2. Frontend passes auth code to MSAL: `builder.setAuthCode(authCode)`
|
|
37
|
+
3. During `initialize()`: auth code exchanged for tokens (before `requiresAuth` check)
|
|
38
|
+
4. Tokens cached by MSAL → user automatically signed in
|
|
39
|
+
5. Falls back to standard MSAL flows on exchange failure
|
|
40
|
+
|
|
41
|
+
## API: `setAuthCode(authCode?: string)`
|
|
42
|
+
|
|
43
|
+
Sets backend-issued auth code for token exchange during initialization.
|
|
44
|
+
|
|
45
|
+
Pass `undefined` to clear/reset a previously configured auth code.
|
|
46
|
+
|
|
47
|
+
**Returns:** configurator instance (chainable)
|
|
48
|
+
|
|
49
|
+
**Behavior:**
|
|
50
|
+
- Exchange happens before `requiresAuth` check
|
|
51
|
+
- On success: user auto-authenticated, no login prompt
|
|
52
|
+
- On failure: falls back to standard MSAL login
|
|
53
|
+
- Auth code cleared after exchange (no reuse)
|
|
54
|
+
- `setAuthCode(undefined)` clears configured auth code
|
|
55
|
+
- `setAuthCode('')` is treated as absent auth code
|
|
56
|
+
- `setAuthCode(' ')` is trimmed and treated as absent auth code
|
|
57
|
+
- No auth-code exchange is attempted when auth code is absent/cleared
|
|
58
|
+
|
|
59
|
+
**Example:**
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
// Best practice: Backend injects auth code on initial page load as window.MSAL_AUTH_CODE
|
|
63
|
+
if (typeof window !== 'undefined' && window.MSAL_AUTH_CODE) {
|
|
64
|
+
builder.setAuthCode(window.MSAL_AUTH_CODE);
|
|
65
|
+
delete (window as any).MSAL_AUTH_CODE; // Clear after consuming to prevent reuse
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Clear/reset auth code when input is missing
|
|
69
|
+
builder.setAuthCode(undefined);
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
## Security
|
|
73
|
+
|
|
74
|
+
- ✅ Auth codes: single-use, short-lived (5-10 min)
|
|
75
|
+
- ✅ MSAL validates tokens from Microsoft authority
|
|
76
|
+
- ✅ Tokens stored securely, refresh tokens auto-managed
|
|
77
|
+
- ⚠️ Pass codes securely: HTTPS, HTTP-only cookies, or encrypted channels
|
|
78
|
+
|
|
79
|
+
## Troubleshooting
|
|
80
|
+
|
|
81
|
+
| Issue | Solution |
|
|
82
|
+
|-------|----------|
|
|
83
|
+
| Auth code not exchanged | Verify `setAuthCode()` called before init |
|
|
84
|
+
| Invalid auth code error | Confirm backend `WithSpaAuthCode` enabled, code is fresh |
|
|
85
|
+
| Still shows login prompt | Check auth code exchange completes before `requiresAuth` check |
|
|
86
|
+
| Exchange fails | Auth code may have expired; backend should generate fresh code per load |
|