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

Gate dependencies behind feature flags #177

Open
wants to merge 12 commits into
base: master
Choose a base branch
from

Conversation

aumetra
Copy link
Contributor

@aumetra aumetra commented Nov 27, 2023

This PR gates the following dependencies behind feature flags:

  • rust-argon2
  • rmp-serde

These dependencies are less common dependencies for some web services, since:

a) not everyone uses rust-argon2 (personally, I use the RustCrypto argon2 crate for pretty much all my projects)
b) not everyone uses MessagePack in their projects

In total, this makes the dependency tree 16 dependencies lighter with all features disabled.
Whether these features should be enabled by default or not can definitely be discussed.

All the features are documented on docs.rs using the unstable doc_auto_cfg feature


I license past and future contributions under the dual MIT/Apache-2.0 license, allowing licensees to chose either at their option.

@aumetra
Copy link
Contributor Author

aumetra commented Nov 27, 2023

screenshot_docs

@aumetra aumetra marked this pull request as draft November 27, 2023 10:11
@aumetra aumetra marked this pull request as ready for review November 27, 2023 10:19
Copy link
Owner

@HeroicKatora HeroicKatora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't agree with the approach. The two dependencies exist because some of the in-memory primitives come with default policies. However, the only effect of missing the dependencies could be that one must be provided instead of having a default / fallback. Indeed take ClientMap then it could expose a constructor with an explicit policy argument. Similarly the policy of using rmp-serde for the token signing could be boxed away as a trait that is provided in the constructor.

This allows the types to exist in principle, even when no default policies are found in the dependencies.

oxide-auth/src/endpoint/tests/pkce.rs Outdated Show resolved Hide resolved
oxide-auth/src/primitives/generator.rs Outdated Show resolved Hide resolved
@aumetra
Copy link
Contributor Author

aumetra commented Nov 27, 2023

Makes sense. Will try to refactor towards generalizing the implementations over different inner implementations.

@aumetra
Copy link
Contributor Author

aumetra commented Nov 27, 2023

Okay, the abstraction over different de-/serializers is.. a mess. Unless we pull in erased_serde to erase the actual types of the machinery inside using some dynamic dispatch and a box.

@HeroicKatora
Copy link
Owner

The dispatch need not be byte-compatible with previous version, imo, since in-memory data should be treated as ephemeral. Well, any new design could be validated by trying to swap the current encoding scheme for something like BER or even JWT but those can always live as separate implementations as well. This opens the possibility of having a public wrapper type (with private fields, implementing Serialize) which the policy is to turn into a byte vector. Or rather two methods since there are two different instances of encoding happening. The AssertGrant type codifies essentially already one of those two, the other is currently ad-hoc tuples. Both could be public types, without public constructors.

@aumetra
Copy link
Contributor Author

aumetra commented Nov 27, 2023

Something like that? @HeroicKatora

That's a fully opaque representation that publicly implements Deserialize and Serialize and doesn't expose any other information to the outside.
Explicitly adds a disclaimer to the documentation that the internal representation is unstable and might change at any time.

The internal representation at the moment is just an enum.

Copy link
Owner

@HeroicKatora HeroicKatora left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pretty much except that I'd prefer dynamic dispatch over a generic. It's a rather large operation where not a lot can be gained from an inline optimization. The only problem here are auto-traits, i.e. Send and Sync should still work for the Assertion. The inner types are all good 👍

oxide-auth/src/primitives/generator/assertion_grant.rs Outdated Show resolved Hide resolved
@aumetra
Copy link
Contributor Author

aumetra commented Nov 28, 2023

The only problem here are auto-traits, i.e. Send and Sync should still work for the Assertion

Rewrote the internals to dynamically dispatch instead. Works fine, added the auto traits as a requirement to the trait itself, so that's still fine.

@HeroicKatora
Copy link
Owner

Fyi, the bulk of this PR looks good. But, from the perspective of secure defaults, I don't yet feel comfortable with an interface doesnt provide some secure configuration of all interfaces for users by default. That means, there should be an easy choice and that one should be secure. In particular, new with a parameter seems off. It's congruent with that idiom if the new interface is not available in all configurations so long as it is available in the default one.

The serializer is not as relevant but still I'm not sold on the idea of making it more difficult to most people (I'm basing this on the fact that this is the first time that issue came up) and not having at least an optional dependency that fully implements the trait. Or, at the very least, there should be good guidance within the trait documentation on how to bind it to a generic serialization library.

Just some thoughts here, and why I'm not convinced yet that this PR is ready.

@aumetra
Copy link
Contributor Author

aumetra commented Dec 3, 2023

@HeroicKatora Well, do you think it would be sufficient if we provided a Default implementation (even if gated behind a cfg flag) that uses these secure defaults?

Such as the interfaces that use the argon2 password policy, they get a default impl.

And, of course, the Encoder also gets a default impl gated behind something like messagepack.

And all these features could be enabled by default to make the default experience secure by default.

@aumetra
Copy link
Contributor Author

aumetra commented Feb 16, 2024

@HeroicKatora Hey, sorry for the delay, I wanted to ask if what I proposed in the previous comment would be fine?
Meaning an API where the default features expose these secure defaults?

@HeroicKatora
Copy link
Owner

HeroicKatora commented Feb 23, 2024

The approach is decent with the trait factoring, but I can't quite get over the modification to new and other constructors instead of a with_ method and new being dependent on (non-default) features. In particular note how that introduces an apparently necessity for (or maybe draw to) an improper implementation of the password store in tests and associated churn. Knowing users, this might well be the place used as a template for customized versions. And if this is motivated by being 'just simple' then I can definitely see real cases of it happening in Production where it is a huge security hole. The changes you've made since the initial version do contribute to security-by-default but I'm not fully convinced that the PR overall is positive. It's close though.

Do note that tests can have a dev-dependency on the encoding / hashing crates, maybe that is a direction to use?

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

Successfully merging this pull request may close these issues.

None yet

2 participants