Closed Leonti closed 6 years ago
This is a bug in auth0. Per https://tools.ietf.org/html/rfc7517#section-4.8:
4.8. "x5t" (X.509 Certificate SHA-1 Thumbprint) Parameter
The "x5t" (X.509 certificate SHA-1 thumbprint) parameter is a
base64url-encoded SHA-1 thumbprint (a.k.a. digest) of the DER
encoding of an X.509 certificate [RFC5280].
Therefore the unencoded datum must be 20-bytes in size.
Thanks for your report, but you should raise the issue with auth0. Good luck!
Thanks! I asked the question on their support forum :) I doubt they will change their implementation, but I'd like to know the explanation why they implemented it this way.
@Leonti Did you ever end up getting anywhere with auth0? I just ran into the same issue. What did you end up doing?
@WraithM Unfortunately there is no progress from their side: https://community.auth0.com/questions/7227/certificate-thumbprint-is-longer-than-20-bytes
This what I ended up doing unfortunately:
jwksFix :: ByteString -> ByteString
jwksFix = replace (L.toStrict "x5t") (L.toStrict "x5t_unused") . L.toStrict
It basically removes thumbprint field so it's not decoded anymore At least it still verifies certificate and audience, good enough for my use case.
Fair enough. Be aware that if the x5t
parameter appears in the JWS Protected Header (including any JWS containing the x5t
parameter that uses the Compact Serialization) this approach will not work.
Another option is to actually decode the hex-encoded thumbprint and re-encode it properly. But again that will only work when it is in the unprotected header.
I wrote the x5t re-encoding code. @frasertweedale, is this what you were roughly thinking?:
import Control.Monad
import Data.Aeson
import Data.HashMap.Strict as H
import Data.Text.Encoding
import Data.ByteString.Lazy (ByteString)
import qualified Data.ByteString.Base16 as B16
import qualified Data.ByteString.Base64.URL as B64
import qualified Crypto.JOSE.JWK as JWK
auth0TokenFix :: ByteString -> Maybe JWK.JWKSet
auth0TokenFix jwksBs = do
jwks <- mapM (modifyx5tField <=< fromObject)
=<< fromArray =<< H.lookup "keys"
=<< fromObject =<< decode jwksBs
decode . encode $ object [ "keys" .= jwks ]
where
x5tField = "x5t"
modifyx5tField o = flip (H.insert x5tField) o . String . b64HexToB64 <$> (fromJSONString =<< H.lookup x5tField o)
b64HexToB64 = decodeUtf8 . B64.encode . fst . B16.decode . B64.decodeLenient . encodeUtf8
fromJSONString (String s) = Just s
fromJSONString _ = Nothing
fromArray (Array xs) = Just xs
fromArray _ = Nothing
fromObject (Object o) = Just o
fromObject _ = Nothing
Yes, pretty much along those lines.
Is it worth linking that snippet along side the auth0 being broken comment?
@tmcgilchrist good idea; done: https://github.com/frasertweedale/hs-jose#interoperability-issues.
IIUC this has been fixed upstream with signing key rotation :
https://community.auth0.com/t/jwk-certificate-thumbprint-is-invalid/16070/22
Wow! Only took 2.5 years... Thanks for the update @ocramz.
Hi! While trying to decode JWKSet from Auth0 I stumbled on an issue when decoding fails because
x5t
is more than 20 bytes (I'm gettingLeft "Error in $.keys[0].x5t: incorrect number of bytes"
). Here is the value:The decoded value for this is:
It has 40 characters in hex, so I think it will be 20 bytes when in binary.
This is the JWK set coming from https://auth0.com
The full set if needed:
Cheers, Leonti