Closed frasertweedale closed 7 months ago
@ericpashman what you want is possible now, if you encode the claims payload and use signJWS
directly:
signJWS (encode claims) (Identity (header, key))
But it would be best to document this a bit better, and perhaps generalise the functions as you describe or provide more general variants.
Generalizing signJWT
(or providing another function that accepts custom headers to produce a SignedJWT
specifically) would be a meaningful convenience for other users as minimally knowledgeable as me, I think, just because the presence of the SignedJWT
type would aid the sort of type-sleuthing Haskellers tend to do when encountering a new library.
That is to say, I didn't know that a JWT was a special case of a JWS when I came to the library, so to figure out how to do this, I had to read the RFCs and learn more about the domain than I would've preferred to. 😄
Oh, perhaps what would really help would be to generalize not just the signJWT
function, but also theSignedJWT
type to permit it to carry custom headers:
type SignedJWT h = CompactJWS h
Then signJWT
could retain its SignedJWT
output type, and users wanting to produce a JWT with custom headers would not need to touch anything outside the Crypto.JWT
module. As is, the JWS
type must be used in this case solely because SignedJWT
is defined as CompactJWS JWSHeader
, which disallows custom headers for no reason that is very apparent to me.
In my case, the JWT that I needed ended up as type JWS Identity () MyHeaders
, which cannot be "simplified" into a type synonym. Changing the definition of Signed JWT
would simplify this to SignedJWT MyHeaders
, hiding a lot of the complexity.
However, this would change the kind of SignedJWT
, so it would be a breaking change.
@ericpashman yes, I want to avoid a breaking change.
I might just add some documentation for this scenario, and possibly a new type synonym e.g. type SignedJWTWithHeader h = CompactJWS h
.
Hm, generalising the verifyJWT
signature will be a breaking change anyway, because it will result in type ambiguity when decoding. But this lends further weight to retaining SignedJWT
as-is, for simpler type annotations.
Ah. Yes, probably best just to add documentation then and leave it at that.
@ericpashman could you please review #124 and provide feedback?
Ping @ericpashman could you please review https://github.com/frasertweedale/hs-jose/pull/124 and provide feedback? I will most likely merge soon as-is, if no further feedback received. Thanks!
I was hoping to play around with this to see whether it does cover my use case before responding, but I haven't had the chance to do yet.
By eye, those changes look good. If you want to go ahead and commit, I'll report back later after trying to produce a working JWT with this.
By the way, I ended up rolling my own implementation to meet my immediate need, as I ran into problems with the approach of using the library's JWS functionality to produce a JWT. (I think the problems stemmed mostly from my own abuses of the library's functionality.) I'll try to summarize that experience for you at some point; I can also give you a super-simple, working JWT-with-custom-headers implementation to test against if you want it.
Happy to review whatever feedback or use cases you provide. Regarding the timeline, I intend to merge within the week, and cut a new release in the next few weeks.
I'm happy to report that I was able to use the new, type-generalized signJWT
to make a working JWT. I successfully tested this against an API in the wild that requires a couple of custom headers, a couple of custom claims, and an ES256 signature.
Thanks again, Fraser, for your efforts to get this working.
Great! Thank you for the feedback, @ericpashman.
There isn't an "obvious" way to create a
SignedJWT
with custom headers. (The API I'm working with requires a nonce header in the JWT, which I think is somewhat common.) That is, thesignJWT
function fromCrypto.JWT
explicitly takes aJWSHeader ()
value, so it is not possible to pass custom headers created as documented here.It's possible to get around this by using the
signJWS
function directly, to create aJWS
that meets the definition of a JWT (i.e., a JWS with a payload of JSON-encoded claims), but IMO it'd be kinder to users not to make them figure out how to do this. It looks to me likesignJWT
can be generalized to accept custom header types in a backwards-compatible way simply by generalizing its type signature:Originally posted by @ericpashman in https://github.com/frasertweedale/hs-jose/issues/121#issuecomment-1856638682