diff --git a/src/app-check/app-check-api-client-internal.ts b/src/app-check/app-check-api-client-internal.ts index 08f016ed30..8d25e23cf7 100644 --- a/src/app-check/app-check-api-client-internal.ts +++ b/src/app-check/app-check-api-client-internal.ts @@ -28,7 +28,7 @@ import * as validator from '../utils/validator'; import AppCheckToken = appCheck.AppCheckToken; // App Check backend constants -const FIREBASE_APP_CHECK_V1_API_URL_FORMAT = 'https://firebaseappcheck.googleapis.com/v1alpha/projects/{projectId}/apps/{appId}:exchangeCustomToken'; +const FIREBASE_APP_CHECK_V1_API_URL_FORMAT = 'https://firebaseappcheck.googleapis.com/v1beta/projects/{projectId}/apps/{appId}:exchangeCustomToken'; const FIREBASE_APP_CHECK_CONFIG_HEADERS = { 'X-Firebase-Client': `fire-admin-node/${utils.getSdkVersion()}` @@ -147,9 +147,9 @@ export class AppCheckApiClient { */ private toAppCheckToken(resp: HttpResponse): AppCheckToken { const token = resp.data.attestationToken; - // `timeToLive` is a string with the suffix "s" preceded by the number of seconds, + // `ttl` is a string with the suffix "s" preceded by the number of seconds, // with nanoseconds expressed as fractional seconds. - const ttlMillis = this.stringToMilliseconds(resp.data.timeToLive); + const ttlMillis = this.stringToMilliseconds(resp.data.ttl); return { token, ttlMillis @@ -169,7 +169,7 @@ export class AppCheckApiClient { private stringToMilliseconds(duration: string): number { if (!validator.isNonEmptyString(duration) || !duration.endsWith('s')) { throw new FirebaseAppCheckError( - 'invalid-argument', '`timeToLive` must be a valid duration string with the suffix `s`.'); + 'invalid-argument', '`ttl` must be a valid duration string with the suffix `s`.'); } const seconds = duration.slice(0, -1); return Math.floor(Number(seconds) * 1000); diff --git a/src/app-check/token-generator.ts b/src/app-check/token-generator.ts index bafdbe1ca3..1b557438bb 100644 --- a/src/app-check/token-generator.ts +++ b/src/app-check/token-generator.ts @@ -29,7 +29,7 @@ import { HttpError } from '../utils/api-request'; const ONE_HOUR_IN_SECONDS = 60 * 60; // Audience to use for Firebase App Check Custom tokens -const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1alpha.TokenExchangeService'; +const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1beta.TokenExchangeService'; /** * Class for generating Firebase App Check tokens. diff --git a/src/app-check/token-verifier.ts b/src/app-check/token-verifier.ts index f1b898c460..318a1fd10b 100644 --- a/src/app-check/token-verifier.ts +++ b/src/app-check/token-verifier.ts @@ -27,7 +27,7 @@ import { import DecodedAppCheckToken = appCheck.DecodedAppCheckToken; const APP_CHECK_ISSUER = 'https://firebaseappcheck.googleapis.com/'; -const JWKS_URL = 'https://firebaseappcheck.googleapis.com/v1alpha/jwks'; +const JWKS_URL = 'https://firebaseappcheck.googleapis.com/v1beta/jwks'; /** * Class for verifying Firebase App Check tokens. diff --git a/test/integration/app-check.spec.ts b/test/integration/app-check.spec.ts index 387d441860..32386f32bc 100644 --- a/test/integration/app-check.spec.ts +++ b/test/integration/app-check.spec.ts @@ -70,4 +70,34 @@ describe('admin.appCheck', () => { }); }); }); + + describe('verifyToken', () => { + let validToken: admin.appCheck.AppCheckToken; + + before(async () => { + if (!appId) { + return; + } + // obtain a valid app check token + validToken = await admin.appCheck().createToken(appId as string); + }); + + it('should succeed with a decoded verifed token response', function() { + if (!appId) { + this.skip(); + } + return admin.appCheck().verifyToken(validToken.token) + .then((verifedToken) => { + expect(verifedToken).to.have.keys(['token', 'appId']); + expect(verifedToken.token).to.have.keys(['iss', 'sub', 'aud', 'exp', 'iat', 'app_id']); + expect(verifedToken.token.app_id).to.be.a('string').and.equals(appId); + }); + }); + + it('should propagate API errors', () => { + // rejects with invalid-argument when the token is invalid + return admin.appCheck().verifyToken('invalid-token') + .should.eventually.be.rejected.and.have.property('code', 'app-check/invalid-argument'); + }); + }); }); diff --git a/test/unit/app-check/app-check-api-client-internal.spec.ts b/test/unit/app-check/app-check-api-client-internal.spec.ts index 10912b21f5..fba1fba20d 100644 --- a/test/unit/app-check/app-check-api-client-internal.spec.ts +++ b/test/unit/app-check/app-check-api-client-internal.spec.ts @@ -57,7 +57,7 @@ describe('AppCheckApiClient', () => { const TEST_RESPONSE = { attestationToken: 'token', - timeToLive: '3s' + ttl: '3s' }; const mockOptions = { @@ -182,15 +182,15 @@ describe('AppCheckApiClient', () => { ['', 'abc', '3s2', 'sssa', '3.000000001', '3.2', null, NaN, true, [], {}, 100, 1.2, -200, -2.4] .forEach((invalidDuration) => { - it(`should throw if the returned timeToLive duration is: ${invalidDuration}`, () => { + it(`should throw if the returned ttl duration is: ${invalidDuration}`, () => { const response = deepCopy(TEST_RESPONSE); - (response as any).timeToLive = invalidDuration; + (response as any).ttl = invalidDuration; const stub = sinon .stub(HttpClient.prototype, 'send') .resolves(utils.responseFrom(response, 200)); stubs.push(stub); const expected = new FirebaseAppCheckError( - 'invalid-argument', '`timeToLive` must be a valid duration string with the suffix `s`.'); + 'invalid-argument', '`ttl` must be a valid duration string with the suffix `s`.'); return apiClient.exchangeToken(TEST_TOKEN_TO_EXCHANGE, APP_ID) .should.eventually.be.rejected.and.deep.include(expected); }); @@ -207,7 +207,7 @@ describe('AppCheckApiClient', () => { expect(resp.ttlMillis).to.deep.equal(3000); expect(stub).to.have.been.calledOnce.and.calledWith({ method: 'POST', - url: `https://firebaseappcheck.googleapis.com/v1alpha/projects/test-project/apps/${APP_ID}:exchangeCustomToken`, + url: `https://firebaseappcheck.googleapis.com/v1beta/projects/test-project/apps/${APP_ID}:exchangeCustomToken`, headers: EXPECTED_HEADERS, data: { customToken: TEST_TOKEN_TO_EXCHANGE } }); @@ -219,10 +219,10 @@ describe('AppCheckApiClient', () => { // 3 seconds with 0 nanoseconds expressed as "3s" // 3 seconds and 1 nanosecond expressed as "3.000000001s" // 3 seconds and 1 microsecond expressed as "3.000001s" - it(`should resolve with ttlMillis as ${ttlMillis} when timeToLive + it(`should resolve with ttlMillis as ${ttlMillis} when ttl from server is: ${ttlString}`, () => { const response = deepCopy(TEST_RESPONSE); - (response as any).timeToLive = ttlString; + (response as any).ttl = ttlString; const stub = sinon .stub(HttpClient.prototype, 'send') .resolves(utils.responseFrom(response, 200)); diff --git a/test/unit/app-check/token-generator.spec.ts b/test/unit/app-check/token-generator.spec.ts index 9244601632..66bb7cffba 100644 --- a/test/unit/app-check/token-generator.spec.ts +++ b/test/unit/app-check/token-generator.spec.ts @@ -44,7 +44,7 @@ const expect = chai.expect; const ALGORITHM = 'RS256'; const ONE_HOUR_IN_SECONDS = 60 * 60; -const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1alpha.TokenExchangeService'; +const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1beta.TokenExchangeService'; /** * Verifies a token is signed with the private key corresponding to the provided public key.