Skip to content

Commit

Permalink
translated JwtTokenValidationHandler
Browse files Browse the repository at this point in the history
  • Loading branch information
jan-olaveide committed Dec 6, 2023
1 parent 6728507 commit cdead5f
Show file tree
Hide file tree
Showing 13 changed files with 82 additions and 98 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -107,5 +107,5 @@ data class DemoTokenResponse(
}

internal fun TokenValidationContextPrincipal?.asTokenString(): String =
this?.context?.firstValidToken?.map { it.getTokenAsString() }?.orElse(null)
this?.context?.firstValidToken?.map { it.encodedToken }?.orElse(null)
?: throw RuntimeException("no token found in call context")
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class OAuth2ClientConfiguration : ImportAware {
@ConditionalOnClass(TokenValidationContextHolder::class)
fun jwtBearerTokenResolver(h: TokenValidationContextHolder) =
JwtBearerTokenResolver {
h.getTokenValidationContext().firstValidToken.map { it.getTokenAsString() } ?: Optional.empty()
h.getTokenValidationContext().firstValidToken.map { it.encodedToken } ?: Optional.empty()
}

@Bean
Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER
import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER

@Retention(RUNTIME)
@MustBeDocumented
@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, CLASS)
annotation class Protected
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import no.nav.security.token.support.core.utils.Cluster
@Retention(RUNTIME)
@Target(CLASS, FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER)
@Protected
@MustBeDocumented
annotation class ProtectedWithClaims(val issuer : String,
/**
* Required claims in token in key=value format.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,5 @@ package no.nav.security.token.support.core.api
import kotlin.annotation.AnnotationRetention.RUNTIME

@Retention(RUNTIME)
@MustBeDocumented
annotation class RequiredIssuers(vararg val value : ProtectedWithClaims)
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,6 @@ import kotlin.annotation.AnnotationTarget.PROPERTY_GETTER
import kotlin.annotation.AnnotationTarget.PROPERTY_SETTER

@Retention(RUNTIME)
@MustBeDocumented
@Target(FUNCTION, PROPERTY_GETTER, PROPERTY_SETTER, CLASS)
annotation class Unprotected
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,19 @@ package no.nav.security.token.support.core.jwt

import com.nimbusds.jwt.JWT
import com.nimbusds.jwt.JWTParser
import kotlin.DeprecationLevel.*

open class JwtToken(private val encodedToken : String, protected val jwt : JWT, val jwtTokenClaims : JwtTokenClaims) {
open class JwtToken(val encodedToken : String, protected val jwt : JWT, val jwtTokenClaims : JwtTokenClaims) {
constructor(encodedToken : String) : this(encodedToken, JWTParser.parse(encodedToken), JwtTokenClaims(JWTParser.parse(encodedToken).jwtClaimsSet))

fun getJwtClaimsSet() = jwt.jwtClaimsSet
val jwtClaimsSet = jwt.jwtClaimsSet

fun getSubject() = jwtTokenClaims.subject
val subject = jwtTokenClaims.subject

fun getIssuer() = jwtTokenClaims.issuer
val issuer = jwtTokenClaims.issuer

fun getTokenAsString() = encodedToken
@Deprecated("Use getEncodedToken instead", ReplaceWith("getEncodedToken()"), WARNING)
val tokenAsString = encodedToken

fun containsClaim(name : String, value : String) = jwtTokenClaims.containsClaim(name, value)

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
package no.nav.security.token.support.core.validation

import java.util.AbstractMap.SimpleImmutableEntry
import java.util.concurrent.ConcurrentHashMap
import kotlin.collections.Map.Entry
import org.slf4j.Logger
import org.slf4j.LoggerFactory
import no.nav.security.token.support.core.configuration.MultiIssuerConfiguration
import no.nav.security.token.support.core.context.TokenValidationContext
import no.nav.security.token.support.core.exceptions.IssuerConfigurationException
import no.nav.security.token.support.core.exceptions.JwtTokenValidatorException
import no.nav.security.token.support.core.http.HttpRequest
import no.nav.security.token.support.core.jwt.JwtToken
import no.nav.security.token.support.core.validation.JwtTokenRetriever.retrieveUnvalidatedTokens
import no.nav.security.token.support.core.validation.JwtTokenValidatorFactory.tokenValidator

class JwtTokenValidationHandler(private val config : MultiIssuerConfiguration) {

fun getValidatedTokens(request : HttpRequest) =
retrieveUnvalidatedTokens(config, request).run {
with(mapNotNull(::validate)
.associateByTo(ConcurrentHashMap(), { it.key }, { it.value })) {
LOG.debug("Found {} tokens on request, number of validated tokens is {}", size, this@with.size)
if (this@with.isEmpty() && isNotEmpty()) {
LOG.debug("Found {} unvalidated token(s) with issuer(s) {} on request, is this a configuration error?", size, map(JwtToken::issuer))
}
TokenValidationContext(this)
}
}

private fun validate(jwtToken : JwtToken) : Entry<String, JwtToken>? {
with(jwtToken) {
try {
LOG.debug("Check if token with issuer={} is present in config", issuer)
if (config.getIssuer(issuer).isPresent) {
val issuerShortName = issuerConfiguration(issuer).name
LOG.debug("Found token from trusted issuer={} with shortName={} in request", issuer, issuerShortName)
tokenValidator(jwtToken).assertValidToken(encodedToken)
LOG.debug("Validated token from issuer[{}]", issuer)
return SimpleImmutableEntry(issuerShortName, this)
}
return null.also {
LOG.info("Found token from unknown issuer[{}], skipping validation.", issuer)
}
}
catch (e : JwtTokenValidatorException) {
return null.also {
LOG.info("Found invalid token for issuer [{}, expires at {}], message:{} ",issuer, e.expiryDate, e.message)
}
}
}


}

private fun tokenValidator(jwtToken : JwtToken) = issuerConfiguration(jwtToken.issuer).tokenValidator

private fun issuerConfiguration(issuer : String) = config.getIssuer(issuer)
.orElseThrow { IssuerConfigurationException("Could not find IssuerConfiguration for issuer $issuer") }

companion object {
private val LOG : Logger = LoggerFactory.getLogger(JwtTokenValidationHandler::class.java)
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public void filter(ClientRequestContext requestContext) {
StringBuilder headerValue = new StringBuilder();
context.getIssuers().forEach(issuer -> {
LOG.debug("adding token for issuer {}", issuer);
headerValue.append("Bearer ").append(context.getJwtToken(issuer).getTokenAsString());
headerValue.append("Bearer ").append(context.getJwtToken(issuer).getEncodedToken());
});
requestContext.getHeaders().put(JwtTokenConstants.AUTHORIZATION_HEADER, singletonList(headerValue.toString()));
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ public class TokenResource {
@Path("token")
public Response getToken() {
return Response.ok()
.entity(JaxrsTokenValidationContextHolder.getHolder().getTokenValidationContext().getJwtToken("protected").getTokenAsString())
.entity(JaxrsTokenValidationContextHolder.getHolder().getTokenValidationContext().getJwtToken("protected").getEncodedToken())
.build();
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ class EnableJwtTokenValidationConfiguration (private val env: Environment) : Web
fun requestContextListener() = RequestContextListener()

@Bean
fun tokenValidationFilter(config: MultiIssuerConfiguration?, h: TokenValidationContextHolder) = JwtTokenValidationFilter(JwtTokenValidationHandler(config), h)
fun tokenValidationFilter(config: MultiIssuerConfiguration, h: TokenValidationContextHolder) = JwtTokenValidationFilter(JwtTokenValidationHandler(config), h)

@Bean
@ConditionalOnProperty(EXPIRY_THRESHOLD_ENV_PROPERTY)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ class BearerTokenClientHttpRequestInterceptor(private val holder: TokenValidatio
log.debug("Adding tokens to Authorization header")
req.headers.add(
AUTHORIZATION_HEADER,
issuers.joinToString { "Bearer " + getJwtToken(it)?.getTokenAsString() })
issuers.joinToString { "Bearer " + getJwtToken(it)?.encodedToken })
}
} ?: log.debug("no tokens found, nothing added to request")
return execution.execute(req, body)
Expand Down

0 comments on commit cdead5f

Please sign in to comment.