Skip to content

Commit

Permalink
test(userinfo): add tests for non-default algorithm
Browse files Browse the repository at this point in the history
  • Loading branch information
antoineauger committed Oct 14, 2022
1 parent eebcd29 commit 7574e12
Show file tree
Hide file tree
Showing 3 changed files with 72 additions and 1 deletion.
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ open class MockOAuth2Server(
vararg additionalRoutes: Route
) {
constructor(vararg additionalRoutes: Route) : this(config = OAuth2Config(), additionalRoutes = additionalRoutes)
constructor(config: OAuth2Config) : this(config = config, additionalRoutes = emptyArray())

private val httpServer = config.httpServer
private val defaultRequestHandler: OAuth2HttpRequestHandler = OAuth2HttpRequestHandler(config)
Expand Down Expand Up @@ -304,9 +305,10 @@ internal fun Map<String, Any>.toJwtClaimsSet(): JWTClaimsSet =
}.build()

fun <R> withMockOAuth2Server(
config: OAuth2Config = OAuth2Config(),
test: MockOAuth2Server.() -> R
): R {
val server = MockOAuth2Server()
val server = MockOAuth2Server(config)
server.start()
try {
return server.test()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,28 @@
package no.nav.security.mock.oauth2.e2e

import com.nimbusds.jose.JWSAlgorithm
import com.nimbusds.jwt.SignedJWT
import io.kotest.assertions.asClue
import io.kotest.matchers.maps.shouldContainAll
import io.kotest.matchers.shouldBe
import no.nav.security.mock.oauth2.MockOAuth2Server
import no.nav.security.mock.oauth2.OAuth2Config
import no.nav.security.mock.oauth2.testutils.claims
import no.nav.security.mock.oauth2.testutils.client
import no.nav.security.mock.oauth2.testutils.get
import no.nav.security.mock.oauth2.testutils.parse
import no.nav.security.mock.oauth2.token.KeyProvider
import no.nav.security.mock.oauth2.token.OAuth2TokenProvider
import no.nav.security.mock.oauth2.withMockOAuth2Server
import okhttp3.Headers
import org.junit.jupiter.api.Test

class UserInfoIntegrationTest {

private val client = client()
private val rs384Config = OAuth2Config(
tokenProvider = OAuth2TokenProvider(keyProvider = KeyProvider(initialKeys = emptyList(), algorithm = JWSAlgorithm.RS384.name))
)

@Test
fun `userinfo should return claims from token when valid bearer token is present`() {
Expand All @@ -33,6 +42,41 @@ class UserInfoIntegrationTest {
}
}

@Test
fun `userinfo should return claims from token signed with non-default algorithm when valid bearer token is present`() {
withMockOAuth2Server(config = rs384Config) {
val issuerId = "default"
val token = this.issueToken(issuerId = issuerId, subject = "foo", claims = mapOf("extra" to "bar"))
token.header.algorithm.shouldBe(JWSAlgorithm.RS384)
client.get(
url = this.userInfoUrl(issuerId),
headers = token.asBearerTokenHeader()
).asClue {
it.parse<Map<String, Any>>() shouldContainAll mapOf(
"sub" to token.claims["sub"],
"iss" to token.claims["iss"],
"extra" to token.claims["extra"]
)
}
}
}

@Test
fun `userinfo should return error from token signed with non-default algorithm does not match server config`() {
val issuerId = "default"
val rs384Server = MockOAuth2Server(config = rs384Config)
val token = rs384Server.issueToken(issuerId = issuerId, subject = "foo", claims = mapOf("extra" to "bar"))
withMockOAuth2Server {
client.get(
url = this.userInfoUrl(issuerId),
headers = token.asBearerTokenHeader()
).asClue {
it.code shouldBe 401
it.message shouldBe "Client Error"
}
}
}

private fun SignedJWT.asBearerTokenHeader(): Headers = this.serialize().let {
Headers.headersOf("Authorization", "Bearer $it")
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ package no.nav.security.mock.oauth2.userinfo

import com.fasterxml.jackson.module.kotlin.jacksonObjectMapper
import com.fasterxml.jackson.module.kotlin.readValue
import com.nimbusds.jose.JWSAlgorithm
import io.kotest.assertions.asClue
import io.kotest.assertions.throwables.shouldThrow
import io.kotest.matchers.maps.shouldContainAll
Expand All @@ -11,6 +12,7 @@ import no.nav.security.mock.oauth2.extensions.OAuth2Endpoints.USER_INFO
import no.nav.security.mock.oauth2.http.OAuth2HttpRequest
import no.nav.security.mock.oauth2.http.OAuth2HttpResponse
import no.nav.security.mock.oauth2.http.routes
import no.nav.security.mock.oauth2.token.KeyProvider
import no.nav.security.mock.oauth2.token.OAuth2TokenProvider
import okhttp3.Headers
import okhttp3.HttpUrl.Companion.toHttpUrl
Expand All @@ -36,6 +38,29 @@ internal class UserInfoTest {
}
}

@Test
fun `userinfo should throw OAuth2Exception when algorithm does not match`() {
val issuerUrl = "http://localhost/default"
val tokenProvider = OAuth2TokenProvider(keyProvider = KeyProvider(algorithm = JWSAlgorithm.RS384.name))
val claims = mapOf(
"iss" to issuerUrl,
"sub" to "foo",
"extra" to "bar"
)
val bearerToken = tokenProvider.jwt(claims)
val request = request("$issuerUrl$USER_INFO", bearerToken.serialize())

shouldThrow<OAuth2Exception> {
routes {
userInfo(tokenProvider)
}.invoke(request)
}.asClue {
it.errorObject?.code shouldBe "invalid_token"
it.errorObject?.description shouldBe "Signed JWT rejected: Another algorithm expected, or no matching key(s) found"
it.errorObject?.httpStatusCode shouldBe 401
}
}

@Test
fun `userinfo should throw OAuth2Exception when bearer token is missing`() {
val url = "http://localhost/default$USER_INFO"
Expand Down

0 comments on commit 7574e12

Please sign in to comment.