Last active
May 25, 2024 08:39
-
-
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
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// 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