Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

JWT caching #2698

Closed
mkleczek opened this issue Mar 8, 2023 · 10 comments · Fixed by #2928
Closed

JWT caching #2698

mkleczek opened this issue Mar 8, 2023 · 10 comments · Fixed by #2928
Labels
difficulty: beginner Pure Haskell task enhancement a feature, ready for implementation perf

Comments

@mkleczek
Copy link
Contributor

mkleczek commented Mar 8, 2023

In our set-up we use asymmetric encryption to authenticate JWTs. As crypto operations are quite resource intensive we would like to cache results of JWT validation.

From my limited understanding of Haskell code JWTs are validated and parsed upon every request.

Do you think it would be a good idea to provide a time limited cache of JWT validation/parsing results?

@steve-chavez
Copy link
Member

Yes, JWTs are validated on each request.

Do you know a good implementation of JWT caching? I'm not sure if doing this would be safe.

Some references:

@mkleczek
Copy link
Contributor Author

I think caching validation/parsing result is secure as long as cache entry is not available longer than the token validity. I guess https://hackage.haskell.org/package/cache might support what is needed.

I will try to provide a patch but it is going to be my first real Haskell adventure :)

@steve-chavez steve-chavez added the enhancement a feature, ready for implementation label Jun 9, 2023
@steve-chavez steve-chavez added the difficulty: beginner Pure Haskell task label Aug 7, 2023
@taimoorzaeem
Copy link
Collaborator

@steve-chavez, for this I am thinking of adding two config options: jwt-exp-claim and jwt-caching. As specified in RFC 7519 the exp should be a NumericDate value.

I guess we need this other config jwt-caching which would be a boolean specifying if caching is used. What do you think? Also what do you think would be an appropriate format for NumericDate value?

@steve-chavez
Copy link
Member

I am thinking of adding two config options: jwt-exp-claim and jwt-caching

@taimoorzaeem Hm, I don't think we need a jwt-exp-claim. The exp should already be specified by the JWT provider, we only need to consume it. Also exp is a registered claim so we shouldn't need to specify a different key for obtaining it (like we do with the role on jwt-role-claim-key).

As for a boolean jwt-caching, yes, we would need it.

@steve-chavez
Copy link
Member

@taimoorzaeem Thinking more about this, we would need a way to test that the jwt decoding is fast the second time.

Looks like #2771 might help. Check my comment, for starters it seems possible to add Server-Timing for only the jwt decoding.

@taimoorzaeem
Copy link
Collaborator

From #2771 (comment) here:

To avoid doing this work for all the requests:

  • We can add Server-Timing whenever the Prefer: server-timing=include header is specified.
  • Or enable it whenever db-plan-enabled is turned on.

@steve-chavez What do you think, should I add Prefer: server-timing=include or just go with db-plan-enabled for now?

@steve-chavez
Copy link
Member

@taimoorzaeem db-plan-enabled would be good, seems simpler.

@taimoorzaeem
Copy link
Collaborator

taimoorzaeem commented Sep 1, 2023

@steve-chavez The exact matching won't work here, because the duration changes from run to run. How should I compare them?

Failures:

  test/spec/Feature/Auth/JwtCaching.hs:19:7: 
  1) Feature.Auth.JwtCaching.jwtCaching initial jwt validation
       missing header:
         Server-Timing: jwt;dur=
       the actual headers were:
         Content-Range: 0-0/*
         Server-Timing: jwt;dur=223.9
         Content-Type: application/json; charset=utf-8

  test/spec/Feature/Auth/JwtCaching.hs:27:7: 
  2) Feature.Auth.JwtCaching.jwtCaching jwt validation time reduces this time
       missing header:
         Server-Timing: jwt;dur=
       the actual headers were:
         Content-Range: 0-0/*
         Server-Timing: jwt;dur=136.4
         Content-Type: application/json; charset=utf-8

Also, the way I have currently implemented this is that I have made the JWT Cache a part of AppState. So, does running two spec-tests one after the other changes AppState or is it "rollbacked" after running a single test case?

@steve-chavez
Copy link
Member

How should I compare them?

@taimoorzaeem I think we can assert that the 2nd duration will always be less than the 1st. Some light parsing (like using Text.split, there's an example in Mediatype.hs IIRC) could be done to extract the value from the header and compare them.

@steve-chavez
Copy link
Member

Re AppState, seeing the feature code would give me a better idea (maybe you can do a draft PR). But if it's too difficult as a spec test, you could always make it an io test.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
difficulty: beginner Pure Haskell task enhancement a feature, ready for implementation perf
Development

Successfully merging a pull request may close this issue.

4 participants