diff --git a/knockoutwhistfrontend b/knockoutwhistfrontend index 6b8488e..240be41 160000 --- a/knockoutwhistfrontend +++ b/knockoutwhistfrontend @@ -1 +1 @@ -Subproject commit 6b8488e7a4b47c397e8e366412d32f40781e3b8b +Subproject commit 240be41dc7b21a5f44a37f59c202e3dd71759bb6 diff --git a/knockoutwhistweb/app/controllers/OpenIDController.scala b/knockoutwhistweb/app/controllers/OpenIDController.scala index 263a014..589d0d9 100644 --- a/knockoutwhistweb/app/controllers/OpenIDController.scala +++ b/knockoutwhistweb/app/controllers/OpenIDController.scala @@ -1,7 +1,5 @@ package controllers -import auth.AuthAction -import com.typesafe.config.Config import logic.user.{SessionManager, UserManager} import model.users.User import play.api.Configuration @@ -22,7 +20,7 @@ class OpenIDController @Inject()( val config: Configuration )(implicit ec: ExecutionContext) extends BaseController { - def loginWithProvider(provider: String) = Action.async { implicit request => + def loginWithProvider(provider: String): Action[AnyContent] = Action.async { implicit request => val state = openIDService.generateState() val nonce = openIDService.generateNonce() @@ -40,7 +38,7 @@ class OpenIDController @Inject()( } } - def callback(provider: String) = Action.async { implicit request => + def callback(provider: String): Action[AnyContent] = Action.async { implicit request => val sessionState = request.session.get("oauth_state") val sessionNonce = request.session.get("oauth_nonce") val sessionProvider = request.session.get("oauth_provider") @@ -63,7 +61,7 @@ class OpenIDController @Inject()( openIDService.getUserInfo(provider, tokenResponse.accessToken).map { case Some(userInfo) => // Store user info in session for username selection - Redirect(config.get[String]("app.url") + "/select-username") + Redirect(config.get[String]("openid.selectUserRoute")) .withSession( "oauth_user_info" -> Json.toJson(userInfo).toString(), "oauth_provider" -> provider, @@ -81,7 +79,7 @@ class OpenIDController @Inject()( } } - def selectUsername() = Action.async { implicit request => + def selectUsername(): Action[AnyContent] = Action.async { implicit request => request.session.get("oauth_user_info") match { case Some(userInfoJson) => val userInfo = Json.parse(userInfoJson).as[OpenIDUserInfo] @@ -90,14 +88,15 @@ class OpenIDController @Inject()( "email" -> userInfo.email, "name" -> userInfo.name, "picture" -> userInfo.picture, - "provider" -> userInfo.provider + "provider" -> userInfo.provider, + "providerName" -> userInfo.providerName ))) case None => Future.successful(Redirect("/login").flashing("error" -> "No authentication information found")) } } - def submitUsername() = Action.async { implicit request => + def submitUsername(): Action[AnyContent] = Action.async { implicit request => val username = request.body.asJson.flatMap(json => (json \ "username").asOpt[String]) .orElse(request.body.asFormUrlEncoded.flatMap(_.get("username").flatMap(_.headOption))) val userInfoJson = request.session.get("oauth_user_info") diff --git a/knockoutwhistweb/app/logic/user/impl/StubUserManager.scala b/knockoutwhistweb/app/logic/user/impl/StubUserManager.scala index c68d87a..7d699f1 100644 --- a/knockoutwhistweb/app/logic/user/impl/StubUserManager.scala +++ b/knockoutwhistweb/app/logic/user/impl/StubUserManager.scala @@ -19,6 +19,12 @@ class StubUserManager @Inject()(config: Config) extends UserManager { name = "Janis", passwordHash = UserHash.hashPW("password123") ), + "Leon" -> User( + internalId = 2L, + id = java.util.UUID.randomUUID(), + name = "Jakob", + passwordHash = UserHash.hashPW("password123") + ), "Jakob" -> User( internalId = 2L, id = java.util.UUID.fromString("323e4567-e89b-12d3-a456-426614174000"), diff --git a/knockoutwhistweb/app/model/sessions/UserSession.scala b/knockoutwhistweb/app/model/sessions/UserSession.scala index 4d89cba..4d1b187 100644 --- a/knockoutwhistweb/app/model/sessions/UserSession.scala +++ b/knockoutwhistweb/app/model/sessions/UserSession.scala @@ -26,8 +26,11 @@ class UserSession(val user: User, val host: Boolean, val gameLobby: GameLobby) e else canInteract = Some(InteractionType.Card) case _ => } + + lock.lock() websocketActor.foreach(_.solveRequests()) websocketActor.foreach(_.transmitEventToClient(event)) + lock.unlock() } override def id: UUID = user.id diff --git a/knockoutwhistweb/app/services/OpenIDConnectService.scala b/knockoutwhistweb/app/services/OpenIDConnectService.scala index cdce115..4c4bdb9 100644 --- a/knockoutwhistweb/app/services/OpenIDConnectService.scala +++ b/knockoutwhistweb/app/services/OpenIDConnectService.scala @@ -19,7 +19,8 @@ case class OpenIDUserInfo( email: Option[String], name: Option[String], picture: Option[String], - provider: String + provider: String, + providerName: String ) object OpenIDUserInfo { @@ -51,7 +52,7 @@ class OpenIDConnectService@Inject(ws: WSClient, config: Configuration)(implicit private val providers = Map( "discord" -> OpenIDProvider( - name = "discord", + name = "Discord", clientId = config.get[String]("openid.discord.clientId"), clientSecret = config.get[String]("openid.discord.clientSecret"), redirectUri = config.get[String]("openid.discord.redirectUri"), @@ -61,7 +62,7 @@ class OpenIDConnectService@Inject(ws: WSClient, config: Configuration)(implicit scopes = Set("identify", "email") ), "keycloak" -> OpenIDProvider( - name = "keycloak", + name = "Identity", clientId = config.get[String]("openid.keycloak.clientId"), clientSecret = config.get[String]("openid.keycloak.clientSecret"), redirectUri = config.get[String]("openid.keycloak.redirectUri"), @@ -74,16 +75,30 @@ class OpenIDConnectService@Inject(ws: WSClient, config: Configuration)(implicit def getAuthorizationUrl(providerName: String, state: String, nonce: String): Option[String] = { providers.get(providerName).map { provider => - val authRequest = new AuthenticationRequest.Builder( - new ResponseType(ResponseType.Value.CODE), - new com.nimbusds.oauth2.sdk.Scope(provider.scopes.mkString(" ")), - new com.nimbusds.oauth2.sdk.id.ClientID(provider.clientId), - URI.create(provider.redirectUri) - ) - .state(new com.nimbusds.oauth2.sdk.id.State(state)) - .nonce(new Nonce(nonce)) - .endpointURI(URI.create(provider.authorizationEndpoint)) - .build() + val authRequest = if (provider.scopes.contains("openid")) { + // Use OpenID Connect AuthenticationRequest for OpenID providers + new AuthenticationRequest.Builder( + new ResponseType(ResponseType.Value.CODE), + new com.nimbusds.oauth2.sdk.Scope(provider.scopes.mkString(" ")), + new com.nimbusds.oauth2.sdk.id.ClientID(provider.clientId), + URI.create(provider.redirectUri) + ) + .state(new com.nimbusds.oauth2.sdk.id.State(state)) + .nonce(new Nonce(nonce)) + .endpointURI(URI.create(provider.authorizationEndpoint)) + .build() + } else { + // Use standard OAuth2 AuthorizationRequest for non-OpenID providers (like Discord) + new AuthorizationRequest.Builder( + new ResponseType(ResponseType.Value.CODE), + new com.nimbusds.oauth2.sdk.id.ClientID(provider.clientId) + ) + .scope(new com.nimbusds.oauth2.sdk.Scope(provider.scopes.mkString(" "))) + .state(new com.nimbusds.oauth2.sdk.id.State(state)) + .redirectionURI(URI.create(provider.redirectUri)) + .endpointURI(URI.create(provider.authorizationEndpoint)) + .build() + } authRequest.toURI.toString } @@ -139,7 +154,8 @@ class OpenIDConnectService@Inject(ws: WSClient, config: Configuration)(implicit email = (json \ "email").asOpt[String], name = (json \ "name").asOpt[String].orElse((json \ "login").asOpt[String]), picture = (json \ "picture").asOpt[String].orElse((json \ "avatar_url").asOpt[String]), - provider = providerName + provider = providerName, + providerName = provider.name )) } else { None diff --git a/knockoutwhistweb/conf/application.conf b/knockoutwhistweb/conf/application.conf index 10e73a7..6c8444e 100644 --- a/knockoutwhistweb/conf/application.conf +++ b/knockoutwhistweb/conf/application.conf @@ -25,11 +25,13 @@ play.filters.cors { # Local Development OpenID Connect Configuration openid { + selectUserRoute="http://localhost:5173/select-username" + discord { clientId = ${?DISCORD_CLIENT_ID} - clientId = "your-discord-client-id" + clientId = "1462555597118509126" clientSecret = ${?DISCORD_CLIENT_SECRET} - clientSecret = "your-discord-client-secret" + clientSecret = "xZZrdd7_tNpfJgnk-6phSG53DSTy-eMK" redirectUri = ${?DISCORD_REDIRECT_URI} redirectUri = "http://localhost:9000/auth/discord/callback" } diff --git a/knockoutwhistweb/conf/prod.conf b/knockoutwhistweb/conf/prod.conf index de3349a..3ec33a9 100644 --- a/knockoutwhistweb/conf/prod.conf +++ b/knockoutwhistweb/conf/prod.conf @@ -17,7 +17,7 @@ play.filters.cors { # OpenID Connect Configuration openid { - selectUserRoute="https://knockout.janis-eccarius.de/select-user" + selectUserRoute="https://knockout.janis-eccarius.de/select-username" discord { clientId = ${?DISCORD_CLIENT_ID} diff --git a/knockoutwhistweb/conf/staging.conf b/knockoutwhistweb/conf/staging.conf index d2dc761..b16d2dc 100644 --- a/knockoutwhistweb/conf/staging.conf +++ b/knockoutwhistweb/conf/staging.conf @@ -10,4 +10,24 @@ play.filters.cors { allowedCredentials = true allowedHttpMethods = ["GET", "POST", "PUT", "DELETE", "OPTIONS"] allowedHttpHeaders = ["Accept", "Content-Type", "Origin", "X-Requested-With"] -} \ No newline at end of file +} + +openid { + + selectUserRoute="https://st.knockout.janis-eccarius.de/select-username" + + discord { + clientId = ${?DISCORD_CLIENT_ID} + clientSecret = ${?DISCORD_CLIENT_SECRET} + redirectUri = ${?DISCORD_REDIRECT_URI} + redirectUri = "https://st.knockout.janis-eccarius.de/auth/discord/callback" + } + + keycloak { + clientId = "your-keycloak-client-id" + clientSecret = "your-keycloak-client-secret" + redirectUri = "https://st.knockout.janis-eccarius.de/api/auth/keycloak/callback" + authUrl = ${?KEYCLOAK_AUTH_URL} + authUrl = "https://identity.janis-eccarius.de/realms/master" + } +}