Resource example

First we need to import everthing that we need.

import java.time.Instant
import cats.effect._
import io.github.jkobejs.google.oauth4s.ServerToServer
import io.github.jkobejs.google.oauth4s.ServiceAccountKeyReader
import scala.concurrent.ExecutionContext.Implicits.global
import scala.concurrent.duration._

We will use cats.effect.IO effect wrapper to make our computation pure. To be able to use it we need to create context shift and timer which are need for shifting execution and scheduling of tasks.

implicit val ctx = IO.contextShift(global)
// ctx: ContextShift[IO] = cats.effect.internals.IOContextShift@5521ef71
implicit val timer = IO.timer(global)
// timer: Timer[IO] = cats.effect.internals.IOTimer@2e62fa50

To communicate with google auth api we need to create claims and settings. We can do it in two ways, create them manually (private key should we read in a safe way) or read service account key data from google service account key file and use it to create settings.

Let’s first do it manually.

{
  val privateKey = "sample-private-key" // read it in a safe way
  val clientEmail = "sample@email.iam.gserviceaccount.com"
  val url = "https://www.googleapis.com/oauth2/v4/token"
  val scope = "https://www.googleapis.com/auth/devstorage.read_write"
  
  val claims = ServerToServer.GoogleClaims(
    issuer = clientEmail,
    scope = scope,
    audience = url,
    expiration = Instant.now().plusSeconds(3600),
    issuedAt = Instant.now()
  )
  
  val settings = ServerToServer.Settings(
    uri = "https://www.googleapis.com/oauth2/v4/token",
    privateKey = privateKey,
    grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer",
    claims = claims
  )

  ServerToServer
      .resource[IO](settings, global)
      .use { authenticator =>
        for {
          authResponse1 <- authenticator.auth
          _             <- IO.sleep(2.seconds)
          authResponse2 <- authenticator.auth
        } yield List(authResponse1, authResponse2)
      }
}
// res0: IO[List[ServerToServer.AuthResponse]] = Async(
//   cats.effect.internals.IOBracket$$$Lambda$7711/1364152185@7d93ccac,
//   false
// )

Now lets see how to use ServiceAccountKey.

val authenticatorResource = for {
  serviceAccountKey <- Resource.liftF(ServiceAccountKeyReader.readServiceAccountKey[IO]("src/test/resources/service-account.json", global))
  scope = "https://www.googleapis.com/auth/devstorage.read_write"
  claims = ServerToServer.GoogleClaims(
    issuer = serviceAccountKey.client_email,
    scope = scope,
    audience = serviceAccountKey.token_uri,
    expiration = Instant.now().plusSeconds(3600),
    issuedAt = Instant.now()
  )
  settings =  ServerToServer.Settings(
    uri = serviceAccountKey.token_uri,
    privateKey = serviceAccountKey.private_key,
    grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer",
    claims = claims
  )
 resource <- ServerToServer.resource[IO](settings, global)
} yield resource
// authenticatorResource: Resource[IO, ServerToServer.Authenticator[IO]] = Bind(
//   Bind(
//     Suspend(
//       Map(
//         Bind(
//           Pure(src/test/resources/service-account.json),
//           io.github.jkobejs.google.oauth4s.ServiceAccountKeyReader$$$Lambda$7713/7612392@6374dae6
//         ),
//         cats.effect.Resource$$$Lambda$7822/1443211186@39f4eb9e,
//         0
//       )
//     ),
//     cats.effect.Resource$$Lambda$7823/258705713@fd368a
//   ),
//   <function1>
// )

authenticatorResource.use { authenticator =>
  for {
    authResponse1 <- authenticator.auth
    _             <- IO.sleep(2.seconds)
    authResponse2 <- authenticator.auth
  } yield List(authResponse1, authResponse2)
}
// res1: IO[List[ServerToServer.AuthResponse]] = Bind(
//   Map(
//     Bind(
//       Pure(src/test/resources/service-account.json),
//       io.github.jkobejs.google.oauth4s.ServiceAccountKeyReader$$$Lambda$7713/7612392@6374dae6
//     ),
//     cats.effect.Resource$$$Lambda$7822/1443211186@39f4eb9e,
//     0
//   ),
//   cats.effect.Resource$$Lambda$7824/1475944472@63e86c95
// )