Skip to main content
Version: 2.x

Authentication Example

Authentication Server Example

zio-http-example/src/main/scala/example/AuthenticationServer.scala
package example

import java.time.Clock

import scala.util.Try

import zio._

import zio.http._

import pdi.jwt.{Jwt, JwtAlgorithm, JwtClaim}

/**
* This is an example to demonstrate bearer Authentication middleware. The
* Server has 2 routes. The first one is for login, Upon a successful login, it
* will return a jwt token for accessing protected routes. The second route is a
* protected route that is accessible only if the request has a valid jwt token.
* AuthenticationClient example can be used to makes requests to this server.
*/
object AuthenticationServer extends ZIOAppDefault {
implicit val clock: Clock = Clock.systemUTC

// Secret Authentication key
val SECRET_KEY = "secretKey"

def jwtEncode(username: String, key: String): String =
Jwt.encode(JwtClaim(subject = Some(username)).issuedNow.expiresIn(300), key, JwtAlgorithm.HS512)

def jwtDecode(token: String, key: String): Try[JwtClaim] =
Jwt.decode(token, key, Seq(JwtAlgorithm.HS512))

val bearerAuthWithContext: HandlerAspect[Any, String] =
HandlerAspect.interceptIncomingHandler(Handler.fromFunctionZIO[Request] { request =>
request.header(Header.Authorization) match {
case Some(Header.Authorization.Bearer(token)) =>
ZIO
.fromTry(jwtDecode(token.value.asString, SECRET_KEY))
.orElseFail(Response.badRequest("Invalid or expired token!"))
.flatMap(claim => ZIO.fromOption(claim.subject).orElseFail(Response.badRequest("Missing subject claim!")))
.map(u => (request, u))

case _ => ZIO.fail(Response.unauthorized.addHeaders(Headers(Header.WWWAuthenticate.Bearer(realm = "Access"))))
}
})

def routes: Routes[Any, Response] =
Routes(
// A route that is accessible only via a jwt token
Method.GET / "profile" / "me" -> handler { (_: Request) =>
ZIO.serviceWith[String](name => Response.text(s"Welcome $name!"))
} @@ bearerAuthWithContext,

// A login route that is successful only if the password is the reverse of the username
Method.GET / "login" ->
handler { (request: Request) =>
val form = request.body.asMultipartForm.orElseFail(Response.badRequest)
for {
username <- form
.map(_.get("username"))
.flatMap(ff => ZIO.fromOption(ff).orElseFail(Response.badRequest("Missing username field!")))
.flatMap(ff => ZIO.fromOption(ff.stringValue).orElseFail(Response.badRequest("Missing username value!")))
password <- form
.map(_.get("password"))
.flatMap(ff => ZIO.fromOption(ff).orElseFail(Response.badRequest("Missing password field!")))
.flatMap(ff => ZIO.fromOption(ff.stringValue).orElseFail(Response.badRequest("Missing password value!")))
} yield
if (password.reverse.hashCode == username.hashCode)
Response.text(jwtEncode(username, SECRET_KEY))
else
Response.unauthorized("Invalid username or password.")
},
) @@ Middleware.debug

override val run = Server.serve(routes).provide(Server.default)
}

Authentication Client Example

zio-http-example/src/main/scala/example/AuthenticationClient.scala
package example

import zio._

import zio.http._

object AuthenticationClient extends ZIOAppDefault {

/**
* This example is trying to access a protected route in AuthenticationServer
* by first making a login request to obtain a jwt token and use it to access
* a protected route. Run AuthenticationServer before running this example.
*/
val url = "http://localhost:8080"

val loginUrl = URL.decode(s"${url}/login").toOption.get
val greetUrl = URL.decode(s"${url}/profile/me").toOption.get

val program = for {
client <- ZIO.service[Client]
// Making a login request to obtain the jwt token. In this example the password should be the reverse string of username.
token <- client
.batched(
Request
.get(loginUrl)
.withBody(
Body.fromMultipartForm(
Form(
FormField.simpleField("username", "John"),
FormField.simpleField("password", "nhoJ"),
),
Boundary("boundary123"),
),
),
)
.flatMap(_.body.asString)
// Once the jwt token is procured, adding it as a Bearer token in Authorization header while accessing a protected route.
response <- client.batched(Request.get(greetUrl).addHeader(Header.Authorization.Bearer(token)))
body <- response.body.asString
_ <- Console.printLine(body)
} yield ()

override val run = program.provide(Client.default)

}

Middleware Basic Authentication Example

zio-http-example/src/main/scala/example/BasicAuth.scala
package example

import zio._

import zio.http.Middleware.basicAuth
import zio.http._
import zio.http.codec.PathCodec.string

object BasicAuth extends ZIOAppDefault {

// Http app that requires basic auth
val user: Routes[Any, Response] = Routes(
Method.GET / "user" / string("name") / "greet" ->
handler { (name: String, _: Request) =>
Response.text(s"Welcome to the ZIO party! ${name}")
},
)

// Add basic auth middleware
val routes: Routes[Any, Response] = user @@ basicAuth("admin", "admin")

val run = Server.serve(routes).provide(Server.default)
}