none
Unable to use JSON Web Token Handler GA with Windows Store and REST scenario

    Question

  • I'm exploring Windows Azure AD and I'm trying out the scenario "Securing a Windows Store Application and REST Web Service Using Windows Azure AD (Preview)"

    http://msdn.microsoft.com/en-us/library/windowsazure/dn169448.aspx

    If I use the JWT WEb TOken Handler preview Library (http://nuget.org/packages/Microsoft.IdentityModel.Tokens.JWT/) the sample works fine; if I instead try to use the GA library (http://nuget.org/packages/System.IdentityModel.Tokens.Jwt/) the project compiles (after adjusting namespaces and class names) but the call to tokenHandler.ValidateToken() fails with the exception attached at the end of the post. I tried to put ValidateIssuer=false in the validationParameters object, but I still got the exception.

    As an aside: is it correct that the signing key (associated to accounts.accesscontrol.windows.net) is not trusted? If I serialize the signing key published in the tenant metadata in a cer file Windows tell me that "this CA Root certificate cannot be trusted".

    Exception details:

    System.IdentityModel.Tokens.SecurityTokenValidationException was caught
      HResult=-2146233087
      Message=The X.509 certificate CN=accounts.accesscontrol.windows.net is not in the trusted people store. The X.509 certificate CN=accounts.accesscontrol.windows.net chain building failed. The certificate that was used has a trust chain that cannot be verified. Replace the certificate or change the certificateValidationMode. A certificate chain processed, but terminated in a root certificate which is not trusted by the trust provider.

      Source=System.IdentityModel
      StackTrace:
           at System.IdentityModel.Selectors.X509CertificateValidator.PeerOrChainTrustValidator.Validate(X509Certificate2 certificate)
           at System.IdentityModel.X509CertificateValidatorEx.Validate(X509Certificate2 certificate)
           at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateSigningToken(JwtSecurityToken jwt)
           at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(JwtSecurityToken jwt, TokenValidationParameters validationParameters)
           at System.IdentityModel.Tokens.JwtSecurityTokenHandler.ValidateToken(String jwtEncodedString, TokenValidationParameters validationParameters)
           at TodoListService.TokenValidationHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in c:\akite\common\sandbox\TodoListService\TodoListService\Global.asax.cs:line 85
      InnerException: 


    Eric Miotto, aKite Retail Web Services, http://www.akite.net



    • Edited by Eric Miotto Thursday, June 20, 2013 3:48 PM Exception in italic
    Thursday, June 20, 2013 3:45 PM

Answers

  • Hi,

    this happens because the JwtHandler has recognized that the key used to sign the jwt is backed by a X509Certificate.  There are a couple of things you can do.

    1. JwtSecurityTokenHandler.CerificateValidator is what controls the validation of the certs.  Default is to chain build or look into trusted people store (local machine NOT current user). You can set this value to different X509CertificateValidator. see http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.x509certificatevalidator.aspx.

    2. The error is thrown from within virtual JwtSecurityTokenHandler.ValidatSigningToken.  So you can override this method.

    • Marked as answer by Eric Miotto Friday, June 21, 2013 6:30 AM
    Thursday, June 20, 2013 4:39 PM
  • Eric,

    >>> not trusted by design. 

    If you mean we don't burn in trust for a specific cert, yes.

    We added an API to the ValidatingIssuerNameRegistry  (VINR) a month or so ago, from 

    https://nuget.org/packages/System.IdentityModel.Tokens.ValidatingIssuerNameRegistry

    If you point VINR.GetIssuingAuthority at your metatdata endpoint, the thumbprint of the cert will be in the authority returned.  You can easily add a Custom X509Validator that will allow only those certs (there may be more than one during rollover.

    About the .FindFirst ....

    By default, we apply an incoming mapping on the claim types to keep claims from Saml and Jwt the same.  This is due to AAD preforming a long to short mapping outbound. 

    See JwtSecurityTokenHandler.InboundClaimTypeMap.  This is something you can customize.


    Brent Schmaltz - MSFT

    • Marked as answer by Eric Miotto Tuesday, June 25, 2013 7:41 AM
    Monday, June 24, 2013 9:35 PM
  • Eric,

    Sorry for the slow reply, the world is not fully under my control yet :-).

    on 1.

    This is an extension of existing logic where we ensure we trust the issuer of the token. This is usually the signer of the token.  Usually a cert.  For multi-tenant scenarios using an external IDP such as WAAD, all the tokens from the IDP are usually signed by the same cert.  However, the issuer will reflect the tenant. An HR app hosted in AAD that sells services to Boeing and Intel, will have tokens signed by WAAD, but the 'issuer' will contain 'Boeing' and 'Intel'.  This enables HR app to reject other tokens signed by WAAD for some other app.

    on 2.

    Yes the VINR reaches out to the fedmetadata address you set and ensures that the https channel is protected by a cert that satisfies proof of ownership of the endpoint.  For example a cert with the subject name: cn=contoso.com will satisfy ownership of an endpoint https://www.contoso.com.  A weakness exists if DNS is compromised. In case, an attacker could redirect you to an endpoint that satisfies proof of ownership, but you really went to https://www.dontoso.com and the attack begins.


    Brent Schmaltz - MSFT

    • Marked as answer by Eric Miotto Thursday, June 27, 2013 6:32 AM
    Wednesday, June 26, 2013 4:17 PM
  • Eric,

    Your questioning is interesting and I endeavor to provide an explanation of the VINR.  The VINR was developed for securing multi-tenant scenarios. In these scenarios a application developer sells their application to different tenants. In Windows Azure Active Directory (WAAD), each tenant controls their set of users.  The key is that WAAD issued tokens are all signed by the same key. Given two tenants, T1 and T2, all issued tokens will be signed by WAAD. Hence,  must take more into account otherwise both bob(T1) and bob(T2) would be accepted by the ConfigurationBasedIssuerNameRegistry. In order to distinguish between the 'bobs', WADD always adds the JWT claim { iss, <tenant> }.  That's where VINR comes into play. Validation requires the consideration of the order pair < thumbprint, issuer >.

    That said, you are correct we really don't help you obtaining the key. An oversight that we hope to address soon as we reach out to WADD obtain the X509Cert to get the thumbprint :-)

    Short story is, if you an not supporting multi-tenant (tokens from same IdentityProvider, different security realms) you don't need VINR.  Keep in mind that is where our future development is pointed.


    Brent Schmaltz - MSFT

    • Marked as answer by Eric Miotto Monday, July 01, 2013 2:58 PM
    Monday, July 01, 2013 2:50 PM

All replies

  • Hi,

    this happens because the JwtHandler has recognized that the key used to sign the jwt is backed by a X509Certificate.  There are a couple of things you can do.

    1. JwtSecurityTokenHandler.CerificateValidator is what controls the validation of the certs.  Default is to chain build or look into trusted people store (local machine NOT current user). You can set this value to different X509CertificateValidator. see http://msdn.microsoft.com/en-us/library/system.identitymodel.selectors.x509certificatevalidator.aspx.

    2. The error is thrown from within virtual JwtSecurityTokenHandler.ValidatSigningToken.  So you can override this method.

    • Marked as answer by Eric Miotto Friday, June 21, 2013 6:30 AM
    Thursday, June 20, 2013 4:39 PM
  • Thanks Brent for pointing me in the right direction.

    I've followed the first solution (setting JwtSecurityToken.CertificateValidator to X509CertificateValidator.None) and now the validation executes successfully. Can you confirm that the signing key in Windows Azure AD is not trusted (by design I guess) and thus I don't have to validate it?

    I want also to share two other changes I had to made to get the scenario working with JSON Web Token Handler GA library (probably because some claims have been renamed):

    • in Global.asax,  change this line
     if (ClaimsPrincipal.Current.FindFirst("scp").Value != "user_impersonation")

    to

     if (ClaimsPrincipal.Current.FindFirst("http://schemas.microsoft.com/identity/claims/scope").Value != "user_impersonation")

    • In TodoList controller, replace the two occurences of "oid" with "http://schemas.microsoft.com/identity/claims/objectidentifier"


    Eric Miotto, aKite Retail Web Services, http://www.akite.net


    • Edited by Eric Miotto Friday, June 21, 2013 6:36 AM Fixed grammatical errors
    Friday, June 21, 2013 6:35 AM
  • Eric,

    >>> not trusted by design. 

    If you mean we don't burn in trust for a specific cert, yes.

    We added an API to the ValidatingIssuerNameRegistry  (VINR) a month or so ago, from 

    https://nuget.org/packages/System.IdentityModel.Tokens.ValidatingIssuerNameRegistry

    If you point VINR.GetIssuingAuthority at your metatdata endpoint, the thumbprint of the cert will be in the authority returned.  You can easily add a Custom X509Validator that will allow only those certs (there may be more than one during rollover.

    About the .FindFirst ....

    By default, we apply an incoming mapping on the claim types to keep claims from Saml and Jwt the same.  This is due to AAD preforming a long to short mapping outbound. 

    See JwtSecurityTokenHandler.InboundClaimTypeMap.  This is something you can customize.


    Brent Schmaltz - MSFT

    • Marked as answer by Eric Miotto Tuesday, June 25, 2013 7:41 AM
    Monday, June 24, 2013 9:35 PM
  • Thanks Brent for your further explanations.

    About the .FindFirst, using JwtSecurityTokenHandler.InboudClaimTypeMap, I've removed the mapping about scp and oid and now the old FindFirst code works correctly.

    I've also played with ValidatingIssuerNameRegistry (obtaining the thumbprint from the metadata endpoint) and read the explanations given by Vittorio Bertocci in his blog (http://www.cloudidentity.com/blog/2013/03/25/a-refresh-of-the-identity-and-access-tool-for-vs-2012/ and http://www.cloudidentity.com/blog/2013/04/02/auto-update-of-the-signing-keys-via-metadata/ ). However I still have some doubts:

    1. the sample already retrieves the signing key from the metadata endpoint, as probably ValidatingIssuedNameRegistry does under the hood. The use of this class is an alternative of the current approach or it's an additional step in validating the token?
    2. How am I sure that the signing certificate (read in the GetTenantInformation function in Global.asax in the sample and with no trust information in it) and thumbprint (obtained with VINRR.GetIssuingAuthority) are not forged by an attacker that wants to access my application? So far I understand that the signing certificate is valid because I retrieve them directly from the metadata endpoint which is secured with HTTPS -- an attacker would have to obtain access to a valid certificate for Windows Azure AD (among other things) in order to send me a fake signing certificate. Can you point me to some resources in this sense?

    Eric Miotto, aKite Retail Web Services, http://www.akite.net

    Tuesday, June 25, 2013 7:41 AM
  • Eric,

    Sorry for the slow reply, the world is not fully under my control yet :-).

    on 1.

    This is an extension of existing logic where we ensure we trust the issuer of the token. This is usually the signer of the token.  Usually a cert.  For multi-tenant scenarios using an external IDP such as WAAD, all the tokens from the IDP are usually signed by the same cert.  However, the issuer will reflect the tenant. An HR app hosted in AAD that sells services to Boeing and Intel, will have tokens signed by WAAD, but the 'issuer' will contain 'Boeing' and 'Intel'.  This enables HR app to reject other tokens signed by WAAD for some other app.

    on 2.

    Yes the VINR reaches out to the fedmetadata address you set and ensures that the https channel is protected by a cert that satisfies proof of ownership of the endpoint.  For example a cert with the subject name: cn=contoso.com will satisfy ownership of an endpoint https://www.contoso.com.  A weakness exists if DNS is compromised. In case, an attacker could redirect you to an endpoint that satisfies proof of ownership, but you really went to https://www.dontoso.com and the attack begins.


    Brent Schmaltz - MSFT

    • Marked as answer by Eric Miotto Thursday, June 27, 2013 6:32 AM
    Wednesday, June 26, 2013 4:17 PM
  • Hi Brent,

    thanks again for your explanations (and your patience).

    Point 2 is clear, instead Point 1 still is not clear to me. I am trying to see whether I can employ VINR (a proven library) in the sample to obtain the signing key and establish its validity -- instead of using the GetTenantInformation function which I have to ensure it is correct and secure. In my previous question I incorrectly wrote "validating the token" instead of "validating the signing key" and thus my question was not clear.

    This is my understanding so far:

    1. I cannot use VINR directly to obtain the signing key that I should use in JwtSecurityTokenHandler.ValidateToken -- I can only obtain issuer name and thumbprint;
    2. It seems I can call ValidateToken omitting ValidationParameters and by populating the configuration property -- I can set IssuerNameRegistry to the VINR I obtained from the metadata endpoint but then I don't know what to set for IssuerTokenResolver (it seems to me an object that can resolve the signing key);
    3. given the reply you gave me for Point 2, it seems that the GetTenantInformation function is apt enough for obtaining the signing key and thumbprint (through MetadataSerializer) and establishing its validity (the HTTPS channel gives the proof of ownership of the endpoint that I'm accessing, I don't think VINR does additional checks beyond the ones of the classes that handles SSL/HTTPS). So I'm wondering whether in the sample I can gain any "advantage" by using VINR (in particular, reuse of an existing library that is more robust and secure than my own code or additional checks) or instead it is not (strictly) necessary to employ it.

    Eric Miotto, aKite Retail Web Services, http://www.akite.net


    • Edited by Eric Miotto Thursday, June 27, 2013 10:33 AM Added more context
    Thursday, June 27, 2013 8:27 AM
  • Eric,

    Your questioning is interesting and I endeavor to provide an explanation of the VINR.  The VINR was developed for securing multi-tenant scenarios. In these scenarios a application developer sells their application to different tenants. In Windows Azure Active Directory (WAAD), each tenant controls their set of users.  The key is that WAAD issued tokens are all signed by the same key. Given two tenants, T1 and T2, all issued tokens will be signed by WAAD. Hence,  must take more into account otherwise both bob(T1) and bob(T2) would be accepted by the ConfigurationBasedIssuerNameRegistry. In order to distinguish between the 'bobs', WADD always adds the JWT claim { iss, <tenant> }.  That's where VINR comes into play. Validation requires the consideration of the order pair < thumbprint, issuer >.

    That said, you are correct we really don't help you obtaining the key. An oversight that we hope to address soon as we reach out to WADD obtain the X509Cert to get the thumbprint :-)

    Short story is, if you an not supporting multi-tenant (tokens from same IdentityProvider, different security realms) you don't need VINR.  Keep in mind that is where our future development is pointed.


    Brent Schmaltz - MSFT

    • Marked as answer by Eric Miotto Monday, July 01, 2013 2:58 PM
    Monday, July 01, 2013 2:50 PM
  • Thanks Brent for your answer.

    For now I'll continue using the code of the sample to retrieve the signing key. I'm looking forward to an update of VINR that retrieves and validates the signing key and that can be used together with JwtSecurityTokenHandler to validate the incoming token.


    Eric Miotto, aKite Retail Web Services, http://www.akite.net

    Wednesday, July 03, 2013 8:59 AM