Skip to content

Add NativeAuthRequestInterceptor for custom per-request HTTP headers, Fixes AB#3606613#2511

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

Add NativeAuthRequestInterceptor for custom per-request HTTP headers, Fixes AB#3606613#2511
spetrescu84 merged 24 commits into
devfrom
spetrescu/custom_headers

Conversation

@spetrescu84

@spetrescu84 spetrescu84 commented May 13, 2026

Copy link
Copy Markdown
Contributor

Summary

Enables apps to inject custom x-* headers into native auth HTTP requests (e.g., for CIAM fraud-signal scenarios like Akamai sensor data).

Changes

MSAL (this PR)

  • NativeAuthRequestInterceptor.kt — Defined in com.microsoft.identity.nativeauth package (public API for customers to implement)
  • NativeAuthPublicClientApplicationConfiguration.kt — Added @Transient var requestInterceptor property with mergeConfiguration support
  • NativeAuthPublicClientApplicationParameters.kt — Exposes interceptor using the MSAL-level type
  • PublicClientApplication.java — Updated import to use MSAL-level NativeAuthRequestInterceptor
  • CommandParametersAdapter.java — Propagated requestInterceptor through all 16 native auth builder calls; fixed pre-existing duplicate .challengeType() in createSignInResendCodeCommandParameters()

UI Automation Tests (this PR)

  • AbstractCrossAppUiTest.java — Lightweight base class for cross-app UI tests (provides mDevice, mLabClient, CopyFileRule, uninstall/reinstall of target app). Avoids the heavy RulesHelper chain that causes SIGKILL in cross-app scenarios.
  • NativeAuthSignInTest.kt — End-to-end sign-in test using CIAM tenant with custom headers on the initiate endpoint. Gets credentials from lab vault, launches NativeAuthSampleApp with injected config, and verifies sign-in completes successfully.
  • AndroidManifest.xml — Added <queries> block for NativeAuthSampleApp package (required on Android 11+ for getLaunchIntentForPackage())

Common (spetrescu/custom_headers)

  • NativeAuthHeaderValidator.kt — Validates headers (must start with x-, must NOT start with x-ms-, x-client-, x-broker-, x-app-)
  • Wired interceptor through full propagation chain using OAuth2RequestInterceptor base type
  • Applied applyInterceptorHeaders() before every httpClient.post() call in all 4 interactors

Architecture Decision: Interface Placement

NativeAuthRequestInterceptor is defined in MSAL (com.microsoft.identity.nativeauth) following the established pattern where all public-facing nativeauth types live in the MSAL module. Common uses the parent OAuth2RequestInterceptor type internally. This ensures sample apps and customers never need to import from com.microsoft.identity.common.*.

Testing

  • 11 unit tests for NativeAuthHeaderValidator (all validation scenarios)
  • Integration tests for interceptor wiring in all 4 interactors (SignIn, SignUp, ResetPassword, JIT)
  • RequestInterceptorHeaderUtilsTest for merge logic
  • NativeAuthSignInTest — E2E UI automation test (sign-in with custom headers via CIAM tenant)
  • All tests pass

Design Decisions

  • Synchronous interface (vs iOS async callbacks) — Android interactors already run on background threads
  • Same propagation pattern as challengeTypes/capabilities for consistency
  • Reserved prefix filtering prevents SDK header override
  • @Transient on config field — set programmatically, not from JSON
  • Public type in MSAL, base type in common — customers import from com.microsoft.identity.nativeauth, not common

AB#3606613

Wire the NativeAuthRequestInterceptor through MSAL's public configuration
and command parameters adapter, matching the iOS PR
(microsoft-authentication-library-for-objc#2862).

- Add requestInterceptor property to NativeAuthPublicClientApplicationConfiguration
- Propagate interceptor in all 16 native auth CommandParametersAdapter builder calls
- Update common submodule to spetrescu/custom_headers branch
- Fix pre-existing duplicate .challengeType() call in createSignInResendCodeCommandParameters()

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

Copy link
Copy Markdown

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

Click here to Learn more.

Co-authored-by: Copilot <223556219+Copilot@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 native auth support for per-request custom HTTP headers by introducing a request interceptor on native auth configuration and propagating it into native auth command parameters.

Changes:

  • Added a transient requestInterceptor property to NativeAuthPublicClientApplicationConfiguration and included it in configuration merging.
  • Propagated requestInterceptor through most native auth command parameter builders.
  • Removed a duplicate challengeType() call in sign-in resend-code parameter construction.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.

File Description
msal/src/main/java/com/microsoft/identity/nativeauth/NativeAuthPublicClientApplicationConfiguration.kt Adds the native auth request interceptor configuration field and merge behavior.
msal/src/main/java/com/microsoft/identity/client/internal/CommandParametersAdapter.java Passes the configured interceptor into native auth command parameter builders.

@spetrescu84

Copy link
Copy Markdown
Contributor Author

@copilot apply changes based on the comments in this thread

…eAuthPublicClientApplicationParameters

Agent-Logs-Url: https://github.com/AzureAD/microsoft-authentication-library-for-android/sessions/3e578494-fa53-4d9a-92cc-5f88b974f149

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

Copilot AI commented May 13, 2026

Copy link
Copy Markdown
Contributor

@copilot apply changes based on the comments in this thread

Applied both fixes in commit d6bb35a:

  1. JIT builders — Added .requestInterceptor(configuration.getRequestInterceptor()) to both createJITChallengeAuthMethodCommandParameters and createJITSubmitChallengeCommandParameters in CommandParametersAdapter.java, so custom headers are propagated consistently across all native auth flows including JIT.

  2. Public factory path — Added a requestInterceptor field to NativeAuthPublicClientApplicationParameters.kt, updated the private createNativeAuthApplication to accept and apply it to the config, and threaded parameters.getRequestInterceptor() through the createNativeAuthPublicClientApplication(Context, NativeAuthPublicClientApplicationParameters) factory. The three legacy/file-based factory paths continue to pass null (the interceptor is set programmatically, not from JSON).

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=512m -XX:&#43;HeapDumpOnOutOfMemoryError -Xmx2048m -Dfile.encoding=UTF-8 -Duser.country -Duser.language=en -Duser.variant (dns block)

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

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
spetrescu84 and others added 2 commits May 14, 2026 17:22
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@spetrescu84 spetrescu84 changed the title Add NativeAuthRequestInterceptor for custom per-request HTTP headers Add NativeAuthRequestInterceptor for custom per-request HTTP headers AB#3600652 May 15, 2026
.challengeType(configuration.getChallengeTypes())
.requestInterceptor(configuration.getRequestInterceptor())
.continuationToken(continuationToken)
.challengeType(configuration.getChallengeTypes())

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.

This was removed because it's duplicated above

@spetrescu84 spetrescu84 changed the title Add NativeAuthRequestInterceptor for custom per-request HTTP headers AB#3600652 Add NativeAuthRequestInterceptor for custom per-request HTTP headers AB#3606613 May 15, 2026
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplication;
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationConfiguration;
import com.microsoft.identity.nativeauth.NativeAuthPublicClientApplicationParameters;
import com.microsoft.identity.common.java.nativeauth.providers.NativeAuthRequestInterceptor;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

@spetrescu84 we should not import common core in public api

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.

Removed

Define NativeAuthRequestInterceptor in com.microsoft.identity.nativeauth
so that sample apps and customers import from the MSAL public API package
instead of directly from common. This follows the established pattern
where all public-facing nativeauth types (AuthMethod, RequiredUserAttribute,
INativeAuthPublicClientApplication, etc.) live in the MSAL module.

The interface extends OAuth2RequestInterceptor from common, maintaining
full compatibility with common's internal usage of the base type.

Updates common submodule to use OAuth2RequestInterceptor internally.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
* (which sets custom headers via NativeAuthRequestInterceptor on the initiate call),
* enters credentials from the lab vault, and attempts sign-in.
*/
class NativeAuthSignInTest : AbstractCrossAppUiTest() {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Could we rename to reflect type of test and purpose of it? Like NativeAuthSignInWithCustomHeadersUiTest or NativeAuthSignInUiTest.

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.

Updated to NativeAuthSignInUIAutomationTest as it's an UI Automation test. The class will have other sign in tests so only the name of the test will highlight what the test does.

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This naming is reverted back?

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.

Good catch, merge conflict incorrectly solved. Renamed file to match

spetrescu84 and others added 5 commits May 28, 2026 10:48
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Explicitly cast to String to resolve Kotlin overload resolution
between pkg(Pattern) and pkg(String).

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Comment thread pop-benchmarker/build.gradle Outdated
// An optional interceptor that is called before each native auth network request, allowing the application to inject custom HTTP header fields.
// Refer to [NativeAuthRequestInterceptor] for more details.
@Transient
var requestInterceptor: NativeAuthRequestInterceptor? = null

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

How is this populated?

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.

The developer sets class they are working from as a NativeAuthRequestInterceptor, then after creating the INativeAuthPublicClientApplication the developer sets it like this:

(authClient as? PublicClientApplication)?.let { app ->
    (app.configuration as? NativeAuthPublicClientApplicationConfiguration)?.requestInterceptor = this
}

and they set they override this function:

override fun additionalHeaders(requestUrl: URL): Map<String, String>? {
    if (requestUrl.path.contains("oauth2/v2.0/initiate")) {
        return mapOf(
            "ignored-custom-header-value" to "ignored-custom-header",       // Will be ignored: doesn't start with "x-"
            "x-client-header" to "customer_header_2",                       // Will be ignored: starts with reserved prefix "x-client-"
            "X-my-custom-header" to "my data"                               // Will be added to the network request
        )
    }
    return null
}

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I see, ok lgtm then

@spetrescu84 spetrescu84 merged commit ebc0d1c into dev Jun 3, 2026
15 checks passed
shahzaibj added a commit that referenced this pull request Jun 9, 2026
When release-integration/8.3.3 was back-merged into dev (#2520), entry
#2511 (which was merged to dev *after* the 8.3.3 RC cut) got listed
under `Version 8.3.3`. It actually shipped to dev after 8.3.3 was
finalized, so it belongs under `vNext` for the next release.

This PR moves 1 entry from `Version 8.3.3` to `vNext`:

- #2511 Provide public api to set custom headers for CIAM requests

### Verification
Identified by diffing the current dev `changelog` against the file at
commit `12b16091c` (final 8.3.3 "remove RC" cut).


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

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

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

7 participants