feat: Async Refresh for Regional Access Boundaries#1880
feat: Async Refresh for Regional Access Boundaries#1880vverman wants to merge 5 commits intogoogleapis:feat-tb-safrom
Conversation
…pe. Updated tests.
…rors are now retryable.
oauth2_http/java/com/google/auth/http/HttpCredentialsAdapter.java
Outdated
Show resolved
Hide resolved
oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java
Show resolved
Hide resolved
oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundary.java
Outdated
Show resolved
Hide resolved
oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryManager.java
Outdated
Show resolved
Hide resolved
oauth2_http/java/com/google/auth/oauth2/RegionalAccessBoundaryProvider.java
Show resolved
Hide resolved
| private static final Pattern INVALID_TOKEN_ERROR = | ||
| Pattern.compile("\\s*error\\s*=\\s*\"?invalid_token\"?"); | ||
|
|
||
| private static final String STALE_RAB_ERROR_MESSAGE = "stale regional access boundary"; |
There was a problem hiding this comment.
Still waiting for the backend team to confirm the wording, we might need to update this based on their response. see b/450921839
| LOGGER_PROVIDER, | ||
| Level.INFO, | ||
| null, | ||
| "RAB lookup failed; entering cooldown for " |
There was a problem hiding this comment.
| "RAB lookup failed; entering cooldown for " | |
| "Regional Access Boundary lookup failed; entering cooldown for " |
nit: Spell out for user-facing error messages/ logs
| @@ -193,33 +228,41 @@ static TrustBoundary refresh( | |||
| .setInitialIntervalMillis(OAuth2Utils.INITIAL_RETRY_INTERVAL_MILLIS) | |||
| .setRandomizationFactor(OAuth2Utils.RETRY_RANDOMIZATION_FACTOR) | |||
| .setMultiplier(OAuth2Utils.RETRY_MULTIPLIER) | |||
| .setMaxElapsedTimeMillis(maxRetryElapsedTimeMillis) | |||
| .build(); | |||
|
|
|||
| HttpUnsuccessfulResponseHandler unsuccessfulResponseHandler = | |||
| new HttpBackOffUnsuccessfulResponseHandler(backoff); | |||
| new HttpBackOffUnsuccessfulResponseHandler(backoff) | |||
| .setBackOffRequired( | |||
| response -> { | |||
| int statusCode = response.getStatusCode(); | |||
| return statusCode == 500 | |||
| || statusCode == 502 | |||
| || statusCode == 503 | |||
| || statusCode == 504; | |||
| }); | |||
There was a problem hiding this comment.
qq, since the server-side will still make the RAB call if we have failure (e.g. no header is attached), can we have this call not have any retries and max duration of like 1-2s?
The reason I'm thinking this way is that the async RAB call uses ForkJoinPool's commonPool. I think we should try to time bound the IO requests that go to that executor pool since that is a JDK executor pool that is shared with the customer's application.
| public final RegionalAccessBoundary getRegionalAccessBoundary() { | ||
| return regionalAccessBoundaryManager.getCachedRAB(); | ||
| } |
There was a problem hiding this comment.
are the methods here intentional to be public (e.g. used by the customer)? Can they be package-private.
I know you mentioned the seeded RAB token is used by the customer
| final class TrustBoundary { | ||
| public final class RegionalAccessBoundary implements Serializable { | ||
|
|
||
| public static final String HEADER_KEY = "x-allowed-locations"; |
There was a problem hiding this comment.
nit: Can you give this a more descriptive constant name
e.g. perhaps ALLOWED_LOCATIONS_HEADER_KEY
| } | ||
|
|
||
| private static class CooldownState { | ||
| /** The time (in milliseconds) when the current cooldown period started. */ |
There was a problem hiding this comment.
nit:
| /** The time (in milliseconds) when the current cooldown period started. */ | |
| /** The time (milliseconds from epoch) when the current cooldown period started. */ |
| List<String> authHeaders = requestMetadata.get(AuthHttpConstants.AUTHORIZATION); | ||
| if (authHeaders != null && !authHeaders.isEmpty()) { | ||
| // Extract the token value to trigger a background Regional Access Boundary refresh. | ||
| String authHeader = authHeaders.get(0); | ||
| if (authHeader.startsWith(AuthHttpConstants.BEARER + " ")) { | ||
| String tokenValue = authHeader.substring((AuthHttpConstants.BEARER + " ").length()); | ||
| // Use a null expiration as JWTs are short-lived anyway. | ||
| AccessToken wrappedToken = new AccessToken(tokenValue, null); | ||
| refreshRegionalAccessBoundaryIfExpired(uri, wrappedToken); | ||
| } | ||
| } |
There was a problem hiding this comment.
Add a small comment to explain this custom logic here.
e.g. something like SSJWT's don't go through the typical Oauth2Credential getRequestMetadata() flow that triggers the async RAB
| metadata = new HashMap<>(metadata); | ||
| metadata.put( | ||
| RegionalAccessBoundary.HEADER_KEY, Collections.singletonList(rab.getEncodedLocations())); |
There was a problem hiding this comment.
Can you use the ImmuatableMap.Builder() from guava to build the new metadata
|
Thanks for the clarification @vverman! Would it be possible to split the stable RAB + seeding logic into a separate PR? |
Contains changes for the feature Regional Access Boundary (Previously Called Trust Boundaries).
The following are salient changes:
Calls to refresh RAB are now all async and in a separate thread.
Logic for refreshing RAB now exists in its own class for cleaner maintenance.
Self-signed jwts are within scope.
Changes to how we trigger RAB refresh and deal with refresh errors.