Skip to content

Commit 2424335

Browse files
authored
auth: Add credential service (#1011)
1 parent 8367fa7 commit 2424335

21 files changed

+369
-196
lines changed

src/auth/token-generator.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@
1515
*/
1616

1717
import { FirebaseApp } from '../firebase-app';
18-
import { ServiceAccountCredential } from './credential';
18+
import { ServiceAccountCredential } from '../credential/credential';
1919
import { AuthClientErrorCode, FirebaseAuthError } from '../utils/error';
2020
import { AuthorizedHttpClient, HttpError, HttpRequestConfig, HttpClient } from '../utils/api-request';
2121

src/credential.d.ts

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
import * as _admin from './index.d';
2+
import { Agent } from 'http';
3+
4+
export namespace admin.credential {
5+
/**
6+
* Interface that provides Google OAuth2 access tokens used to authenticate
7+
* with Firebase services.
8+
*
9+
* In most cases, you will not need to implement this yourself and can instead
10+
* use the default implementations provided by
11+
* {@link admin.credential `admin.credential`}.
12+
*/
13+
interface Credential {
14+
15+
/**
16+
* Returns a Google OAuth2 access token object used to authenticate with
17+
* Firebase services.
18+
*
19+
* This object contains the following properties:
20+
* * `access_token` (`string`): The actual Google OAuth2 access token.
21+
* * `expires_in` (`number`): The number of seconds from when the token was
22+
* issued that it expires.
23+
*
24+
* @return A Google OAuth2 access token object.
25+
*/
26+
getAccessToken(): Promise<_admin.GoogleOAuthAccessToken>;
27+
}
28+
29+
30+
/**
31+
* Returns a credential created from the
32+
* {@link
33+
* https://developers.google.com/identity/protocols/application-default-credentials
34+
* Google Application Default Credentials}
35+
* that grants admin access to Firebase services. This credential can be used
36+
* in the call to
37+
* {@link
38+
* https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp
39+
* `admin.initializeApp()`}.
40+
*
41+
* Google Application Default Credentials are available on any Google
42+
* infrastructure, such as Google App Engine and Google Compute Engine.
43+
*
44+
* See
45+
* {@link
46+
* https://firebase.google.com/docs/admin/setup#initialize_the_sdk
47+
* Initialize the SDK}
48+
* for more details.
49+
*
50+
* @example
51+
* ```javascript
52+
* admin.initializeApp({
53+
* credential: admin.credential.applicationDefault(),
54+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
55+
* });
56+
* ```
57+
*
58+
* @param {!Object=} httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent)
59+
* to be used when retrieving access tokens from Google token servers.
60+
*
61+
* @return {!admin.credential.Credential} A credential authenticated via Google
62+
* Application Default Credentials that can be used to initialize an app.
63+
*/
64+
function applicationDefault(httpAgent?: Agent): admin.credential.Credential;
65+
66+
/**
67+
* Returns a credential created from the provided service account that grants
68+
* admin access to Firebase services. This credential can be used in the call
69+
* to
70+
* {@link
71+
* https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp
72+
* `admin.initializeApp()`}.
73+
*
74+
* See
75+
* {@link
76+
* https://firebase.google.com/docs/admin/setup#initialize_the_sdk
77+
* Initialize the SDK}
78+
* for more details.
79+
*
80+
* @example
81+
* ```javascript
82+
* // Providing a path to a service account key JSON file
83+
* var serviceAccount = require("path/to/serviceAccountKey.json");
84+
* admin.initializeApp({
85+
* credential: admin.credential.cert(serviceAccount),
86+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
87+
* });
88+
* ```
89+
*
90+
* @example
91+
* ```javascript
92+
* // Providing a service account object inline
93+
* admin.initializeApp({
94+
* credential: admin.credential.cert({
95+
* projectId: "<PROJECT_ID>",
96+
* clientEmail: "foo@<PROJECT_ID>.iam.gserviceaccount.com",
97+
* privateKey: "-----BEGIN PRIVATE KEY-----<KEY>-----END PRIVATE KEY-----\n"
98+
* }),
99+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
100+
* });
101+
* ```
102+
*
103+
* @param serviceAccountPathOrObject The path to a service
104+
* account key JSON file or an object representing a service account key.
105+
* @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent)
106+
* to be used when retrieving access tokens from Google token servers.
107+
*
108+
* @return A credential authenticated via the
109+
* provided service account that can be used to initialize an app.
110+
*/
111+
function cert(serviceAccountPathOrObject: string | _admin.ServiceAccount, httpAgent?: Agent): admin.credential.Credential;
112+
113+
/**
114+
* Returns a credential created from the provided refresh token that grants
115+
* admin access to Firebase services. This credential can be used in the call
116+
* to
117+
* {@link
118+
* https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp
119+
* `admin.initializeApp()`}.
120+
*
121+
* See
122+
* {@link
123+
* https://firebase.google.com/docs/admin/setup#initialize_the_sdk
124+
* Initialize the SDK}
125+
* for more details.
126+
*
127+
* @example
128+
* ```javascript
129+
* // Providing a path to a refresh token JSON file
130+
* var refreshToken = require("path/to/refreshToken.json");
131+
* admin.initializeApp({
132+
* credential: admin.credential.refreshToken(refreshToken),
133+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
134+
* });
135+
* ```
136+
*
137+
* @param refreshTokenPathOrObject The path to a Google
138+
* OAuth2 refresh token JSON file or an object representing a Google OAuth2
139+
* refresh token.
140+
* @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent)
141+
* to be used when retrieving access tokens from Google token servers.
142+
*
143+
* @return A credential authenticated via the
144+
* provided service account that can be used to initialize an app.
145+
*/
146+
function refreshToken(refreshTokenPathOrObject: string | object, httpAgent?: Agent): admin.credential.Credential;
147+
}

src/auth/credential.ts renamed to src/credential/credential.ts

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,10 @@ const REFRESH_TOKEN_PATH = '/oauth2/v4/token';
5353
const ONE_HOUR_IN_SECONDS = 60 * 60;
5454
const JWT_ALGORITHM = 'RS256';
5555

56+
let globalAppDefaultCred: Credential;
57+
const globalCertCreds: { [key: string]: ServiceAccountCredential } = {};
58+
const globalRefreshTokenCreds: { [key: string]: RefreshTokenCredential } = {};
59+
5660
/**
5761
* Interface for Google OAuth 2.0 access tokens.
5862
*/
@@ -64,12 +68,164 @@ export interface GoogleOAuthAccessToken {
6468
}
6569

6670
/**
67-
* Interface for things that generate access tokens.
71+
* Interface that provides Google OAuth2 access tokens used to authenticate
72+
* with Firebase services.
73+
*
74+
* In most cases, you will not need to implement this yourself and can instead
75+
* use the default implementations provided by
76+
* {@link admin.credential `admin.credential`}.
6877
*/
6978
export interface Credential {
79+
/**
80+
* Returns a Google OAuth2 access token object used to authenticate with
81+
* Firebase services.
82+
*
83+
* This object contains the following properties:
84+
* * `access_token` (`string`): The actual Google OAuth2 access token.
85+
* * `expires_in` (`number`): The number of seconds from when the token was
86+
* issued that it expires.
87+
*
88+
* @return A Google OAuth2 access token object.
89+
*/
7090
getAccessToken(): Promise<GoogleOAuthAccessToken>;
7191
}
7292

93+
/**
94+
* Returns a credential created from the
95+
* {@link
96+
* https://developers.google.com/identity/protocols/application-default-credentials
97+
* Google Application Default Credentials}
98+
* that grants admin access to Firebase services. This credential can be used
99+
* in the call to
100+
* {@link
101+
* https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp
102+
* `admin.initializeApp()`}.
103+
*
104+
* Google Application Default Credentials are available on any Google
105+
* infrastructure, such as Google App Engine and Google Compute Engine.
106+
*
107+
* See
108+
* {@link
109+
* https://firebase.google.com/docs/admin/setup#initialize_the_sdk
110+
* Initialize the SDK}
111+
* for more details.
112+
*
113+
* @example
114+
* ```javascript
115+
* admin.initializeApp({
116+
* credential: admin.credential.applicationDefault(),
117+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
118+
* });
119+
* ```
120+
*
121+
* @param {!Object=} httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent)
122+
* to be used when retrieving access tokens from Google token servers.
123+
*
124+
* @return {!admin.credential.Credential} A credential authenticated via Google
125+
* Application Default Credentials that can be used to initialize an app.
126+
*/
127+
export function applicationDefault(httpAgent?: Agent): Credential {
128+
if (typeof globalAppDefaultCred === 'undefined') {
129+
globalAppDefaultCred = getApplicationDefault(httpAgent);
130+
}
131+
return globalAppDefaultCred;
132+
}
133+
134+
/**
135+
* Returns a credential created from the provided service account that grants
136+
* admin access to Firebase services. This credential can be used in the call
137+
* to
138+
* {@link
139+
* https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp
140+
* `admin.initializeApp()`}.
141+
*
142+
* See
143+
* {@link
144+
* https://firebase.google.com/docs/admin/setup#initialize_the_sdk
145+
* Initialize the SDK}
146+
* for more details.
147+
*
148+
* @example
149+
* ```javascript
150+
* // Providing a path to a service account key JSON file
151+
* var serviceAccount = require("path/to/serviceAccountKey.json");
152+
* admin.initializeApp({
153+
* credential: admin.credential.cert(serviceAccount),
154+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
155+
* });
156+
* ```
157+
*
158+
* @example
159+
* ```javascript
160+
* // Providing a service account object inline
161+
* admin.initializeApp({
162+
* credential: admin.credential.cert({
163+
* projectId: "<PROJECT_ID>",
164+
* clientEmail: "foo@<PROJECT_ID>.iam.gserviceaccount.com",
165+
* privateKey: "-----BEGIN PRIVATE KEY-----<KEY>-----END PRIVATE KEY-----\n"
166+
* }),
167+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
168+
* });
169+
* ```
170+
*
171+
* @param serviceAccountPathOrObject The path to a service
172+
* account key JSON file or an object representing a service account key.
173+
* @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent)
174+
* to be used when retrieving access tokens from Google token servers.
175+
*
176+
* @return A credential authenticated via the
177+
* provided service account that can be used to initialize an app.
178+
*/
179+
export function cert(serviceAccountPathOrObject: string | object, httpAgent?: Agent): Credential {
180+
const stringifiedServiceAccount = JSON.stringify(serviceAccountPathOrObject);
181+
if (!(stringifiedServiceAccount in globalCertCreds)) {
182+
globalCertCreds[stringifiedServiceAccount] = new ServiceAccountCredential(serviceAccountPathOrObject, httpAgent);
183+
}
184+
return globalCertCreds[stringifiedServiceAccount];
185+
}
186+
187+
/**
188+
* Returns a credential created from the provided refresh token that grants
189+
* admin access to Firebase services. This credential can be used in the call
190+
* to
191+
* {@link
192+
* https://firebase.google.com/docs/reference/admin/node/admin#.initializeApp
193+
* `admin.initializeApp()`}.
194+
*
195+
* See
196+
* {@link
197+
* https://firebase.google.com/docs/admin/setup#initialize_the_sdk
198+
* Initialize the SDK}
199+
* for more details.
200+
*
201+
* @example
202+
* ```javascript
203+
* // Providing a path to a refresh token JSON file
204+
* var refreshToken = require("path/to/refreshToken.json");
205+
* admin.initializeApp({
206+
* credential: admin.credential.refreshToken(refreshToken),
207+
* databaseURL: "https://<DATABASE_NAME>.firebaseio.com"
208+
* });
209+
* ```
210+
*
211+
* @param refreshTokenPathOrObject The path to a Google
212+
* OAuth2 refresh token JSON file or an object representing a Google OAuth2
213+
* refresh token.
214+
* @param httpAgent Optional [HTTP Agent](https://nodejs.org/api/http.html#http_class_http_agent)
215+
* to be used when retrieving access tokens from Google token servers.
216+
*
217+
* @return A credential authenticated via the
218+
* provided service account that can be used to initialize an app.
219+
*/
220+
export function refreshToken(refreshTokenPathOrObject: string | object, httpAgent?: Agent): Credential {
221+
const stringifiedRefreshToken = JSON.stringify(refreshTokenPathOrObject);
222+
if (!(stringifiedRefreshToken in globalRefreshTokenCreds)) {
223+
globalRefreshTokenCreds[stringifiedRefreshToken] = new RefreshTokenCredential(
224+
refreshTokenPathOrObject, httpAgent);
225+
}
226+
return globalRefreshTokenCreds[stringifiedRefreshToken];
227+
}
228+
73229
/**
74230
* Implementation of Credential that uses a service account.
75231
*/

src/credential/index.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/*!
2+
* Copyright 2020 Google Inc.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
import * as credentialApi from './credential';
18+
19+
/**
20+
* Temporarily, admin.credential is used as the namespace name because we
21+
* cannot barrel re-export the contents from credential.ts, and we want it to
22+
* match the namespacing in the re-export inside src/index.d.ts
23+
*/
24+
/* eslint-disable @typescript-eslint/no-namespace */
25+
export namespace admin.credential {
26+
// See https://github.com/microsoft/TypeScript/issues/4336
27+
/* eslint-disable @typescript-eslint/no-unused-vars */
28+
// See https://github.com/typescript-eslint/typescript-eslint/issues/363
29+
// Allows for exposing classes as interfaces in typings
30+
/* eslint-disable @typescript-eslint/no-empty-interface */
31+
export import Credential = credentialApi.Credential;
32+
export const applicationDefault = credentialApi.applicationDefault;
33+
export const cert = credentialApi.cert;
34+
export const refreshToken = credentialApi.refreshToken;
35+
}

src/firebase-app.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,9 @@
1414
* limitations under the License.
1515
*/
1616

17-
import { Credential, GoogleOAuthAccessToken, getApplicationDefault } from './auth/credential';
17+
import {
18+
Credential, GoogleOAuthAccessToken, getApplicationDefault
19+
} from './credential/credential';
1820
import * as validator from './utils/validator';
1921
import { deepCopy, deepExtend } from './utils/deep-copy';
2022
import { FirebaseServiceInterface } from './firebase-service';

0 commit comments

Comments
 (0)