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

Let oxide_auth_rocket::OAuthFailure be a #[non_exhaustive] enum #118

Open
1 task
lfxgroove opened this issue Oct 17, 2020 · 5 comments
Open
1 task

Let oxide_auth_rocket::OAuthFailure be a #[non_exhaustive] enum #118

lfxgroove opened this issue Oct 17, 2020 · 5 comments
Labels
improvement Improve existing implementation

Comments

@lfxgroove
Copy link
Contributor

Project Improvement

Currently oxide_auth_rocket::OAuthFailure is a struct with a private inner field Kind which one can retrieve variants from via the oauth() and web() methods. If you want to roll your own error type (since OAuthFailure is not stable) you have to do some cumbersome matching that feels a bit un-rusty:

if let Some(web_failure) = failure.web() {
    //...
} else if let Some(oauth_failure) = failure.oauth() {
    //...
}

Turning the failure type into a non-exhaustive enum would allow users to match on the enum instead of having to call functions while still allowing the project to add new failure modes.

Other context

It just feels like a more ergonomic interface to use.

Tracking pull request

  • A pull request does not yet exist, I could create it if this seems like a reasonable request.
@lfxgroove lfxgroove added the improvement Improve existing implementation label Oct 17, 2020
@HeroicKatora
Copy link
Owner

HeroicKatora commented Oct 17, 2020

When implementing the type, I wasn't really expecting it to be used for storing an error type. It's mostly a wrapper that implements Responder<'r> and not much else, since it can't implement the trait for the foreign OAuthError type from oxide-auth. I don't think it's actually used in any of the functions or flows. I might misunderstand the problem though. What's the use that requires it to be stored?

@lfxgroove
Copy link
Contributor Author

lfxgroove commented Oct 21, 2020

I don't really require it to be stored, I just realized that I probably worded the op a bit strangely. I'm currently trying to implement FromRequest to protect resources, in there I've got something like this in the error case when calling execute() for a resource flow:

match protection {
    Ok(_grant) => {
        Outcome::Success(())
    },
    Err(e) => {
        match e {
            Ok(ok) => {
                let resp: RocketResponse = ok.into();
                Outcome::Failure((resp.status(), resp))
            },
            Err(err) => {
                // Logic here is the same as in oxide_auth_rocket/failure.rs, the Responder
                // impl for OAuthFailure
                if let Some(oauth) = err.oauth() {
                    match oauth {
                        OAuthError::BadRequest | OAuthError::DenySilently => {
                            Outcome::Failure((Status::BadRequest, RocketResponse::new()))
                        },
                        OAuthError::PrimitiveError => {
                            Outcome::Failure((Status::InternalServerError, RocketResponse::new()))
                        }
                    }
                } else if let Some(_web) = err.web() {
                    Outcome::Failure((Status::BadRequest, RocketResponse::new()))
                } else {
                    unreachable!();
                }
            },
        }
    }
}

To me it would make more sense to match on err instead of using a chain of if let in the Err(Err(..)) case, e.g:

match err {
    OAuthFailure::OAuth(oauth_err) => {},
    OAuthFailure::Web(web_err) => {},
    _ => unreachable!(), // or some other kind of handling
}

Does that make sense?

I guess a follow-up question is if there's a better way to handle this in general, currently I've just copied the implementation details from the Responder impl for oxide_auth_rocket/failure.rs since I'm not really sure how to use a Responder to create a Outcome, the status code is what's tripping me up. I'll dig a bit into this and see if I can't figure something out.

@HeroicKatora
Copy link
Owner

HeroicKatora commented Oct 21, 2020

If you want to treat use a different error that's fine. You only have to implement the Endpoint trait with the one you intend to use. Then the type of err in Err(err) => is different and you don't have that problem. As for the Responder trait impl you'll want to consult the documentation of rocket.

@lfxgroove
Copy link
Contributor Author

I see, thanks for the info!

I guess you do not want to replace oxide_auth_rocket::OAuthFailure with the enum oxide_auth_rocket::Kind to enable matching instead of calling web() and oauth()?

@HeroicKatora
Copy link
Owner

Hm, I hadn't considered that #[non_exhaustive] was not actually available when I first implemented this type 🙂 So changing it to Kind and adding the attribute would be a more modern alternative. I'm inclined to agree with that replacement.

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

No branches or pull requests

2 participants