Skip to content

Add NativeAuthRequestInterceptor for custom per-request headers and UI Automation, Fixes AB#3600652#3112

Merged
spetrescu84 merged 28 commits into
devfrom
spetrescu/custom_headers
Jun 3, 2026
Merged

Add NativeAuthRequestInterceptor for custom per-request headers and UI Automation, Fixes AB#3600652#3112
spetrescu84 merged 28 commits into
devfrom
spetrescu/custom_headers

Conversation

@spetrescu84

@spetrescu84 spetrescu84 commented May 13, 2026

Copy link
Copy Markdown
Contributor

Implement custom HTTP headers request interceptor for native auth CIAM requests

  • Add NativeAuthHeaderValidator to enforce header naming rules
  • Pass interceptor through the full config propagation chain: OAuth2StrategyParameters → NativeAuthCIAMAuthority → NativeAuthOAuth2Configuration → NativeAuthOAuth2StrategyFactory → all 4 interactors (SignIn, SignUp, ResetPassword, JIT)
  • Add interceptor field to BaseNativeAuthCommandParameters
  • Wire interceptor in NativeAuthMsalController.createOAuth2Strategy()
  • Add unit tests for header validation
  • Add integration tests for interceptor in all interactors

UI Automation Test Support

  • NativeAuthSampleApp.java — App model class for UI automation (package name, APK filename constants, no-op handleFirstRun()/initialiseAppImpl())
  • CopyFileRule.java — Added NativeAuthSampleApp.NATIVE_AUTH_SAMPLE_APK to the list of APKs copied from /sdcard/ to /data/local/tmp/ at test runtime
  • LabConstants.java — Added CIAM user type constant
  • UserType.java — Added CIAM enum value for lab queries

API Change: NativeAuthRequestInterceptor moved to MSAL

The NativeAuthRequestInterceptor interface has been moved to the MSAL module (com.microsoft.identity.nativeauth package) as it is a public-facing type that customers implement. Common now uses the base OAuth2RequestInterceptor type internally, which NativeAuthRequestInterceptor extends. This follows the established pattern where public nativeauth types live in MSAL.

AB#3600652

Implement custom HTTP headers request interceptor for native auth CIAM
requests, matching the iOS PR (microsoft-authentication-library-for-objc#2862).

- Add NativeAuthRequestInterceptor interface for injecting custom x-* headers
- Add NativeAuthHeaderValidator to enforce header naming rules
- Pass interceptor through the full config propagation chain:
  OAuth2StrategyParameters → NativeAuthCIAMAuthority →
  NativeAuthOAuth2Configuration → NativeAuthOAuth2StrategyFactory →
  all 4 interactors (SignIn, SignUp, ResetPassword, JIT)
- Add interceptor field to BaseNativeAuthCommandParameters
- Wire interceptor in NativeAuthMsalController.createOAuth2Strategy()
- Add unit tests for header validation (11 tests)
- Add integration tests for interceptor in SignInInteractor (6 tests)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spetrescu84 spetrescu84 self-assigned this May 13, 2026
Copilot AI review requested due to automatic review settings May 13, 2026 16:41
@spetrescu84 spetrescu84 requested review from a team as code owners May 13, 2026 16:41
@spetrescu84

Copy link
Copy Markdown
Contributor Author

@copilot resolve the merge conflicts in this pull request

Co-authored-by: spetrescu84 <111577419+spetrescu84@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a NativeAuthRequestInterceptor extension point that lets applications inject custom x-* HTTP headers (e.g., Akamai/fraud signals) into native-auth (CIAM) network requests. The interceptor is threaded from BaseNativeAuthCommandParameters through OAuth2StrategyParametersNativeAuthCIAMAuthorityNativeAuthOAuth2ConfigurationNativeAuthOAuth2StrategyFactory and into all four interactors (SignIn, SignUp, ResetPassword, JIT). A NativeAuthHeaderValidator enforces the x- prefix and rejects reserved prefixes (x-ms-, x-client-, x-broker-, x-app-).

Changes:

  • New NativeAuthRequestInterceptor interface and NativeAuthHeaderValidator (with unit tests) for additive, validated headers.
  • Interceptor field added to OAuth2StrategyParameters, BaseNativeAuthCommandParameters, NativeAuthOAuth2Configuration, and the four interactors; NativeAuthMsalController.createOAuth2Strategy propagates it; identical applyInterceptorHeaders helper duplicated in each interactor.
  • Integration tests for SignInInteractor (only) covering merge, null/empty interceptor, reserved-prefix filtering, URL passthrough; changelog entry added.

Reviewed changes

Copilot reviewed 15 out of 15 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
common4j/.../providers/NativeAuthRequestInterceptor.kt New SAM-style interface for per-request additional headers.
common4j/.../providers/NativeAuthHeaderValidator.kt New validator filtering headers by prefix rules.
common4j/.../providers/NativeAuthOAuth2Configuration.kt Adds nullable requestInterceptor config field.
common4j/.../providers/NativeAuthOAuth2StrategyFactory.kt Passes interceptor into all four interactors.
common4j/.../providers/interactors/SignInInteractor.kt Adds interceptor field + applyInterceptorHeaders and applies to 4 calls.
common4j/.../providers/interactors/SignUpInteractor.kt Same interceptor wiring as SignIn (no tests added).
common4j/.../providers/interactors/ResetPasswordInteractor.kt Same interceptor wiring (no tests added).
common4j/.../providers/interactors/JITInteractor.kt Same interceptor wiring (no tests added).
common4j/.../authorities/NativeAuthCIAMAuthority.kt Threads interceptor from strategy params into config; uses fully-qualified type name.
common4j/.../providers/oauth2/OAuth2StrategyParameters.java Adds mRequestInterceptor; introduces dependency from generic OAuth2 layer to nativeauth package.
common4j/.../commands/parameters/BaseNativeAuthCommandParameters.java Adds transient requestInterceptor field (excluded from equals/hash).
common/.../controllers/NativeAuthMsalController.kt Forwards parameters.requestInterceptor into the strategy params builder.
common4j/.../NativeAuthHeaderValidatorTest.kt New unit tests for the prefix validator.
common4j/.../SignInInteractorRequestInterceptorTest.kt New tests for interceptor behavior, only covering SignIn.
changelog.txt Adds entry with empty PR number (#) and misleading "HttpRequest interceptor" wording.
Comments suppressed due to low confidence (1)

common4j/src/main/com/microsoft/identity/common/java/nativeauth/providers/interactors/SignInInteractor.kt:74

  • additionalHeaders is invoked from inside the network call path with no try/catch around it (see applyInterceptorHeaders in each interactor). A buggy interceptor implementation that throws (e.g., NPE while computing a fraud signal) will tear down the entire token request with an unrelated exception type that the existing native-auth error mapping is not designed to handle. Consider catching Throwable from the interceptor invocation, logging it, and falling back to no extra headers so an interceptor crash cannot break sign-in/sign-up/reset-password/JIT for end users.
    private fun applyInterceptorHeaders(requestUrl: URL, headers: Map<String, String?>): Map<String, String?> {
        if (requestInterceptor == null) return headers
        val additionalHeaders = requestInterceptor.additionalHeaders(requestUrl) ?: return headers
        val validHeaders = NativeAuthHeaderValidator.filterValidHeaders(additionalHeaders)
        if (validHeaders.isEmpty()) return headers
        val mergedHeaders = headers.toMutableMap()
        mergedHeaders.putAll(validHeaders)
        return mergedHeaders
    }

Comment thread changelog.txt Outdated
Comment on lines +27 to +69
@@ -61,6 +62,12 @@ public class OAuth2StrategyParameters {
@Nullable
public final List<String> mCapabilities;

/**
* An optional interceptor for injecting custom HTTP headers into native auth requests.
*/
@Nullable
public final transient NativeAuthRequestInterceptor mRequestInterceptor;

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor

@copilot resolve the merge conflicts in this pull request

Resolved and pushed. I merged origin/dev, fixed the changelog.txt conflict, and finalized the merge commit in f04517b.

@spetrescu84

Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Applied the linked review-thread updates in d415f28 (plus the main refactor/tests in f450877): shared interceptor-header merge utility used by all 4 interactors, added shared tests, normalized validated custom header names to lowercase with coverage, improved interceptor threading/exception contract docs, removed FQCN usage, decoupled OAuth2StrategyParameters from native-auth type via generic OAuth2RequestInterceptor, and updated changelog wording/PR number.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.google.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=1024m -XX:&#43;HeapDumpOnOutOfMemoryError -Xmx3072m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant (dns block)
  • identitydivision.pkgs.visualstudio.com
    • Triggering command: /usr/lib/jvm/temurin-17-jdk-amd64/bin/java /usr/lib/jvm/temurin-17-jdk-amd64/bin/java --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.base/java.lang.invoke=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.api=ALL-UNNAMED --add-exports=jdk.compiler/com.sun.tools.javac.util=ALL-UNNAMED --add-opens=java.base/java.util=ALL-UNNAMED --add-opens=java.prefs/java.util.prefs=ALL-UNNAMED --add-opens=java.base/java.nio.charset=ALL-UNNAMED --add-opens=java.base/java.net=ALL-UNNAMED --add-opens=java.base/java.util.concurrent.atomic=ALL-UNNAMED --add-opens=java.xml/javax.xml.namespace=ALL-UNNAMED -XX:MaxMetaspaceSize=1024m -XX:&#43;HeapDumpOnOutOfMemoryError -Xmx3072m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant (dns block)
  • www.puppycrawl.com
    • Triggering command: /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/tools/linux64/java/bin/java /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/tools/linux64/java/bin/java -jar /opt/hostedtoolcache/CodeQL/2.25.1/x64/codeql/xml/tools/xml-extractor.jar --fileList=/tmp/codeql-scratch-b2678d0398b00160/dbs/java/working/files-to-index8567248807499331946.list --sourceArchiveDir=/tmp/codeql-scratch-b2678d0398b00160/dbs/java/src --outputDir=/tmp/codeql-scratch-b2678d0398b00160/dbs/java/trap/java (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

- Add SignUpInteractorRequestInterceptorTest (8 tests)
- Add ResetPasswordInteractorRequestInterceptorTest (7 tests)
- Add JITInteractorRequestInterceptorTest (5 tests)
- Expand SignInInteractorRequestInterceptorTest to cover all 7 methods (12 tests)
- Total: 43 tests (32 interactor + 11 validator), all passing

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

Copy link
Copy Markdown

❌ Work item link check failed. Description does not contain AB#{ID}.

Click here to Learn more.

@spetrescu84

Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Applied. The review-thread items are implemented in the latest commits, including shared interceptor header handling, case-insensitive header normalization/merge behavior, generic OAuth2 interceptor decoupling, and follow-up docs/guards/tests (1611df9, d415f28, f450877).

@spetrescu84

Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Applied; the linked review-thread changes are already in the branch via 1611df9, d415f28, and f450877 (shared interceptor merge utility + coverage, case-insensitive header normalization/merge behavior, generic OAuth2 interceptor decoupling, and docs/changelog updates).

Switches Maven publishing from the AndroidADAL feed to the NewAndroid feed
to eliminate the upstream-feed traversal latency that occurs when downstream
pipeline stages resolve freshly-published artifacts.

Background:
- Builds publish to AndroidADAL but consumers fetch from NewAndroid.
- NewAndroid resolves AndroidADAL only via its upstream chain, after walking
  Maven Central, Google, etc., causing first-fetch latency on every newly
  published version (which CI agents always pay due to ephemeral caches).
- AndroidADAL will be added as an upstream of NewAndroid (done outside this
  change) so any external consumers still resolving from AndroidADAL keep
  working transparently.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
override fun createOAuth2Strategy(parameters: OAuth2StrategyParameters): NativeAuthOAuth2Strategy {
val config = createNativeAuthOAuth2Configuration(parameters.mChallengeTypes, parameters.mCapabilities)
if (parameters.mRequestInterceptor != null && parameters.mRequestInterceptor !is NativeAuthRequestInterceptor) {
Logger.warn(TAG, "Ignoring non-native OAuth2RequestInterceptor for NativeAuthCIAMAuthority.")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think WARN message is misleading the situation.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've changed how this works, please take a look

Each test now uses the actual production endpoint URL for its method
(e.g., /signup/v1.0/challenge for performChallenge) instead of a
single shared URL per file.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
fadidurah and others added 4 commits May 21, 2026 12:27
The publishing block in testutils and uiautomationutilities iterates
implementation dependencies to populate the generated POM. For project
deps like project(':keyvault'), it.version reads project(':keyvault').version
-- which Gradle defaults to the string 'unspecified' if that sibling
project hasn't been configured yet.

With Gradle configure-on-demand enabled, the publish task for common
may run without configuring sibling projects like :keyvault, :labapi,
:common, etc. The published POM then contained 'keyvault:unspecified',
which caused downstream consumers (AADAuthenticator distDebugUnitTest)
to fail at dependency resolution.

Fix: for project dependencies, write the current artifact's version
(getAppVersionName()) since all sibling library projects share the same
version. External dependencies still use it.version as before.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
thentication-library-common-for-android into fadi/publish-libraries-to-newandroid-feed
Replace all usages of NativeAuthRequestInterceptor in common with the
base OAuth2RequestInterceptor type. The NativeAuthRequestInterceptor
interface is a public-facing API that customers implement, so it belongs
in the MSAL module (com.microsoft.identity.nativeauth package) following
the established pattern for public nativeauth types.

Common now uses OAuth2RequestInterceptor internally, which is the parent
interface with the same contract. Any MSAL-level NativeAuthRequestInterceptor
implementation will satisfy this type since it extends OAuth2RequestInterceptor.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>

@mustafamizrak mustafamizrak left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why did you move NativeAuthRequestInterceptor to MSAL module? What are the reasons?

@antrix1989

Copy link
Copy Markdown

Why did you move NativeAuthRequestInterceptor to MSAL module? What are the reasons?

We use NativeAuthRequestInterceptor in public api, that is why we moved it to the main MSAL module. Common core code should not be exposed in the public api.

@spetrescu84 spetrescu84 changed the title Add NativeAuthRequestInterceptor for custom per-request headers Fixes AB#3600652 Add NativeAuthRequestInterceptor for custom per-request headers and UI Automation, Fixes AB#3600652 May 27, 2026
spetrescu84 and others added 2 commits June 3, 2026 11:17
…t) to resolve MSAL submodule conflict

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spetrescu84 spetrescu84 merged commit 242da8c into dev Jun 3, 2026
28 checks passed
shahzaibj added a commit that referenced this pull request Jun 9, 2026
…xes AB#3618269 (#3146)

[AB#3618269](https://identitydivision.visualstudio.com/fac9d424-53d2-45c0-91b5-ef6ba7a6bf26/_workitems/edit/3618269)

When release-integration/24.3.0 was back-merged into dev (#3131), some
entries that were merged to dev *after* the 24.3.0 RC cut got listed
under `Version 24.3.0`. They actually shipped in dev *after* 24.3.0 was
finalized, so they belong under `vNext` for the next release.

This PR moves 5 entries from `Version 24.3.0` to `vNext`:

- #3133 SwitchBrowserRedirectActivity for non-broker Switch Browser
flows
- #3125 Update OpenTelemetry to 1.62.0
- #3135 Disabling GetCookies API from webapps APIs
- #3132 Enable passkey registration by default in CommonFlight
- #3112 Add NativeAuthRequestInterceptor for native auth flows

Note: #3128 (Fix Token Endpoint Server Telemetry Parsing) correctly
stays under Version 24.3.0 because it was cherry-picked into the release
branch before the RC cut.

### Verification
Identified by diffing the current dev `changelog.txt` against the file
at commit `e80117451` (final 24.3.0 "remove RC" cut). Any entry
currently listed under Version 24.3.0 that wasn't present at the RC cut
must have arrived via the back-merge.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

6 participants