Skip to content
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

feat: Make full implementation on DownscopedClient. #1232

Merged
merged 5 commits into from
Sep 2, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
80 changes: 76 additions & 4 deletions src/auth/downscopedclient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,13 @@
// See the License for the specific language governing permissions and
// limitations under the License.

import {GaxiosOptions, GaxiosPromise, GaxiosResponse} from 'gaxios';
import {
GaxiosError,
GaxiosOptions,
GaxiosPromise,
GaxiosResponse,
} from 'gaxios';
import * as stream from 'stream';

import {BodyResponseCallback} from '../transporters';
import {Credentials} from './credentials';
Expand Down Expand Up @@ -122,11 +128,14 @@ export class DownscopedClient extends AuthClient {
* @param additionalOptions Optional additional behavior customization
* options. These currently customize expiration threshold time and
* whether to retry on 401/403 API request errors.
* @param quotaProjectId Optional quota project id for setting up in the
* x-goog-user-project header.
*/
constructor(
private readonly authClient: AuthClient,
private readonly credentialAccessBoundary: CredentialAccessBoundary,
additionalOptions?: RefreshOptions
additionalOptions?: RefreshOptions,
quotaProjectId?: string
) {
super();
// Check 1-10 Access Boundary Rules are defined within Credential Access
Expand Down Expand Up @@ -168,6 +177,7 @@ export class DownscopedClient extends AuthClient {
.eagerRefreshThresholdMillis as number;
}
this.forceRefreshOnFailure = !!additionalOptions?.forceRefreshOnFailure;
this.quotaProjectId = quotaProjectId;
}

/**
Expand Down Expand Up @@ -214,7 +224,11 @@ export class DownscopedClient extends AuthClient {
* { Authorization: 'Bearer <access_token_value>' }
*/
async getRequestHeaders(): Promise<Headers> {
throw new Error('Not implemented.');
const accessTokenResponse = await this.getAccessToken();
const headers: Headers = {
Authorization: `Bearer ${accessTokenResponse.token}`,
};
return this.addSharedMetadataHeaders(headers);
}

/**
Expand All @@ -232,7 +246,65 @@ export class DownscopedClient extends AuthClient {
opts: GaxiosOptions,
callback?: BodyResponseCallback<T>
): GaxiosPromise<T> | void {
throw new Error('Not implemented.');
if (callback) {
this.requestAsync<T>(opts).then(
r => callback(null, r),
e => {
return callback(e, e.response);
}
);
} else {
return this.requestAsync<T>(opts);
}
}

/**
* Authenticates the provided HTTP request, processes it and resolves with the
* returned response.
* @param opts The HTTP request options.
* @param retry Whether the current attempt is a retry after a failed attempt.
* @return A promise that resolves with the successful response.
*/
protected async requestAsync<T>(
opts: GaxiosOptions,
retry = false
): Promise<GaxiosResponse<T>> {
xil222 marked this conversation as resolved.
Show resolved Hide resolved
let response: GaxiosResponse;
try {
const requestHeaders = await this.getRequestHeaders();
opts.headers = opts.headers || {};
if (requestHeaders && requestHeaders['x-goog-user-project']) {
opts.headers['x-goog-user-project'] =
requestHeaders['x-goog-user-project'];
}
if (requestHeaders && requestHeaders.Authorization) {
opts.headers.Authorization = requestHeaders.Authorization;
}
response = await this.transporter.request<T>(opts);
} catch (e) {
const res = (e as GaxiosError).response;
if (res) {
const statusCode = res.status;
// Retry the request for metadata if the following criteria are true:
// - We haven't already retried. It only makes sense to retry once.
// - The response was a 401 or a 403
// - The request didn't send a readableStream
// - forceRefreshOnFailure is true
const isReadableStream = res.config.data instanceof stream.Readable;
const isAuthErr = statusCode === 401 || statusCode === 403;
if (
!retry &&
isAuthErr &&
!isReadableStream &&
this.forceRefreshOnFailure
) {
await this.refreshAccessTokenAsync();
return await this.requestAsync<T>(opts, true);
}
}
throw e;
}
return response;
}

/**
Expand Down
4 changes: 4 additions & 0 deletions src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,10 @@ export {
BaseExternalAccountClient,
BaseExternalAccountClientOptions,
} from './auth/baseexternalclient';
export {
CredentialAccessBoundary,
DownscopedClient,
} from './auth/downscopedclient';
xil222 marked this conversation as resolved.
Show resolved Hide resolved
export {DefaultTransporter} from './transporters';

const auth = new GoogleAuth();
Expand Down