security.claim.scala Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of otoroshi_2.12 Show documentation
Show all versions of otoroshi_2.12 Show documentation
Lightweight api management on top of a modern http reverse proxy
The newest version!
package otoroshi.security
import java.nio.charset.StandardCharsets
import java.util.{Base64, Date}
import com.auth0.jwt.algorithms.Algorithm
import otoroshi.env.Env
import otoroshi.models.AlgoSettings
import org.joda.time.DateTime
import otoroshi.utils.syntax.implicits.BetterJsValue
import play.api.Logger
import play.api.libs.json._
case class OtoroshiClaim(
iss: String, // issuer
sub: String, // subject
aud: String, // audience
exp: Long, // date d'expiration
iat: Long = DateTime.now().getMillis, // issued at
jti: String, // unique id forever
metadata: JsObject = Json.obj() // private claim
) {
def toJson: JsValue = OtoroshiClaim.format.writes(this)
def serialize(jwtSettings: AlgoSettings)(implicit env: Env): String = OtoroshiClaim.serialize(this, jwtSettings)(env)
def withClaims(claims: JsValue): OtoroshiClaim =
copy(metadata = metadata ++ claims.asOpt[JsObject].getOrElse(Json.obj()))
def withClaims(claims: Option[JsValue]): OtoroshiClaim =
claims match {
case Some(c) => withClaims(c)
case None => this
}
def withClaim(name: String, value: String): OtoroshiClaim = copy(metadata = metadata ++ Json.obj(name -> value))
def withRootClaim(name: String, value: String): OtoroshiClaim =
this // copy(metadata = metadata ++ Json.obj(name -> value))
def withClaim(name: String, value: Option[String]): OtoroshiClaim =
value match {
case Some(v) => copy(metadata = metadata ++ Json.obj(name -> v))
case None => this
}
def withJsObjectClaim(name: String, value: Option[JsObject]): OtoroshiClaim =
value match {
case Some(v) => copy(metadata = metadata ++ Json.obj(name -> v))
case None => this
}
def withJsArrayClaim(name: String, value: Option[JsArray]): OtoroshiClaim =
value match {
case Some(v) => copy(metadata = metadata ++ Json.obj(name -> v))
case None => this
}
def payload(implicit env: Env): JsObject = Json.obj(
"iss" -> env.Headers.OtoroshiIssuer, // TODO: maybe using iss is better ?
"sub" -> sub,
"aud" -> aud,
"exp" -> new Date(exp).getTime / 1000,
"iat" -> new Date(iat).getTime / 1000,
"nbr" -> new Date(iat).getTime / 1000,
"jti" -> jti
) ++ metadata
}
object OtoroshiClaim {
val encoder = Base64.getUrlEncoder
val decoder = Base64.getUrlDecoder
val format = Json.format[OtoroshiClaim]
lazy val logger = Logger("otoroshi-claim")
def serialize(claim: OtoroshiClaim, jwtSettings: AlgoSettings)(implicit env: Env): String = {
val algorithm = jwtSettings.asAlgorithm(otoroshi.models.OutputMode).get
// Here we bypass JWT lib limitations ...
val header = Json.obj(
"typ" -> "JWT",
"alg" -> algorithm.getName
)
val payload = claim.payload
val signed = sign(algorithm, header, payload)
if (logger.isDebugEnabled) logger.debug(s"signed: $signed")
signed
}
private def sign(algorithm: Algorithm, headerJson: JsObject, payloadJson: JsObject): String = {
if (logger.isDebugEnabled) {
logger.debug(s"signing following header: ${headerJson.prettify}")
logger.debug(s"signing following payload: ${payloadJson.prettify}")
}
val header: String = org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(Json.toBytes(headerJson))
val payload: String = org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(Json.toBytes(payloadJson))
val signatureBytes: Array[Byte] =
algorithm.sign(header.getBytes(StandardCharsets.UTF_8), payload.getBytes(StandardCharsets.UTF_8))
val signature: String = org.apache.commons.codec.binary.Base64.encodeBase64URLSafeString(signatureBytes)
String.format("%s.%s.%s", header, payload, signature)
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy