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

Support for biscuit-rust v3.0.0 #3

Open
divarvel opened this issue May 12, 2023 · 2 comments
Open

Support for biscuit-rust v3.0.0 #3

divarvel opened this issue May 12, 2023 · 2 comments

Comments

@divarvel
Copy link

Hi,

Thanks for working on this!

Biscuit-auth maintainer here, thanks for working on a tower integration. Are there plans for bumping the biscuit-auth dependency to 3.0.0 / would you welcome a PR doing that?

I have built biscuit middleware for haskell and JS web frameworks, so I'm starting to get a clear view of how to integrate biscuit with web apps / APIs, maybe we could compare notes?

@vlmutolo
Copy link
Owner

vlmutolo commented May 12, 2023

I haven't touched this crate in a while, and I'd definitely consider it still under "experimental" status. But I have some free time starting around this Monday, so I could see about revamping it in a few days to work with the latest Biscuit API.

I would also welcome ideas on how to best integrate biscuit-auth as middleware. I'm happy to have that conversation here or in the Matrix channel; whichever is easier.

If I recall correctly, I think the larger questions we had with the current prototype were around:

  1. How to avoid re-parsing biscuit tokens in layered services / if this is even possible
  2. Ergonomics for storing and updating auth policies. Should this be handled in an opinionated way by this library (basically provide a wrapper around ArcSwap) or leave it to a higher layer? I think most people will have a relatively static authorizer that they want to use with dynamic tokens, and we don't want to rebuild that authorizer on each request. Most people also want the authorizer to auto-update when some external event happens.

@divarvel
Copy link
Author

to give some context, one of the most prominent changes in biscuit 3.0 is the introduction of datalog macros, along with parameters substitution:

  • datalog is parsed only once, at compile time
  • it is safe to inject dynamic values at run time

the actual authorizer is meant to be different for each request (because ambient facts will be different), so an authorizer will have to be built at each request (either by cloning an existing authorizer or building one from scratch).

based on biscuit middlewares built in other languages (javascript and haskell), as well as based on experience with securing services with macaroons (one of the main inspirations for biscuit)

  • biscuit's expressive auth model means that it shines specifically when using per-endpoint logic, especially using data extracted from the request with endpoint-specific logic (URL segment, parsed request body)
  • auth logic expressed as code is part of the application source code, and as such should share its life cycle. It can be rendered dynamic through parameter injection, the the code itself should be versioned an considered part of the source code
  • some authorization logic is still common to all endpoints (extracting some ambient facts like the current time, the client ip address, …)

Based on all this, here is the approach that was used for support on other platforms:

  • the minimum is providing a middleware that parses the token and checks signatures, without performing authorization
    (because actual authorization logic happens after routing). The verified token is then stored in the request context so that the endpoint can run authorization from there (using values extracted from the request by the routing layer).
  • this middleware should allow overriding the token extraction logic (Request -> String), the token parsing logic ((String, (Option<u32> -> PublicKey)) -> Result<Biscuit, Error>) and the error response generation Error -> Response).
  • optionally, in the case where authorization logic is not endpoint-dependent (or if it is okay to entirely replicate the routing logic inside the authorization layer: that can make sense for very small applications, but is usually not worth it), the middleware can carry an optional Request -> Authorizer function that allows the middleware to perform authorization itself.
  • this middleware should be able to precompute an authorizer context from the raw request, for cross concern ambient facts (time(), …). This step can also load service-wide authorization rules through the use of a macro or a 'static authorizer
  • if the framework allows to plug the middleware after routing, giving access to routing information, then a ParsedRequest -> Authorizer function can be provided so that the middleware itself can perform authorization itself.

See for instance the current work on the actix middleware: https://github.com/biscuit-auth/biscuit-actix-middleware/blob/main/examples/nominal.rs (for now token extraction, parsing and error responses are hard-coded, and it does not yet handle common authorization logic (common authorization logic can be achieved by the users with regular programming techniques).

This structure served me well in production with macaroons for 5 years, and allowed a biscuit deployment in haskell (with the servant web framework) to go through without any modification to the middleware code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants