Conversation
…activeController actions - Created MSIDWebInstallProfileResponse class to detect and parse msauth://profileInstalled URLs - Added response detection in MSIDAADWebviewFactory response chain - Updated MSIDInteractiveAuthorizationCodeRequest to pass through profile installed responses - Added handleWebInstallProfileResponse method in MSIDLocalInteractiveController - Controller can now perform custom actions before continuing authentication flow - Created comprehensive unit tests for the new response type Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
- Created MSIDWebviewTransitionCoordinator to manage state-preserving transitions - Added MSIDWebProfileInstallTriggerResponse to detect msauth://profileInstall with HTTP headers - Updated MSIDAADOAuthEmbeddedWebviewController to: - Capture HTTP response headers via navigationResponseBlock - Detect profile install trigger (msauth://profileInstall) - Extract profile installation URL from X-Profile-Install-URL header - Suspend embedded webview (hide UI but keep alive) - Launch ASWebAuthenticationSession with extracted URL - Wait for msauth://profileInstalled callback - Resume suspended embedded webview seamlessly - Continue authentication flow without cancellation - Created comprehensive tests for trigger response detection Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
Corrected URL scheme names to match requirements: - First 302: msauth://installProfile - triggers transition FROM embedded webview TO ASWebAuthenticationSession - Second 302: msauth://profileInstalled - happens IN ASWebAuthenticationSession, triggers transition BACK to embedded webview Changes: - Updated MSIDWebProfileInstallTriggerResponse to detect msauth://installProfile - Changed constant from PROFILE_INSTALL to INSTALL_PROFILE - Updated all references to use installProfile instead of profileInstall - Updated tests to use correct URL msauth://installProfile - Updated comments and log messages for clarity - Verified MSIDWebInstallProfileResponse correctly detects msauth://profileInstalled Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
…tiveController - Removed profile installation handling from MSIDAADOAuthEmbeddedWebviewController - Embedded webview now only detects msauth://installProfile and returns it via completion handler - MSIDLocalInteractiveController now orchestrates the entire profile installation flow - Added handleProfileInstallTrigger method to controller - Added handleProfileInstallationCompletion method to controller - Controller now manages MSIDWebviewTransitionCoordinator - Controller suspends/resumes webview and launches ASWebAuthenticationSession - Follows existing pattern used for MSIDWebWPJResponse - Maintains separation of concerns: view detects, controller orchestrates Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
- Updated MSIDWebProfileInstallTriggerResponse to extract x-intune-url and x-intune-token headers - Replaced profileInstallURL property with intuneURL - Added intuneToken property for authentication - Added extractHeaderValue helper for case-insensitive header matching - Updated MSIDASWebAuthenticationSessionHandler to support additionalHeaders parameter (iOS 18+) - Added new initializer with additionalHeaders support - Use additionalHeaderFields property when iOS 18+ available - Updated MSIDWebviewTransitionCoordinator to pass additional headers - Updated MSIDLocalInteractiveController to build headers dictionary with x-intune-token - Updated all tests to use new header names (x-intune-url, x-intune-token) - Added tests for both headers extraction and case-insensitive matching Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
…ctory access - Added lastHTTPResponse property to MSIDWebviewSession - Updated MSIDAADOAuthEmbeddedWebviewController to store HTTP response in session - Updated MSIDWebviewAuthorization to pass httpResponse to factory - Added new overload methods to configuration classes accepting httpResponse parameter - Added new overload to MSIDWebviewFactory.oAuthResponseWithURL with httpResponse - Updated MSIDAADWebviewFactory to create MSIDWebProfileInstallTriggerResponse with HTTP headers - HTTP response now flows: Webview → Session → Authorization → Factory → Response - Intune URL and token now properly extracted from HTTP headers - Controller receives response with both intuneURL and intuneToken populated - Backward compatible: New methods with default implementations Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
…AuthenticationSession flow - Renamed profileSessionHandler → externalSessionHandler (generic naming) - Renamed launchProfileInstallationSession → launchExternalSession (generic naming) - Renamed dismissProfileInstallationSession → dismissExternalSession (generic naming) - Updated all method parameters to be generic (profileURL → url) - Updated documentation to describe generic external session flows - Updated log messages to be generic (external session vs profile installation) - Updated error messages to be generic - Updated MSIDLocalInteractiveController to use new method names - Coordinator is now reusable for ANY flow requiring ASWebAuthenticationSession transition - No functional changes, only naming improvements for reusability Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
|
@copilot can you create the design doc for this change |
| @@ -729,8 +729,24 @@ | |||
| 60F94D332210E8BD0035D956 /* MSIDV1IdToken.m in Sources */ = {isa = PBXBuildFile; fileRef = 60F94D312210E8BD0035D956 /* MSIDV1IdToken.m */; }; | |||
There was a problem hiding this comment.
This pull request does not update changelog.txt.
Please consider if this change would be noticeable to a partner or user and either update changelog.txt or resolve this conversation.
There was a problem hiding this comment.
Pull request overview
This PR implements Mobile Device Management (MDM) profile installation support for iOS applications. It introduces a sophisticated webview transition mechanism that allows seamless switching between embedded webviews and ASWebAuthenticationSession during the authentication flow, specifically to handle Intune MDM profile installation.
Changes:
- Adds new response classes (MSIDWebMDMInstallProfileResponse, MSIDWebMDMEnrollmentCompletionResponse) for handling MDM enrollment flows
- Implements MSIDWebviewTransitionCoordinator to manage transitions between embedded webview and ASWebAuthenticationSession
- Introduces navigation action system (MSIDWebviewNavigationAction, MSIDWebviewNavigationActionUtil) for handling special URL schemes (msauth://, browser://)
- Extends ASWebAuthenticationSession to support additional HTTP headers (iOS 17.4+)
- Adds BRT (Broker Refresh Token) acquisition logic placeholder in MSIDLocalInteractiveController
Reviewed changes
Copilot reviewed 38 out of 38 changed files in this pull request and generated 29 comments.
Show a summary per file
| File | Description |
|---|---|
| MSIDWebProfileInstallTriggerResponseTests.m | Test file for profile install trigger response (references non-existent class) |
| MSIDWebInstallProfileResponseTests.m | Test file for profile installed response (references non-existent class) |
| MSIDASWebAuthenticationSessionHandler.m | Adds additional headers support for ASWebAuthenticationSession |
| MSIDASWebAuthenticationSessionHandler.h | New initializer with additionalHeaders parameter |
| MSIDWebMDMInstallProfileResponse.m | Implementation for detecting profile install trigger URLs |
| MSIDWebMDMInstallProfileResponse.h | Header for profile install trigger response class |
| MSIDWebMDMEnrollmentCompletionResponse.m | Implementation for profile installation completion response |
| MSIDWebMDMEnrollmentCompletionResponse.h | Header for enrollment completion response class |
| MSIDWebviewSpecialNavigationDelegate.h | Protocol for handling special navigation redirects |
| MSIDWebviewNavigationAction.m | Implementation of navigation action types and factory methods |
| MSIDWebviewNavigationAction.h | Header defining navigation action types and enum |
| MSIDOAuth2EmbeddedWebviewController.h | Adds specialNavigationDelegate property |
| MSIDAADOAuthEmbeddedWebviewController.m | Implements special redirect handling and action execution |
| MSIDAADOAuthEmbeddedWebviewController.h | Documentation for special navigation delegate |
| MSIDWebviewTransitionCoordinator.m | Coordinates webview transitions with suspend/resume logic |
| MSIDWebviewTransitionCoordinator.h | Header for webview transition coordinator |
| MSIDWebviewNavigationActionUtil.m | Utility for resolving actions from msauth URLs |
| MSIDWebviewNavigationActionUtil.h | Header for navigation action utility |
| MSIDLocalInteractiveController.m | Orchestrates MDM flow, BRT acquisition, and webview configuration |
| MSIDLocalInteractiveController.h | Adds MSIDWebviewSpecialNavigationDelegate conformance |
| MSIDLocalInteractiveController+Internal.h | Internal properties for BRT state tracking |
| MSIDInteractiveTokenRequest.m | Updates completion block type to use MSIDWebviewResponse |
| MSIDInteractiveTokenRequest+Internal.h | Forward declaration for webview interacting protocol |
| MSIDInteractiveRequestControlling.h | Updates completion block type signature |
| MSIDInteractiveAuthorizationCodeRequest.m | Adds webview configuration, handles MDM responses |
| MSIDInteractiveAuthorizationCodeRequest.h | Adds currentWebview property, updates completion block |
| MSIDInteractiveTokenRequestParameters.h | Adds webviewConfigurationBlock property |
| MSIDAADWebviewFactory.m | Adds MDM response creation in factory |
| MSIDWebviewFactory.m | Adds responseHeaders parameter support |
| MSIDWebviewFactory.h | New method signature with responseHeaders |
| MSIDBaseWebRequestConfiguration.m | Implements responseHeaders parameter support |
| MSIDBaseWebRequestConfiguration.h | Declares method with responseHeaders parameter |
| MSIDAuthorizeWebRequestConfiguration.m | Forwards responseHeaders to factory |
| MSIDWebviewSession.h | Adds lastResponseHeaders property |
| MSIDWebviewAuthorization.m | Passes response headers to configuration |
| MSIDError.m | Adds error code description for MDM enrollment |
| MSIDError.h | Defines MSIDErrorMDMEnrollmentCompletedNeedsRetry constant |
| project.pbxproj | Adds new files to Xcode project |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if (@available(iOS 17.4, macOS 15.0, *)) | ||
| { | ||
| self.webAuthSession.additionalHeaderFields = self.additionalHeaders; | ||
| MSID_LOG_WITH_CTX(MSIDLogLevelInfo, nil, @"Set %lu additional headers for ASWebAuthenticationSession", (unsigned long)self.additionalHeaders.count); | ||
| } | ||
| else | ||
| { | ||
| MSID_LOG_WITH_CTX(MSIDLogLevelWarning, nil, @"Additional headers provided but iOS 18+ required. Headers will be ignored."); |
There was a problem hiding this comment.
The availability check uses iOS 17.4, but the log message warns about iOS 18+ being required. This is inconsistent. According to Apple documentation, ASWebAuthenticationSession.additionalHeaderFields was introduced in iOS 17.4, not iOS 18. Update the log message to match the actual availability check or vice versa.
| - Acquired on FIRST msauth:// or browser:// redirect if needed | ||
| - Only ONE attempt per session (no retry) | ||
|
|
||
| Check before acquisition: !brtAcquired && !brtAttemptAttempted |
There was a problem hiding this comment.
Comment has a typo: "Attemptted" should be "Attempted" (double 't' should be single 't').
| Check before acquisition: !brtAcquired && !brtAttemptAttempted | |
| Check before acquisition: !brtAcquired && !brtAttempted |
| if (@available(iOS 18.0, macOS 15.0, *)) | ||
| { | ||
| self.aSWebAuthenticationSessionHandler = [[MSIDASWebAuthenticationSessionHandler alloc] initWithParentController:parentController | ||
| startURL:url | ||
| callbackScheme:callbackScheme | ||
| useEmpheralSession:NO | ||
| additionalHeaders:additionalHeaders]; | ||
| } | ||
| else | ||
| { | ||
| // Fallback for older OS versions (shouldn't happen since minimum is iOS 18 ?) | ||
| self.aSWebAuthenticationSessionHandler = [[MSIDASWebAuthenticationSessionHandler alloc] initWithParentController:parentController | ||
| startURL:url | ||
| callbackScheme:callbackScheme | ||
| useEmpheralSession:NO]; | ||
|
|
||
| if (additionalHeaders && additionalHeaders.count > 0) | ||
| { | ||
| MSID_LOG_WITH_CTX(MSIDLogLevelWarning, nil, @"[MSIDWebviewTransitionCoordinator] Additional headers ignored - iOS 18+ required"); | ||
| } | ||
| } |
There was a problem hiding this comment.
Same version check issue: line 246 uses iOS 18.0 but should use iOS 17.4 to match the actual availability of additionalHeaderFields. This is duplicated logic from the first method and should be corrected.
| // | ||
| // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
| // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE |
There was a problem hiding this comment.
Missing space after period in license header: "NONINFRINGEMENT.IN NO EVENT" should be "NONINFRINGEMENT. IN NO EVENT".
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE | |
| // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
| // THE SOFTWARE. | ||
|
|
||
| #import <XCTest/XCTest.h> | ||
| #import "MSIDWebInstallProfileResponse.h" |
There was a problem hiding this comment.
The test file imports MSIDWebInstallProfileResponse which doesn't exist in the implementation files. The actual implementation uses MSIDWebMDMEnrollmentCompletionResponse instead. This will cause compilation errors.
| case MSIDWebviewNavigationActionTypeOpenInASWebAuthenticationSession: | ||
| { | ||
| if (action.url) | ||
| { | ||
| MSID_LOG_WITH_CTX_PII(MSIDLogLevelInfo, self.context, @"URL needs to be opened in ASWebAuthenticationSession, calling CompleteWebauth with URL so controller can handle this via response: %@", MSID_PII_LOG_MASKABLE(action.url)); | ||
| [self completeWebAuthWithURL:action.url]; // should we complete request or invoke a delegate method to handoff to ASWebAuthSession ? | ||
| } | ||
| else | ||
| { | ||
| MSID_LOG_WITH_CTX(MSIDLogLevelError, self.context, @"OpenASWebAuthSession action has nil URL"); | ||
| [self completeWebAuthWithURL:requestURL]; | ||
| } | ||
| // alternatively replace above action with the delegate method to handoff to ASWebAuthSession | ||
| id<MSIDWebviewSpecialNavigationDelegate> strongSpecialNavigationDelegate = self.specialNavigationDelegate; | ||
| if (strongSpecialNavigationDelegate) | ||
| { | ||
| if ([strongSpecialNavigationDelegate respondsToSelector:@selector(webviewController:handleSpecialRedirect:completion:)]) | ||
| { | ||
| MSID_LOG_WITH_CTX(MSIDLogLevelInfo, self.context, | ||
| @"Detected special redirect scheme: %@. Delegating to navigationDelegate.", requestURL.scheme); | ||
|
|
||
| // Call delegate on main thread | ||
| __weak typeof(self) weakSelf = self; | ||
| dispatch_async(dispatch_get_main_queue(), ^{ | ||
| __strong typeof(self) strongSelf = weakSelf; | ||
| if (!strongSelf) return; | ||
|
|
||
| [strongSpecialNavigationDelegate handleASWebAuthenticationTransitionWithUrl:action.url embeddedWebview:strongSelf additionalHeaders:action.additionalHeaders MSIDSystemWebviewPurpose:action.purpose completion:^(MSIDWebviewNavigationAction * _Nonnull navigationAction, NSError * _Nonnull aswebAuthError) { | ||
| [strongSelf executeViewNavigationAction:navigationAction requestURL:requestURL error:aswebAuthError]; //need to test if this recursion could cause any issue | ||
| }]; | ||
| }); | ||
| } | ||
| } | ||
| break; | ||
| } |
There was a problem hiding this comment.
Missing break statement at the end of this case. While the closing brace on line 289 ends the case block, the code structure is confusing because the delegate call and its closing brace are outside the main if statement. This could lead to fall-through issues if the switch statement is modified later.
| nil, nil, error, | ||
| self.context.correlationId, | ||
| nil, NO); | ||
| [self endWebAuthWithURL:nil error:localerror]; //endWebAuth or do completeWebAuth ? |
There was a problem hiding this comment.
Comment on line 232 has a typo or unclear syntax: "endWebAuth or do completeWebAuth ?" should either be formatted as a proper question with consistent capitalization, or rephrased as a statement. Consider: "TODO: Should this call endWebAuth or completeWebAuth?"
| [self endWebAuthWithURL:nil error:localerror]; //endWebAuth or do completeWebAuth ? | |
| [self endWebAuthWithURL:nil error:localerror]; // TODO: Should this call endWebAuth or completeWebAuth? |
| @@ -4669,6 +4702,9 @@ | |||
| 60B3856D20AB3CDC00D546D0 /* ui */, | |||
| 96235FA0207D786A007EAB36 /* challangeHandlers */, | |||
There was a problem hiding this comment.
Spelling error: "challangeHandlers" should be "challengeHandlers" (single 'l').
| 96235FA0207D786A007EAB36 /* challangeHandlers */, | |
| 96235FA0207D786A007EAB36 /* challengeHandlers */, |
| @@ -0,0 +1,195 @@ | |||
| // Copyright (c) Microsoft Corporation. | |||
There was a problem hiding this comment.
The PR title "Veena/mob onb2" doesn't follow the required format specified in the PR description. According to the description, the title should be in the format "[Keyword1] [Keyword2]: Description" where Keyword1 is major/minor/patch and Keyword2 is feature/bugfix/engg/tests. Examples: "[MAJOR] [Feature]: new API" or "[minor] [bugfix]: fix crash". The current title needs to be updated to match this format.
| // THE SOFTWARE. | ||
|
|
||
| #import <XCTest/XCTest.h> | ||
| #import "MSIDWebProfileInstallTriggerResponse.h" |
There was a problem hiding this comment.
The test files reference classes MSIDWebProfileInstallTriggerResponse and MSIDWebInstallProfileResponse that don't exist in the implementation. The actual implementation uses MSIDWebMDMInstallProfileResponse and MSIDWebMDMEnrollmentCompletionResponse instead. Either the test files are outdated or the class names in tests need to be updated to match the implementation.
Co-authored-by: Veena11 <9446116+Veena11@users.noreply.github.com>
…t Flow (#1691) Documents the architecture and implementation details of PR #1689's in-app MDM profile installation and enrollment support for MSAL Objective-C. ## Added - **Design document** at `docs/design/mobile-mdm-onboarding-design.md` covering: - Architecture flow for embedded WKWebView → ASWebAuthenticationSession transitions - New components: navigation action resolver, transition coordinator, MDM response models - Modified components: local interactive controller, AAD webview controller, request/response pipeline - `msauth://` URL scheme routing for profile installation and enrollment completion - Broker Refresh Token (BRT) acquisition strategy - Post-enrollment handoff to SSO Extension - Risk assessment and known incomplete items (BRT placeholder, dual transition paths, no tests) ## Structure ``` docs/ └── design/ └── mobile-mdm-onboarding-design.md # 266 lines, 11 sections ``` Document includes flow diagrams, component tables, and files changed summary for the 38-file, +2,814/-37 line change in PR #1689. <!-- START COPILOT ORIGINAL PROMPT --> <details> <summary>Original prompt</summary> Add a comprehensive design document as a markdown file to the repository that documents the changes introduced in PR #1689 (`veena/mob_onb2` branch). The design doc should be placed at `docs/design/mobile-mdm-onboarding-design.md` and contain the following content exactly: --- # Design Document: Mobile MDM Onboarding & In-App Enrollment Flow **PR:** [#1689 – Veena/mob onb2](#1689) **Branch:** `veena/mob_onb2` → `dev` **Repository:** `AzureAD/microsoft-authentication-library-common-for-objc` **Author:** @swasti29 **Status:** Draft, Open **Stats:** +2,814 additions, -37 deletions across 38 files --- ## 1. Executive Summary This change introduces **in-app MDM (Mobile Device Management) profile installation and enrollment** support into the MSAL common library for Objective-C. It enables an interactive authentication flow where, when the AAD server signals that an Intune management profile must be installed, the embedded webview (WKWebView) seamlessly transitions to an `ASWebAuthenticationSession` for profile installation, then returns to the embedded webview to complete the original authentication. Additionally, it introduces **Broker Refresh Token (BRT)** acquisition logic triggered on special `msauth://` and `browser://` redirects. --- ## 2. Problem Statement Currently, when AAD determines that a device requires MDM enrollment (Intune profile installation) before authentication can complete, the library lacks the ability to: 1. **Detect** an MDM profile installation trigger (`msauth://installProfile`) from the server. 2. **Orchestrate** a seamless transition from the embedded webview to `ASWebAuthenticationSession` for profile download (which requires system-level handling). 3. **Resume** the embedded webview after profile installation completes (`msauth://in_app_enrollment_complete`). 4. **Acquire a Broker Refresh Token (BRT)** opportunistically on special redirect schemes. 5. **Hand off to the broker** (SSO Extension) after successful enrollment for final token acquisition. --- ## 3. Architecture Overview ### 3.1 High-Level Flow ``` ┌──────────────┐ ┌─────────────────────┐ ┌──────────────────────────┐ │ App calls │────▶│ LocalInteractive │────▶│ Embedded WKWebview │ │ acquireToken │ │ Controller │ │ (AAD login page) │ └──────────────┘ └─────────────────────┘ └──────────┬───────────────┘ │ ┌──────────────────────────────────┤ │ Server returns msauth:// │ │ installProfile redirect │ ▼ │ ┌──────────────────────┐ │ │ SpecialNavigation │ Delegate intercepts │ │ Delegate callback │◀──────────────────────┘ └──────┬───────────────┘ │ 1. Acquire BRT (first time only) │ 2. Resolve navigation action ▼ ┌──────────────────────┐ │ NavigationActionUtil │ Parses msauth:// URL │ resolves action │ + HTTP response headers └──────┬───────────────┘ │ Returns: OpenInASWebAuthSession ▼ ┌──────────────────────┐ ┌─────────────────────────┐ │ TransitionCoordinator│────▶│ ASWebAuthenticationSession│ │ - Suspend WKWebview │ │ (Profile installation) │ │ - Launch ASWebAuth │ └──────────┬──────────────┘ └──────────────────────┘ │ │ Callback: │ msauth://in_app_enrollment_complete ▼ ┌──────────────────────┐ ┌──────────────────────────┐ │ TransitionCoordinator│────▶│ Resume WKWebview │ │ - Resume webview │ │ Load enrollment callback │ │ - Clean up ASWebAuth │ └──────────┬───────────────┘ └──────────────────────┘ │ │ WKWebview processes │ enrollment completion ▼ ┌──────────────────────────────────────────────────────┐ │ Controller handles MDMEnrollmentCompletionResponse │ │ → Creates BrokerInteractiveController │ │ → Delegates to SSO Extension for final token │ └─────────────────────────────────────────────────────┘ ``` --- ## 4. New Components ### 4.1 New Classes | Class | Location | Purpose | |---|---|---| | **`... </details> <!-- START COPILOT CODING AGENT SUFFIX --> *This pull request was created from Copilot chat.* > <!-- START COPILOT CODING AGENT TIPS --> --- 💬 We'd love your input! Share your thoughts on Copilot coding agent in our [2 minute survey](https://gh.io/copilot-coding-agent-survey).
PR Title Format
Required Format:
[Keyword1] [Keyword2]: Descriptionmajor,minor, orpatch(case-insensitive)feature,bugfix,engg, ortests(case-insensitive)Examples:
[MAJOR] [Feature]: new API[minor] [bugfix]: fix crash[PATCH][tests]:add coverageProposed changes
Describe what this PR is trying to do.
Type of change
Risk
Additional information