-
Notifications
You must be signed in to change notification settings - Fork 123
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
Restructure the authentication process #173
Comments
Yep, but in this case, I think I personally prefer to the second proposal, since it has horizontal hierarchy than the first proposal. As for the idea that dispatching different endpoints to different authentication clients based on its authentication requirement and flow, it's a great point. But IMHO it will make the endpoints a little bit difficult to use and become less ergonomic, since the developers have to know that does this endpoint need to authenticate with Code Auth Flow or not? As for other solutions, I am a bit busy on my job, I would propose my thought later, perhaps on this weekend. |
What I recognize may make it less ergonomic is the fact that the users will have to |
I've been experimenting with this a bit, here's how it would look like more or less: https://play.rust-lang.org/?version=stable&mode=debug&edition=2018&gist=e5192749c928122c465d3aa18ede67ce |
And I've made a post on the official Rust subreddit to get some advice regarding Rspotify's architecture, see the discussion here: https://www.reddit.com/r/rust/comments/lkdw6o/designing_a_new_architecture_for_rspotify_based/? |
I've been tinkering with a library for the Twitch Helix API and have been running into similar issues. Would be very interested to see what you come up with. |
Note: this will be modified after #173 is merged, since the HTTP request methods are no longer implemented over the Spotify client. Instead, the HTTP client is a different struct to keep the logic separate and the abstraction over multiple http libraries clean. |
Here's a draft of how it would look like with the proposed changes https://github.com/marioortizmanero/rspotify-architecture, can you take a look before we modify the client anymore, @ramsayleung? Thanks! The final architecture is still based on the trait system, with a few tweaks to incude I'd like to get this done after #161 to avoid possible conflicts. |
The full trait inheritance is a little bit complex: Let's take a look at the simplied one: The new architecture is much clear than the current one, since the endpoint flow and auth flow are coupling tightly. There is one question about this architecture proposal, since the |
Yes, I asked that in the original comment and you agreed with me that it'd be better to have a horizontal hierarchy:
But it might be unnecessarily complex for now. I don't think there'll be an auth method that will let you access user methods but not the basic ones, so a vertical hierarchy makes sense to me. While we're at it we can merge Can you create a diagram with the full hierarchy once the architecture is definitive so that we can include it in the docs for newcomers? Do you agree with everything else? Maybe other contributors like @kstep or @martinmake can provide their opinions. |
It makes sense.
Yep, great idea, it will be a pleasure to do that.
Yeah, your proposal is complete, it seems there is nothing left to discuss. |
Last thing to consider before I start working on this small rewrite: Now that the main client doesn't need the builder pattern for its initialization, should we completely get rid of it? Other structs like let tok = TokenBuilder::default()
.access_token("test-access_token")
.expires_in(Duration::seconds(3600))
.expires_at(now)
.scope(scope.clone())
.refresh_token("...")
.build()
.unwrap(); You can just write: let tok = Token {
access_token: "test-access_token".to_owned(),
expires_in: Duration::seconds(3600),
expires_at: now,
scope: scope.clone(),
refresh_token: "...".to_owned(),
..Default::default()
}; We could get rid of All structs currently using the builder pattern:
The Feel free to deny this proposal as I understand it might not be necessary, I just want to at least mention it before this is implemented, now that it's possible. |
Back when I first rewrote the authentication process I did improve a few things I thought were necessary, like only having a single http client per
Spotify
instance, and being more ergonomic and easy to use.But it still leaves a few things to be desired. For example, an
InvalidAuth
error variant is needed for whenever a user calls a method fromSpotify
that isn't available due to the authentication process. If one were to use the Client Credentials Flow, the endpoints with access to the user's data shouldn't be available (sayme
orcurrently_playing_track
). You need to use the Authentication Code Flow for that. This worked well enough because we only had two different methods: the Client Credentials and the Authentication Code.Now that the PKCE Code Authentication Flow has to be implemented, I think it's time to re-think the architecture we're following and make it more idiomatic. This auth flow can access the same endpoints the regular Code Authentication Flow can, but it requires a different setup.
We mainly need three levels for this problem:
rspotify::http::BaseClient
. I'll call itHTTPClient
from now on.BaseClient
, a base Spotify client, with access to endpoints such astracks
orsearch
that don't need access to the user's data. This usesHTTPClient
to make requests.UserClient
, an user authenticated Spotify client, with access to endpoints such asme
. This may inherit fromBaseClient
and implement the extra endpoints over it, or useHTTPClient
directly and only have the user-authenticated ones.Rust doesn't really have OOP, but we can work it out with traits. Here's the first solution, where the
UserClient
inherits fromBaseClient
so that the Code Auth flows can access bothUserClient
andBaseClient
methods:And here's another solution with a more horizontal hierarchy.
UserClient
only includes the user authenticated methods because it doesn't inherit fromBaseClient
, so the Code Auth flows have to implement both of them. I feel like this is more natural in Rust, and it seems more modular because it doesn't assume that user authenticated flows can access the regular ones.In both of these models the implementations take place in the traits, which only needs access to some
HTTPClient
methods, so the structs would only need to implement at most a couple getters for their internal fields, kinda like howIterator
works.This is purely theoretical so I'm not sure how easy it is to implement. I'll try to code a rough sketch of the latter model, although I'll be quite busy soon with finals so I'm not sure when that'll be possible.
What do you think? Any more ideas?
The text was updated successfully, but these errors were encountered: