Skip to content

Commit 530f36f

Browse files
On Demand Rides and Deliveriescopybara-github
authored andcommitted
feat: use CallCredentials to add request header for better error messages
PiperOrigin-RevId: 468318869
1 parent 178382d commit 530f36f

4 files changed

Lines changed: 87 additions & 51 deletions

File tree

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright 2021 Google LLC
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package com.google.fleetengine.auth.client;
16+
17+
import com.google.fleetengine.auth.token.FleetEngineToken;
18+
import io.grpc.CallCredentials;
19+
import io.grpc.Metadata;
20+
import io.grpc.Status;
21+
import java.util.concurrent.Executor;
22+
23+
/** Adds an athorization header containing a Fleet Engine JWT to the request metadata. */
24+
public class FleetEngineAuthCallCredentials extends CallCredentials {
25+
26+
/** Authorization header name. */
27+
private static final Metadata.Key<String> AUTHORIZATION_HEADER =
28+
Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
29+
30+
private final FleetEngineTokenProvider tokenProvider;
31+
32+
public static FleetEngineAuthCallCredentials create(FleetEngineTokenProvider tokenProvider) {
33+
return new FleetEngineAuthCallCredentials(tokenProvider);
34+
}
35+
36+
private FleetEngineAuthCallCredentials(FleetEngineTokenProvider tokenProvider) {
37+
this.tokenProvider = tokenProvider;
38+
}
39+
40+
@Override
41+
public void applyRequestMetadata(
42+
RequestInfo requestInfo, Executor appExecutor, CallCredentials.MetadataApplier applier) {
43+
Metadata headers = new Metadata();
44+
try {
45+
FleetEngineToken token = tokenProvider.getSignedToken();
46+
headers.put(AUTHORIZATION_HEADER, String.format("Bearer %s", token.jwt()));
47+
} catch (Exception e) {
48+
applier.fail(Status.UNAUTHENTICATED.withDescription("Unable to create token").withCause(e));
49+
return;
50+
}
51+
52+
applier.apply(headers);
53+
}
54+
55+
@Override
56+
public void thisUsesUnstableApi() {}
57+
}

src/main/java/com/google/fleetengine/auth/client/FleetEngineAuthClientInterceptor.java

Lines changed: 10 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,19 +14,15 @@
1414

1515
package com.google.fleetengine.auth.client;
1616

17-
import com.google.common.annotations.VisibleForTesting;
18-
import com.google.fleetengine.auth.token.FleetEngineToken;
19-
import com.google.fleetengine.auth.token.factory.signer.SigningTokenException;
2017
import io.grpc.CallOptions;
2118
import io.grpc.Channel;
2219
import io.grpc.ClientCall;
2320
import io.grpc.ClientInterceptor;
24-
import io.grpc.ForwardingClientCall;
25-
import io.grpc.Metadata;
2621
import io.grpc.MethodDescriptor;
2722

2823
/**
29-
* Intercepts an outgoing gRPC request and attaches a valid Fleet Engine JWT to the header.
24+
* Intercepts an outgoing gRPC request and attaches a valid Fleet Engine JWT to the header by using
25+
* {@link FleetEngineAuthCallCredentials}.
3026
*
3127
* <p>Works with generated gRPC stubby classes:
3228
*
@@ -37,11 +33,7 @@
3733
*/
3834
public class FleetEngineAuthClientInterceptor implements ClientInterceptor {
3935

40-
/** Authorization header name. */
41-
private static final Metadata.Key<String> AUTHORIZATION_HEADER =
42-
Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
43-
44-
private final FleetEngineTokenProvider tokenProvider;
36+
private final FleetEngineAuthCallCredentials callCredentials;
4537

4638
/**
4739
* Creates a gRPC client interceptor that attaches tokens from {@code tokenProvider} to outgoing
@@ -50,39 +42,22 @@ public class FleetEngineAuthClientInterceptor implements ClientInterceptor {
5042
* @param tokenProvider provides valid Fleet Engine JWTs for an outgoing gRPC request.
5143
*/
5244
public static FleetEngineAuthClientInterceptor create(FleetEngineTokenProvider tokenProvider) {
53-
return new FleetEngineAuthClientInterceptor(tokenProvider);
45+
return new FleetEngineAuthClientInterceptor(
46+
FleetEngineAuthCallCredentials.create(tokenProvider));
5447
}
5548

5649
/** Constructor. */
57-
private FleetEngineAuthClientInterceptor(FleetEngineTokenProvider tokenProvider) {
58-
this.tokenProvider = tokenProvider;
50+
private FleetEngineAuthClientInterceptor(FleetEngineAuthCallCredentials callCredentials) {
51+
this.callCredentials = callCredentials;
5952
}
6053

6154
/**
62-
* Overrides {@link ClientInterceptor#interceptCall(MethodDescriptor, CallOptions, Channel)} and
63-
* attaches a Fleet Engine JWT to the header of the outgoing gRPC request.
55+
* Returns a new call with the {@link FleetEngineAuthCallCredentials} added to the {@code
56+
* callOptions}.
6457
*/
6558
@Override
6659
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(
6760
MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
68-
ClientCall<ReqT, RespT> call = next.newCall(method, callOptions);
69-
return new ForwardingClientCall.SimpleForwardingClientCall<ReqT, RespT>(call) {
70-
@Override
71-
public void start(Listener<RespT> responseListener, Metadata headers) {
72-
addAuthorizationHeader(headers);
73-
super.start(responseListener, headers);
74-
}
75-
};
76-
}
77-
78-
/** Adds the signed base64 encode JWT to the header. */
79-
@VisibleForTesting
80-
void addAuthorizationHeader(Metadata headers) {
81-
try {
82-
FleetEngineToken token = tokenProvider.getSignedToken();
83-
headers.put(AUTHORIZATION_HEADER, String.format("Bearer %s", token.jwt()));
84-
} catch (SigningTokenException e) {
85-
throw new WritingAuthorizationHeaderException("Exception while getting token.", e);
86-
}
61+
return next.newCall(method, callOptions.withCallCredentials(this.callCredentials));
8762
}
8863
}

src/main/java/com/google/fleetengine/auth/client/WritingAuthorizationHeaderException.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package com.google.fleetengine.auth.client;
1616

1717
/** Signals that an exception occurred while writing the authorization header. */
18+
@Deprecated
1819
public class WritingAuthorizationHeaderException extends RuntimeException {
1920

2021
/**

src/test/java/com/google/fleetengine/auth/client/FleetEngineAuthClientInterceptorTest.java renamed to src/test/java/com/google/fleetengine/auth/client/FleetEngineAuthCallCredentialsTest.java

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -15,39 +15,41 @@
1515
package com.google.fleetengine.auth.client;
1616

1717
import static com.google.common.truth.Truth.assertThat;
18+
import static org.mockito.ArgumentMatchers.any;
1819
import static org.mockito.Mockito.mock;
20+
import static org.mockito.Mockito.verify;
1921
import static org.mockito.Mockito.when;
2022

2123
import com.google.fleetengine.auth.EmptyFleetEngineTokenClaims;
2224
import com.google.fleetengine.auth.token.FleetEngineToken;
2325
import com.google.fleetengine.auth.token.FleetEngineTokenType;
2426
import com.google.fleetengine.auth.token.factory.signer.SigningTokenException;
27+
import io.grpc.CallCredentials;
2528
import io.grpc.Metadata;
2629
import java.time.Instant;
2730
import java.util.Date;
28-
import org.junit.Assert;
2931
import org.junit.Before;
3032
import org.junit.Test;
3133
import org.junit.runner.RunWith;
3234
import org.junit.runners.JUnit4;
35+
import org.mockito.ArgumentCaptor;
3336
import org.mockito.Mock;
3437
import org.mockito.MockitoAnnotations;
3538

3639
@RunWith(JUnit4.class)
37-
public class FleetEngineAuthClientInterceptorTest {
40+
public class FleetEngineAuthCallCredentialsTest {
3841

3942
private static final Metadata.Key<String> AUTHORIZATION_HEADER =
4043
Metadata.Key.of("authorization", Metadata.ASCII_STRING_MARSHALLER);
4144
private static final String FAKE_JWT = "fake.jwt.token";
4245
private static final String FAKE_AUTHORIZATION_HEADER = String.format("Bearer %s", FAKE_JWT);
43-
private Metadata headers = new Metadata();
4446
@Mock private FleetEngineTokenProvider tokenProvider;
47+
@Mock private CallCredentials.MetadataApplier applier;
4548
private FleetEngineToken fleetEngineToken;
4649

4750
@Before
4851
public void setUp() {
4952
MockitoAnnotations.openMocks(this);
50-
headers = new Metadata();
5153

5254
fleetEngineToken =
5355
FleetEngineToken.builder()
@@ -59,27 +61,28 @@ public void setUp() {
5961
}
6062

6163
@Test
62-
public void addAuthorizationHeader_addsHeaderCorrectly() throws SigningTokenException {
64+
public void applyRequestMetadata_addsHeaderCorrectly() throws SigningTokenException {
6365
fleetEngineToken = fleetEngineToken.toBuilder().setJwt(FAKE_JWT).build();
6466
when(tokenProvider.getSignedToken()).thenReturn(fleetEngineToken);
65-
FleetEngineAuthClientInterceptor clientInterceptor =
66-
FleetEngineAuthClientInterceptor.create(this.tokenProvider);
67+
FleetEngineAuthCallCredentials callCredentials =
68+
FleetEngineAuthCallCredentials.create(this.tokenProvider);
6769

68-
clientInterceptor.addAuthorizationHeader(headers);
70+
callCredentials.applyRequestMetadata(null, null, applier);
6971

70-
assertThat(headers.get(AUTHORIZATION_HEADER)).isEqualTo(FAKE_AUTHORIZATION_HEADER);
72+
ArgumentCaptor<Metadata> metadataCaptor = ArgumentCaptor.forClass(Metadata.class);
73+
verify(applier).apply(metadataCaptor.capture());
74+
assertThat(metadataCaptor.getValue().get(AUTHORIZATION_HEADER))
75+
.isEqualTo(FAKE_AUTHORIZATION_HEADER);
7176
}
7277

7378
@Test
74-
public void addAuthorizationHeader_onException_throwsException() throws SigningTokenException {
79+
public void applyRequestMetadata_onException_callsDail() throws SigningTokenException {
7580
fleetEngineToken = fleetEngineToken.toBuilder().setJwt(FAKE_JWT).build();
7681
when(tokenProvider.getSignedToken()).thenThrow(mock(SigningTokenException.class));
82+
FleetEngineAuthCallCredentials callCredentials =
83+
FleetEngineAuthCallCredentials.create(this.tokenProvider);
7784

78-
FleetEngineAuthClientInterceptor clientInterceptor =
79-
FleetEngineAuthClientInterceptor.create(this.tokenProvider);
80-
81-
Assert.assertThrows(
82-
WritingAuthorizationHeaderException.class,
83-
() -> clientInterceptor.addAuthorizationHeader(headers));
85+
callCredentials.applyRequestMetadata(null, null, applier);
86+
verify(applier).fail(any());
8487
}
8588
}

0 commit comments

Comments
 (0)