Skip to content

Commit

Permalink
Merge pull request #58 from cjww-development/bug/well-known-config
Browse files Browse the repository at this point in the history
#57 Fixed issue with well known config not using the correct domain in the config response
  • Loading branch information
chrisjwwalker authored Jul 15, 2021
2 parents 7d27979 + 8d41322 commit ff0af7a
Show file tree
Hide file tree
Showing 8 changed files with 213 additions and 121 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,7 @@ The following table describes what each of the gatekeeper envs means in the dock
| MONGO_URI | mongodb://mongo.local | Where MongoDB lives. The database that backs Gatekeeper |
| APP_SECRET | 23817cc7d0e6460e9c1515aa4047b29b | The app secret scala play uses to sign session cookies and CSRF tokens. Should be changed to run in prod |
| ENC_KEY | 23817cc7d0e6460e9c1515aa4047b29b | The key used to secure data. Should be changed to run in prod |
| WKC_ISSUER | http://localhost:5678 | Used in the tokens issued by Gatekeeper. Used in the token `iss` claim. Should be configured as the domain you connect to Gatekeeper with |
| MFA_ISSUER | Gatekeeper (docker) | The string used to describe the TOTP Code in apps like Google Authenticator |
| SMS_SENDER_ID | SmsVerify | The string used to say where SMS messages have come from |
| EMAIL_PROVIDER | n/a | Used to determine what email provider to use. Valid options are ses or mail-gun |
Expand Down
35 changes: 18 additions & 17 deletions app/orchestrators/WellKnownConfigOrchestrator.scala
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ import play.api.mvc.RequestHeader
import javax.inject.Inject

class DefaultWellKnownConfigOrchestrator @Inject()(val config: Configuration) extends WellKnownConfigOrchestrator {
override val authEndpoint: RequestHeader => String = rh => controllers.ui.routes.OAuthController.authoriseGet("", "", "").absoluteURL()(rh).split("\\?").head
override val tokenEndpoint: RequestHeader => String = rh => controllers.ui.routes.OAuthController.getToken().absoluteURL()(rh).split("\\?").head
override val revokeEndpoint: RequestHeader => String = rh => controllers.api.routes.RevokationController.revokeToken().absoluteURL()(rh).split("\\?").head
override val userDetailsEndpoint: RequestHeader => String = rh => controllers.api.routes.AccountController.getUserDetails.absoluteURL()(rh).split("\\?").head
override val jwksEndpoint: RequestHeader => String = rh => controllers.api.routes.JwksController.getCurrentJwks().absoluteURL()(rh).split("\\?").head
override val authEndpoint: String = controllers.ui.routes.OAuthController.authoriseGet("", "", "").url.split("\\?").head
override val tokenEndpoint: String = controllers.ui.routes.OAuthController.getToken().url.split("\\?").head
override val revokeEndpoint: String = controllers.api.routes.RevokationController.revokeToken().url.split("\\?").head
override val userDetailsEndpoint: String = controllers.api.routes.AccountController.getUserDetails.url.split("\\?").head
override val jwksEndpoint: String = controllers.api.routes.JwksController.getCurrentJwks().url.split("\\?").head

override val grantTypes: Seq[String] = config.get[Seq[String]]("well-known-config.grant-types")
override val supportedScopes: Seq[String] = config.get[Seq[String]]("well-known-config.scopes")
Expand All @@ -38,11 +38,11 @@ class DefaultWellKnownConfigOrchestrator @Inject()(val config: Configuration) ex
}

trait WellKnownConfigOrchestrator {
val authEndpoint: RequestHeader => String
val tokenEndpoint: RequestHeader => String
val revokeEndpoint: RequestHeader => String
val userDetailsEndpoint: RequestHeader => String
val jwksEndpoint: RequestHeader => String
val authEndpoint: String
val tokenEndpoint: String
val revokeEndpoint: String
val userDetailsEndpoint: String
val jwksEndpoint: String

val grantTypes: Seq[String]
val supportedScopes: Seq[String]
Expand All @@ -52,19 +52,20 @@ trait WellKnownConfigOrchestrator {
val idTokenAlgs: Seq[String]

def getConfig(implicit rh: RequestHeader): WellKnownConfig = {
val protocol = if(rh.secure) "https://" else "http://"
val protocol = rh.headers.get("X-Forwarded-Proto").map(proto => s"$proto://").getOrElse("http://")
val issuer = s"$protocol${rh.host}"
WellKnownConfig(
s"$protocol${rh.host}",
authorizationEndpoint = authEndpoint(rh),
tokenEndpoint = tokenEndpoint(rh),
userInfoEndpoint = userDetailsEndpoint(rh),
jwksUri = jwksEndpoint(rh),
issuer,
authorizationEndpoint = issuer + authEndpoint,
tokenEndpoint = issuer + tokenEndpoint,
userInfoEndpoint = issuer + userDetailsEndpoint,
jwksUri = issuer + jwksEndpoint,
registrationEndpoint = "",
scopesSupported = supportedScopes,
responseTypesSupported = responseTypes,
grantTypesSupported = grantTypes,
tokenEndpointAuth = tokenEndpointAuth,
revokeEndpoint = revokeEndpoint(rh),
revokeEndpoint = issuer + revokeEndpoint,
idTokenSigningAlgs = idTokenAlgs
)
}
Expand Down
9 changes: 5 additions & 4 deletions build.sbt
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,10 @@ lazy val microservice = Project(appName, file("."))
"dev.cjww.libs" % "inbound-outbound_2.13" % "1.0.0",
"io.github.nremond" % "pbkdf2-scala_2.13" % "0.6.5",
"com.pauldijou" % "jwt-core_2.13" % "5.0.0",
"com.nimbusds" % "nimbus-jose-jwt" % "9.11",
"com.nimbusds" % "nimbus-jose-jwt" % "9.11.1",
"dev.samstevens.totp" % "totp" % "1.7.1",
"com.amazonaws" % "aws-java-sdk-ses" % "1.12.22",
"com.amazonaws" % "aws-java-sdk-sns" % "1.12.22",
"com.amazonaws" % "aws-java-sdk-ses" % "1.12.24",
"com.amazonaws" % "aws-java-sdk-sns" % "1.12.24",
"com.fasterxml.jackson.module" %% "jackson-module-scala" % "2.12.4",
"org.mockito" % "mockito-core" % "3.11.2" % Test,
"org.scalatestplus" % "scalatestplus-mockito_2.13" % "1.0.0-M2" % Test,
Expand All @@ -63,9 +63,10 @@ lazy val microservice = Project(appName, file("."))
IntegrationTest / fork := false,
IntegrationTest / unmanagedSourceDirectories := (IntegrationTest / baseDirectory)(base => Seq(base / "it")).value,
IntegrationTest / parallelExecution := false,
IntegrationTest / logBuffered := true,
Test / fork := true,
Test / testForkedParallel := true,
Test / parallelExecution := true,
Test / logBuffered := false
Test / logBuffered := true
)

2 changes: 1 addition & 1 deletion conf/logback-test.xml
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,6 @@
<logger name="com.gargoylesoftware.htmlunit.javascript" level="OFF" />

<root level="OFF">
<appender-ref ref="ASYNCSTDOUT" />
<appender-ref ref="STDOUT" />
</root>
</configuration>
1 change: 1 addition & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ services:
ENC_KEY: "23817cc7d0e6460e9c1515aa4047b29b"
MFA_ISSUER: "Gatekeeper (docker)"
SMS_SENDER_ID: "SmsVerify"
WKC_ISSUER: "http://localhost:5678"

#Email settings
EMAIL_PROVIDER: "ses" #Can be "ses" or "mail-gun"
Expand Down
98 changes: 97 additions & 1 deletion it/api/ConfigGetApisISpec.scala
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class ConfigGetApisISpec

"GET /api/.well-known/openid-configuration" should {
"return an Ok" when {
"the well known config endpoint returns the relevant config" in {
"the well known config endpoint returns the relevant config (locally)" in {
val result = ws
.url(s"$testAppUrl/api/.well-known/openid-configuration")
.withFollowRedirects(follow = false)
Expand Down Expand Up @@ -98,6 +98,102 @@ class ConfigGetApisISpec
)
}
}

"the well known config endpoint returns the relevant config (insecure with host)" in {
val result = ws
.url(s"$testAppUrl/api/.well-known/openid-configuration")
.withHttpHeaders("Host" -> "test.example.com")
.withFollowRedirects(follow = false)
.get()

awaitAndAssert(result) { resp =>
resp.status mustBe OK
resp.json mustBe Json.parse(
s"""
|{
| "issuer":"http://test.example.com",
| "authorization_endpoint":"http://test.example.com/gatekeeper/oauth2/authorize",
| "token_endpoint":"http://test.example.com/gatekeeper/oauth2/token",
| "userinfo_endpoint":"http://test.example.com/gatekeeper/api/oauth2/userinfo",
| "jwks_uri":"http://test.example.com/gatekeeper/api/oauth2/jwks",
| "scopes_supported":[
| "openid",
| "profile",
| "email",
| "address",
| "phone"
| ],
| "response_types_supported":[
| "code"
| ],
| "grant_types_supported":[
| "authorization_code",
| "client_credentials",
| "refresh_token"
| ],
| "id_token_signing_alg_values_supported":["RS256"],
| "token_endpoint_auth_methods_supported":[
| "client_secret_basic",
| "client_secret_post"
| ],
| "revocation_endpoint":"http://test.example.com/gatekeeper/api/oauth2/revoke",
| "revocation_endpoint_auth_methods_supported":[
| "client_secret_basic",
| "client_secret_post"
| ]
|}
""".stripMargin
)
}
}

"the well known config endpoint returns the relevant config (secure with host)" in {
val result = ws
.url(s"$testAppUrl/api/.well-known/openid-configuration")
.withHttpHeaders("Host" -> "test.example.com", "X-Forwarded-Proto" -> "https")
.withFollowRedirects(follow = false)
.get()

awaitAndAssert(result) { resp =>
resp.status mustBe OK
resp.json mustBe Json.parse(
s"""
|{
| "issuer":"https://test.example.com",
| "authorization_endpoint":"https://test.example.com/gatekeeper/oauth2/authorize",
| "token_endpoint":"https://test.example.com/gatekeeper/oauth2/token",
| "userinfo_endpoint":"https://test.example.com/gatekeeper/api/oauth2/userinfo",
| "jwks_uri":"https://test.example.com/gatekeeper/api/oauth2/jwks",
| "scopes_supported":[
| "openid",
| "profile",
| "email",
| "address",
| "phone"
| ],
| "response_types_supported":[
| "code"
| ],
| "grant_types_supported":[
| "authorization_code",
| "client_credentials",
| "refresh_token"
| ],
| "id_token_signing_alg_values_supported":["RS256"],
| "token_endpoint_auth_methods_supported":[
| "client_secret_basic",
| "client_secret_post"
| ],
| "revocation_endpoint":"https://test.example.com/gatekeeper/api/oauth2/revoke",
| "revocation_endpoint_auth_methods_supported":[
| "client_secret_basic",
| "client_secret_post"
| ]
|}
""".stripMargin
)
}
}
}
}
}
2 changes: 1 addition & 1 deletion project/build.properties
Original file line number Diff line number Diff line change
Expand Up @@ -14,4 +14,4 @@
# limitations under the License.
#

sbt.version=1.5.3
sbt.version=1.5.4
Loading

0 comments on commit ff0af7a

Please sign in to comment.