Skip to content

Commit

Permalink
fix #2022
Browse files Browse the repository at this point in the history
  • Loading branch information
mathieuancelin committed Nov 6, 2024
1 parent b16df2f commit c02de9e
Show file tree
Hide file tree
Showing 2 changed files with 26 additions and 25 deletions.
31 changes: 14 additions & 17 deletions otoroshi/app/models/apikey.scala
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,7 @@ import com.auth0.jwt.interfaces.DecodedJWT
import com.google.common.base.Charsets
import com.google.common.hash.Hashing
import otoroshi.env.Env
import otoroshi.events.{
Alerts,
ApiKeyQuotasAlmostExceededAlert,
ApiKeyQuotasAlmostExceededReason,
ApiKeyQuotasExceededAlert,
ApiKeyQuotasExceededReason,
ApiKeySecretHasRotated,
ApiKeySecretWillRotate,
RevokedApiKeyUsageAlert
}
import otoroshi.events.{Alerts, ApiKeyQuotasAlmostExceededAlert, ApiKeyQuotasAlmostExceededReason, ApiKeyQuotasExceededAlert, ApiKeyQuotasExceededReason, ApiKeySecretHasRotated, ApiKeySecretWillRotate, RevokedApiKeyUsageAlert}
import otoroshi.gateway.Errors
import org.joda.time.DateTime
import otoroshi.next.plugins.api.NgAccess
Expand All @@ -31,16 +22,11 @@ import otoroshi.security.{IdGenerator, OtoroshiClaim}
import otoroshi.storage.BasicStore
import otoroshi.utils.TypedMap
import otoroshi.ssl.DynamicSSLEngineProvider
import otoroshi.utils.syntax.implicits.{
BetterDecodedJWT,
BetterJsLookupResult,
BetterJsReadable,
BetterJsValue,
BetterSyntax
}
import otoroshi.utils.syntax.implicits.{BetterDecodedJWT, BetterJsLookupResult, BetterJsReadable, BetterJsValue, BetterSyntax}

import java.security.Signature
import scala.concurrent.{ExecutionContext, Future}
import scala.jdk.CollectionConverters.asScalaBufferConverter
import scala.util.{Failure, Success, Try}

case class RemainingQuotas(
Expand Down Expand Up @@ -1835,6 +1821,8 @@ object ApiKeyHelper {
case ApikeyTuple(_, None, _, _, Some(otoBearer)) if !apikey.checkBearer(otoBearer) => apikey.some.left
case ApikeyTuple(_, None, Some(jwt), _, _) => {
val possibleKeyPairId = apikey.metadata.get("jwt-sign-keypair")
val aud = jwt.getAudience.asScala.headOption.filter(v => v.startsWith("http://") || v.startsWith("https://"))
println(s"audience is: ${aud}")
val kid = Option(jwt.getKeyId)
.orElse(possibleKeyPairId)
.filter(_ => constraints.jwtAuth.keyPairSigned)
Expand Down Expand Up @@ -1904,6 +1892,15 @@ object ApiKeyHelper {
.acceptLeeway(10)
.build
Try(verifier.verify(jwt))
.filter { token =>
if (aud.isDefined) {
val currentUrl = req.theUrl
val audience = aud.get
currentUrl.startsWith(audience)
} else {
true
}
}
.filter { token =>
val xsrfToken = token.getClaim("xsrfToken")
val xsrfTokenHeader = req.headers.get("X-XSRF-TOKEN")
Expand Down
20 changes: 12 additions & 8 deletions otoroshi/app/next/plugins/clientcredentials.scala
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import org.biscuitsec.biscuit.datalog.SymbolTable
import org.biscuitsec.biscuit.token.builder.parser.Parser
import org.joda.time.DateTime
import otoroshi.env.Env
import otoroshi.models.ApiKeyHelper
import otoroshi.next.plugins.api._
import otoroshi.next.proxy.NgProxyEngineError
import otoroshi.plugins.apikeys.ClientCredentialFlowBody
Expand Down Expand Up @@ -476,7 +477,8 @@ case class NgClientCredentialTokenEndpointBody(
clientId: String,
clientSecret: String,
scope: Option[String],
bearerKind: String
bearerKind: String,
aud: Option[String],
)
case class NgClientCredentialTokenEndpointConfig(expiration: FiniteDuration, defaultKeyPair: String)
extends NgPluginConfig {
Expand Down Expand Up @@ -508,7 +510,7 @@ class NgClientCredentialTokenEndpoint extends NgBackendCall {
override def name: String = "Client credential token endpoint"

override def description: Option[String] =
"This plugin provide the endpoint for the client_credential flow token endpoint".some
"This plugin provide the endpoint for the client_credential flow".some

override def useDelegates: Boolean = false

Expand Down Expand Up @@ -579,7 +581,7 @@ class NgClientCredentialTokenEndpoint extends NgBackendCall {
ctx: NgbBackendCallContext
)(implicit env: Env, ec: ExecutionContext): Future[Result] =
ccfb match {
case NgClientCredentialTokenEndpointBody("client_credentials", clientId, clientSecret, scope, bearerKind) => {
case NgClientCredentialTokenEndpointBody("client_credentials", clientId, clientSecret, scope, bearerKind, aud) => {
val possibleApiKey = env.datastores.apiKeyDataStore.findById(clientId)
possibleApiKey.flatMap {
case Some(apiKey) if apiKey.isValid(clientSecret) && apiKey.isActive() => {
Expand All @@ -604,7 +606,7 @@ class NgClientCredentialTokenEndpoint extends NgBackendCall {
.withClaim("cid", apiKey.clientId)
.withIssuer(ctx.rawRequest.theProtocol + "://" + ctx.rawRequest.host)
.withSubject(apiKey.clientId)
.withAudience("otoroshi")
.withAudience(aud.getOrElse("otoroshi"))
.withKeyId(keyPairId)
.sign(algo)
// no refresh token possible because of https://tools.ietf.org/html/rfc6749#section-4.4.3
Expand Down Expand Up @@ -675,11 +677,12 @@ class NgClientCredentialTokenEndpoint extends NgBackendCall {
body.get("client_id"),
body.get("client_secret"),
body.get("scope"),
body.get("bearer_kind")
body.get("bearer_kind"),
body.get("aud"),
) match {
case (Some(gtype), Some(clientId), Some(clientSecret), scope, kind) =>
case (Some(gtype), Some(clientId), Some(clientSecret), scope, kind, aud) =>
handleTokenRequest(
NgClientCredentialTokenEndpointBody(gtype, clientId, clientSecret, scope, kind.getOrElse("jwt")),
NgClientCredentialTokenEndpointBody(gtype, clientId, clientSecret, scope, kind.getOrElse("jwt"), aud),
config,
ctx
)
Expand All @@ -700,7 +703,8 @@ class NgClientCredentialTokenEndpoint extends NgBackendCall {
clientId,
clientSecret,
None,
body.getOrElse("bearer_kind", "jwt")
body.getOrElse("bearer_kind", "jwt"),
body.get("aud")
),
config,
ctx
Expand Down

0 comments on commit c02de9e

Please sign in to comment.