Skip to content

Commit

Permalink
Uusi OAuth2 client käyttöön
Browse files Browse the repository at this point in the history
  • Loading branch information
a544jh committed Jan 24, 2025
1 parent 4908c72 commit 32f6aac
Show file tree
Hide file tree
Showing 3 changed files with 89 additions and 75 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -33,24 +33,23 @@ case class OppijanumeroRekisteriClient(
def withRetryStrategy(strategy: OppijanumeroRekisteriClientRetryStrategy): OppijanumeroRekisteriClient =
this.copy(retryStrategy = strategy)

private val virkailjaUrl = makeServiceConfig(config).virkailijaUrl

private val baseUrl = "/oppijanumerorekisteri-service"

private val oidServiceHttp = VirkailijaHttpClient(makeServiceConfig(config), baseUrl, true)
private val otuvaTokenEndpoint = config.getString("otuvaTokenEndpoint")

private val oauth2clientFactory = new OtuvaOAuth2ClientFactory(OtuvaOAuth2Credentials.fromSecretsManager, otuvaTokenEndpoint)

private val oidServiceHttp = oauth2clientFactory(virkailjaUrl, Http.retryingClient(baseUrl))

private val postRetryingOidServiceHttp = {
// Osa POST-metodilla ONR:ään tehtävistä kyselyistä on oikeasti idempotentteja,
// joten niiden uudelleenyrittäminen on ok: siksi unsafeRetryingClient. Tällä saadaan
// esim. raportointoinkannan generointi jatkamaan, vaikka onr-yhteys hetken pätkisikin.
val client = unsafeRetryingClient(baseUrl, retryStrategy.applyConfig, retryStrategy.backoffPolicy)

/*VirkailijaHttpClient(
makeServiceConfig(config),
baseUrl,
client,
preferGettingCredentialsFromSecretsManager = true
)*/
// TODO
VirkailijaOAuth2Client()
oauth2clientFactory(virkailjaUrl, client)
}

private def makeServiceConfig(config: Config) =
Expand Down
81 changes: 81 additions & 0 deletions src/main/scala/fi/oph/koski/http/OtuvaOAuth2ClientFactory.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
package fi.oph.koski.http

import cats.effect.{IO, Resource}
import cats.effect.std.Hotswap
import fi.oph.koski.config.SecretsManager
import fi.oph.koski.log.NotLoggable
import fi.oph.scalaschema.Serializer.format
import org.http4s.{Header, Method, Request, Response, Uri, UrlForm}
import org.http4s.client.Client
import org.json4s.jackson.JsonMethods
import org.typelevel.ci.CIStringSyntax

case class OtuvaOAuth2Credentials(id: String, secret: String) extends NotLoggable

object OtuvaOAuth2Credentials {
def fromSecretsManager: OtuvaOAuth2Credentials = {
val cachedSecretsClient = new SecretsManager
val secretId = cachedSecretsClient.getSecretId("Otuva OAuth2 credentials", "OPINTOPOLKU_VIRKAILIJA_OAUTH2_SECRET_ID")
cachedSecretsClient.getStructuredSecret[OtuvaOAuth2Credentials](secretId)
}
}

class OtuvaOAuth2ClientFactory(
credetials: OtuvaOAuth2Credentials,
otuvaTokenEndpoint: String,
) {

private var token: Option[String] = None
private val otuvaTokenEndpointUri = Uri.fromString(otuvaTokenEndpoint).right.get

def apply(serviceUrl: String, serviceClient: Client[IO]): Http = {

def withOAuth2Token(req: Request[IO], hotswap: Hotswap[IO, Response[IO]]) = {
getToken().flatMap(requestWithToken(req, hotswap, retry = true))
}

def getToken(): IO[String] = {
token match {
case Some(s) => IO.pure(s)
case None => refreshToken()
}
}

def requestWithToken(req: Request[IO], hotswap: Hotswap[IO, Response[IO]], retry: Boolean)(token: String): IO[Response[IO]] = {
val newReq = req.withHeaders(Header.Raw(ci"Authentication", "Bearer " + token))
hotswap.swap(serviceClient.run(newReq)).flatMap {
// TODO: better expiry check
case r: Response[IO] if r.status.code != 200 =>
refreshToken().flatMap(requestWithToken(req, hotswap, retry = false))
case r: Response[IO] => IO.pure(r)
}
}

def refreshToken(): IO[String] = {
val body = UrlForm("grant_type" -> "client_credentials", "client_id" -> credetials.id, "client_secret" -> credetials.secret)
val req = Request[IO](Method.POST, otuvaTokenEndpointUri).withEntity(body)

serviceClient.run(req).use(
res => {
res.as[String].map {
s =>
val v = JsonMethods.parse(s)
val t = (v \ "access_token").extract[String]
token = Some(t)
t
}
}
)
}

val client = Client[IO] {
req =>
Hotswap.create[IO, Response[IO]].flatMap { hotswap =>
Resource.eval(withOAuth2Token(req, hotswap))
}
}

Http(serviceUrl, client)
}

}
66 changes: 0 additions & 66 deletions src/main/scala/fi/oph/koski/http/VirkailijaOAuth2Client.scala

This file was deleted.

0 comments on commit 32f6aac

Please sign in to comment.