-
Notifications
You must be signed in to change notification settings - Fork 62
/
Copy pathOAuth2HttpRequest.kt
132 lines (119 loc) · 5.34 KB
/
OAuth2HttpRequest.kt
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
package no.nav.security.mock.oauth2.http
import com.nimbusds.oauth2.sdk.GrantType
import com.nimbusds.oauth2.sdk.TokenRequest
import com.nimbusds.oauth2.sdk.http.HTTPRequest
import com.nimbusds.openid.connect.sdk.AuthenticationRequest
import no.nav.security.mock.oauth2.extensions.clientAuthentication
import no.nav.security.mock.oauth2.extensions.isAuthorizationEndpointUrl
import no.nav.security.mock.oauth2.extensions.isDebuggerCallbackUrl
import no.nav.security.mock.oauth2.extensions.isDebuggerUrl
import no.nav.security.mock.oauth2.extensions.isEndSessionEndpointUrl
import no.nav.security.mock.oauth2.extensions.isJwksUrl
import no.nav.security.mock.oauth2.extensions.isTokenEndpointUrl
import no.nav.security.mock.oauth2.extensions.isWellKnownUrl
import no.nav.security.mock.oauth2.extensions.keyValuesToMap
import no.nav.security.mock.oauth2.extensions.requirePrivateKeyJwt
import no.nav.security.mock.oauth2.extensions.toAuthorizationEndpointUrl
import no.nav.security.mock.oauth2.extensions.toEndSessionEndpointUrl
import no.nav.security.mock.oauth2.extensions.toIssuerUrl
import no.nav.security.mock.oauth2.extensions.toJwksUrl
import no.nav.security.mock.oauth2.extensions.toTokenEndpointUrl
import no.nav.security.mock.oauth2.grant.TokenExchangeGrant
import no.nav.security.mock.oauth2.http.RequestType.AUTHORIZATION
import no.nav.security.mock.oauth2.http.RequestType.DEBUGGER
import no.nav.security.mock.oauth2.http.RequestType.DEBUGGER_CALLBACK
import no.nav.security.mock.oauth2.http.RequestType.END_SESSION
import no.nav.security.mock.oauth2.http.RequestType.FAVICON
import no.nav.security.mock.oauth2.http.RequestType.JWKS
import no.nav.security.mock.oauth2.http.RequestType.TOKEN
import no.nav.security.mock.oauth2.http.RequestType.UNKNOWN
import no.nav.security.mock.oauth2.http.RequestType.WELL_KNOWN
import no.nav.security.mock.oauth2.missingParameter
import okhttp3.Headers
import okhttp3.HttpUrl
data class OAuth2HttpRequest(
val headers: Headers,
val method: String,
val originalUrl: HttpUrl,
val body: String? = null
) {
val url: HttpUrl get() = proxyAwareUrl()
val formParameters: Parameters = Parameters(body)
val cookies: Map<String, String> = headers["Cookie"]?.keyValuesToMap(";") ?: emptyMap()
fun asTokenExchangeRequest(): TokenRequest {
val httpRequest: HTTPRequest = this.asNimbusHTTPRequest()
val clientAuthentication = httpRequest.clientAuthentication().requirePrivateKeyJwt(this.url.toString(), 120)
val tokenExchangeGrant = TokenExchangeGrant.parse(formParameters.map)
// TODO: add scope if present in request
return TokenRequest(
this.url.toUri(),
clientAuthentication,
tokenExchangeGrant,
null,
emptyList(),
formParameters.map.mapValues { mutableListOf(it.value) }
)
}
@Suppress("MemberVisibilityCanBePrivate")
fun asNimbusHTTPRequest(): HTTPRequest {
return HTTPRequest(HTTPRequest.Method.valueOf(method), url.toUrl())
.apply {
headers.forEach { header -> this.setHeader(header.first, header.second) }
query = body
}
}
fun asNimbusTokenRequest(): TokenRequest =
TokenRequest.parse(
this.asNimbusHTTPRequest()
)
fun asAuthenticationRequest(): AuthenticationRequest = AuthenticationRequest.parse(this.url.toUri())
fun type() = when {
url.isWellKnownUrl() -> WELL_KNOWN
url.isAuthorizationEndpointUrl() -> AUTHORIZATION
url.isTokenEndpointUrl() -> TOKEN
url.isEndSessionEndpointUrl() -> END_SESSION
url.isJwksUrl() -> JWKS
url.isDebuggerUrl() -> DEBUGGER
url.isDebuggerCallbackUrl() -> DEBUGGER_CALLBACK
url.encodedPath == "/favicon.ico" -> FAVICON
else -> UNKNOWN
}
fun grantType(): GrantType =
this.formParameters.map["grant_type"]
?.ifBlank { null }
?.let { GrantType(it) }
?: missingParameter("grant_type")
fun toWellKnown() =
WellKnown(
issuer = this.proxyAwareUrl().toIssuerUrl().toString(),
authorizationEndpoint = this.proxyAwareUrl().toAuthorizationEndpointUrl().toString(),
tokenEndpoint = this.proxyAwareUrl().toTokenEndpointUrl().toString(),
endSessionEndpoint = this.proxyAwareUrl().toEndSessionEndpointUrl().toString(),
jwksUri = this.proxyAwareUrl().toJwksUrl().toString()
)
internal fun proxyAwareUrl(): HttpUrl {
val hostheader = this.headers["host"]
val proto = this.headers["x-forwarded-proto"]
val port = this.headers["x-forwarded-port"]
return if (hostheader != null && proto != null) {
HttpUrl.Builder()
.scheme(proto)
.host(hostheader)
.apply {
port?.toInt()?.let { port(it) }
}
.encodedPath(originalUrl.encodedPath)
.query(originalUrl.query).build()
} else {
originalUrl
}
}
data class Parameters(val parameterString: String?) {
val map: Map<String, String> = parameterString?.keyValuesToMap("&") ?: emptyMap()
fun get(name: String): String? = map[name]
}
}
enum class RequestType {
WELL_KNOWN, AUTHORIZATION, TOKEN, END_SESSION, JWKS,
DEBUGGER, DEBUGGER_CALLBACK, FAVICON, UNKNOWN
}