Skip to content

Commit 23868e8

Browse files
committed
chore: resolve comments
1 parent f603e65 commit 23868e8

File tree

7 files changed

+99
-120
lines changed

7 files changed

+99
-120
lines changed

src/firebase-namespace-api.ts

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
import { appCheck } from './app-check/app-check-namespace';
1818
import { auth } from './auth/auth-namespace';
19-
import { fpnv } from './fpnv/fpnv-namespace';
2019
import { database } from './database/database-namespace';
2120
import { firestore } from './firestore/firestore-namespace';
2221
import { instanceId } from './instance-id/instance-id-namespace';
@@ -44,7 +43,6 @@ export namespace app {
4443
export interface App extends AppCore {
4544
appCheck(): appCheck.AppCheck;
4645
auth(): auth.Auth;
47-
fpnv(): fpnv.Fpnv;
4846
database(url?: string): database.Database;
4947
firestore(): firestore.Firestore;
5048
installations(): installations.Installations;
@@ -83,7 +81,6 @@ export namespace app {
8381
export * from './credential/index';
8482
export { appCheck } from './app-check/app-check-namespace';
8583
export { auth } from './auth/auth-namespace';
86-
export { fpnv } from './fpnv/fpnv-namespace';
8784
export { database } from './database/database-namespace';
8885
export { firestore } from './firestore/firestore-namespace';
8986
export { instanceId } from './instance-id/instance-id-namespace';

src/fpnv/base-fpnv.ts

Lines changed: 0 additions & 23 deletions
This file was deleted.
Lines changed: 21 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*!
2-
* Copyright 2021 Google LLC
2+
* @license
3+
* Copyright 2025 Google LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
56
* you may not use this file except in compliance with the License.
@@ -14,26 +15,25 @@
1415
* limitations under the License.
1516
*/
1617

17-
import { App } from '../app';
18-
19-
// Import all public types with aliases, and re-export from the auth namespace.
20-
21-
import { Fpnv as TFpnv } from './fpnv';
22-
23-
import {
24-
BaseFpnv as TBaseFpnv,
25-
} from './base-fpnv';
26-
27-
import {
28-
FpnvToken as TFpnvToken,
29-
} from './token-verifier';
3018

19+
/**
20+
* Interface representing a Fpnv token.
21+
*/
22+
export interface FpnvToken {
23+
aud: string;
24+
auth_time: number;
25+
exp: number;
26+
iat: number;
27+
iss: string;
28+
sub: string;
29+
30+
getPhoneNumber(): string;
31+
32+
/**
33+
* Other arbitrary claims included in the ID token.
34+
*/
35+
[key: string]: any;
36+
}
3137

32-
export declare function fpnv(app?: App): fpnv.Fpnv;
38+
export {FpnvErrorCode, FirebasePnvError, ErrorInfo} from '../utils/error';
3339

34-
/* eslint-disable @typescript-eslint/no-namespace */
35-
export namespace fpnv {
36-
export type BaseFpnv = TBaseFpnv;
37-
export type Fpnv = TFpnv;
38-
export type FpnvToken = TFpnvToken;
39-
}

src/fpnv/fpnv.ts

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
/*!
22
* @license
3-
* Copyright 2017 Google LLC
3+
* Copyright 2025 Google LLC
44
*
55
* Licensed under the Apache License, Version 2.0 (the "License");
66
* you may not use this file except in compliance with the License.
@@ -16,26 +16,36 @@
1616
*/
1717

1818
import { App } from '../app';
19-
import { BaseFpnv } from './base-fpnv';
19+
import { FpnvToken } from './fpnv-api';
20+
import {
21+
FirebasePhoneNumberTokenVerifier,
22+
createFPNTVerifier,
23+
} from './token-verifier';
2024

2125
/**
2226
* Fpnv service bound to the provided app.
2327
*/
24-
export class Fpnv extends BaseFpnv {
28+
export class Fpnv {
2529
private readonly app_: App;
2630

31+
protected readonly fpnvVerifier: FirebasePhoneNumberTokenVerifier;
32+
2733
constructor(app: App) {
28-
super(app);
2934

3035
this.app_ = app;
36+
this.fpnvVerifier = createFPNTVerifier(app);
3137
}
3238

3339
/**
34-
* Returns the app associated with this Fpnv instance.
35-
*
36-
* @returns The app associated with this Fpnv instance.
37-
*/
40+
* Returns the app associated with this Auth instance.
41+
*
42+
* @returns The app associated with this Auth instance.
43+
*/
3844
get app(): App {
3945
return this.app_;
4046
}
47+
48+
public async verifyToken(idToken: string): Promise<FpnvToken> {
49+
return await this.fpnvVerifier.verifyJWT(idToken);
50+
}
4151
}

src/fpnv/index.ts

Lines changed: 2 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
/*!
2-
* Copyright 2020 Google LLC
2+
* @license
3+
* Copyright 2025 Google LLC
34
*
45
* Licensed under the Apache License, Version 2.0 (the "License");
56
* you may not use this file except in compliance with the License.
@@ -53,22 +54,3 @@ export function getFirebasePnv(app?: App): Fpnv {
5354
const firebaseApp: FirebaseApp = app as FirebaseApp;
5455
return firebaseApp.getOrInitService('fpnv', (app) => new Fpnv(app));
5556
}
56-
57-
export {
58-
Fpnv,
59-
} from './fpnv';
60-
61-
export {
62-
BaseFpnv,
63-
} from './base-fpnv';
64-
65-
66-
export {
67-
FpnvToken,
68-
} from './token-verifier';
69-
70-
71-
export {
72-
FirebasePnvError,
73-
FpnvErrorCode,
74-
} from '../utils/error';

src/fpnv/token-verifier.ts

Lines changed: 42 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,40 +1,43 @@
1+
/*!
2+
* @license
3+
* Copyright 2025 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
118
import { App } from '../app';
2-
import { FpnvErrorCode, ErrorInfo, FirebasePnvError } from '../utils/error';
19+
import { FpnvErrorCode, FirebasePnvError, ErrorInfo } from '../utils/error';
20+
import {FirebasePhoneNumberTokenInfo, FpnvToken} from './fpnv-api';
321
import * as util from '../utils/index';
422
import * as validator from '../utils/validator';
523
import {
624
DecodedToken, decodeJwt, JwtError, JwtErrorCode,
725
PublicKeySignatureVerifier, ALGORITHM_ES256, SignatureVerifier,
826
} from '../utils/jwt';
927

10-
export interface FpnvToken {
11-
aud: string;
12-
auth_time: number;
13-
exp: number;
14-
iat: number;
15-
iss: string;
16-
sub: string;
17-
18-
getPhoneNumber(): string;
19-
20-
/**
21-
* Other arbitrary claims included in the ID token.
22-
*/
23-
[key: string]: any;
24-
}
25-
2628
const CLIENT_CERT_URL = 'https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com';
2729

28-
export const PN_TOKEN_INFO: FirebasePhoneNumberTokenInfo = {
30+
31+
const PN_TOKEN_INFO: FirebasePhoneNumberTokenInfo = {
2932
url: 'https://firebase.google.com/docs/phone-number-verification',
3033
verifyApiName: 'verifyToken()',
3134
jwtName: 'Firebase Phone Verification token',
3235
shortName: 'FPNV token',
3336
typ: 'JWT',
34-
expiredErrorCode: FpnvErrorCode.COMMON_ISSUE,
37+
expiredErrorCode: FpnvErrorCode.EXPIRED_TOKEN,
3538
};
3639

37-
export interface FirebasePhoneNumberTokenInfo {
40+
interface FirebasePhoneNumberTokenInfo {
3841
/** Documentation URL. */
3942
url: string;
4043
/** verify API name. */
@@ -63,42 +66,42 @@ export class FirebasePhoneNumberTokenVerifier {
6366

6467
if (!validator.isURL(clientCertUrl)) {
6568
throw new FirebasePnvError(
66-
FpnvErrorCode.COMMON_ISSUE,
69+
FpnvErrorCode.INVALID_ARGUMENT,
6770
'The provided public client certificate URL is an invalid URL.',
6871
);
6972
} else if (!validator.isURL(issuer)) {
7073
throw new FirebasePnvError(
71-
FpnvErrorCode.COMMON_ISSUE,
74+
FpnvErrorCode.INVALID_ARGUMENT,
7275
'The provided JWT issuer is an invalid URL.',
7376
);
7477
} else if (!validator.isNonNullObject(tokenInfo)) {
7578
throw new FirebasePnvError(
76-
FpnvErrorCode.COMMON_ISSUE,
79+
FpnvErrorCode.INVALID_ARGUMENT,
7780
'The provided JWT information is not an object or null.',
7881
);
7982
} else if (!validator.isURL(tokenInfo.url)) {
8083
throw new FirebasePnvError(
81-
FpnvErrorCode.COMMON_ISSUE,
84+
FpnvErrorCode.INVALID_ARGUMENT,
8285
'The provided JWT verification documentation URL is invalid.',
8386
);
8487
} else if (!validator.isNonEmptyString(tokenInfo.verifyApiName)) {
8588
throw new FirebasePnvError(
86-
FpnvErrorCode.COMMON_ISSUE,
89+
FpnvErrorCode.INVALID_ARGUMENT,
8790
'The JWT verify API name must be a non-empty string.',
8891
);
8992
} else if (!validator.isNonEmptyString(tokenInfo.jwtName)) {
9093
throw new FirebasePnvError(
91-
FpnvErrorCode.COMMON_ISSUE,
94+
FpnvErrorCode.INVALID_ARGUMENT,
9295
'The JWT public full name must be a non-empty string.',
9396
);
9497
} else if (!validator.isNonEmptyString(tokenInfo.shortName)) {
9598
throw new FirebasePnvError(
96-
FpnvErrorCode.COMMON_ISSUE,
99+
FpnvErrorCode.INVALID_ARGUMENT,
97100
'The JWT public short name must be a non-empty string.',
98101
);
99102
} else if (!validator.isNonNullObject(tokenInfo.expiredErrorCode) || !('code' in tokenInfo.expiredErrorCode)) {
100103
throw new FirebasePnvError(
101-
FpnvErrorCode.COMMON_ISSUE,
104+
FpnvErrorCode.INVALID_ARGUMENT,
102105
'The JWT expiration error code must be a non-null ErrorInfo object.',
103106
);
104107
}
@@ -113,7 +116,7 @@ export class FirebasePhoneNumberTokenVerifier {
113116
public async verifyJWT(jwtToken: string): Promise<FpnvToken> {
114117
if (!validator.isString(jwtToken)) {
115118
throw new FirebasePnvError(
116-
FpnvErrorCode.COMMON_ISSUE,
119+
FpnvErrorCode.PROJECT_NOT_FOUND,
117120
`First argument to ${this.tokenInfo.verifyApiName} must be a ${this.tokenInfo.jwtName} string.`,
118121
);
119122
}
@@ -129,7 +132,7 @@ export class FirebasePhoneNumberTokenVerifier {
129132
const projectId = await util.findProjectId(this.app);
130133
if (!validator.isNonEmptyString(projectId)) {
131134
throw new FirebasePnvError(
132-
FpnvErrorCode.COMMON_ISSUE,
135+
FpnvErrorCode.PROJECT_NOT_FOUND,
133136
'Must initialize app with a cert credential or set your Firebase project ID as the ' +
134137
`GOOGLE_CLOUD_PROJECT environment variable to call ${this.tokenInfo.verifyApiName}.`);
135138
}
@@ -156,10 +159,10 @@ export class FirebasePhoneNumberTokenVerifier {
156159
const errorMessage = `Decoding ${this.tokenInfo.jwtName} failed. Make sure you passed ` +
157160
`the entire string JWT which represents ${this.shortNameArticle} ` +
158161
`${this.tokenInfo.shortName}.` + verifyJwtTokenDocsMessage;
159-
throw new FirebasePnvError(FpnvErrorCode.COMMON_ISSUE,
162+
throw new FirebasePnvError(FpnvErrorCode.INVALID_ARGUMENT,
160163
errorMessage);
161164
}
162-
throw new FirebasePnvError(FpnvErrorCode.COMMON_ISSUE, err.message);
165+
throw new FirebasePnvError(FpnvErrorCode.INVALID_ARGUMENT, err.message);
163166
}
164167
}
165168

@@ -183,14 +186,14 @@ export class FirebasePhoneNumberTokenVerifier {
183186
errorMessage = `${this.tokenInfo.jwtName} has no "kid" claim.`;
184187
errorMessage += verifyJwtTokenDocsMessage;
185188
} else if (header.alg !== ALGORITHM_ES256) {
186-
errorMessage = `${this.tokenInfo.jwtName} has incorrect algorithm. Expected "` + ALGORITHM_ES256 + '" but got ' +
187-
'"' + header.alg + '".' + verifyJwtTokenDocsMessage;
189+
errorMessage = `${this.tokenInfo.jwtName} has incorrect algorithm. Expected ` +
190+
`"${ALGORITHM_ES256}" but got "${header.alg}". ${verifyJwtTokenDocsMessage}`;
188191
} else if (header.typ !== this.tokenInfo.typ) {
189192
errorMessage = `${this.tokenInfo.jwtName} has incorrect typ. Expected "${this.tokenInfo.typ}" but got ` +
190193
'"' + header.typ + '".' + verifyJwtTokenDocsMessage;
191194
}
192195
// FPNV Token
193-
else if (!((payload.aud as string[]).some(item => item === this.issuer + projectId))) {
196+
else if (!((Array.isArray(payload.aud) ? payload.aud : []).some(item => item === this.issuer + projectId))) {
194197
errorMessage = `${this.tokenInfo.jwtName} has incorrect "aud" (audience) claim. Expected "` +
195198
this.issuer + projectId + '" to be one of "' + payload.aud + '".' + projectIdMatchMessage +
196199
verifyJwtTokenDocsMessage;
@@ -202,7 +205,7 @@ export class FirebasePhoneNumberTokenVerifier {
202205
}
203206

204207
if (errorMessage) {
205-
throw new FirebasePnvError(FpnvErrorCode.COMMON_ISSUE, errorMessage);
208+
throw new FirebasePnvError(FpnvErrorCode.INVALID_ARGUMENT, errorMessage);
206209
}
207210
}
208211

@@ -224,14 +227,14 @@ export class FirebasePhoneNumberTokenVerifier {
224227
return new FirebasePnvError(this.tokenInfo.expiredErrorCode, errorMessage);
225228
} else if (error.code === JwtErrorCode.INVALID_SIGNATURE) {
226229
const errorMessage = `${this.tokenInfo.jwtName} has invalid signature.` + verifyJwtTokenDocsMessage;
227-
return new FirebasePnvError(FpnvErrorCode.COMMON_ISSUE, errorMessage);
230+
return new FirebasePnvError(FpnvErrorCode.INVALID_ARGUMENT, errorMessage);
228231
} else if (error.code === JwtErrorCode.NO_MATCHING_KID) {
229232
const errorMessage = `${this.tokenInfo.jwtName} has "kid" claim which does not ` +
230233
`correspond to a known public key. Most likely the ${this.tokenInfo.shortName} ` +
231234
'is expired, so get a fresh token from your client app and try again.';
232-
return new FirebasePnvError(FpnvErrorCode.COMMON_ISSUE, errorMessage);
235+
return new FirebasePnvError(FpnvErrorCode.INVALID_ARGUMENT, errorMessage);
233236
}
234-
return new FirebasePnvError(FpnvErrorCode.COMMON_ISSUE, error.message);
237+
return new FirebasePnvError(FpnvErrorCode.INVALID_ARGUMENT, error.message);
235238
}
236239

237240
}

0 commit comments

Comments
 (0)