Skip to content

Instantly share code, notes, and snippets.

@dacr
Last active May 25, 2024 08:39
Show Gist options
  • Save dacr/224b3674fd31b93eb1a90e97edb5413b to your computer and use it in GitHub Desktop.
Save dacr/224b3674fd31b93eb1a90e97edb5413b to your computer and use it in GitHub Desktop.
securing API with tapir - basic auth / published by https://github.com/dacr/code-examples-manager #3a3193d3-e9ea-410f-9d4c-8b230993849a/2fd255cd11366824f4a21d8615d01f980f8a29c1
// summary : securing API with tapir - basic auth
// keywords : scala, zio, tapir, http, zhttp, endpoints, auth, basicauth, swagger, secured, @testable, @exclusive
// publish : gist
// authors : David Crosson
// license : Apache NON-AI License Version 2.0 (https://raw.githubusercontent.com/non-ai-licenses/non-ai-licenses/main/NON-AI-APACHE2)
// id : 3a3193d3-e9ea-410f-9d4c-8b230993849a
// created-on : 2023-05-12T18:31:42+02:00
// managed-by : https://github.com/dacr/code-examples-manager
// run-with : scala-cli $file
// test-with : curl -v -u admin:admin http://127.0.0.1:8080/hello
// ---------------------
//> using scala "3.4.2"
//> using dep "com.softwaremill.sttp.tapir::tapir-core:1.6.0"
//> using dep "com.softwaremill.sttp.tapir::tapir-zio-http-server:1.6.0"
//> using dep "com.softwaremill.sttp.tapir::tapir-json-zio:1.6.0"
//> using dep "com.softwaremill.sttp.tapir::tapir-swagger-ui-bundle:1.6.0"
//> using dep "fr.janalyse::zio-worksheet:2.0.15.0"
// ---------------------
import sttp.tapir.ztapir.*
import sttp.tapir.server.ziohttp.ZioHttpInterpreter
import zio.*, zio.worksheet.*, zio.http.Server, zio.json.*
import sttp.model.headers.WWWAuthenticateChallenge
import sttp.tapir.model.UsernamePassword
import sttp.tapir.json.zio.*
import sttp.tapir.generic.auto.*
import sttp.model.StatusCode
import sttp.tapir.swagger.bundle.SwaggerInterpreter
import sttp.apispec.openapi.Info
/*
curl -u admin:x http://127.0.0.1:8080/hello
curl -u admin:admin http://127.0.0.1:8080/hello
curl -u admin:x http://127.0.0.1:8080/docs
*/
case class User(userName: String)
case class PermissionDenied(message: String) derives JsonCodec
def checkUsernamePassword(usernamePassword: UsernamePassword): ZIO[Any, PermissionDenied, User] =
usernamePassword match {
case UsernamePassword(username @ "admin", Some("admin")) => ZIO.succeed(User(username))
case UsernamePassword(username, _) => ZIO.fail(PermissionDenied("Invalid username or password"))
}
val statusForPermissionDenied = oneOfVariant(StatusCode.Forbidden, jsonBody[PermissionDenied].description("Permission denied"))
val challengeBasic = WWWAuthenticateChallenge.basic("basicAuth")
val securedBasicEndpoint =
endpoint.get
.tag("greetings")
.securityIn(auth.basic[UsernamePassword](challengeBasic))
.errorOutVariant(statusForPermissionDenied)
.zServerSecurityLogic[Any, User](usernamePassword => checkUsernamePassword(usernamePassword))
val helloEndPoint =
securedBasicEndpoint
.in("hello")
.out(stringBody)
def hello(user: User): Unit => ZIO[Any, PermissionDenied, String] = _ => ZIO.succeed(s"Hello ${user.userName}")
val helloRoute = helloEndPoint.serverLogic[Any](user => hello(user))
val apiDocInfo = Info(title = "Greeting API", version = "1.0", description = Some("Everything required to be polite"))
val swaggerEndpoints = SwaggerInterpreter().fromServerEndpoints(List(helloRoute), apiDocInfo)
val helloHttp = ZioHttpInterpreter().toHttp(helloRoute :: swaggerEndpoints)
Server.serve(helloHttp.withDefaultErrorResponse).provide(Server.default).unsafeRun
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment