Skip to content

Commit b40a43d

Browse files
authored
Proxy aware urls in wellknown document (#22)
* feat: support running behind a proxy by using host header and x-forwarded-for when creating well known doc Co-authored-by: @jksolbakken
1 parent 5485b2a commit b40a43d

File tree

4 files changed

+86
-12
lines changed

4 files changed

+86
-12
lines changed

src/main/kotlin/no/nav/security/mock/oauth2/debugger/DebuggerRequestHandler.kt

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,10 @@ import com.nimbusds.jose.Payload
99
import com.nimbusds.jose.crypto.DirectDecrypter
1010
import com.nimbusds.jose.crypto.DirectEncrypter
1111
import com.nimbusds.oauth2.sdk.OAuth2Error
12+
import java.net.URLEncoder
13+
import java.nio.charset.StandardCharsets
14+
import javax.crypto.KeyGenerator
15+
import javax.crypto.SecretKey
1216
import mu.KotlinLogging
1317
import no.nav.security.mock.oauth2.OAuth2Exception
1418
import no.nav.security.mock.oauth2.extensions.removeAllEncodedQueryParams
@@ -30,10 +34,6 @@ import okhttp3.OkHttpClient
3034
import okhttp3.Request
3135
import okhttp3.RequestBody.Companion.toRequestBody
3236
import okhttp3.internal.toHostHeader
33-
import java.net.URLEncoder
34-
import java.nio.charset.StandardCharsets
35-
import javax.crypto.KeyGenerator
36-
import javax.crypto.SecretKey
3737

3838
private val log = KotlinLogging.logger { }
3939

@@ -166,7 +166,7 @@ private fun debuggerAuthorizationRequest(
166166
OAuth2HttpRequest(
167167
headers = Headers.headersOf(),
168168
method = "GET",
169-
url = it
169+
originalUrl = it
170170
)
171171
}
172172

src/main/kotlin/no/nav/security/mock/oauth2/http/OAuth2HttpRequest.kt

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -36,9 +36,10 @@ import okhttp3.HttpUrl
3636
data class OAuth2HttpRequest(
3737
val headers: Headers,
3838
val method: String,
39-
val url: HttpUrl,
39+
val originalUrl: HttpUrl,
4040
val body: String? = null
4141
) {
42+
val url: HttpUrl get() = proxyAwareUrl()
4243
val formParameters: Parameters = Parameters(body)
4344
val cookies: Map<String, String> = headers["Cookie"]?.keyValuesToMap(";") ?: emptyMap()
4445

@@ -93,13 +94,31 @@ data class OAuth2HttpRequest(
9394

9495
fun toWellKnown() =
9596
WellKnown(
96-
issuer = this.url.toIssuerUrl().toString(),
97-
authorizationEndpoint = this.url.toAuthorizationEndpointUrl().toString(),
98-
tokenEndpoint = this.url.toTokenEndpointUrl().toString(),
99-
endSessionEndpoint = this.url.toEndSessionEndpointUrl().toString(),
100-
jwksUri = this.url.toJwksUrl().toString()
97+
issuer = this.proxyAwareUrl().toIssuerUrl().toString(),
98+
authorizationEndpoint = this.proxyAwareUrl().toAuthorizationEndpointUrl().toString(),
99+
tokenEndpoint = this.proxyAwareUrl().toTokenEndpointUrl().toString(),
100+
endSessionEndpoint = this.proxyAwareUrl().toEndSessionEndpointUrl().toString(),
101+
jwksUri = this.proxyAwareUrl().toJwksUrl().toString()
101102
)
102103

104+
internal fun proxyAwareUrl(): HttpUrl {
105+
val hostheader = this.headers["host"]
106+
val proto = this.headers["x-forwarded-proto"]
107+
val port = this.headers["x-forwarded-port"]
108+
return if (hostheader != null && proto != null) {
109+
HttpUrl.Builder()
110+
.scheme(proto)
111+
.host(hostheader)
112+
.apply {
113+
port?.toInt()?.let { port(it) }
114+
}
115+
.encodedPath(originalUrl.encodedPath)
116+
.query(originalUrl.query).build()
117+
} else {
118+
originalUrl
119+
}
120+
}
121+
103122
data class Parameters(val parameterString: String?) {
104123
val map: Map<String, String> = parameterString?.keyValuesToMap("&") ?: emptyMap()
105124
fun get(name: String): String? = map[name]

src/test/kotlin/no/nav/security/mock/oauth2/grant/AuthorizationCodeHandlerTest.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ internal class AuthorizationCodeHandlerTest {
104104
return OAuth2HttpRequest(
105105
headers = Headers.headersOf("Content-Type", "application/x-www-form-urlencoded"),
106106
method = "POST",
107-
url = "http://localhost/token".toHttpUrl(),
107+
originalUrl = "http://localhost/token".toHttpUrl(),
108108
body = "grant_type=authorization_code&" +
109109
"client_id=client1&" +
110110
"client_secret=secret&" +
Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
package no.nav.security.mock.oauth2.http
2+
3+
import io.kotest.matchers.shouldBe
4+
import okhttp3.Headers
5+
import okhttp3.HttpUrl.Companion.toHttpUrl
6+
import org.junit.jupiter.api.Test
7+
8+
internal class OAuth2HttpRequestTest {
9+
10+
@Test
11+
fun `proxyAwareUrl should use host header and x-forwarded-for-* `() {
12+
val req1 = OAuth2HttpRequest(
13+
headers = Headers.headersOf(),
14+
method = "GET",
15+
originalUrl = "http://localhost:8080/mypath?query=1".toHttpUrl()
16+
)
17+
req1.proxyAwareUrl().toString() shouldBe "http://localhost:8080/mypath?query=1"
18+
val req2 = OAuth2HttpRequest(
19+
headers = Headers.headersOf(
20+
"host",
21+
"fakedings.nais.io",
22+
"x-forwarded-proto",
23+
"https",
24+
"x-forwarded-port",
25+
"444"
26+
),
27+
method = "GET",
28+
originalUrl = "http://localhost:8080/mypath?query=1".toHttpUrl()
29+
)
30+
req2.proxyAwareUrl().toString() shouldBe "https://fakedings.nais.io:444/mypath?query=1"
31+
}
32+
33+
@Test
34+
fun `wellKnown should use proxyAwareUrl when headers are set`() {
35+
val req1 = OAuth2HttpRequest(
36+
headers = Headers.headersOf(),
37+
method = "GET",
38+
originalUrl = "http://localhost:8080/mypath?query=1".toHttpUrl()
39+
)
40+
req1.toWellKnown().issuer shouldBe "http://localhost:8080/mypath"
41+
val req2 = OAuth2HttpRequest(
42+
headers = Headers.headersOf(
43+
"host",
44+
"fakedings.nais.io",
45+
"x-forwarded-proto",
46+
"https",
47+
"x-forwarded-port",
48+
"444"
49+
),
50+
method = "GET",
51+
originalUrl = "http://localhost:8080/mypath?query=1".toHttpUrl()
52+
)
53+
req2.toWellKnown().issuer shouldBe "https://fakedings.nais.io:444/mypath"
54+
}
55+
}

0 commit comments

Comments
 (0)