Skip to content

Commit

Permalink
feat: migrate to google-auth-library for pre-emptive oauth refresh (g…
Browse files Browse the repository at this point in the history
…oogleapis#3228)

Now that auth library bundles [pre-emptive refresh](googleapis/google-auth-library-java#646), RefreshingOAuth2CredentialsInterceptor can be removed. Also, remove retry on UNAUTHENTICATED
  • Loading branch information
mutianf committed Sep 20, 2022
1 parent 772d176 commit cccef65
Show file tree
Hide file tree
Showing 9 changed files with 58 additions and 1,160 deletions.
Expand Up @@ -47,11 +47,7 @@ public class RetryOptions implements Serializable, Cloneable {
/** For internal use only - public for technical reasons. */
@InternalApi("For internal usage only")
public static final Set<Status.Code> DEFAULT_ENABLE_GRPC_RETRIES_SET =
ImmutableSet.of(
Status.Code.DEADLINE_EXCEEDED,
Status.Code.UNAVAILABLE,
Status.Code.ABORTED,
Status.Code.UNAUTHENTICATED);
ImmutableSet.of(Status.Code.DEADLINE_EXCEEDED, Status.Code.UNAVAILABLE, Status.Code.ABORTED);

/**
* We can timeout when reading large cells with a low value here.
Expand Down
Expand Up @@ -29,7 +29,6 @@
import com.google.cloud.bigtable.config.BulkOptions;
import com.google.cloud.bigtable.config.CredentialOptions;
import com.google.cloud.bigtable.config.Logger;
import com.google.cloud.bigtable.config.RetryOptions;
import com.google.cloud.bigtable.core.IBigtableDataClient;
import com.google.cloud.bigtable.core.IBigtableTableAdminClient;
import com.google.cloud.bigtable.core.IBulkMutation;
Expand Down Expand Up @@ -392,10 +391,9 @@ private WatchdogInterceptor setupWatchdog() {
private static ClientInterceptor createAuthInterceptor(BigtableOptions options)
throws IOException {
CredentialInterceptorCache credentialsCache = CredentialInterceptorCache.getInstance();
RetryOptions retryOptions = options.getRetryOptions();
CredentialOptions credentialOptions = options.getCredentialOptions();
try {
return credentialsCache.getCredentialsInterceptor(credentialOptions, retryOptions);
return credentialsCache.getCredentialsInterceptor(credentialOptions);
} catch (GeneralSecurityException e) {
throw new IOException("Could not initialize credentials.", e);
}
Expand Down
Expand Up @@ -21,20 +21,13 @@
import com.google.cloud.bigtable.config.CredentialFactory;
import com.google.cloud.bigtable.config.CredentialOptions;
import com.google.cloud.bigtable.config.CredentialOptions.CredentialType;
import com.google.cloud.bigtable.config.RetryOptions;
import com.google.cloud.bigtable.util.ThreadUtil;
import com.google.common.util.concurrent.MoreExecutors;
import io.grpc.ClientInterceptor;
import io.grpc.auth.ClientAuthInterceptor;
import java.io.IOException;
import java.security.GeneralSecurityException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
* Caches {@link com.google.cloud.bigtable.grpc.io.RefreshingOAuth2CredentialsInterceptor} for
* default authorization cases. In other types of authorization, such as file based Credentials, it
* will create a new one.
* Caches {@link CredentialsInterceptor} for default authorization cases. In other types of
* authorization, such as file based Credentials, it will create a new one.
*
* <p>For internal use only - public for technical reasons.
*/
Expand All @@ -51,9 +44,6 @@ public static CredentialInterceptorCache getInstance() {
return instance;
}

private final ExecutorService executor =
Executors.newCachedThreadPool(ThreadUtil.getThreadFactory("Credentials-Refresh-%d", true));

private ClientInterceptor defaultCredentialInterceptor;

private CredentialInterceptorCache() {}
Expand All @@ -64,26 +54,24 @@ private CredentialInterceptorCache() {}
*
* <ol>
* <li>Look up the credentials
* <li>If there are credentials, create a gRPC interceptor that gets OAuth2 security tokens and
* add that token as a header on all calls. <br>
* NOTE: {@link com.google.cloud.bigtable.grpc.io.RefreshingOAuth2CredentialsInterceptor}
* ensures that the token stays fresh. It does token lookups asynchronously so that the
* calls themselves take as little performance penalty as possible.
* <li>If there are credentials, create a gRPC interceptor that adds the credentials to {@link
* io.grpc.CallOptions}. <br>
* NOTE: {@link OAuth2Credentials} ensures that the token stays fresh. It does token lookups
* asynchronously so that the calls themselves take as little performance penalty as
* possible.
* <li>Cache the interceptor in step #2 if the {@link
* com.google.cloud.bigtable.config.CredentialOptions} uses <a
* href="https://developers.google.com/identity/protocols/application-default-credentials">
* default application credentials </a>
* </ol>
*
* @param credentialOptions Defines how credentials should be achieved
* @param retryOptions a {@link com.google.cloud.bigtable.config.RetryOptions} object.
* @return a HeaderInterceptor
* @return a ClientInterceptor
* @throws java.io.IOException if any.
* @throws java.security.GeneralSecurityException if any.
*/
public synchronized ClientInterceptor getCredentialsInterceptor(
CredentialOptions credentialOptions, RetryOptions retryOptions)
throws IOException, GeneralSecurityException {
CredentialOptions credentialOptions) throws IOException, GeneralSecurityException {
// Default credentials is the most likely CredentialType. It's also the only CredentialType
// that can be safely cached.
boolean isDefaultCredentials =
Expand All @@ -99,34 +87,11 @@ public synchronized ClientInterceptor getCredentialsInterceptor(
return null;
}

// Optimization for asyncOAuth refresh
if (credentials instanceof OAuth2Credentials) {
RefreshingOAuth2CredentialsInterceptor oauth2Interceptor =
new RefreshingOAuth2CredentialsInterceptor(executor, (OAuth2Credentials) credentials);

// The RefreshingOAuth2CredentialsInterceptor uses the credentials to get a security token
// that
// will live for a short time. That token is added on all calls by the gRPC interceptor to
// allow users to access secure resources.
//
// Perform that token lookup asynchronously. This permits other work to be done in
// parallel. The RefreshingOAuth2CredentialsInterceptor has internal locking that assures that
// the oauth2 token is loaded before the interceptor proceeds with any calls.
oauth2Interceptor.asyncRefresh();
if (isDefaultCredentials) {
defaultCredentialInterceptor = oauth2Interceptor;
}
return oauth2Interceptor;
}

// Normal path
ClientInterceptor jwtAuthInterceptor =
new ClientAuthInterceptor(credentials, MoreExecutors.directExecutor());
CredentialsInterceptor credentialsInterceptor = new CredentialsInterceptor(credentials);

if (isDefaultCredentials) {
defaultCredentialInterceptor = jwtAuthInterceptor;
defaultCredentialInterceptor = credentialsInterceptor;
}

return jwtAuthInterceptor;
return credentialsInterceptor;
}
}
@@ -0,0 +1,43 @@
/*
* Copyright 2021 Google Inc. All Rights Reserved.
*
* 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 com.google.cloud.bigtable.grpc.io;

import com.google.auth.Credentials;
import io.grpc.CallCredentials;
import io.grpc.CallOptions;
import io.grpc.Channel;
import io.grpc.ClientCall;
import io.grpc.ClientInterceptor;
import io.grpc.ForwardingClientCall;
import io.grpc.MethodDescriptor;
import io.grpc.auth.MoreCallCredentials;

public class CredentialsInterceptor implements ClientInterceptor {

private CallCredentials callCredentials;

public CredentialsInterceptor(Credentials credentials) {
this.callCredentials = MoreCallCredentials.from(credentials);
}

@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
MethodDescriptor<ReqT, RespT> methodDescriptor, CallOptions callOptions, Channel channel) {
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(
channel.newCall(methodDescriptor, callOptions.withCallCredentials(callCredentials))) {};
}
}

0 comments on commit cccef65

Please sign in to comment.