Merge pull request #1400 from murgatroid99/grpc-js_internal_google_creds

grpc-js: Add internal "Google default" channel credentials
This commit is contained in:
Michael Lumish 2020-04-29 15:52:22 -07:00 committed by GitHub
commit 4db637e543
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 86 additions and 62 deletions

View File

@ -55,6 +55,7 @@
"posttest": "npm run check"
},
"dependencies": {
"google-auth-library": "^6.0.0",
"semver": "^6.2.0"
},
"files": [

View File

@ -26,6 +26,35 @@ export type CallMetadataGenerator = (
cb: (err: Error | null, metadata?: Metadata) => void
) => void;
// google-auth-library pre-v2.0.0 does not have getRequestHeaders
// but has getRequestMetadata, which is deprecated in v2.0.0
export interface OldOAuth2Client {
getRequestMetadata: (
url: string,
callback: (
err: Error | null,
headers?: {
[index: string]: string;
}
) => void
) => void;
}
export interface CurrentOAuth2Client {
getRequestHeaders: (url?: string) => Promise<{ [index: string]: string }>;
}
export type OAuth2Client = OldOAuth2Client | CurrentOAuth2Client;
function isCurrentOauth2Client(
client: OAuth2Client
): client is CurrentOAuth2Client {
return (
'getRequestHeaders' in client &&
typeof client.getRequestHeaders === 'function'
);
}
/**
* A class that represents a generic method of adding authentication-related
* metadata on a per-request basis.
@ -65,6 +94,47 @@ export abstract class CallCredentials {
return new SingleCallCredentials(metadataGenerator);
}
/**
* Create a gRPC credential from a Google credential object.
* @param googleCredentials The authentication client to use.
* @return The resulting CallCredentials object.
*/
static createFromGoogleCredential(
googleCredentials: OAuth2Client
): CallCredentials {
return CallCredentials.createFromMetadataGenerator((options, callback) => {
let getHeaders: Promise<{ [index: string]: string }>;
if (isCurrentOauth2Client(googleCredentials)) {
getHeaders = googleCredentials.getRequestHeaders(options.service_url);
} else {
getHeaders = new Promise((resolve, reject) => {
googleCredentials.getRequestMetadata(
options.service_url,
(err, headers) => {
if (err) {
reject(err);
return;
}
resolve(headers);
}
);
});
}
getHeaders.then(
(headers) => {
const metadata = new Metadata();
for (const key of Object.keys(headers)) {
metadata.add(key, headers[key]);
}
callback(null, metadata);
},
(err) => {
callback(err);
}
);
});
}
static createEmpty(): CallCredentials {
return new EmptyCallCredentials();
}

View File

@ -19,6 +19,7 @@ import { ConnectionOptions, createSecureContext, PeerCertificate } from 'tls';
import { CallCredentials } from './call-credentials';
import { CIPHER_SUITES, getDefaultRootsData } from './tls-helpers';
import { GoogleAuth as GoogleAuthType } from 'google-auth-library';
// eslint-disable-next-line @typescript-eslint/no-explicit-any
function verifyIsBufferOrNull(obj: any, friendlyName: string): void {
@ -278,3 +279,13 @@ class ComposedChannelCredentialsImpl extends ChannelCredentials {
}
}
}
export function createGoogleDefaultCredentials(): ChannelCredentials {
const GoogleAuth = require('google-auth-library')
.GoogleAuth as typeof GoogleAuthType;
const sslCreds = ChannelCredentials.createSsl();
const googleAuthCreds = CallCredentials.createFromGoogleCredential(
new GoogleAuth()
);
return sslCreds.compose(googleAuthCreds);
}

View File

@ -24,7 +24,7 @@ import {
ClientWritableStream,
ServiceError,
} from './call';
import { CallCredentials } from './call-credentials';
import { CallCredentials, OAuth2Client } from './call-credentials';
import { Deadline, StatusObject } from './call-stream';
import { Channel, ConnectivityState, ChannelImplementation } from './channel';
import { ChannelCredentials } from './channel-credentials';
@ -33,7 +33,7 @@ import {
Client,
CallInvocationTransformer,
CallProperties,
UnaryCallback
UnaryCallback,
} from './client';
import { LogVerbosity, Status } from './constants';
import * as logging from './logging';
@ -87,71 +87,13 @@ function mixin(...sources: IndexedObject[]) {
return result;
}
export interface OAuth2Client {
getRequestMetadata: (
url: string,
callback: (
err: Error | null,
headers?: {
[index: string]: string;
}
) => void
) => void;
getRequestHeaders: (url?: string) => Promise<{ [index: string]: string }>;
}
export { OAuth2Client };
/**** Client Credentials ****/
// Using assign only copies enumerable properties, which is what we want
export const credentials = mixin(
{
/**
* Create a gRPC credential from a Google credential object.
* @param googleCredentials The authentication client to use.
* @return The resulting CallCredentials object.
*/
createFromGoogleCredential: (
googleCredentials: OAuth2Client
): CallCredentials => {
return CallCredentials.createFromMetadataGenerator(
(options, callback) => {
// google-auth-library pre-v2.0.0 does not have getRequestHeaders
// but has getRequestMetadata, which is deprecated in v2.0.0
let getHeaders: Promise<{ [index: string]: string }>;
if (typeof googleCredentials.getRequestHeaders === 'function') {
getHeaders = googleCredentials.getRequestHeaders(
options.service_url
);
} else {
getHeaders = new Promise((resolve, reject) => {
googleCredentials.getRequestMetadata(
options.service_url,
(err, headers) => {
if (err) {
reject(err);
return;
}
resolve(headers);
}
);
});
}
getHeaders.then(
(headers) => {
const metadata = new Metadata();
for (const key of Object.keys(headers)) {
metadata.add(key, headers[key]);
}
callback(null, metadata);
},
(err) => {
callback(err);
}
);
}
);
},
/**
* Combine a ChannelCredentials with any number of CallCredentials into a
* single ChannelCredentials object.
@ -211,7 +153,7 @@ export {
CallInvocationTransformer,
ChannelImplementation as Channel,
Channel as ChannelInterface,
UnaryCallback as requestCallback
UnaryCallback as requestCallback,
};
/**