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

Authorization question #3342

Closed
subtil-next opened this issue Dec 25, 2023 · 2 comments
Closed

Authorization question #3342

subtil-next opened this issue Dec 25, 2023 · 2 comments
Labels
server Rust server SDK

Comments

@subtil-next
Copy link

I am struggling with the current flows for authentication.

Taking a look at the example folder, the example implements authorization as a binding to the model input. This is great, you have benefits of being able to scope the operation to conform with @optionalAuth traits. However I find it difficult to use this method when using bearer auth tokens, or with other tokens passed as a header.

I could bind a httpHeader such as:

    @httpHeader("Authorization")
    bearer: String

However, this is explicitly not allowed by smithy.

129| @input
130| structure SubscribeToEventsInput {
131|     @httpHeader("Authorization")
  |     ^

`Authorization` is not an allowed HTTP header binding

I could switch to another header, but then we are adding headers to every input, and that seems counter intuitive?

I could write the authorizer as a layer similar to the ServerRequestIdProviderLayer. However this doesn't appear to be something that can be scoped in the same way the model plugins can. (I didn't dig too much into it tbh)

--

Is there a way to bound the Op::Input to get access to the authorization headers? or is it more appropriate to write the authorizer as a HTTP Layer?

It feels like Authorization like this should be added to the Service rather than as a layer or plugin since it should conform to the smithy file?

@david-perez
Copy link
Contributor

It seems like you want to implement auth as a middleware. There's four phases when you can run middleware: A, B, C, and D, corresponding to the phases labelled in this diagram.

Depending on what auth protocol you're implementing, you'll want to run it in one of the 4 positions. Authz is generally a middleware that is run after routing, since authz permissions vary by operation, so that leaves positions C and D.

  • Position C middleware is implemented using HTTP plugins.
  • Position D middleware is implemented using model plugins.

The main difference is model plugins run after deserialization. See docs for more info.

My recommendation is that if to implement auth you don't need access to the modeled data, you implement it as an HTTP plugin, since deserialization might be expensive. If you do, a model plugin is the only way.

Both plugin types can be scoped using aws_smithy_http_server::plugin::Scoped.

Now, to get access to the bearer token, you can either:

  1. Model it in your Smithy operations with @httpHeader. But as you mention, it looks counterintuitive to model middleware requirements in all your protected operation inputs. Operation inputs are generally for use by your business logic. That's why Smithy even disavows the use of the Authorization header with @httpHeader binding.
  2. Implement an extractor (see Accessing Un-modelled Data), akin to ServerRequestIdProviderLayer, that will read the HTTP header and insert its value into the HTTP extensions. You can then access extensions in the implementation of your plugins. For example, see this line in the auth example you cited. You can implement the extractor as an HTTP plugin or as a simple middleware layer in A or B positions.

It feels like Authorization like this should be added to the Service rather than as a layer or plugin since it should conform to the smithy file?

Yep, you're absolutely right. I'm currently working on that, see #3111. Essentially, we want codegen decorators to pre-bake plugins in the generated service, so that it always honors what was modeled in Smithy. That way users don't need to manually import and apply the plugins themselves, which is an error-prone process.

@david-perez david-perez added the server Rust server SDK label Dec 27, 2023
@subtil-next
Copy link
Author

Thank you for the detailed reply. I was able to replicate what I needed to by making use of the print plugin sample. (I was trying to hook the HTTP plugin in the wrong place which was giving me issues with the scope.

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
server Rust server SDK
Projects
None yet
Development

No branches or pull requests

2 participants