smallrye-jwt icon indicating copy to clipboard operation
smallrye-jwt copied to clipboard

Initial implementation of a DPoP proofing mechanism

Open acoburn opened this issue 5 years ago • 7 comments

Resolves #165

This adds the initial structure for handling proof of possession semantics at the application layer.

This PR does not contain any tests, as I would like some initial feedback on this implementation. The basic design assumes that the AbstractDpopTokenValidator class would be extended and the following methods implemented:

  • ::getDpopHeaderValue -- this returns the raw HTTP DPoP header value
  • ::getRequestUri -- this returns the HTTP request URI
  • ::getRequestMethod -- this returns the HTTP request method

Then, given a @JsonWebToken object, the ::verify method checks that the thumbprint of any access token-bound key:

"cnf": {
    "jkt": "<thumbprint>"
}

matches the public key in the request-specific DPoP token (i.e. that the DPoP token is scoped to the request URI and method). This method will throw a ParseException in any of the following cases:

  • No key binding in the access token
  • No DPoP proofing token available
  • Any parse or validation failures of the DPoP token itself

If this seems like a reasonable approach, I will add accompanying tests.

Note: this does not implement jti constraints (which are optional according to DPoP and difficult to support across a cluster of stateless resource servers)

Also note: this makes use of the algorithm defined in authContextInfo.getKeyEncryptionAlgorithm().getAlgorithm(), though it isn't strictly necessary to align those (an identity provider may sign with RSA and a browser may use ECDSA for DPoP proofing -- ephemeral ECDSA keys for use with DPoP are considerably faster to generate in a browser)

DPoP Specification Reference: https://tools.ietf.org/html/draft-fett-oauth-dpop-04

acoburn avatar Jul 08 '20 23:07 acoburn

@acoburn Thanks, let me add a few initial comments

sberyozkin avatar Jul 09 '20 09:07 sberyozkin

Note: this does not implement jti constraints

If the IDP providers will start supporting the DPoP proof verification too then they'd deal with jti. Perhaps we can also add a protected validateJti method which would return a no op Validatorand then in Quarkus/etc it would be possible to extend and return some custom jti validator... We can also ship basic validator OOB which will keep a size-constraint Set of jti and keep optimizing it going forward..

Overall, a great start, thanks for spending the time on it. Let me also ping Keycloak colleagues :-)

sberyozkin avatar Jul 09 '20 11:07 sberyozkin

Hey @acoburn How are you, by the way I've been thinking of having an implementation/jwt-dpop module with your PR eventually making it to that new module. I might give it a go after we release 3.0.0 final, cheers

sberyozkin avatar Feb 07 '21 17:02 sberyozkin

@sberyozkin that sounds great. I know this PR has gone largely dormant, but I would be happy to help move it forward

acoburn avatar Feb 08 '21 19:02 acoburn

Keycloak now supports DPoP as an experimental feature so it is a good time to complete this PR, I'll try to have a look

sberyozkin avatar Nov 24 '23 10:11 sberyozkin

Hey @acoburn @sberyozkin ,

I hope you are well. I was wondering if there have been any updates regarding the PR? Additionally, are there any significant hurdles or blockers that need to be addressed? Your insights would be greatly appreciated.

I believe this is the correct link to the RFC : https://datatracker.ietf.org/doc/html/rfc9449 Reference implementation in JavaScript/node - https://github.com/search?q=repo%3Apanva%2Fnode-openid-client+dpop&type=commits

VinodAnandan avatar Mar 17 '24 19:03 VinodAnandan

@VinodAnandan Hi, preparation for testing it is in #772, but I won't have time to fix it in the very short term in smallrye-jwt. I'll play with it at the quarkus-oidc level and test against Keycloak and once it is done, this PR will be complete, though of course if Aaron @acoburn decides to finalize it sooner, it would be awesome

sberyozkin avatar Mar 20 '24 18:03 sberyozkin