smithy4s
smithy4s copied to clipboard
`AuthedRoutes` support (http4s)
Hi there 👋 I just want to ask, are there plans to add support for authenticated routes / middleware for http4s, or is this not on the roadmap?
My comment will be mostly useless as I don't have any information on this :) so waiting for @Baccata's input. Here are my thoughts though, based on what I do know about http4s/smithy:
If we exposed AuthedRoutes[F, User]
, you'd still have to pass a function like Request[F] => F[User]
(simplified). So you'd need to know where the auth data is located in the request, and Smithy is supposed to help you avoid dealing with that.
I imagine we could support Smithy's authentication traits and give you some sort of convenient API so that you implement something closer to Authentication => F[User]
, and User
is part of your service's methods' inputs. Just spitballing though, this is just one of the possible implementations.
If we get into this territory, clients should also be able to send auth metadata.
We don't currently have plans to support auth out of the box, I'm afraid. However, all annotations that are part of smithy specifications are code-generated in something called "Hints", which is exposed in various abstractions like smithy4s.Service
(the instances of which are also code generated).
So users should be able to retrieve the auth annotations themselves and use them in their own middleware.
It is no coincidence that the http4s methods return http4s' HttpRoutes, as it allows for using existing middleware at that level.
That being said, I wouldn't mind looking at PRs if people have ideas on how auth should be implemented
Apologies for the late response, must have missed the notification.
So users should be able to retrieve the auth annotations themselves and use them in their own middleware.
Thanks a lot for the info, didn't know we could retrieve the annotations!
It is no coincidence that the http4s methods return http4s' HttpRoutes, as it allows for using existing middleware at that level
The only downside is that Http4s' AuthMiddleware
wraps around an AuthedRoutes
instead of the normal HttpRoutes
. Although it's not a huge issue since there's probably some way that the HttpRoutes
trait can be converted to AuthedRoutes
.
Thanks again!
Hello. Is there still interest in adding this? I was poking around a bit to see what it would take.
Going off of how http4s
does things, we'd need to specify a special structure that contains authorization information (typically called User
). This could be marked with a new special trait - nothing in the current https://awslabs.github.io/smithy/1.0/spec/core/auth-traits.html
seems to fit. Then the user would provide a function Request => F[User]
that extracts user information from the request that is then passed on to the service. The User
would be added as an extra param to all operations
The currently provided authentication traits could be used to simplify the process. ie if the service was marked with @httpBasicAuth
, we could do the work of extracting the username and password from the Request
into a case class BasicAuth(user: String, password: String)
, and then the user would just have to specify a BasicAuth => F[User]
function, removing the low-level parsing step
This probably needs to be thought out a lot more so it can be properly generalized to other frameworks and use cases, but just my 2¢
Hey @sbly. The guiding design principle of smithy4s is to avoid the tweak of the generated code at all cost for protocol-specific purposes. The reason is that we're keeping smithy4s protocol agnostic, and doing so unlocks a lot of interesting things. If we start catering to specific usecases by means of special handling in the code generation, we'll easily get trapped in a position that will prevent the implementation of these interesting, very valuable things.
The User would be added as an extra param to all operations
This can be achieved reasonably easily via other means without impacting code generation, for instance via ReaderT/Kleisli (or the Ask
typeclass in cats-mtl). I'll probably get around to writing a cookbook at some point, to examplify solutions to these usecases.
Apologies if that's not a very satisfying answer. What we are trying to achieve with smithy4s goes way past the "http/rest" usecase, and I have to be harsh/fair and ensure that we don't take on any code that would prevent the achievement of that goal.
On the brightside however, it is really, really easy to write code generators for smithy, and that's in part why we're really interested in it. If you want a specific UX that caters to their own choices, it is definitely achievable to write your own tooling, and make it http/rest specific :)
Hi everyone, sorry to bring this dusty ol'book up again. I was wondering, is there a way to extract the value of the Authorization
header in the generated code? I managed to do it with custom headers but not with Authorization. Any pointers to examples would be very highly appreciated 😅
Sure : there's two ways of handling this, depending on whether you're okay for the Auth header to pollute the generated interfaces or not.
#### First approach
The first way is to disable the Danger
diagnostic associated with the attempting to set a header to Authorization
. You can disable it by adding this at the top of the file :
metadata suppressions = [
{
id: "HttpHeaderTrait",
namespace: "my.namespace",
reason: "I really want to use discouraged headers"
}
]
This lets you do
structure MyOperationInput {
@httpHeader("Authorization")
auth: String
}
The reason for these headers to be discouraged is that AWS thinks such headers should be handled by the frameworks, and they're not entirely wrong. But smithy4s doesn't support authorization just yet.
#### Second approach
The second approach is more involved, but cleaner and more flexible : it has to do with creating an http4s middleware that would extract the values of the headers you need. Essentially it means you need a function that does :
type AuthedIO[A] = Kleisli[IO, Auth, A]
def authMiddleware(app: HttpApp[AuthedIO]): HttpApp[IO]
Then you code your service against AuthedIO
instead of IO
, get an HttpApp[AuthedIO]
from smithy4s, and feed that to the http4s server builder of your choice.
@Baccata Hi,
would (or how would) the second approach be possible with a tagless final encoding/making the effect type polymorphic? I tried for a while but ended up playing "type tetris" and losing unfortunately. Thanks!
@scala-impala, we have written a little guide to describe an approach we recommend for writing this kind of middleware. The guide describes monomorphic code, but we have a note at the end that gives tips on how to make it polymorphic.
@Baccata I've got us set up with a cats-mtl (implicit Ask[F, User]
) tagless final example which took some type tetris and would be happy to share it as an example in the docs
Functionally works really, however if we have a mix of auth/no-auth routes I'd quite like the Swagger docs to specify the header as required, even if Smithy doesn't pass it as a parameter. Is there a way to do that?
@alexcardell I think using auth-trait will result in the corresponding routes being marked as authenticated in the openapi output.
I say "I think" because we're re-using a lot of code provided by AWS and I'm not 100% sure.
Closing this : endpoint-specific middlewares are now available and can be used to implement auth. See the documentation : https://disneystreaming.github.io/smithy4s/docs/guides/endpoint-middleware