@@ -21,6 +21,7 @@ package com.tesobe.oidc.endpoints
2121
2222import cats .effect .IO
2323import com .tesobe .oidc .auth .{AuthService , CodeService }
24+ import com .tesobe .oidc .endpoints .HtmlUtils .htmlEncode
2425import com .tesobe .oidc .models .{OidcError , User }
2526import com .tesobe .oidc .ratelimit .RateLimitService
2627import com .tesobe .oidc .config .OidcConfig
@@ -324,7 +325,7 @@ class AuthEndpoint(
324325 s " Error handling login submission: ${error.getMessage}" ,
325326 error
326327 )
327- BadRequest (s " Invalid form data: ${error.getMessage} " )
328+ BadRequest (" Invalid form data. Please try again. " )
328329 }
329330
330331 private def generateCodeForUser (
@@ -359,31 +360,31 @@ class AuthEndpoint(
359360 clientOpt <- authService.findClientByClientIdThatIsKey(clientId)
360361
361362 stateParam = state
362- .map(s => s """ <input type="hidden" name="state" value=" $s "> """ )
363+ .map(s => s """ <input type="hidden" name="state" value=" ${htmlEncode(s)} "> """ )
363364 .getOrElse(" " )
364365 nonceParam = nonce
365- .map(n => s """ <input type="hidden" name="nonce" value=" $n "> """ )
366+ .map(n => s """ <input type="hidden" name="nonce" value=" ${htmlEncode(n)} "> """ )
366367 .getOrElse(" " )
367368
368369 providerOptions = providers
369370 .map { provider =>
370- s """ <option value=" $provider"> $provider</option> """
371+ s """ <option value=" ${htmlEncode( provider)} "> ${htmlEncode( provider)} </option> """
371372 }
372373 .mkString(" \n " )
373374
374375 clientName = clientOpt.map(_.client_name).getOrElse(" Unknown Client" )
375376 consumerId = clientOpt.map(_.consumer_id).getOrElse(" Unknown Consumer" )
376377
377378 // Format client name for production display: replace dashes with spaces and convert to proper case
378- formattedClientName = clientName
379+ formattedClientName = htmlEncode( clientName
379380 .replace(" -" , " " )
380381 .split(" " )
381382 .map(word =>
382383 if (word.isEmpty) " "
383384 else word.charAt(0 ).toUpper + word.substring(1 ).toLowerCase
384385 )
385386 .mkString(" " )
386- .replace(" Obp " , " OBP " )
387+ .replace(" Obp " , " OBP " ))
387388
388389 errorHtml = errorMessage
389390 .map(msg => s """ <div class="error"> $msg</div> """ )
@@ -434,10 +435,10 @@ class AuthEndpoint(
434435 $errorHtml
435436 ${if (config.localDevelopmentMode) {
436437 s """ <div class="info">
437- <strong>Consumer ID:</strong> $consumerId<br>
438- <strong>Client Name:</strong> $clientName<br>
439- <strong>Client ID:</strong> $clientId<br>
440- <strong>Requested Scopes:</strong> $scope
438+ <strong>Consumer ID:</strong> ${htmlEncode( consumerId)} <br>
439+ <strong>Client Name:</strong> ${htmlEncode( clientName)} <br>
440+ <strong>Client ID:</strong> ${htmlEncode( clientId)} <br>
441+ <strong>Requested Scopes:</strong> ${htmlEncode( scope)}
441442 </div> """
442443 } else {
443444 " "
@@ -469,7 +470,7 @@ class AuthEndpoint(
469470 </div> """
470471 } else if (providers.length == 1 ) {
471472 // Single provider in production: use hidden field
472- s """ <input type="hidden" name="provider" value=" ${providers.head}"> """
473+ s """ <input type="hidden" name="provider" value=" ${htmlEncode( providers.head) }"> """
473474 } else {
474475 // No providers - shouldn't happen but handle gracefully
475476 s """ <div class="form-group">
@@ -480,9 +481,9 @@ class AuthEndpoint(
480481 </div> """
481482 }}
482483
483- <input type="hidden" name="client_id" value=" $clientId">
484- <input type="hidden" name="redirect_uri" value=" $redirectUri">
485- <input type="hidden" name="scope" value=" $scope">
484+ <input type="hidden" name="client_id" value=" ${htmlEncode( clientId)} ">
485+ <input type="hidden" name="redirect_uri" value=" ${htmlEncode( redirectUri)} ">
486+ <input type="hidden" name="scope" value=" ${htmlEncode( scope)} ">
486487 $stateParam
487488 $nonceParam
488489
@@ -617,7 +618,7 @@ class AuthEndpoint(
617618 redirectUri : String ,
618619 error : OidcError
619620 ): IO [Response [IO ]] = {
620- val stateParam = error.state.map(s => s " &state= $s " ).getOrElse(" " )
621+ val stateParam = error.state.map(s => s " &state= ${java.net. URLEncoder .encode(s, " UTF-8 " )} " ).getOrElse(" " )
621622 val descriptionParam = error.error_description
622623 .map(d => s " &error_description= ${java.net.URLEncoder .encode(d, " UTF-8" )}" )
623624 .getOrElse(" " )
0 commit comments