#Looking for feedback on authentication middleware for Wisp

1 messages · Page 1 of 1 (latest)

frigid star
#

Hi all

I'm experimenting with adding an authentication layer to my API. I use Wisp on the server and I have created the following middleware. It seems to work fine but looking on feedback to makes this as Gleamy as possible. Thx!

  req: Request,
  ctx: context.Context,
  next: fn(Request) -> Response,
) -> Response {
  {
    let result = {
      use auth_header <- result.try(
        request.get_header(req, "authorization") |> result.replace_error(Nil),
      )

      use jwt <- result.try(case auth_header {
        "Bearer " <> jwt -> Ok(jwt)
        _ -> Error(Nil)
      })

      authentication.verify_jwt(
        jwt,
        ctx.auth_config.claims,
        ctx.auth_config.verify_key,
      )
      |> result.replace_error(Nil)
    }

    case result {
      Ok(_) -> next(req)
      Error(_) -> wisp.response(401)
    }
  }
}

pub fn verify_jwt(
  jwt: String,
  claims: List(claim.Claim),
  verify_key: verify_key.VerifyKey,
) -> Result(UserSession, ywt.ParseError) {
  let payload_decoder = {
    use user_id <- decode.field("sub", decode.string)
    use org_id <- decode.field("org", decode.string)
    decode.success(UserSession(user_id:, org_id:))
  }

  ywt.decode(jwt, using: payload_decoder, claims: claims, keys: [verify_key])
}```
smoky adder
#

My recommendation would be to avoid JWT unless you are forced to use it by a need to integrate with an existing system

#

It's quite error prone and a common cause of security vulnerabilities

#

It looks like the request isn't modified at all, so you don't need to pass it into next

#

You've got an extra block around the function body that you could remove

#

You may want to log the failed login attempt as a bad token could imply malicious action

frigid star
#

What would you suggest instead of using jwts?

#

Thanks for the feedback!

frigid star
smoky adder
#

It would depend on your system

#

Are you in a large microservice environment?

#

So you have one service that creates tokens and holds login information, and a larger number of services that uses these tokens and the information they hold?

frigid star
#

Not really, it's a single API for customers only. I might open it up to third parties in the future.

#

And the customers access/use it via a Lustre SPA.

#

I should note that I currently store the user's ID in the payload, so when a user authenticates successfully, I extend my Context type to hold that information so I can easily pass it down to my request handler etc. where I would then check if that user is allowed to access a certain resource or not

smoky adder
#

So it’s a monolith?

#

The thing that makes the tokens is the thing that uses the tokens?

#

If so there’s no advantage to JWT, it’s all overhead and security risk

#

A secure random generated token is better, or a signed or encrypted int

frigid star
#

I'll give this some more thought, thanks for your feedback, I appreciate it 🙌

frigid star
#

Just wondering, what would you recommend for a large microservice environment?

smoky adder
#

It depends what you're trying to do, and it's quite a complex topic

#

Macaroons and biscuits are good for fine gained authorisation

#

If you want just identity then paseto could be better than jwt, being more secure and less error prone

#

there's a bunch of other things too

#

Practically a large microservice environment will probably want to use a combination of multiple technologies, as they all do somewhat different things

wild cairn
#

I'd also say it depends on how mature your ops team/process is. I'd argue that mutual TLS is sort of the ideal standard but you have to on point to not kill yourself maintaining it.

golden pike
# frigid star (In the context of a browser as the client)

If you have a browser client hitting a backend, I would say use sessions and a signed HTTP-only cookie containing the session ID.

When the user authenticates, you set the session cookie. And then look up the session when they make a request with the cookie