{"id":22733922,"url":"https://github.com/permutive-engineering/gcp-auth","last_synced_at":"2026-06-02T07:00:59.852Z","repository":{"id":216325301,"uuid":"741047098","full_name":"permutive-engineering/gcp-auth","owner":"permutive-engineering","description":"Methods to authenticate with Google services over HTTP","archived":false,"fork":false,"pushed_at":"2026-06-01T21:23:30.000Z","size":192,"stargazers_count":2,"open_issues_count":2,"forks_count":1,"subscribers_count":4,"default_branch":"main","last_synced_at":"2026-06-01T23:16:02.145Z","etag":null,"topics":["gcp","gcp-auth","google-auth","scala"],"latest_commit_sha":null,"homepage":"","language":"Scala","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"apache-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/permutive-engineering.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-01-09T15:36:42.000Z","updated_at":"2026-06-01T21:23:51.000Z","dependencies_parsed_at":"2024-04-16T09:42:49.379Z","dependency_job_id":"d9f6fa7b-43a0-442e-87e7-e862c3296e1a","html_url":"https://github.com/permutive-engineering/gcp-auth","commit_stats":null,"previous_names":["permutive-engineering/gcp-auth"],"tags_count":8,"template":false,"template_full_name":null,"purl":"pkg:github/permutive-engineering/gcp-auth","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutive-engineering%2Fgcp-auth","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutive-engineering%2Fgcp-auth/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutive-engineering%2Fgcp-auth/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutive-engineering%2Fgcp-auth/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/permutive-engineering","download_url":"https://codeload.github.com/permutive-engineering/gcp-auth/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/permutive-engineering%2Fgcp-auth/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33810343,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-06-02T02:00:07.132Z","response_time":109,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["gcp","gcp-auth","google-auth","scala"],"created_at":"2024-12-10T20:16:59.145Z","updated_at":"2026-06-02T07:00:59.837Z","avatar_url":"https://github.com/permutive-engineering.png","language":"Scala","funding_links":[],"categories":[],"sub_categories":[],"readme":"Methods to authenticate with Google services over HTTP\n\n---\n\n- [Installation](#installation)\n- [Usage](#usage)\n  - [Available token providers](#available-token-providers)\n    - [Identity (via service-account)](#identity-via-service-account)\n    - [Identity (via user-account)](#identity-via-user-account)\n    - [Service-Account](#service-account)\n    - [User-Account](#user-account)\n    - [Auto-selection via Application Default Credentials](#auto-selection-via-application-default-credentials)\n      - [Disabling `auto` in acceptance tests](#disabling-auto-in-acceptance-tests)\n  - [Reading the authenticated principal](#reading-the-authenticated-principal)\n  - [Creating and auto-refreshing \u0026 cached `TokenProvider`](#creating-and-auto-refreshing--cached-tokenprovider)\n  - [Creating an auto-authenticated http4s `Client`](#creating-an-auto-authenticated-http4s-client)\n  - [Loading a different `TokenProvider` depending on the environment with `pureconfig`](#loading-a-different-tokenprovider-depending-on-the-environment-with-pureconfig)\n  - [Authenticating with Google Managed Kafka](#authenticating-with-google-managed-kafka)\n- [Contributors to this project](#contributors-to-this-project)\n\n## Installation\n\nAdd the following line to your `build.sbt` file:\n\n```sbt\nlibraryDependencies += \"com.permutive\" %% \"gcp-auth\" % \"3.0.0-RC1\"\n```\n\nThe library is published for Scala versions: `2.13` and `3`.\n\n## Usage\n\nThis library provides a class `TokenProvider` that is able to retrieve a\nspecific type of access token from [Google OAuth 2.0] API.\n\n### Available token providers\n\n\n#### Identity (via service-account)\n\nRetrieves an [Identity Token] using Google's metadata server for a specific audience.\n\nIdentity tokens can be used for calling Cloud Run services.\n\n**Important!** This method can only be run from within a workload container in\nGCP. The call will fail otherwise.\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\n\nval audience = uri\"https://my-run-app.a.run.app\"\n\nTokenProvider.identity[IO](httpClient, audience)\n```\n\n#### Identity (via user-account)\n\nRetrieves an [Identity Token] using your user account credentials.\n\nIdentity tokens can be used for calling Cloud Run services.\n\n**Warning!** Be sure to keep these tokens secure, and never use them in a\nproduction environment. They are meant to be used during development only.\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\n\nTokenProvider.userIdentity[IO](httpClient)\n```\n\n#### Service-Account\n\nRetrieves a [Google Service Account Token] either via the\n[instance metadata API] (if running from a GCP workload) or using a\nspecific service account file.\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\nimport com.permutive.gcp.auth.models.ClientEmail\n\n// Retrieves a workload service account token using\n// Google's metadata server.\nTokenProvider.serviceAccount[IO](httpClient)\n\n// Retrieves a service account token using a specific\n// file and scopes\nTokenProvider.serviceAccount[IO](\n    pathToServiceAccountFile,\n    scope = \"https://www.googleapis.com/auth/bigquery\" :: Nil,\n    httpClient\n)\n\n// Retrieves a service account token using a specific\n// email/key/scopes\nTokenProvider.serviceAccount[IO](\n    ClientEmail(\"my@example.com\"),\n    privateKey: RSAPrivateKey,\n    scope = \"https://www.googleapis.com/auth/bigquery\" :: Nil,\n    httpClient\n)\n```\n\n#### User-Account\n\nRetrieves a [Google User Account Token] either using the application default\ncredentials or from a specific path.\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\nimport com.permutive.gcp.auth.models.ClientId\nimport com.permutive.gcp.auth.models.ClientSecret\nimport com.permutive.gcp.auth.models.RefreshToken\n\n// Retrieves a user account token using a specific file\n// for the secrets and token\nTokenProvider.userAccount[IO](\n    pathToClientSecretsPath,\n    pathToRefreshTokenPath, \n    httpClient\n)\n\n// Retrieves a service account token using a specific\n// client-id/client-secret/refresh-token\nTokenProvider.userAccount[IO](\n    ClientId(\"client-id\"),\n    ClientSecret(\"client-secret\"),\n    RefreshToken(\"refresh-token\"),\n    httpClient\n)\n\n// Retrieves a user account token using the application\n// default credentials\nTokenProvider.userAccount[IO](httpClient)\n```\n\n#### Auto-selection via Application Default Credentials\n\n`TokenProvider.auto` picks the right token provider using Google's standard\n[ADC precedence] — useful when the same binary runs locally (user account),\nin CI (service-account JSON via `GOOGLE_APPLICATION_CREDENTIALS`), and in\nproduction (workload identity on GCE/GKE).\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\n\nTokenProvider.auto[IO](httpClient)\n\n// or, with explicit scopes for the service-account JSON branch:\nTokenProvider.auto[IO](\n    scopes = \"https://www.googleapis.com/auth/bigquery\" :: Nil,\n    httpClient = httpClient\n)\n```\n\n##### Disabling `auto` in acceptance tests\n\nSetting the `GCP_AUTH_DISABLE=true` environment variable (or the\nequivalent `gcp.auth.disable=true` JVM system property) makes every\n`TokenProvider.auto` overload short-circuit to\n`TokenProvider.const(AccessToken.noop)` — no filesystem reads, no\nmetadata-server probes. Useful for acceptance tests that need to\nsilence credential resolution without otherwise touching the\napplication wiring.\n\n**Caveat:** a stray production setting will silently produce a no-op\nprovider instead of a loud \"credentials not found\" error. When the\nconfiguration channel can be controlled per environment, prefer the\n`gcp-auth-pureconfig` integration with `TokenType.NoOp` selected via\n`application.conf`.\n\n### Reading the authenticated principal\n\nEvery `TokenProvider` exposes a `principal: F[Option[String]]` accessor that\nreturns the subject identifier the provider is authenticated as:\n\n- **Service-account flows** surface the service-account email — taken from\n  the JSON key file, the explicit `ClientEmail` parameter, or the GCE\n  metadata server's `/email` endpoint.\n- **User-account flows** do a best-effort call to Google's `userinfo`\n  endpoint and return `None` if the call fails (the refresh token may have\n  been issued with scopes that don't include `email`/`openid`/`profile`).\n- **`identity` (workload identity token)** hits the GCE metadata server's\n  `/email` endpoint, mirroring the service-account workload flow.\n- **`userIdentity` (user-account identity token)** decodes the issued JWT\n  and returns its `email` claim (falling back to `sub`), or `None` if\n  neither is present.\n- `TokenProvider.const` and `TokenProvider.create` return `None`.\n\nFactories that need an HTTP call to resolve the principal memoise the\nlookup at construction time, so each provider instance makes at most one\nunderlying call regardless of how many times `principal` is read.\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\n\nTokenProvider.serviceAccount[IO](httpClient).flatMap(_.principal)\n```\n\n### Creating and auto-refreshing \u0026 cached `TokenProvider`\n\nYou can use `TokenProvider.cached` to create an auto-refreshing \u0026 cached\nversion of any `TokenProvider` that will cache each token generated for\nthe lifespan of that token and then generates a new one.\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\n\nval tokenProvider =\n    TokenProvider.userAccount[IO](httpClient)\n\nTokenProvider.cached[IO]\n    .safetyPeriod(4.seconds) // 1.\n    .onRefreshFailure { case (_, _) =\u003e IO.unit }\n    .onExhaustedRetries(_ =\u003e IO.unit)\n    .onNewToken { case (_, _) =\u003e IO.unit }\n    .retryPolicy(constantDelay[IO](200.millis)) // 2.\n    .build(tokenProvider)\n\n/**\n * 1. How much time less than the indicated expiry to\n *    cache a token for\n * 2. Defaults to 5 retries with a delay between each\n *    of 200 milliseconds.\n */\n```\n\n### Creating an auto-authenticated http4s `Client`\n\nOnce you have a `TokenProvider` created, you can use its `clientMiddleware`\nmethod to wrap an http4s' `Client` ensuring every request coming out from it\nwill contain an `Authorization` header with the access token provided by the\n`TokenProvider`.\n\n```scala\nimport com.permutive.gcp.auth.TokenProvider\n\nTokenProvider\n    .userAccount[IO](httpClient)\n    .map(_.clientMiddleware(httpClient))\n```\n\n### Loading a different `TokenProvider` depending on the environment with `pureconfig`\n\nThe library also provides a [pureconfig] integration that simplifies the process\nof using a different `TokenProvider` on different environments. For example, you\nmay want to use the workload service-account when running from GCP, but would\nwant to use a user-account when running your service locally, or use a no-op\naccess token when running in tests. You can simplify that process by loading\nthe appropriate `TokenProvider` using pureconfig:\n\n1. Add the following line to your `build.sbt` file:\n\n```sbt\nlibraryDependencies += \"com.permutive\" %% \"gcp-auth-pureconfig\" % \"3.0.0-RC1\"\n```\n\n2. Use the following type in your configuration class:\n\n```scala\nimport com.permutive.gcp.auth.pureconfig._\n\ncase class Config(tokenType: TokenType)\n```\n\n3. In your `application.conf` file provide the appropriate type:\n\n```conf\ntoken-type = \"user-account\"\ntoken-type = \"service-account\"\ntoken-type = \"no-op\"\n```\n\n4. When you want to instantiate your `TokenProvider` simply use:\n\n\n```scala\nval tokenProvider = config.tokenType.tokenProvider(httpClient)\nval identityTokenProvider = config.tokenType.identityTokenProvider(httpClient, myAudience)\n```\n\n### Authenticating with Google Managed Kafka\n\nThe library also provides a SASL/OAUTHBEARER login callback handler for\n[Google Managed Service for Apache Kafka]. It is a drop-in replacement for\nGoogle's `com.google.cloud.hosted.kafka.auth.GcpLoginCallbackHandler` that\nuses `gcp-auth`'s `TokenProvider.auto` under the hood — no\n`kafka-schema-registry-client`, no Google Java SDK on the classpath.\n\n1. Add the following line to your `build.sbt` file:\n\n```sbt\nlibraryDependencies += \"com.permutive\" %% \"gcp-auth-kafka\" % \"3.0.0-RC1\"\n```\n\n2. Wire it into your Kafka client config:\n\n```properties\nsecurity.protocol                 = SASL_SSL\nsasl.mechanism                    = OAUTHBEARER\nsasl.login.callback.handler.class = com.permutive.gcp.auth.kafka.GcpLoginCallbackHandler\nsasl.jaas.config                  = org.apache.kafka.common.security.oauthbearer.OAuthBearerLoginModule required;\n```\n\nThe handler resolves credentials using ADC precedence (via\n`TokenProvider.auto`). The `sub` claim is taken from\n`GOOGLE_MANAGED_KAFKA_AUTH_PRINCIPAL` when set (overrides the provider's\nprincipal — useful for Workforce Identity Federation cases), otherwise\nfrom the provider's `principal`. If neither yields a value, `configure`\nraises `IllegalStateException` pointing at the env var (matching Google's\nfail-fast behavior).\n\n## Contributors to this project\n\n| \u003ca href=\"https://github.com/alejandrohdezma\"\u003e\u003cimg alt=\"alejandrohdezma\" src=\"https://avatars.githubusercontent.com/u/9027541?v=4\u0026s=120\" width=\"120px\" /\u003e\u003c/a\u003e | \u003ca href=\"https://github.com/izsob\"\u003e\u003cimg alt=\"izsob\" src=\"https://avatars.githubusercontent.com/u/669110?v=4\u0026s=120\" width=\"120px\" /\u003e\u003c/a\u003e |\n| :--: | :--: |\n| \u003ca href=\"https://github.com/alejandrohdezma\"\u003e\u003csub\u003e\u003cb\u003ealejandrohdezma\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e | \u003ca href=\"https://github.com/izsob\"\u003e\u003csub\u003e\u003cb\u003eizsob\u003c/b\u003e\u003c/sub\u003e\u003c/a\u003e |\n\n[Google OAuth 2.0]: https://developers.google.com/identity/protocols/OAuth2\n[`TokenProvider`]: modules/google-auth/src/main/scala/com/permutive/google/auth/TokenProvider.scala\n[Google Service Account Token]: https://developers.google.com/identity/protocols/OAuth2ServiceAccount\n[Google User Account Token]: https://developers.google.com/identity/protocols/OAuth2WebServer\n[Identity Token]: https://cloud.google.com/run/docs/securing/service-identity#fetching_identity_and_access_tokens_using_the_metadata_server\n[instance metadata API]: https://cloud.google.com/compute/docs/access/authenticate-workloads\n[ADC precedence]: https://cloud.google.com/docs/authentication/provide-credentials-adc\n[Google Managed Service for Apache Kafka]: https://cloud.google.com/managed-service-for-apache-kafka/docs\n[pureconfig]: https://pureconfig.github.io","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutive-engineering%2Fgcp-auth","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fpermutive-engineering%2Fgcp-auth","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fpermutive-engineering%2Fgcp-auth/lists"}