-
Notifications
You must be signed in to change notification settings - Fork 161
Add FIC Leg 2 over mTLS Proof-of-Possession (stacked on #1040) #1041
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: rginsburg/sni-mtls-pop
Are you sure you want to change the base?
Changes from 2 commits
664593a
7a707cb
38e77e9
26b0e1a
f89163c
7bf1e56
cb3f2d7
387d71d
c76a124
73d50b6
e83266e
227a04c
9718ed2
89ad3b2
15c0705
7e93cbf
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,5 +1,9 @@ | ||
| { | ||
| "tool": "Credential Scanner", | ||
| "suppressions": [ | ||
| { | ||
| "file": "msal4j-sdk/src/test/resources/mtls_test_cert.p12", | ||
| "_justification": "Self-signed, test-only certificate (CN=msal4j-mtls-test) used by unit tests for mTLS Proof-of-Possession. Contains no production secret." | ||
| } | ||
| ] | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -15,11 +15,17 @@ public final class AssertionRequestOptions { | |
| private final String clientId; | ||
| private final String tokenEndpoint; | ||
| private final String clientAssertionFmiPath; | ||
| private final boolean proofOfPossession; | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [P1/API] A |
||
|
|
||
| AssertionRequestOptions(String clientId, String tokenEndpoint, String clientAssertionFmiPath) { | ||
| this(clientId, tokenEndpoint, clientAssertionFmiPath, false); | ||
| } | ||
|
|
||
| AssertionRequestOptions(String clientId, String tokenEndpoint, String clientAssertionFmiPath, boolean proofOfPossession) { | ||
| this.clientId = clientId; | ||
| this.tokenEndpoint = tokenEndpoint; | ||
| this.clientAssertionFmiPath = clientAssertionFmiPath; | ||
| this.proofOfPossession = proofOfPossession; | ||
| } | ||
|
|
||
| /** | ||
|
|
@@ -50,4 +56,15 @@ public String tokenEndpoint() { | |
| public String clientAssertionFmiPath() { | ||
| return clientAssertionFmiPath; | ||
| } | ||
|
|
||
| /** | ||
| * Indicates whether the in-flight token request is an mTLS Proof-of-Possession (mTLS PoP) request. | ||
| * When true, a context-aware assertion provider can mint an appropriately bound assertion for the | ||
| * PoP flow (for example, FIC Leg 2). | ||
| * | ||
| * @return true if the request is an mTLS Proof-of-Possession request, false otherwise | ||
| */ | ||
| public boolean proofOfPossession() { | ||
| return proofOfPossession; | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -21,6 +21,7 @@ public class ConfidentialClientApplication extends AbstractClientApplicationBase | |
|
|
||
| IClientCredential clientCredential; | ||
| private boolean sendX5c; | ||
| IClientCertificate mtlsBindingCertificate; | ||
|
Robbie-Microsoft marked this conversation as resolved.
Outdated
|
||
|
|
||
| /** AppTokenProvider creates a Credential from a function that provides access tokens. The function | ||
| must be concurrency safe. This is intended only to allow the Azure SDK to cache MSI tokens. It isn't | ||
|
|
@@ -89,6 +90,7 @@ private ConfidentialClientApplication(Builder builder) { | |
| log = LoggerFactory.getLogger(ConfidentialClientApplication.class); | ||
|
|
||
| this.clientCredential = builder.clientCredential; | ||
| this.mtlsBindingCertificate = builder.mtlsBindingCertificate; | ||
|
|
||
| this.tenant = this.authenticationAuthority.tenant; | ||
| } | ||
|
|
@@ -110,12 +112,23 @@ public boolean sendX5c() { | |
| return this.sendX5c; | ||
| } | ||
|
|
||
| /** | ||
| * @return the certificate used as the client TLS certificate for mTLS Proof-of-Possession requests | ||
| * when the application's authentication credential is not itself a certificate (e.g. FIC Leg 2, where | ||
| * authentication is a federated assertion), or null if not configured. | ||
| */ | ||
| public IClientCertificate mtlsBindingCertificate() { | ||
|
|
||
| return this.mtlsBindingCertificate; | ||
| } | ||
|
|
||
| public static class Builder extends AbstractClientApplicationBase.Builder<Builder> { | ||
|
|
||
| private IClientCredential clientCredential; | ||
|
|
||
| private boolean sendX5c = true; | ||
|
|
||
| private IClientCertificate mtlsBindingCertificate; | ||
|
|
||
| private Function<AppTokenProviderParameters, CompletableFuture<TokenProviderResult>> appTokenProvider; | ||
|
|
||
| private Builder(String clientId, IClientCredential clientCredential) { | ||
|
|
@@ -139,6 +152,31 @@ public ConfidentialClientApplication.Builder sendX5c(boolean val) { | |
| return self(); | ||
| } | ||
|
|
||
| /** | ||
| * Configures a certificate to present as the client TLS certificate in the mutual-TLS handshake | ||
| * for mTLS Proof-of-Possession requests (see | ||
| * {@link ClientCredentialParameters.ClientCredentialParametersBuilder#mtlsProofOfPossession()}). | ||
| * <p> | ||
| * This is required only when the application authenticates with a credential that is <b>not</b> | ||
| * itself a certificate — for example, FIC Leg 2, where the application authenticates with a | ||
| * federated assertion ({@link ClientCredentialFactory#createFromClientAssertion(String)}) but must | ||
| * still bind the resulting token to a certificate. When the application's authentication credential | ||
| * is already an {@link IClientCertificate} (direct SN/I cert or FIC Leg 1), that same certificate is | ||
| * used as the binding certificate and this option is unnecessary. | ||
| * <p> | ||
| * Only the certificate's public material is ever surfaced on the result (see | ||
| * {@link AuthenticationResultMetadata#bindingCertificate()}); the private key is never exposed. | ||
| * | ||
| * @param val the binding certificate | ||
| * @return instance of the Builder on which method was called | ||
| */ | ||
| public ConfidentialClientApplication.Builder mtlsBindingCertificate(IClientCertificate val) { | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [Blocker/API] The assertion and binding certificate are configured through unrelated channels, so they can rotate independently and MSAL cannot prove they represent the same generation. This API also cannot consume #1059’s |
||
| validateNotNull("mtlsBindingCertificate", val); | ||
| this.mtlsBindingCertificate = val; | ||
|
|
||
| return self(); | ||
| } | ||
|
|
||
| /// <summary> | ||
| /// Allows setting a callback which returns an access token, based on the passed-in parameters. | ||
| /// MSAL will pass in its authentication parameters to the callback and it is expected that the callback | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[Acceptance blocker] Comparing locally generated metadata to the configured lab certificate does not prove protocol continuity. Decode T1 and T2 and require both
cnf.x5t#S256values to match the exact returned binding context’s key ID, then call the resource with T2 and that context. Add no-cert, wrong-scheme, wrong-cert, A→B→A and persistent-cache cases.