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

Introduce UserAuthStrategy to allow third party authentication implementation #1026

Closed
wants to merge 2 commits into from

Conversation

jameswritescode
Copy link
Contributor

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 implement UserAuthStrategy that can be configured via UserApiConfig. The existing strategies of HTTP Basic and JWT have been moved into their own strategy definitions. Anywhere Auth was previously being call for authentication has been switched out with UserAuthStrategy#authenticate.

What should reviewers focus on?

  • A handful of the changes in the auth mod with exception of the auth strategies are existing code from the original auth.rs but put into their own files.
  • We would like to follow up in the future with changes to AuthError to provide more generic enum variants with data to give auth strategies more control over their errors.
  • How do we feel about the changes in hrana::ws? How might we approach this differently otherwise?

@jameswritescode jameswritescode force-pushed the auth-strategies branch 2 times, most recently from 41fd70c to b3b3c75 Compare February 14, 2024 18:46
@penberg
Copy link
Collaborator

penberg commented Feb 16, 2024

Looks good to me, but @MarinPostma or @LucioFranco ought to review this before merging.

@MarinPostma
Copy link
Contributor

I need a bit of time to look at this, but will do asap

Copy link
Contributor

@LucioFranco LucioFranco left a 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!

libsql-server/src/auth/mod.rs Outdated Show resolved Hide resolved
}

pub trait UserAuthStrategy: Sync + Send {
fn authenticate(&self, context: UserAuthContext) -> Result<Authenticated, AuthError>;
Copy link
Contributor

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.

Copy link
Contributor Author

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!

@@ -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>>,
Copy link
Contributor

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?

Copy link
Contributor Author

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.

libsql-server/src/auth/errors.rs Outdated Show resolved Hide resolved
libsql-server/src/hrana/ws/mod.rs Outdated Show resolved Hide resolved
libsql-server/src/hrana/ws/session.rs Show resolved Hide resolved
.await??;

// Convert jwt token into a HeaderValue to be compatible with UserAuthStrategy
let user_credential = jwt
Copy link
Contributor

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?

Copy link
Contributor Author

@jameswritescode jameswritescode Feb 16, 2024

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?

Copy link
Contributor

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.

Copy link
Contributor

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!

@jameswritescode jameswritescode force-pushed the auth-strategies branch 2 times, most recently from 9ccea41 to 5b0c509 Compare February 16, 2024 19:40
Copy link
Contributor

@LucioFranco LucioFranco left a 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.

pub use permission::Permission;
pub use user_auth_strategies::{Disabled, HttpBasic, Jwt, UserAuthContext, UserAuthStrategy};

pub type Auth = Arc<dyn UserAuthStrategy>;
Copy link
Contributor

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`
}

Copy link
Contributor

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

Copy link
Contributor

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
Copy link
Contributor

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)]
Copy link
Contributor

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.

@MarinPostma
Copy link
Contributor

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 :)

@MarinPostma MarinPostma dismissed LucioFranco’s stale review February 22, 2024 10:43

let's address the rest in followups

@MarinPostma MarinPostma mentioned this pull request Feb 22, 2024
@jeremywrowe jeremywrowe deleted the auth-strategies branch February 22, 2024 15:17
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.

5 participants