-
Notifications
You must be signed in to change notification settings - Fork 3.9k
[xDS] A97 - JWT token file call creds #12242
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
6b79ff2
f7fe53a
494439c
f3d5108
13881d8
b61de62
fb3805a
bdcfc0a
dd1de65
76e9e8f
3a36865
743c248
20a50c8
b7c4e55
ae8d9e3
df1208b
d3d2188
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,66 @@ | ||
| /* | ||
| * Copyright 2025 The gRPC Authors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package io.grpc.xds; | ||
|
|
||
| import static com.google.common.base.Preconditions.checkNotNull; | ||
|
|
||
| import com.google.api.client.json.gson.GsonFactory; | ||
| import com.google.api.client.json.webtoken.JsonWebSignature; | ||
| import com.google.auth.oauth2.AccessToken; | ||
| import com.google.auth.oauth2.OAuth2Credentials; | ||
| import com.google.common.io.Files; | ||
| import io.grpc.CallCredentials; | ||
| import io.grpc.auth.MoreCallCredentials; | ||
| import java.io.File; | ||
| import java.io.IOException; | ||
| import java.nio.charset.StandardCharsets; | ||
| import java.util.Date; | ||
|
|
||
| /** | ||
| * JWT token file call credentials. | ||
| * See gRFC A97 (https://github.com/grpc/proposal/pull/492). | ||
| */ | ||
| public final class JwtTokenFileCallCredentials extends OAuth2Credentials { | ||
| private static final long serialVersionUID = 0L; | ||
| private final String path; | ||
|
|
||
| private JwtTokenFileCallCredentials(String path) { | ||
| this.path = checkNotNull(path, "path"); | ||
| } | ||
|
|
||
| @Override | ||
| public AccessToken refreshAccessToken() throws IOException { | ||
| String tokenString = new String(Files.toByteArray(new File(path)), StandardCharsets.UTF_8); | ||
| Long expTime = JsonWebSignature.parse(new GsonFactory(), tokenString) | ||
| .getPayload() | ||
| .getExpirationTimeSeconds(); | ||
| if (expTime == null) { | ||
| throw new IOException("No expiration time found for JWT token"); | ||
| } | ||
|
|
||
| return AccessToken.newBuilder() | ||
| .setTokenValue(tokenString) | ||
| .setExpirationTime(new Date(expTime * 1000L)) | ||
| .build(); | ||
| } | ||
|
|
||
| // using {@link MoreCallCredentials} adapter to be compatible with {@link CallCredentials} iface | ||
| public static CallCredentials create(String path) { | ||
| JwtTokenFileCallCredentials jwtTokenFileCallCredentials = new JwtTokenFileCallCredentials(path); | ||
| return MoreCallCredentials.from(jwtTokenFileCallCredentials); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,53 @@ | ||
| /* | ||
| * Copyright 2025 The gRPC Authors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package io.grpc.xds; | ||
|
|
||
| import io.grpc.CallCredentials; | ||
| import io.grpc.internal.JsonUtil; | ||
| import java.util.Map; | ||
| import java.util.logging.Level; | ||
| import java.util.logging.Logger; | ||
|
|
||
| /** | ||
| * A wrapper class that supports {@link JwtTokenFileXdsCallCredentialsProvider} for | ||
| * xDS by implementing {@link XdsCredentialsProvider}. | ||
| */ | ||
| public final class JwtTokenFileXdsCallCredentialsProvider extends XdsCallCredentialsProvider { | ||
| private static final Logger logger = Logger.getLogger( | ||
| JwtTokenFileXdsCallCredentialsProvider.class.getName()); | ||
| private static final String CREDS_NAME = "jwt_token_file"; | ||
|
|
||
| @Override | ||
| protected CallCredentials newCallCredentials(Map<String, ?> jsonConfig) { | ||
| if (jsonConfig == null) { | ||
| return null; | ||
| } | ||
|
|
||
| String jwtTokenPath = JsonUtil.getString(jsonConfig, getName()); | ||
| if (jwtTokenPath == null) { | ||
| logger.log(Level.WARNING, "jwt_token_file credential requires jwt_token_file in the config"); | ||
| return null; | ||
| } | ||
|
|
||
| return JwtTokenFileCallCredentials.create(jwtTokenPath); | ||
| } | ||
|
|
||
| @Override | ||
| protected String getName() { | ||
| return CREDS_NAME; | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,47 @@ | ||
| /* | ||
| * Copyright 2025 The gRPC Authors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package io.grpc.xds; | ||
|
|
||
| import io.grpc.CallCredentials; | ||
| import io.grpc.Internal; | ||
| import java.util.Map; | ||
|
|
||
| /** | ||
| * Provider of credentials data which will be propagated to the server for each RPC. The actual call | ||
| * credential to be used for a particular xDS communication will be chosen based on the bootstrap | ||
| * configuration. | ||
| */ | ||
| @Internal | ||
| public abstract class XdsCallCredentialsProvider { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not wild about
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Moved JWT classes to |
||
| /** | ||
| * Creates a {@link CallCredentials} from the given jsonConfig, or | ||
| * {@code null} if the given config is invalid. The provider is free to ignore | ||
| * the config if it's not needed for producing the call credentials. | ||
| * | ||
| * @param jsonConfig json config that can be consumed by the provider to create | ||
| * the call credentials | ||
| * | ||
| */ | ||
| protected abstract CallCredentials newCallCredentials(Map<String, ?> jsonConfig); | ||
|
|
||
| /** | ||
| * Returns the xDS call credential name associated with this provider which makes it selectable | ||
| * via {@link XdsCallCredentialsRegistry#getProvider}. This is called only when the class is | ||
| * loaded. It shouldn't change, and there is no point doing so. | ||
| */ | ||
| protected abstract String getName(); | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| /* | ||
| * Copyright 2025 The gRPC Authors | ||
| * | ||
| * Licensed under the Apache License, Version 2.0 (the "License"); | ||
| * you may not use this file except in compliance with the License. | ||
| * You may obtain a copy of the License at | ||
| * | ||
| * http://www.apache.org/licenses/LICENSE-2.0 | ||
| * | ||
| * Unless required by applicable law or agreed to in writing, software | ||
| * distributed under the License is distributed on an "AS IS" BASIS, | ||
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
| * See the License for the specific language governing permissions and | ||
| * limitations under the License. | ||
| */ | ||
|
|
||
| package io.grpc.xds; | ||
|
|
||
| import static com.google.common.base.Preconditions.checkNotNull; | ||
|
|
||
| import com.google.common.annotations.VisibleForTesting; | ||
| import java.util.HashMap; | ||
| import java.util.Map; | ||
| import javax.annotation.Nullable; | ||
| import javax.annotation.concurrent.ThreadSafe; | ||
|
|
||
| /** | ||
| * Registry of {@link XdsCallCredentialsProvider}s. The {@link #getDefaultRegistry default | ||
| * instance} loads hardcoded providers at runtime. | ||
| */ | ||
| @ThreadSafe | ||
| final class XdsCallCredentialsRegistry { | ||
| private static XdsCallCredentialsRegistry instance; | ||
|
|
||
| private final Map<String, XdsCallCredentialsProvider> registeredProviders = | ||
| new HashMap<>(); | ||
|
|
||
| /** | ||
| * Returns the default registry that loads hardcoded providers at runtime. | ||
| */ | ||
| public static synchronized XdsCallCredentialsRegistry getDefaultRegistry() { | ||
| if (instance == null) { | ||
| instance = newRegistry().register(new JwtTokenFileXdsCallCredentialsProvider()); | ||
| } | ||
| return instance; | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| static XdsCallCredentialsRegistry newRegistry() { | ||
| return new XdsCallCredentialsRegistry(); | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| XdsCallCredentialsRegistry register(XdsCallCredentialsProvider... providers) { | ||
| for (XdsCallCredentialsProvider provider : providers) { | ||
| registeredProviders.put(provider.getName(), provider); | ||
| } | ||
| return this; | ||
| } | ||
|
|
||
| @VisibleForTesting | ||
| synchronized Map<String, XdsCallCredentialsProvider> providers() { | ||
| return registeredProviders; | ||
| } | ||
|
|
||
| /** | ||
| * Returns the registered provider for the given xDS call credential name, or {@code null} if no | ||
| * suitable provider can be found. | ||
| * Each provider declares its name via {@link XdsCallCredentialsProvider#getName}. | ||
| */ | ||
| @Nullable | ||
| public synchronized XdsCallCredentialsProvider getProvider(String name) { | ||
| return registeredProviders.get(checkNotNull(name, "name")); | ||
| } | ||
| } |
Uh oh!
There was an error while loading. Please reload this page.