-
Notifications
You must be signed in to change notification settings - Fork 331
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
Introduce UserAuthStrategy to allow third party authentication implementation #1026
Conversation
41fd70c
to
b3b3c75
Compare
Looks good to me, but @MarinPostma or @LucioFranco ought to review this before merging. |
I need a bit of time to look at this, but will do asap |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall this seems like a great improvement, I left a few comments/questions regarding some style things and api designs. Let me know if something isn't clear! thanks!
} | ||
|
||
pub trait UserAuthStrategy: Sync + Send { | ||
fn authenticate(&self, context: UserAuthContext) -> Result<Authenticated, AuthError>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wonder if we can just accept a borrow here, I believe no auth really needs full ownership of this struct? Its likely we don't need this now but its good to encode that contract in a trait if we are going to abstract it.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we keep the struct around, can change!
libsql-server/src/config.rs
Outdated
@@ -60,8 +60,7 @@ pub struct UserApiConfig<A = AddrIncoming> { | |||
pub http_acceptor: Option<A>, | |||
pub enable_http_console: bool, | |||
pub self_url: Option<String>, | |||
pub http_auth: Option<String>, | |||
pub auth_jwt_key: Option<String>, | |||
pub auth_strategy: Arc<Box<dyn UserAuthStrategy>>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Whats the reason for a double pointer indirection here? Could we just do Arc<dyn UserAuthStrategy>
and avoid the box?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This was my misunderstanding, I'd started by passing a Box
and then brought in the Arc
, didn't realize that it eliminated the need for Box
. Thanks for pointing it out.
.await??; | ||
|
||
// Convert jwt token into a HeaderValue to be compatible with UserAuthStrategy | ||
let user_credential = jwt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
unfortunate but I think this is fine for now, WS is not our primary protocol anyways for hrana so this I think is acceptable for now.
I think the only thing this really poses is should we accept a String rather than a HeaderValue in the auth trait?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I like the idea of user_credential
being an Option<String>
though a couple of things off the top of my head I think we'd need to account for:
- In HTTP context, do we pass the entire
Authorization
header value? - In this WS context, do we still prepend an Authorization scheme or does the strategy need to do some extra homework to determine if it's being given a header value or something else?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make sense to strip the prefix at the http/ws level and then only pass through the "token" to the Auth layer? I feel like that would make the most sense. What do you think? That way we can just set this as a string/bytes and not leak HeaderValue through to the auth layer.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@shopifyski Said he'd take this on as a follow up PR!
9ccea41
to
5b0c509
Compare
…entation Co-authored-by: Jeremy Rowe <[email protected]>
5b0c509
to
388c3c9
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall looks good left a question and some feedback, we can merge this and then do that as follow up or yall could implement those changes and we can merge up to you.
libsql-server/src/auth/mod.rs
Outdated
pub use permission::Permission; | ||
pub use user_auth_strategies::{Disabled, HttpBasic, Jwt, UserAuthContext, UserAuthStrategy}; | ||
|
||
pub type Auth = Arc<dyn UserAuthStrategy>; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think we can do this in a follow up and the code change should be pretty small, but I would like this to be a struct since type aliases tend to leak the internal implementation detail. So it could be a struct like:
#[derive(Clone)]
pub struct Auth {
inner: Arc<dyn UserAuthStrategy + Send + Sync>,
}
impl Auth {
// implement functions that we need to be public + constructor via `impl UserAuthStrategy`
}
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd like to take care of this in this pull request. I'll take a stab at it right now :D
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is taken care of here ad18ef0
.await??; | ||
|
||
// Convert jwt token into a HeaderValue to be compatible with UserAuthStrategy | ||
let user_credential = jwt |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think it would make sense to strip the prefix at the http/ws level and then only pass through the "token" to the Auth layer? I feel like that would make the most sense. What do you think? That way we can just set this as a string/bytes and not leak HeaderValue through to the auth layer.
pub use permission::Permission; | ||
pub use user_auth_strategies::{Disabled, HttpBasic, Jwt, UserAuthContext, UserAuthStrategy}; | ||
|
||
#[derive(Clone)] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
TODO: Add a comment on why we chose a struct over a type alias.
folks, since @LucioFranco only has nits, and I need to do auth work, I'm merging this now, I'll let you address the rest in followups :) |
let's address the rest in followups
What are you trying to accomplish?
This PR seeks to open up authentication so that anyone using libsql-server as a library can provide their own authentication strategy.
What approach did you choose and why?
We removed the
Auth
struct in favor of structs that implementUserAuthStrategy
that can be configured viaUserApiConfig
. The existing strategies of HTTP Basic and JWT have been moved into their own strategy definitions. AnywhereAuth
was previously being call for authentication has been switched out withUserAuthStrategy#authenticate
.What should reviewers focus on?
auth
mod with exception of the auth strategies are existing code from the originalauth.rs
but put into their own files.AuthError
to provide more generic enum variants with data to give auth strategies more control over their errors.hrana::ws
? How might we approach this differently otherwise?