Skip to content

Commit f3b7d9b

Browse files
authored
Add design documentation for Mobile MDM Onboarding & In-App Enrollment 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).
2 parents dfe40b0 + b8f1cdd commit f3b7d9b

File tree

1 file changed

+266
-0
lines changed

1 file changed

+266
-0
lines changed
Lines changed: 266 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,266 @@
1+
# Design Document: Mobile MDM Onboarding &amp; In-App Enrollment Flow
2+
3+
**PR:** <a href="https://github.com/AzureAD/microsoft-authentication-library-common-for-objc/pull/1689">#1689 – Veena/mob onb2</a>
4+
**Branch:** `veena/mob_onb2``dev`
5+
**Repository:** `AzureAD/microsoft-authentication-library-common-for-objc`
6+
**Author:** @swasti29
7+
**Status:** Draft, Open
8+
**Stats:** +2,814 additions, -37 deletions across 38 files
9+
10+
---
11+
12+
## 1. Executive Summary
13+
14+
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.
15+
16+
---
17+
18+
## 2. Problem Statement
19+
20+
Currently, when AAD determines that a device requires MDM enrollment (Intune profile installation) before authentication can complete, the library lacks the ability to:
21+
22+
1. **Detect** an MDM profile installation trigger (`msauth://installProfile`) from the server.
23+
2. **Orchestrate** a seamless transition from the embedded webview to `ASWebAuthenticationSession` for profile download (which requires system-level handling).
24+
3. **Resume** the embedded webview after profile installation completes (`msauth://in_app_enrollment_complete`).
25+
4. **Acquire a Broker Refresh Token (BRT)** opportunistically on special redirect schemes.
26+
5. **Hand off to the broker** (SSO Extension) after successful enrollment for final token acquisition.
27+
28+
---
29+
30+
## 3. Architecture Overview
31+
32+
### 3.1 High-Level Flow
33+
34+
```
35+
┌──────────────┐ ┌─────────────────────┐ ┌──────────────────────────┐
36+
│ App calls │────▶│ LocalInteractive │────▶│ Embedded WKWebview │
37+
│ acquireToken │ │ Controller │ │ (AAD login page) │
38+
└──────────────┘ └─────────────────────┘ └──────────┬───────────────┘
39+
40+
┌──────────────────────────────────┤
41+
│ Server returns msauth:// │
42+
│ installProfile redirect │
43+
▼ │
44+
┌──────────────────────┐ │
45+
│ SpecialNavigation │ Delegate intercepts │
46+
│ Delegate callback │◀──────────────────────┘
47+
└──────┬───────────────┘
48+
│ 1. Acquire BRT (first time only)
49+
│ 2. Resolve navigation action
50+
51+
┌──────────────────────┐
52+
│ NavigationActionUtil │ Parses msauth:// URL
53+
│ resolves action │ + HTTP response headers
54+
└──────┬───────────────┘
55+
│ Returns: OpenInASWebAuthSession
56+
57+
┌──────────────────────┐ ┌─────────────────────────┐
58+
│ TransitionCoordinator│────▶│ ASWebAuthenticationSession│
59+
│ - Suspend WKWebview │ │ (Profile installation) │
60+
│ - Launch ASWebAuth │ └──────────┬──────────────┘
61+
└──────────────────────┘ │
62+
│ Callback:
63+
│ msauth://in_app_enrollment_complete
64+
65+
┌──────────────────────┐ ┌──────────────────────────┐
66+
│ TransitionCoordinator│────▶│ Resume WKWebview │
67+
│ - Resume webview │ │ Load enrollment callback │
68+
│ - Clean up ASWebAuth │ └──────────┬───────────────┘
69+
└──────────────────────┘ │
70+
│ WKWebview processes
71+
│ enrollment completion
72+
73+
┌──────────────────────────────────────────────────────┐
74+
│ Controller handles MDMEnrollmentCompletionResponse │
75+
│ → Creates BrokerInteractiveController │
76+
│ → Delegates to SSO Extension for final token │
77+
└─────────────────────────────────────────────────────┘
78+
```
79+
80+
---
81+
82+
## 4. New Components
83+
84+
### 4.1 New Classes
85+
86+
| Class | Location | Purpose |
87+
|---|---|---|
88+
| **`MSIDWebviewNavigationAction`** | `embeddedWebview/` | Value object representing a navigation decision (load request, open ASWebAuth, complete, fail, etc.) with associated action type enum |
89+
| **`MSIDWebviewNavigationActionUtil`** | `util/` | Singleton that resolves `msauth://` URLs into `MSIDWebviewNavigationAction` objects by parsing host/query params and HTTP response headers |
90+
| **`MSIDWebviewTransitionCoordinator`** | `webview/` | Orchestrates suspend/resume of embedded webview and launch/completion of `ASWebAuthenticationSession` |
91+
| **`MSIDWebMDMInstallProfileResponse`** | `webview/response/` | Response model for `msauth://installProfile` trigger, carrying `intuneURL` and `intuneToken` from HTTP headers |
92+
| **`MSIDWebMDMEnrollmentCompletionResponse`** | `webview/response/` | Response model for `msauth://in_app_enrollment_complete`, carrying enrollment `status` and `additionalInfo` |
93+
94+
### 4.2 New Protocol
95+
96+
| Protocol | Location | Purpose |
97+
|---|---|---|
98+
| **`MSIDWebviewSpecialNavigationDelegate`** | `embeddedWebview/` | Delegate protocol allowing controllers to intercept `msauth://` and `browser://` redirects, perform BRT acquisition, and return navigation actions |
99+
100+
### 4.3 New Enums
101+
102+
| Enum | Values | Purpose |
103+
|---|---|---|
104+
| `MSIDWebviewNavigationActionType` | `ContinueDefault`, `LoadRequestInWebview`, `OpenInASWebAuthenticationSession`, `OpenInExternalBrowser`, `CompleteWebAuthWithURL`, `FailWithError` | Defines what the webview should do after a special redirect is intercepted |
105+
| `MSIDSystemWebviewPurpose` | `Unknown`, `InstallProfile` | Defines why an ASWebAuthenticationSession is being launched (affects ephemeral session behavior) |
106+
107+
### 4.4 New Error Code
108+
109+
| Error Code | Value | Purpose |
110+
|---|---|---|
111+
| `MSIDErrorMDMEnrollmentCompletedNeedsRetry` | `-51733` | Signals that MDM enrollment completed and auth should be retried |
112+
113+
---
114+
115+
## 5. Modified Components
116+
117+
### 5.1 `MSIDLocalInteractiveController`
118+
119+
**Major changes** (~+550 / -29 lines):
120+
121+
- **Now conforms to `MSIDWebviewSpecialNavigationDelegate`** – acts as the delegate for embedded webview special redirect handling.
122+
- **New properties:** `transitionCoordinator`, `lastResponseHeaders`, `brtAttempted`, `brtAcquired`.
123+
- **Webview configuration block:** Sets itself as the `specialNavigationDelegate` on the embedded webview controller during initialization.
124+
- **New response handlers:**
125+
- `handleWebMDMInstallProfileResponse:` – validates Intune URL, suspends webview, launches ASWebAuth.
126+
- `handleWebMDMEnrollmentCompletionResponse:` – on success, creates `MSIDBrokerInteractiveController` and delegates to broker for final token; on failure, returns error.
127+
- `handleASWebAuthnSessionCompletion:` – processes ASWebAuth callback, resumes webview.
128+
- **BRT acquisition:** `shouldAcquireBRT` / `acquireBRTWithCompletion:` – acquires BRT on first `msauth://` or `browser://` redirect (one attempt per session, placeholder implementation).
129+
- **Delegate methods:** `webviewController:handleSpecialRedirect:completion:`, `handleMsauthRedirect:`, `handleBrowserRedirect:`, `processResponseHeaders:`, `handleASWebAuthenticationTransitionWithUrl:`.
130+
131+
### 5.2 `MSIDAADOAuthEmbeddedWebviewController`
132+
133+
- **HTTP response capture:** Sets up `navigationResponseBlock` to store the last HTTP response and propagate headers to `MSIDWebviewSession.lastResponseHeaders`.
134+
- **Delegate-based redirect interception:** In `decidePolicyAADForNavigationAction:`, when `msauth://` or `browser://` is detected AND a `specialNavigationDelegate` is set, cancels the navigation and delegates to the controller.
135+
- **New action executor:** `executeViewNavigationAction:requestURL:error:` – switch-based handler that executes the returned `MSIDWebviewNavigationAction` (load request, open ASWebAuth, complete auth, fail).
136+
137+
### 5.3 `MSIDAADWebviewFactory`
138+
139+
- **New response type parsing:** Overloaded `oAuthResponseWithURL:` to accept `responseHeaders` parameter. Now attempts to create `MSIDWebMDMInstallProfileResponse` (using URL + HTTP headers) and `MSIDWebMDMEnrollmentCompletionResponse` before falling through to existing response types.
140+
141+
### 5.4 Completion Block Type Generalization
142+
143+
Several completion block typedefs changed from `MSIDWebWPJResponse` to the more general `MSIDWebviewResponse`:
144+
145+
- `MSIDInteractiveAuthorizationCodeCompletionBlock`
146+
- `MSIDInteractiveRequestCompletionBlock`
147+
- `MSIDInteractiveTokenRequest` completion handler
148+
149+
This allows the same pipeline to carry MDM-specific response types alongside existing WPJ responses.
150+
151+
### 5.5 HTTP Response Header Propagation
152+
153+
A new pipeline ensures HTTP response headers flow from WKWebView navigation responses to the response factory:
154+
155+
1. `MSIDAADOAuthEmbeddedWebviewController.navigationResponseBlock` captures `NSHTTPURLResponse`.
156+
2. Headers stored in `MSIDWebviewSession.lastResponseHeaders`.
157+
3. `MSIDWebviewAuthorization` passes `lastResponseHeaders` to `MSIDBaseWebRequestConfiguration.responseWithResultURL:factory:responseHeaders:context:error:`.
158+
4. Factory uses headers to extract `x-ms-intune-install-url` and `x-ms-intune-token` for MDM response objects.
159+
160+
### 5.6 `MSIDInteractiveTokenRequestParameters`
161+
162+
- **New property:** `webviewConfigurationBlock` – a block called after webview creation to allow controllers to inject delegates/configuration without subclassing the webview.
163+
164+
### 5.7 `MSIDInteractiveAuthorizationCodeRequest`
165+
166+
- **New property:** `currentWebview` – stores reference to the active webview for suspension.
167+
- **Configuration block invocation:** Calls `webviewConfigurationBlock` after webview creation, ensuring it runs on the main thread.
168+
169+
---
170+
171+
## 6. Key Design Decisions
172+
173+
### 6.1 Dual Transition Paths (Option 1 vs Option 2)
174+
175+
The code contains **two implementation paths** for ASWebAuthenticationSession transitions:
176+
177+
| Path | Trigger | Flow |
178+
|---|---|---|
179+
| **Option 1 (Response-based)** | MDM response bubbles up through completion blocks to controller | Controller calls `transitionCoordinator.launchASWebAuthenticationSession:` with `MSIDRequestCompletionBlock` |
180+
| **Option 2 (Delegate-based)** | `MSIDWebviewSpecialNavigationDelegate` callback from webview | Controller calls `transitionCoordinator.launchASWebAuthenticationSessionWithUrl:` returning `MSIDWebviewNavigationAction` |
181+
182+
Both paths coexist in the codebase. The delegate-based path (Option 2) provides finer-grained control at the webview navigation level.
183+
184+
### 6.2 BRT Acquisition Strategy
185+
186+
- **Trigger:** First `msauth://` or `browser://` redirect in a session.
187+
- **Policy:** Single attempt per session (`brtAttempted` flag). No retry on failure.
188+
- **Current state:** **Placeholder implementation** – always returns success. Needs actual BRT acquisition logic.
189+
190+
### 6.3 Webview Suspend/Resume
191+
192+
Rather than dismissing the embedded webview during profile installation, the `TransitionCoordinator` **hides** the parent view controller's view (`view.hidden = YES/NO`). This keeps the WKWebView process alive and avoids losing navigation state.
193+
194+
### 6.4 iOS 18+ Requirement for Additional Headers
195+
196+
`ASWebAuthenticationSession` with additional headers (`initWithParentController:startURL:callbackScheme:useEmpheralSession:additionalHeaders:`) requires iOS 18.0+ / macOS 15.0+. For older OS versions, headers are silently ignored with a warning log.
197+
198+
---
199+
200+
## 7. `msauth://` URL Scheme Routing
201+
202+
The `MSIDWebviewNavigationActionUtil` resolves the following `msauth://` hosts:
203+
204+
| URL Pattern | Action |
205+
|---|---|
206+
| `msauth://enroll?cpurl=...` | Load `cpurl` in embedded webview |
207+
| `msauth://compliance?cpurl=...` | Load `cpurl` in embedded webview |
208+
| `msauth://installProfile?requireASWebAuthenticationSession=true` | Open URL from `x-ms-intune-install-url` header in ASWebAuthenticationSession with `x-ms-intune-token` header |
209+
| `msauth://installProfile` (without ASWebAuth flag) | Load profile URL in embedded webview |
210+
| `msauth://in_app_enrollment_complete` | Complete web auth with this URL |
211+
| Other `msauth://` hosts | Continue default behavior |
212+
213+
---
214+
215+
## 8. Post-Enrollment Flow
216+
217+
On successful enrollment (`msauth://in_app_enrollment_complete` with `status=success`):
218+
219+
1. ASWebAuthenticationSession is dismissed.
220+
2. Embedded webview is resumed.
221+
3. Enrollment completion response is processed.
222+
4. A new `MSIDBrokerInteractiveController` is created with the same request parameters.
223+
5. Control is delegated to the broker (SSO Extension) to complete authentication.
224+
6. On non-iOS platforms, an error is returned ("Broker authentication not supported").
225+
226+
---
227+
228+
## 9. Known TODOs &amp; Incomplete Items
229+
230+
| Item | Location | Status |
231+
|---|---|---|
232+
| BRT acquisition logic | `MSIDLocalInteractiveController.acquireBRTWithCompletion:` | **Placeholder** – always returns success |
233+
| Extra headers/query params for enrollment and compliance URLs | `MSIDWebviewNavigationActionUtil` | TODO comments |
234+
| Telemetry for response headers | `processResponseHeaders:` | TODO comment |
235+
| Delegate method duplication | `MSIDLocalInteractiveController` | TODO: move to common place |
236+
| Commented-out method declarations | `MSIDLocalInteractiveController+Internal.h` | Commented code for `handleWebMDM*` methods |
237+
| Cleanup ordering bug | `MSIDWebviewTransitionCoordinator.cleanup` | Sets `suspendedEmbeddedWebview = nil` before checking it (dead code) |
238+
239+
---
240+
241+
## 10. Risk Assessment
242+
243+
| Area | Risk Level | Notes |
244+
|---|---|---|
245+
| Completion block type change (`MSIDWebWPJResponse``MSIDWebviewResponse`) | **Medium** | Breaking change for downstream consumers that explicitly type-check against `MSIDWebWPJResponse` |
246+
| Webview suspend/resume via `view.hidden` | **Medium** | May not work correctly with all view controller presentation styles |
247+
| Dual transition paths (Option 1 + Option 2) | **Low-Medium** | Code complexity; should converge on one approach before merge |
248+
| BRT acquisition placeholder | **High** | Feature is non-functional without actual implementation |
249+
| iOS 18+ additional headers | **Low** | Graceful fallback for older OS |
250+
| No unit tests in this PR | **High** | 38 changed files with 0 test files added |
251+
252+
---
253+
254+
## 11. Files Changed Summary
255+
256+
| Category | Files |
257+
|---|---|
258+
| **New Classes** (10 files) | `MSIDWebviewNavigationAction.h/m`, `MSIDWebviewNavigationActionUtil.h/m`, `MSIDWebviewTransitionCoordinator.h/m`, `MSIDWebMDMInstallProfileResponse.h/m`, `MSIDWebMDMEnrollmentCompletionResponse.h/m` |
259+
| **New Protocol** (1 file) | `MSIDWebviewSpecialNavigationDelegate.h` |
260+
| **Modified Controllers** (4 files) | `MSIDLocalInteractiveController.h/m`, `MSIDLocalInteractiveController+Internal.h` |
261+
| **Modified Webview** (4 files) | `MSIDAADOAuthEmbeddedWebviewController.h/m`, `MSIDOAuth2EmbeddedWebviewController.h` |
262+
| **Modified Request/Response pipeline** (8 files) | `MSIDInteractiveAuthorizationCodeRequest.h/m`, `MSIDInteractiveTokenRequest.m`, `MSIDInteractiveTokenRequest+Internal.h`, `MSIDInteractiveRequestControlling.h`, `MSIDWebviewFactory.h/m`, `MSIDAADWebviewFactory.m` |
263+
| **Modified Configuration** (3 files) | `MSIDBaseWebRequestConfiguration.h/m`, `MSIDAuthorizeWebRequestConfiguration.m` |
264+
| **Modified Session/Auth** (3 files) | `MSIDWebviewSession.h`, `MSIDWebviewAuthorization.m`, `MSIDInteractiveTokenRequestParameters.h` |
265+
| **Modified Error codes** (2 files) | `MSIDError.h/m` |
266+
| **Project file** (1 file) | `project.pbxproj` |

0 commit comments

Comments
 (0)