import { SPEvent, SPEventArgs } from '@microsoft/sp-core-library'; import type { IInternalAadTokenProvider, IAadTokenProviderConfiguration, IGetTokenOptions, IGetTokenDataOptions, IBeforeRedirectEventArgs, ITokenAcquisitionEventArgs, IPopupEventArgs } from './IAadTokenProvider'; import type { ISPOBOFlowParameters, ITokenData, _OBO3PTokenFunction, OBOTokenFunction } from './ITokenProvider'; /** * Event arguments raised before performing a full-page redirect for authentication. * * **When raised**: Before initiating a full-page redirect to Azure AD for authentication when: * - Silent token acquisition fails and interaction is required * - Popup authentication is disabled via tenant configuration (default behavior) * - Popup authentication is unavailable (iframe, browser blocks) or fails * - The authentication flow needs to fall back to redirect flow * - `_ensureState()` determines a redirect is needed to establish authentication state * * **Note**: If popup authentication is not enabled by the tenant, this event is raised immediately * after silent authentication fails, without attempting popup first. * * **NAA (Nested App Authentication) Exception**: * This event is **NOT raised** when running in Teams, Outlook, or mobile webviews with NAA support. * NAA uses the host application's native broker and does not perform browser redirects. * * **Use cases**: * - Cancel the automatic redirect and handle authentication manually * - Show custom UI or save application state before navigation * - Implement custom redirect logic or error handling * - Prevent redirect in specific scenarios (e.g., iframe contexts, recent redirect attempts) * * **How to use**: Subscribe to `AadTokenProvider.onBeforeRedirectEvent` and call `cancel()` to prevent * the automatic redirect. The `redirectUrl` property contains the destination URL that would be navigated to. * * @example * ```typescript * aadTokenProvider.onBeforeRedirectEvent.add(observer, (args: BeforeRedirectEventArgs) => { * // Save application state before redirect * saveCurrentState(); * * // Or cancel and handle manually * args.cancel(); * window.location.href = args.redirectUrl; * }); * ``` * * @public */ export declare class BeforeRedirectEventArgs extends SPEventArgs implements IBeforeRedirectEventArgs { /** * The id of the instance generating the event. */ configuration?: IAadTokenProviderConfiguration; /** * The url of the page to redirect to if automatic redirect is cancelled */ redirectUrl: string; /** * Call this method if the redirect should be cancelled because it is being handled by code in the cancelling class. */ cancel: () => void; constructor(redirectUrl: string, cancel: () => void, configuration?: IAadTokenProviderConfiguration | undefined); } /** * Event arguments for token acquisition failures requiring user interaction. * * **When raised**: This event was originally used with ADAL.js for token acquisition failures. * In the current MSAL-based implementation, most interaction scenarios now use {@link PopupEventArgs} * and {@link BeforeRedirectEventArgs} instead. * * **Use cases** (legacy): * - Notify the application that token acquisition failed * - Provide the user with a redirect URL for manual authentication * - Handle MFA (Multi-Factor Authentication) requirements * * **Note**: This event may be deprecated in future versions. For new code, prefer subscribing to * `popupEvent` and `onBeforeRedirectEvent` which provide more control over the authentication flow. * * @public */ export declare class TokenAcquisitionEventArgs extends SPEventArgs implements ITokenAcquisitionEventArgs { /** * The message returned from ADAL fails to retrieve a token from Azure AD. */ message: string; /** * The url of the page for the end user to perform Multi Factor Authentication */ redirectUrl?: string; constructor(message: string, redirectUrl?: string); } /** * Event arguments raised when attempting popup-based authentication. * * **When Popup Authentication is Enabled**: * This event is raised when popup authentication is enabled, which can be controlled by: * - **Tenant configuration**: `Set-SPOTenant -IsEnableAppAuthPopupEnabled $true` (sp-client areas) * - Property chain: `IsEnableAppAuthPopupEnabled` → `SpfxIsAcquireTokenPopupEnabled` → `IsMsalTokenProviderPopupEnabled` → `isMsalTokenProviderPopupEnabled` * - **Caller/app context**: Application code setting popup preference based on codepath (odsp-next areas) * Without popup enabled (default), authentication skips popup and goes directly to redirect. * * **NAA (Nested App Authentication) Exception**: * This event is **NOT raised** when running in Teams, Outlook, or mobile webviews with NAA support. * NAA uses the host application's native broker and bypasses the event system entirely. * * **When raised**: Before opening an authentication popup when: * - Popup is enabled (via tenant configuration or app context, see above) * - Silent token acquisition fails with interaction_required error * - The token provider needs user consent or authentication * - PoP (Proof of Possession) nonce fetching fails and requires interaction * - CAP (Conditional Access Policy) errors require user interaction * - The authentication flow falls back to popup before trying redirect * * **Use cases**: * - Show custom loading UI or messages while popup is opening * - Prevent popups in specific contexts (e.g., mobile devices, iframe environments) * - Coordinate with application-level popup blockers or modal managers * - Implement custom popup timing or debouncing logic * - Handle popup failures and provide fallback behavior * * **How to use**: * - Call `cancel(error?)` immediately to prevent the popup and optionally provide an error * - Call `requestPopup()` to signal intent to show the popup (reserves popup "slot") * - Call `showPopup()` when ready to actually launch the popup window * - If multiple handlers call `requestPopup()`, only the first wins (popup debouncing) * * **Popup flow**: * 1. Event raised before popup attempt * 2. Handler calls `requestPopup()` to signal intent (or `cancel()` to prevent) * 3. Handler performs any necessary UI updates or validation * 4. Handler calls `showPopup()` when ready to launch the popup * 5. Popup opens and authentication proceeds * * @example * ```typescript * aadTokenProvider.popupEvent.add(observer, (args: PopupEventArgs) => { * // Show custom loading UI * showLoadingIndicator(); * * // Signal intent to show popup * args.requestPopup(); * * // Launch popup after UI updates * setTimeout(() => { * hideLoadingIndicator(); * args.showPopup(); * }, 100); * }); * ``` * * @example * ```typescript * // Cancel popup in mobile environments * aadTokenProvider.popupEvent.add(observer, (args: PopupEventArgs) => { * if (isMobileDevice()) { * args.cancel(new Error('Popups not supported on mobile')); * } * }); * ``` * * @public */ export declare class PopupEventArgs extends SPEventArgs implements IPopupEventArgs { /** * A handler should call this immediately when handling the event to signal that * it will not permit a popup. */ cancel: (error?: Error) => void; /** * A handler should call this immediately when handling the event to signal that * it intends to show a popup. */ requestPopup: () => void; /** * A handler should call this when ready, to indicate that the login flow should * now continue and launch the popup. */ showPopup: () => Promise | void; resource: string; options: IGetTokenDataOptions; constructor(cancel: (error?: Error) => void, requestPopup: () => void, showPopup: () => Promise | void, resource: string, options: IGetTokenDataOptions); } /** * This class allows a developer to obtain OAuth2 tokens from Azure AD. * * OAuth2 tokens are used to authenticate the user from the SharePoint page * to other services such as PowerBI, Sway, Exchange, Yammer, etc. * * @privateRemarks * AadTokenProvider is replacing the /_api.SP.OAuth.Token/Acquire endpoint * for authentication with ADAL.js. At some point in the near future, when Azure AD v2.0 * can support the same scenarios as the original version, we will switch to MSAL. * * @public * @sealed */ export default class AadTokenProvider { /** * Token Acquisition Event String * * @internal */ static _tokenAcquisitionEventId: string; /** * Auth redirect Event String * * @internal */ static _onBeforeRedirectEventId: string; /** * Popup Event String * * @internal */ static _popupEventId: string; /** * Notifies the developer before a full page redirect occurs. * * **⚠️ STRONGLY DISCOURAGED**: Using this event is strongly discouraged due to race condition issues. * When multiple callers use the same provider instance (recommended pattern), event handlers from * one token fetch may inadvertently respond to token fetches from different callers, causing * unpredictable behavior. This event is maintained only for backward compatibility with existing * 3rd party code. * * @eventproperty */ readonly onBeforeRedirectEvent: SPEvent; /** * Notifies the developer if the logic flow would like to request a popup flow for user interaction. * * **⚠️ STRONGLY DISCOURAGED**: Using this event is strongly discouraged due to race condition issues. * When multiple callers use the same provider instance (recommended pattern), event handlers from * one token fetch may inadvertently respond to token fetches from different callers, causing * unpredictable behavior. This event is maintained only for backward compatibility with existing * 3rd party code. * * @eventproperty */ readonly popupEvent: SPEvent; /** * @internal */ _oboFirstPartyTokenCallback: OBOTokenFunction | undefined; /** * @internal */ _oboThirdPartyTokenCallback: _OBO3PTokenFunction | undefined; private readonly _logSource; private _tokenAcquisitionEvent; private _aadTokenProvider; private _aadConfiguration; private _oboConfiguration; private static _getLastEnsureStateRedirectTime; private static _setLastEnsureStateRedirectTime; private static _isUsingLocalStorage; /** * @internal */ constructor(configuration: IAadTokenProviderConfiguration, oboConfiguration?: ISPOBOFlowParameters); /** * Fetches an OAuth2 access token for the specified resource endpoint. * * **Authentication Flow**: * This method automatically handles the authentication flow based on tenant configuration and host environment: * 1. **Silent Token Acquisition** - Always attempts to get token silently using cached credentials or SSO * 2. **NAA (Nested App Authentication)** - If in Teams/Outlook and silent fails: * - Uses host application's native broker for seamless authentication * - **Does NOT raise {@link PopupEventArgs} or {@link BeforeRedirectEventArgs}** (bypasses event system) * - Shows native account picker UI (not browser popup) * - Cannot be cancelled or controlled via event handlers * 3. **Popup Authentication** (Optional, tenant-configurable) - If NAA unavailable, popup enabled, and silent fails: * - Raises {@link PopupEventArgs} event before opening popup * - Requires tenant admin to enable via: `Set-SPOTenant -IsEnableAppAuthPopupEnabled $true` * - Property mapping: `IsEnableAppAuthPopupEnabled` → `SpfxIsAcquireTokenPopupEnabled` → `isMsalTokenProviderPopupEnabled` * - If disabled (default), skips directly to redirect * 4. **Redirect Authentication** - If popup disabled/unavailable/cancelled, raises {@link BeforeRedirectEventArgs} event * * **Default Flow** (browser): Silent → Redirect * **With Popup Enabled** (browser): Silent → Popup → Redirect * **In Teams/Outlook** (NAA): Silent → Native Broker (no events) * * **Token Caching**: * Tokens are automatically cached by the provider. Do NOT cache tokens returned by this method - * the provider manages caching, expiration, and refresh automatically. * * **State Management**: * On first call, automatically calls `_ensureState()` to establish authentication state with Azure AD. * This may trigger a redirect if the user has never authenticated. To control this timing, applications * can explicitly call `_ensureState()` during initialization. * * **Event Handling**: * Subscribe to events to customize authentication UX: * - {@link AadTokenProvider.popupEvent} - Control popup timing, show loading UI, or prevent popups (only raised if popup enabled) * - {@link AadTokenProvider.onBeforeRedirectEvent} - Save application state before redirect or cancel automatic redirects * * **NAA (Nested App Authentication) Limitation**: * When running in Teams, Outlook, or mobile webviews with NAA support: * - **Events are NOT raised** - {@link AadTokenProvider.popupEvent} and {@link AadTokenProvider.onBeforeRedirectEvent} will not fire * - **Cannot cancel authentication** - No way to prevent native account picker from showing * - **No application control** - Host application manages authentication UI entirely * - **Seamless experience** - Users see native account picker instead of browser popup * - **Error handling only** - Can only catch errors when NAA fails (e.g., user cancels) * - To avoid authentication UI, ensure tokens are valid (silent auth succeeds) * * **Common Error Scenarios**: * - **User cancelled** - User closed popup or cancelled authentication * - **Access denied** - User or admin denied consent for the requested resource * - **Network errors** - Transient network failures (automatically retried once) * - **Interaction required** - Cached credentials expired, user must re-authenticate * * @param resourceEndpoint - The resource URL for which to obtain a token (e.g., `https://graph.microsoft.com`) * @param options - Optional configuration: `{ useCachedToken?, authenticationScheme?, claims? }` * - `useCachedToken`: If false, forces refresh even if valid cached token exists (default: true) * - `authenticationScheme`: Token type - BEARER (default) or POP (Proof of Possession) * - `claims`: CAP (Conditional Access Policy) claims for advanced scenarios * @returns Promise resolving to the access token string * * @example * ```typescript * // Basic token acquisition * const token = await aadTokenProvider.getToken('https://graph.microsoft.com'); * * // Use token with Microsoft Graph * const response = await fetch('https://graph.microsoft.com/v1.0/me', { * headers: { 'Authorization': `Bearer ${token}` } * }); * ``` * * @example * ```typescript * // Handle popup events to show custom UI * aadTokenProvider.popupEvent.add(observer, (args) => { * showLoadingSpinner('Authenticating...'); * args.requestPopup(); * setTimeout(() => args.showPopup(), 100); * }); * * const token = await aadTokenProvider.getToken('https://graph.microsoft.com'); * ``` * * @example * ```typescript * // Prevent popups and handle errors manually * aadTokenProvider.popupEvent.add(observer, (args) => { * args.cancel(new Error('Popups not allowed in this context')); * }); * * try { * const token = await aadTokenProvider.getToken('https://graph.microsoft.com'); * } catch (error) { * // Handle authentication failure - may need to show custom UI * if (error.message.includes('Popups not allowed')) { * showAuthenticationRequiredBanner(); * } * } * ``` * * @example * ```typescript * // Force token refresh * const freshToken = await aadTokenProvider.getToken('https://graph.microsoft.com', { * useCachedToken: false * }); * ``` * * @example * ```typescript * // Handle NAA (Teams/Outlook) - events won't fire, can only handle errors * try { * const token = await aadTokenProvider.getToken('https://graph.microsoft.com'); * // NAA authentication succeeded (or silent auth succeeded) * } catch (error) { * if (error.errorCode === 'user_cancelled') { * // User cancelled NAA native account picker * showMessage('Please sign in to continue'); * } * // Note: Cannot prevent NAA from showing - must handle failure * } * ``` */ getToken(resourceEndpoint: string, options?: IGetTokenOptions): Promise; getToken(resourceEndpoint: string, useCachedToken?: boolean): Promise; /** * This method sets the value in the default config for supports nested app auth set * * @internal */ _setSupportsNestedAppAuth(isNAASupported: boolean): void; /** * Establishes authentication state with Azure AD by attempting to acquire a token silently. * * **Purpose**: * Verifies that the user has authenticated with Azure AD and can acquire tokens. If the user * has never authenticated (cold start), this may trigger a full-page redirect to Azure AD. * This "warms up" the authentication system before tokens are actually needed. * * **When Called Automatically**: * The first call to `getToken()` automatically calls `_ensureState()` internally. Most applications * do NOT need to call this method explicitly - the automatic call is sufficient. * * **When to Call Explicitly**: * Call this method during application initialization in these scenarios: * - **Control redirect timing** - Redirect during splash screen instead of during critical user actions * - **Pre-warm authentication** - Establish auth state before user needs to access protected resources * - **Show loading UI** - Display custom UI while authentication state is being established * - **Defer authentication** - Call later to avoid redirect on initial page load * * **Redirect Behavior**: * If user not authenticated: * 1. Raises {@link BeforeRedirectEventArgs} event - subscribe to cancel, save state, or show UI * 2. Redirects to Azure AD login page (full-page navigation) * 3. After login, redirects back to application with auth tokens * * **Redirect Loop Prevention**: * To prevent infinite redirect loops, this method will NOT redirect if: * - A redirect occurred within the last 5 days (default) * - The redirect is cancelled via {@link BeforeRedirectEventArgs} * - Running in an iframe context (iframes cannot redirect) * * **Important Notes**: * - Does NOT raise {@link PopupEventArgs} - never attempts popup authentication * - Only redirects on cold start (user never authenticated) - subsequent calls succeed silently * - Calling multiple times is safe - redirect prevention ensures it won't loop * - Does NOT guarantee tokens for specific resources - only establishes baseline auth state * * @returns Promise that resolves when authentication state is confirmed or rejects if state * cannot be established (e.g., authentication required but redirect cancelled) * * @example * ```typescript * // Call during application initialization to control redirect timing * async function initializeApp() { * showSplashScreen(); * * try { * // Establish auth state - may redirect to Azure AD * await aadTokenProvider._ensureState(); * * // If we get here, user is authenticated * await loadApplicationData(); * } catch (error) { * // Authentication failed - show error or retry * showAuthenticationError(error); * } finally { * hideSplashScreen(); * } * } * ``` * * @example * ```typescript * // Save application state before redirect * aadTokenProvider.onBeforeRedirectEvent.add(observer, (args) => { * // Save any in-progress work * localStorage.setItem('unsavedWork', JSON.stringify(currentState)); * * // Allow redirect to proceed * }); * * await aadTokenProvider._ensureState(); * ``` * * @example * ```typescript * // Prevent redirect and handle authentication manually * aadTokenProvider.onBeforeRedirectEvent.add(observer, (args) => { * args.cancel(); * // Show custom authentication UI instead of redirecting * showCustomAuthenticationDialog(args.redirectUrl); * }); * * try { * await aadTokenProvider._ensureState(); * } catch (error) { * // Expected - we cancelled the redirect * console.log('Redirect cancelled, showing custom UI'); * } * ``` * * @internal */ _ensureState(): Promise; /** * Acquires a token using an alternate configuration (primarily for first-party token acquisition). * * **Primary Use Case - First-Party Token Acquisition**: * This method enables the first-party/third-party token switching pattern used throughout the codebase * (39+ call sites). Most commonly used to acquire first-party tokens while using a third-party provider: * * ```typescript * // Get first-party token (app ID: 00000003-0000-0ff1-ce00-000000000000) * const token = await _AadTokenProviders.configurable._getTokenInternal( * resource, * _AadTokenProviders.preAuthorizedConfiguration // First-party config * ); * ``` * * **When to Use**: * - **First-party token acquisition**: Get 1P tokens from a 3P provider instance (most common) * - **Subclasses**: `DeferredAadTokenProvider` passes its own `_defaultConfiguration` * - **Testing**: Unit tests with mock configurations * * **Why This Exists**: * - Enables acquiring tokens with different app IDs (1P vs 3P) from the same provider instance * - Maintains the provider's state, caching, and event handling while switching app contexts * - Supports TypeScript inheritance pattern for subclasses * * **Important Constraints**: * - ✅ Switch between app IDs (1P ↔ 3P) within the same tenant * - ❌ DO NOT use for different tenants (same tenant only) * - ❌ DO NOT use for different users/accounts (use separate provider instances) * - ❌ DO NOT change configuration per-request (not thread-safe, causes auth failures) * * **Authentication Flow**: * Same as `getToken()`: silent → popup (if enabled) → redirect * * **Events**: * Raises the same events as `getToken()`: {@link PopupEventArgs} (if popup enabled) and {@link BeforeRedirectEventArgs} * * @param resourceEndpoint - The resource URL for which to obtain a token * @param configuration - Alternate configuration (typically `_AadTokenProviders.preAuthorizedConfiguration`) * @param options - Optional configuration: `{ useCachedToken? }` * @returns Promise resolving to the access token string * * @internal */ _getTokenInternal(resourceEndpoint: string, configuration: IAadTokenProviderConfiguration, options?: IGetTokenOptions): Promise; /** * @internal */ _getTokenInternal(resourceEndpoint: string, configuration: IAadTokenProviderConfiguration, useCachedToken?: boolean): Promise; /** * Fetches complete token data including access token, expiration, and metadata. * * **Purpose**: * Returns the full token response object instead of just the access token string. * Use this when you need additional token metadata like expiration time, account info, * or token type. * * **When to Use**: * - Need token expiration time (`expiresOn`) to implement custom caching logic * - Need to verify token properties before using it * - Building token inspection or debugging tools * - Need account information associated with the token * * **Return Value** (`ITokenData`): * - `accessToken`: The token string to use in Authorization headers * - `expiresOn`: Token expiration timestamp (Date object) * - `account`: Account information (UPN, tenant, etc.) * - `fromCache`: Whether token was retrieved from cache * * **Authentication Flow**: * Same as `getToken()`: silent → popup (if enabled) → redirect * * **Events**: * Raises the same events as `getToken()`: {@link PopupEventArgs} (if popup enabled) and {@link BeforeRedirectEventArgs} * * @param resourceEndpoint - The resource URL for which to obtain a token * @param options - Optional configuration (same as `getToken()`) * @returns Promise resolving to complete token data object * * @example * ```typescript * const tokenData = await aadTokenProvider._getTokenData('https://graph.microsoft.com'); * console.log(`Token expires: ${tokenData.expiresOn}`); * console.log(`User: ${tokenData.account?.username}`); * * // Use the access token * const response = await fetch('https://graph.microsoft.com/v1.0/me', { * headers: { 'Authorization': `Bearer ${tokenData.accessToken}` } * }); * ``` * * @internal */ _getTokenData(resourceEndpoint: string, options?: IGetTokenDataOptions): Promise; /** * @internal */ _getTokenData(resourceEndpoint: string, useCachedToken?: boolean): Promise; /** * Notifies the developer when Token Acquisition requires user action. * * **⚠️ STRONGLY DISCOURAGED**: Using this event is strongly discouraged due to race condition issues. * When multiple callers use the same provider instance (recommended pattern), event handlers from * one token fetch may inadvertently respond to token fetches from different callers, causing * unpredictable behavior. This event is maintained only for backward compatibility with existing * 3rd party code. * * @eventproperty */ get tokenAcquisitionEvent(): SPEvent; /** * Ensures proper monitoring is set up for token acquisition * * @internal */ _ensureTelemetry(): Promise; private _getAadTokenProvider; } /** * Internal version which enforces the internal interface. * @internal */ export declare class InternalAadTokenProvider extends AadTokenProvider implements IInternalAadTokenProvider { } //# sourceMappingURL=AadTokenProvider.d.ts.map