Skip to content

Commit d927b3e

Browse files
authored
Enable 'strictFunctionTypes' ts compiler option. (#675)
1 parent 8678922 commit d927b3e

File tree

4 files changed

+37
-19
lines changed

4 files changed

+37
-19
lines changed

src/auth/token-verifier.ts

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import {AuthClientErrorCode, FirebaseAuthError, ErrorInfo} from '../utils/error'
1919
import * as validator from '../utils/validator';
2020
import * as jwt from 'jsonwebtoken';
2121
import { HttpClient, HttpRequestConfig, HttpError } from '../utils/api-request';
22+
import { DecodedIdToken } from './auth';
2223

2324
// Audience to use for Firebase Auth Custom tokens
2425
const FIREBASE_AUDIENCE = 'https://identitytoolkit.googleapis.com/google.identity.identitytoolkit.v1.IdentityToolkit';
@@ -130,10 +131,10 @@ export class FirebaseTokenVerifier {
130131
* Verifies the format and signature of a Firebase Auth JWT token.
131132
*
132133
* @param {string} jwtToken The Firebase Auth JWT token to verify.
133-
* @return {Promise<object>} A promise fulfilled with the decoded claims of the Firebase Auth ID
134+
* @return {Promise<DecodedIdToken>} A promise fulfilled with the decoded claims of the Firebase Auth ID
134135
* token.
135136
*/
136-
public verifyJWT(jwtToken: string): Promise<object> {
137+
public verifyJWT(jwtToken: string): Promise<DecodedIdToken> {
137138
if (!validator.isString(jwtToken)) {
138139
throw new FirebaseAuthError(
139140
AuthClientErrorCode.INVALID_ARGUMENT,
@@ -224,16 +225,16 @@ export class FirebaseTokenVerifier {
224225
* Verifies the JWT signature using the provided public key.
225226
* @param {string} jwtToken The JWT token to verify.
226227
* @param {string} publicKey The public key certificate.
227-
* @return {Promise<object>} A promise that resolves with the decoded JWT claims on successful
228+
* @return {Promise<DecodedIdToken>} A promise that resolves with the decoded JWT claims on successful
228229
* verification.
229230
*/
230-
private verifyJwtSignatureWithKey(jwtToken: string, publicKey: string): Promise<object> {
231+
private verifyJwtSignatureWithKey(jwtToken: string, publicKey: string): Promise<DecodedIdToken> {
231232
const verifyJwtTokenDocsMessage = ` See ${this.tokenInfo.url} ` +
232233
`for details on how to retrieve ${this.shortNameArticle} ${this.tokenInfo.shortName}.`;
233234
return new Promise((resolve, reject) => {
234235
jwt.verify(jwtToken, publicKey, {
235236
algorithms: [this.algorithm],
236-
}, (error: jwt.VerifyErrors, decodedToken: any) => {
237+
}, (error: jwt.VerifyErrors, decodedToken: string | object) => {
237238
if (error) {
238239
if (error.name === 'TokenExpiredError') {
239240
const errorMessage = `${this.tokenInfo.jwtName} has expired. Get a fresh ${this.tokenInfo.shortName}` +
@@ -246,8 +247,19 @@ export class FirebaseTokenVerifier {
246247
}
247248
return reject(new FirebaseAuthError(AuthClientErrorCode.INVALID_ARGUMENT, error.message));
248249
} else {
249-
decodedToken.uid = decodedToken.sub;
250-
resolve(decodedToken);
250+
// TODO(rsgowman): I think the typing on jwt.verify is wrong. It claims that this can be either a string or an
251+
// object, but the code always seems to call it as an object. Investigate and upstream typing changes if this
252+
// is actually correct.
253+
if (typeof decodedToken === 'string') {
254+
return reject(new FirebaseAuthError(
255+
AuthClientErrorCode.INTERNAL_ERROR,
256+
"Unexpected decodedToken. Expected an object but got a string: '" + decodedToken + "'",
257+
));
258+
} else {
259+
const decodedIdToken = (decodedToken as DecodedIdToken);
260+
decodedIdToken.uid = decodedIdToken.sub;
261+
resolve(decodedIdToken);
262+
}
251263
}
252264
});
253265
});

src/messaging/messaging.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ import {
2121
Message, validateMessage, MessagingDevicesResponse,
2222
MessagingDeviceGroupResponse, MessagingTopicManagementResponse,
2323
MessagingPayload, MessagingOptions, MessagingTopicResponse,
24-
MessagingConditionResponse, BatchResponse, MulticastMessage,
24+
MessagingConditionResponse, BatchResponse, MulticastMessage, DataMessagePayload, NotificationMessagePayload,
2525
} from './messaging-types';
2626
import {FirebaseMessagingRequestHandler} from './messaging-api-request';
2727
import {FirebaseServiceInterface, FirebaseServiceInternalsInterface} from '../firebase-service';
@@ -758,9 +758,7 @@ export class Messaging implements FirebaseServiceInterface {
758758
);
759759
}
760760

761-
payloadKeys.forEach((payloadKey: keyof MessagingPayload) => {
762-
const value = payloadCopy[payloadKey];
763-
761+
const validatePayload = (payloadKey: string, value: DataMessagePayload | NotificationMessagePayload) => {
764762
// Validate each top-level key in the payload is an object
765763
if (!validator.isNonNullObject(value)) {
766764
throw new FirebaseMessagingError(
@@ -786,7 +784,14 @@ export class Messaging implements FirebaseServiceInterface {
786784
);
787785
}
788786
});
789-
});
787+
};
788+
789+
if (payloadCopy.data !== undefined) {
790+
validatePayload('data', payloadCopy.data);
791+
}
792+
if (payloadCopy.notification !== undefined) {
793+
validatePayload('notification', payloadCopy.notification);
794+
}
790795

791796
// Validate the data payload object does not contain blacklisted properties
792797
if ('data' in payloadCopy) {

test/unit/messaging/messaging.spec.ts

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,7 @@ import * as mocks from '../../resources/mocks';
2828

2929
import {FirebaseApp} from '../../../src/firebase-app';
3030
import {
31-
Message, MessagingOptions, MessagingPayload, MessagingDevicesResponse,
31+
Message, MessagingOptions, MessagingPayload, MessagingDevicesResponse, MessagingDeviceGroupResponse,
3232
MessagingTopicManagementResponse, BatchResponse, SendResponse, MulticastMessage,
3333
} from '../../../src/messaging/messaging-types';
3434
import {
@@ -1309,10 +1309,11 @@ describe('Messaging', () => {
13091309
mocks.messaging.registrationToken + '2',
13101310
],
13111311
mocks.messaging.payload,
1312-
).then((response: MessagingDevicesResponse) => {
1312+
).then((response: MessagingDevicesResponse | MessagingDeviceGroupResponse) => {
13131313
expect(response).to.have.keys([
13141314
'failureCount', 'successCount', 'canonicalRegistrationTokenCount', 'multicastId', 'results',
13151315
]);
1316+
response = response as MessagingDevicesResponse;
13161317
expect(response.failureCount).to.equal(2);
13171318
expect(response.successCount).to.equal(1);
13181319
expect(response.canonicalRegistrationTokenCount).to.equal(1);
@@ -2084,19 +2085,19 @@ describe('Messaging', () => {
20842085
invalidPayloads.forEach((invalidPayload) => {
20852086
it(`should throw given invalid type for payload argument: ${ JSON.stringify(invalidPayload) }`, () => {
20862087
expect(() => {
2087-
messaging.sendToDevice(mocks.messaging.registrationToken, invalidPayload as MessagingPayload);
2088+
messaging.sendToDevice(mocks.messaging.registrationToken, invalidPayload as any);
20882089
}).to.throw('Messaging payload must be an object');
20892090

20902091
expect(() => {
2091-
messaging.sendToDeviceGroup(mocks.messaging.notificationKey, invalidPayload as MessagingPayload);
2092+
messaging.sendToDeviceGroup(mocks.messaging.notificationKey, invalidPayload as any);
20922093
}).to.throw('Messaging payload must be an object');
20932094

20942095
expect(() => {
2095-
messaging.sendToTopic(mocks.messaging.topic, invalidPayload as MessagingPayload);
2096+
messaging.sendToTopic(mocks.messaging.topic, invalidPayload as any);
20962097
}).to.throw('Messaging payload must be an object');
20972098

20982099
expect(() => {
2099-
messaging.sendToCondition(mocks.messaging.condition, invalidPayload as MessagingPayload);
2100+
messaging.sendToCondition(mocks.messaging.condition, invalidPayload as any);
21002101
}).to.throw('Messaging payload must be an object');
21012102
});
21022103
});

tsconfig.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
"alwaysStrict": true,
1010
"strictBindCallApply": true,
1111
//"strictNullChecks": true,
12-
//"strictFunctionTypes": true,
12+
"strictFunctionTypes": true,
1313
//"strictPropertyInitialization": true,
1414
"lib": ["es2015"],
1515
"outDir": "lib",

0 commit comments

Comments
 (0)