Skip to content

Commit 86094a7

Browse files
fix(auth): Fix credentials issue in createToken() when using ADC (#2801)
* fix(auth): Fix service account issue in createToken() using ADC * fix tests
1 parent 69d6494 commit 86094a7

File tree

2 files changed

+22
-13
lines changed

2 files changed

+22
-13
lines changed

src/utils/crypto-signer.ts

+16-7
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@ import { AuthorizedHttpClient, HttpRequestConfig, HttpClient, RequestResponseErr
2222

2323
import { Algorithm } from 'jsonwebtoken';
2424
import { ErrorInfo } from '../utils/error';
25+
import * as utils from '../utils/index';
2526
import * as validator from '../utils/validator';
2627

2728
const ALGORITHM_RS256: Algorithm = 'RS256' as const;
@@ -105,22 +106,23 @@ export class IAMSigner implements CryptoSigner {
105106

106107
private readonly httpClient: AuthorizedHttpClient;
107108
private serviceAccountId?: string;
109+
private app?: App;
108110

109-
constructor(httpClient: AuthorizedHttpClient, serviceAccountId?: string) {
111+
constructor(httpClient: AuthorizedHttpClient, app?: App) {
110112
if (!httpClient) {
111113
throw new CryptoSignerError({
112114
code: CryptoSignerErrorCode.INVALID_ARGUMENT,
113115
message: 'INTERNAL ASSERT: Must provide a HTTP client to initialize IAMSigner.',
114116
});
115117
}
116-
if (typeof serviceAccountId !== 'undefined' && !validator.isNonEmptyString(serviceAccountId)) {
118+
if (app && (typeof app !== 'object' || app === null || !('options' in app))) {
117119
throw new CryptoSignerError({
118120
code: CryptoSignerErrorCode.INVALID_ARGUMENT,
119-
message: 'INTERNAL ASSERT: Service account ID must be undefined or a non-empty string.',
121+
message: 'INTERNAL ASSERT: Must provide a valid Firebase app instance.',
120122
});
121123
}
122124
this.httpClient = httpClient;
123-
this.serviceAccountId = serviceAccountId;
125+
this.app = app;
124126
}
125127

126128
/**
@@ -152,9 +154,16 @@ export class IAMSigner implements CryptoSigner {
152154
/**
153155
* @inheritDoc
154156
*/
155-
public getAccountId(): Promise<string> {
157+
public async getAccountId(): Promise<string> {
156158
if (validator.isNonEmptyString(this.serviceAccountId)) {
157-
return Promise.resolve(this.serviceAccountId);
159+
return this.serviceAccountId;
160+
}
161+
if (this.app) {
162+
const accountId = await utils.findServiceAccountEmail(this.app!)
163+
if (accountId) {
164+
this.serviceAccountId = accountId;
165+
return accountId;
166+
}
158167
}
159168
const request: HttpRequestConfig = {
160169
method: 'GET',
@@ -197,7 +206,7 @@ export function cryptoSignerFromApp(app: App): CryptoSigner {
197206
return new ServiceAccountSigner(credential);
198207
}
199208

200-
return new IAMSigner(new AuthorizedHttpClient(app as FirebaseApp), app.options.serviceAccountId);
209+
return new IAMSigner(new AuthorizedHttpClient(app as FirebaseApp), app);
201210
}
202211

203212
/**

test/unit/utils/crypto-signer.spec.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ describe('CryptoSigner', () => {
103103
const input = Buffer.from('input');
104104
const signRequest = {
105105
method: 'POST',
106-
url: 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/test-service-account:signBlob',
106+
url: 'https://iamcredentials.googleapis.com/v1/projects/-/serviceAccounts/foo@project_id.iam.gserviceaccount.com:signBlob',
107107
headers: {
108108
Authorization: `Bearer ${mockAccessToken}`,
109109
'X-Goog-Api-Client': getMetricsHeader()
@@ -120,7 +120,7 @@ describe('CryptoSigner', () => {
120120
const expectedResult = utils.responseFrom(response);
121121
stub = sinon.stub(HttpClient.prototype, 'send').resolves(expectedResult);
122122
const requestHandler = new AuthorizedHttpClient(mockApp);
123-
const signer = new IAMSigner(requestHandler, 'test-service-account');
123+
const signer = new IAMSigner(requestHandler, mockApp);
124124
return signer.sign(input).then((signature) => {
125125
expect(signature.toString('base64')).to.equal(response.signedBlob);
126126
expect(stub).to.have.been.calledOnce.and.calledWith(signRequest);
@@ -136,7 +136,7 @@ describe('CryptoSigner', () => {
136136
});
137137
stub = sinon.stub(HttpClient.prototype, 'send').rejects(expectedResult);
138138
const requestHandler = new AuthorizedHttpClient(mockApp);
139-
const signer = new IAMSigner(requestHandler, 'test-service-account');
139+
const signer = new IAMSigner(requestHandler, mockApp);
140140
return signer.sign(input).catch((err) => {
141141
expect(err).to.be.instanceOf(CryptoSignerError);
142142
expect(err.message).to.equal('Server responded with status 500.');
@@ -145,9 +145,9 @@ describe('CryptoSigner', () => {
145145
});
146146
});
147147

148-
it('should return the explicitly specified service account', () => {
149-
const signer = new IAMSigner(new AuthorizedHttpClient(mockApp), 'test-service-account');
150-
return signer.getAccountId().should.eventually.equal('test-service-account');
148+
it('should return the service account from the app', () => {
149+
const signer = new IAMSigner(new AuthorizedHttpClient(mockApp), mockApp);
150+
return signer.getAccountId().should.eventually.equal('foo@project_id.iam.gserviceaccount.com');
151151
});
152152
});
153153

0 commit comments

Comments
 (0)