ZIO-HTTP: Partie 3 – authentification et CORS

Cet article fait suite aux deux précédents articles qui présentaient ZIO-HTTP (introduction et injection de dépendances).

Aujourd’hui nous allons nous pencher sur la sécurité, plus particlulièrement sur l’authentification et CORS.

Authentification

Une API d’authentification est en cours de développement au sein de ZIO-HTTP. En attendant qu’elle soit prête à être utilisée, il est assez simple d’écrire soit même une méthode d’authentification, par exemple basée sur le décodage de jetons JWT.

Considérons dans notre exemple que toutes les erreurs pouvant être générées par notre application étendent le trait ‘AppError’, et que le type ‘ExtServices’ est la combinaison de toutes les dépendances nécessaires au fonctionnement de notre application.

Cette méthode permet de traiter un contenu de type A en récupérant au préalable un jeton JWT dans une entête (header) de requête HTTP :

def withAuth[A: Encoder](request: Request, content: ZIO[ExtServices, AppError, A]): URIO[ExtServices, UResponse] = {
      val jwtToken = request.headers.find(_.name.toString == "authorization").map(_.value.toString.stripPrefix("Bearer "))
      jwtToken match {
        case None => ZIO.succeed(Response.fromHttpError(Forbidden("Authentication needed (missing token)")))
        case Some(token) => ??? // décoder et vérifier les informations du token pour savoir si l’utilisateur peut accéder au contenu
    }
}


Pour accéder au contenu du token, on peut utiliser la librairie JwtCirce comme ceci : 

val payload = JwtCirce.decode(token, issuerCertificate, Seq(JwtAlgorithm.RS256)).map(_.content)

On pourra ensuite accéder aux différents champs du jeton, par exemple au nom, email, ou identifiant de groupe d’utilisateur.

Pour utiliser la méthode withAuth, nous lui passerons le contenu à gérer ainsi que la réquête courante depuis la définition de nos routes : 

val app = request@Http.collect[Request] {
    case Method.GET -> Root / "hello" / name =>
        val content = Response.text(s"Hello $name!")
        withAuth(request, content)    
}

CORS

Pour rendre une API Web compatible avec CORS (Cross-origin resource sharing), il suffit d’utiliser le helper CORS comme ceci : 

CORS(Http.collectM[Request] {
  //... <- vos routes ici
})

ZIO-HTTP ajoutera alors automatiquement les routes et les headers nécessaires au fonctionnement de CORS.

Aller plus loin

SI vous souhaitez aller plus loin dans l’exploration de ZIO-HTTP, vous pouvez également jet un oeil au support des WebSockets, qui se marient naturellement avec la gestion de flux de données de ZIO-Streams, que nous avions présentés dans cette vidéo.