Open kylegoetz opened 3 years ago
Actually I apparently cannot write a cookie since that requires me to be in HeadersOpen, but I'm still in AuthenticationOpen phase.
OK what I've settled on is the authentication middleware uses fromConnection
to getRequest().rawHeaders
and add a custom header that is the authenticated user.
Then I have a getAuthenticatedUser
middleware that retrieves it, and is of type Middleware<AuthenticationOpen,AuthorizationOpen,Error,AuthenticatedUser>
.
I can then describe a route as
const routeController: Middleware<AuthenticationOpen,ResponseEnded,Error,void> = pipe(
getAuthenticatedUser(), // index AuthenticationOpen -> AuthorizationOpen
user => pipe(
noAuthorizationRequired(), // index AuthorizationOpen -> StatusOpen
H.ichain(() => H.status(H.Status.OK)), // proceeds as normal
H.ichain(() => H.closeHeaders()),
H.ichain(() => H.send(user.name))
)
Now the auth middleware is decoupled, and if I try to write a status without trying to get the authenticated user (which will do the left case if no custom auth user header is set), and I also have to specify there is no authorization required. So the indexed monad provides type safety still!
Is there any interest in a PR to update with an example like this?
I've spent the day playing with how cookie-based sessions would work. https://github.com/PREreview/prereview-display-prototype/commit/8f51ab348950289d6ac569d7e91058654f81f5d8 is large commit that adds it to a prototype app, but https://github.com/PREreview/prereview-display-prototype/blob/8f51ab348950289d6ac569d7e91058654f81f5d8/packages/hyper-ts-session/src/index.ts is the interesting bit: I've been looking at how Hyper's sessions work and trying to replicate it. Definitely a work in progress still.
I've released them as https://www.npmjs.com/package/hyper-ts-session and https://www.npmjs.com/package/hyper-ts-oauth. Still pretty basic.
I suggest there should be an auth middleware example. I'm developing my own but wondering about thoughts on best practices. Assume we have
Technically this creates a middleware with type
A=AuthenticatedUser
. So you couldflow(authenticate, user => middlewareRequiring(user)
but the "express way" is to register theauthenticate
middleware in a single place and never reference it again.This suggests to me we need to write data to the connection and then from another middleware, have a middleware like
and then we'd have some other route controller that requires authentication:
The idea is have a flow of
[AuthenticationOpen -> [AuthorizationOpen ->]] StatusOpen -> ...
so some controllers can enforce authentication (and authorization) in a typesafe manner.HOWEVER, the issue is that you apparently cannot do something like
because
authenticatedUser
does not exist on typeConnection
. So I was thinking how to persist state for another middleware to optionally access it down the middleware chain.setCookie
andsetHeader
both present that option, but I'd need to serialize authenticatedUser since they each take a string.Sounds good. But feels a bit weird, to set a cookie/header and before sending status, remove the cookie/header since they aren't actually supposed to be sent back in the request but are just kludges to pass state from one middleware to another.
Thoughts?