[Bug] SHR POP tokens are broken due to invalid alg (it should be PS256 not RS256) #4839
Labels
bug
confidential-client
P2
regression
Behavior that worked in a previous release that no longer works in a newer release
Milestone
Library version used
4.60.3
.NET version
net 8.0
Scenario
ConfidentialClient - service to service (AcquireTokenForClient)
Is this a new or an existing app?
The app is in production, and I have upgraded to a new version of MSAL
Issue description and reproduction steps
I think the signed pop token produced by Microsoft.Identity.Client is not specifying the correct cryptographic algorithm in the JsonWebToken header.
I have a client application that uses ConfidentialClientApplicationBuilder AcquireTokenForClient to generate a signed pop token. Then my server application uses SignedHttpRequestHandler ValidateSignedHttpRequestAsync to validate the signature.
This code was working fine up through M.I.C version 4.59.1: the token generated by the client could be validated successfully in the server. However, in version 4.60.3 (the next one we tried), this validation starts failing with an error on signature validation (IDX23034).
Debugging deep into the validation code, I have figured out that the failure is because the validation code examines the algorithm from the JWT header and determines that the signature padding is Pkcs1. But with version 4.60 and higher, actually it is Pss.
To reproduce, use 4.60.3 or more recent to generate a signed pop token and then run ValidateSignedHttpRequestAsync on it.
I can provide sample code if necessary--I have a standalone application that generates a pop token and then validates it--but I think this issue can be seen just by reading the project code.
More details
I am pretty sure the issue stems from a recent fix in InMemoryCryptoProvider to use PSS for the padding. See PR #4633.
The code now reads:
However, the crypto provider still returns "RS256" for the CryptographicAlgorithm:
I think it needs to return "PS256" to get Pss signature padding (I got that value from https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/wiki/Supported-Algorithms).
Here's how it all goes down:
The value returned from CryptographicAlgorithm gets put into the JsonWebToken header in the "alg" field. Later when the signature is being verified in the server (using SignedHttpRequestHandler ValidateSignatureAsync), the alg field is read and used to determine the padding on the signature field. Padding determination is done in this code (from AsymmetricAdapter.cs):
Since the header alg field is not any of the "Pss" versions, the padding is set to Pkcs1. Then the signature validation fails because it's not using the correct padding.
Relevant code snippets
We are building the confidential client app as:
var uamiCcApp = ConfidentialClientApplicationBuilder
.Create(sniClientId)
.WithCertificate(cert, true)
.WithTenantId(tenantId)
.WithAuthority(new Uri(authConfig.Authority))
.WithExperimentalFeatures() // for PoP
.Build();
and the pop token as:
popConfig.HttpHost = host.Replace("https://", "");
popConfig.HttpMethod = httpMethod;
popConfig.HttpPath = path;
popConfig.SignHttpRequest = true;
Expected behavior
The signature validates correctly.
Identity provider
Microsoft Entra ID (Work and School accounts and Personal Microsoft accounts)
Regression
MSAL Version: 4.59.1
Solution and workarounds
I think the solution is to modify InMemoryCryptoProvider to return PS256 for the algorithm:
public string CryptographicAlgorithm { get => "PS256"; }
I am trying this solution out locally by implementing a custom crypto provider, and so far it appears to work.
The text was updated successfully, but these errors were encountered: