Skip to content

Google authentication overhaul#3448

Merged
johanandren merged 1 commit into
akka:mainfrom
jroper:google-credentials-rework
Feb 27, 2026
Merged

Google authentication overhaul#3448
johanandren merged 1 commit into
akka:mainfrom
jroper:google-credentials-rework

Conversation

@jroper

@jroper jroper commented Feb 16, 2026

Copy link
Copy Markdown
Contributor

Fixes #3447 and #3446.

This adds a number of features and improves Google authentication.

  • Project ID is no longer always provided by the credentials provider. It's now an Option, which means credentials provider that are not able to provide a project ID can be implemented. The API that has been changed was internal so nothing has broken here.
  • A new project-id parameter has been introduced at the top level of the Alpakka Google configuration. Rather than just loading the project id from the credentials, this is now read first, and takes precedence. If not provided, then we use the optional project id from the credentials provider, and if that's not provided, we then use another new configuration parameter called default-project-id, which is configured to read the project id from environment variables, eg GCP_PROJECT or GOOGLE_CLOUD_PROJECT. In many environments, eg when workload identity is being used, these environment variables may be automatically set. This is actually an important feature, because the project id from the credentials provider is tied to which project your principal is in, but there's no reason why that project must be the same project that you want to access. Google principals are global, you can grant a service account in one project access to GCP in another, for example.
  • The application-default credentials provider has been reworked, and is now more robust. Instead of each provider being responsible for loading the file itself, and this selecting one based on which one didn't fail to load, the type field of the application JSON file is read, to decide what type of credentials to read. So, a new path parameter has been added for what to use when application-default is configured. Failure to configure from the JSON file results in attempting to load GCE credentials.
  • A new credentials provider has been added, external account. This allows federated workload identity to work, for example, in GitHub actions. So far it has only been tested with GitHub actions.
  • Credential providers can now be closed, with a new close() public API method being added. This means that when dynamically loading credentials, resources, such as the actors running the caching stream for OAuth2 credentials, can be cleaned up when the credentials are no longer needed.
  • A new custom OAuth2 credentials class has been created, allowing custom access token loading strategies, which is useful on platforms where an access token is provided in an environment variable via workload identity, or so that users can implement their own authentication providers, which is a necessary escape hatch since there's several other Google application JSON credentials types that we still don't support.

I've been careful to ensure that this is backwards compatible, there's no binary breakages, but the following scenarios may break:

  • If someone is relying on the application-default provider, but they have placed configuration in one of the other providers, the old application-default provider would read that config. Now it doesn't. For example, if you configure a service account using the config keys, rather than using an application json file, and you don't set provider = service-account, that used to work, but it doesn't now. The solution is to be specific about which provider you want to use.
  • If someone has generated their own application json file without the type parameter, this will break the application-default provider. This parameter is considered compulsory by the Google SDK.

}
}

def loadFromJson(json: String, scopes: Seq[String])(implicit system: ClassicActorSystemProvider): Credentials = {

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is intentionally public, to allow for programmatic instantiation of credentials from a loaded JSON file, something we use in Akka platform.

@jroper

jroper commented Feb 16, 2026

Copy link
Copy Markdown
Contributor Author

This is failing because mima does not like the fact that private[alpakka] classes that are annotated with @InternalApi have changed. It's been a while since I've dealt with mima, not sure if there's a simple way to exclude checking of private classes like that.

@jroper jroper force-pushed the google-credentials-rework branch from a658924 to 0b8e043 Compare February 19, 2026 02:49
@jroper jroper requested a review from johanandren February 26, 2026 03:25

@johanandren johanandren left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changes looks good so far.

Needs Java APIs for the public parts.

Mima filters would go in a text file google-common/src/main/mima-filters/10.0.1.backwards.excludes/goggle-auth-overhaul.excludes, you can copy the suggested ProblemFilters.exclude entries from the mima output as individual lines into that file.

* This class is intended for users to implement themselves to provide a custom means of loading Google OAuth2
* credentials.
*/
trait CustomOAuth2Credentials {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We'll also need a Java version of this with CompletionStage and Pair rather than Scala types, not sure if the best thing there would be some clever naming or introduce scaladsl and javadsl packages like generally do.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've done this by using the existing Google scaladsl and javadsl packages.

import scala.io.Source

object ApplicationDefaultCredentials {
def apply(c: Config, scopes: Seq[String])(implicit system: ClassicActorSystemProvider): Credentials = {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Add Java create methods parallel with apply, and loadFromJson that accept Java collections etc. as parameters and doesn't use implicit.

Scaladoc on the methods with /** Java API */ or /** Scala API */ to make the distinction extra clear for users, so add that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added.

@jroper jroper force-pushed the google-credentials-rework branch from 0b8e043 to d284869 Compare February 27, 2026 01:52
@jroper

jroper commented Feb 27, 2026

Copy link
Copy Markdown
Contributor Author

I've made the following changes:

  • Added support for file credential sources. I added this because I'm guessing on some platforms the JWT may come from a file - it does on Kubernetes for example using a projected service account token (though on GKE, you generally use the metadata server, which does everything for you).
  • Added a test which mocks the whole external account credentials flow in an HTTP server. I was careful when implementing this to not look at the source code that I was testing, but rather to look at the API docs of the endpoints. So basically I've implemented both sides independently, and verified that they match.
  • Added the requested Java APIs as described above.
  • Added mima exclusions for internal private APIs.

@jroper jroper force-pushed the google-credentials-rework branch from d284869 to b6dffd4 Compare February 27, 2026 02:00
Fixes akka#3447 and akka#3446.

This adds a number of features and improves Google authentication.

* Project ID is no longer always provided by the credentials provider. It's
  now an Option, which means credentials provider that are not able to provide
  a project ID can be implemented.
* A new project-id parameter has been introduced at the top level of the
  Alpakka Google configuration. This takes precedence, then the optional
  project id provided by the credential provider, and then the default project
  id is used, which is read from the GCP_PROJECT or GOOGLE_CLOUD_PROJECT
  environment variables.
* application-default has been reworked, and is now more robust. Instead of
  each provider being responsible for loading the file itself, and this
  selecting one based on which one didn't fail to load, the type field of the
  application JSON file is read, to decide what type of credentials to read.
  Failure then results in attempting to load GCE credentials.
* A new credentials provider has been added, external account. This allows
  workload identity to work, for example, in GitHub actions.
* Credentials can now be closed. This means that when dynamically loading
  credentials, resources, such as the actors running the caching stream for
  OAuth2 credentials, can be cleaned up when the credentials are no longer
  needed.
* A new custom OAuth2 credentials class has been created, allowing custom
  access token loading strategies, which is useful on platforms where an access
  token is provided in an environment variable via workload identity.

I've been careful to ensure that this is backwards compatible, but the
following scenarios may break:

* If someone is relying on the application-default provider, but they have
  placed configuration in one of the other providers, the old
  application-default provider would read that config. Now it doesn't. The
  solution is to be specific about which provider you want to use.
* If someone has generated their own application json file without the type
  parameter, this will break the application-default provider. This parameter
  is considered compulsory by the Google SDK.
@jroper jroper force-pushed the google-credentials-rework branch from b6dffd4 to 9f05ff3 Compare February 27, 2026 02:15

@johanandren johanandren left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM!

@johanandren johanandren merged commit 6314a61 into akka:main Feb 27, 2026
51 of 52 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Deprecate Google project id

2 participants