15
15
* limitations under the License.
16
16
*/
17
17
18
+ import { appCheck } from './index' ;
19
+
18
20
import * as validator from '../utils/validator' ;
19
- import { toWebSafeBase64 } from '../utils' ;
21
+ import { toWebSafeBase64 , transformMillisecondsToSecondsString } from '../utils' ;
20
22
21
23
import { CryptoSigner , CryptoSignerError , CryptoSignerErrorCode } from '../utils/crypto-signer' ;
22
24
import {
@@ -26,7 +28,11 @@ import {
26
28
} from './app-check-api-client-internal' ;
27
29
import { HttpError } from '../utils/api-request' ;
28
30
31
+ import AppCheckTokenOptions = appCheck . AppCheckTokenOptions ;
32
+
29
33
const ONE_HOUR_IN_SECONDS = 60 * 60 ;
34
+ const ONE_MINUTE_IN_MILLIS = 60 * 1000 ;
35
+ const ONE_DAY_IN_MILLIS = 24 * 60 * 60 * 1000 ;
30
36
31
37
// Audience to use for Firebase App Check Custom tokens
32
38
const FIREBASE_APP_CHECK_AUDIENCE = 'https://firebaseappcheck.googleapis.com/google.firebase.appcheck.v1beta.TokenExchangeService' ;
@@ -63,12 +69,16 @@ export class AppCheckTokenGenerator {
63
69
* @return A Promise fulfilled with a custom token signed with a service account key
64
70
* that can be exchanged to an App Check token.
65
71
*/
66
- public createCustomToken ( appId : string ) : Promise < string > {
72
+ public createCustomToken ( appId : string , options ?: AppCheckTokenOptions ) : Promise < string > {
67
73
if ( ! validator . isNonEmptyString ( appId ) ) {
68
74
throw new FirebaseAppCheckError (
69
75
'invalid-argument' ,
70
76
'`appId` must be a non-empty string.' ) ;
71
77
}
78
+ let customOptions = { } ;
79
+ if ( typeof options !== 'undefined' ) {
80
+ customOptions = this . validateTokenOptions ( options ) ;
81
+ }
72
82
return this . signer . getAccountId ( ) . then ( ( account ) => {
73
83
const header = {
74
84
alg : this . signer . algorithm ,
@@ -83,6 +93,7 @@ export class AppCheckTokenGenerator {
83
93
aud : FIREBASE_APP_CHECK_AUDIENCE ,
84
94
exp : iat + ONE_HOUR_IN_SECONDS ,
85
95
iat,
96
+ ...customOptions ,
86
97
} ;
87
98
const token = `${ this . encodeSegment ( header ) } .${ this . encodeSegment ( body ) } ` ;
88
99
return this . signer . sign ( Buffer . from ( token ) )
@@ -98,6 +109,35 @@ export class AppCheckTokenGenerator {
98
109
const buffer : Buffer = ( segment instanceof Buffer ) ? segment : Buffer . from ( JSON . stringify ( segment ) ) ;
99
110
return toWebSafeBase64 ( buffer ) . replace ( / = + $ / , '' ) ;
100
111
}
112
+
113
+ /**
114
+ * Checks if a given `AppCheckTokenOptions` object is valid. If successful, returns an object with
115
+ * custom properties.
116
+ *
117
+ * @param options An options object to be validated.
118
+ * @returns A custom object with ttl converted to protobuf Duration string format.
119
+ */
120
+ private validateTokenOptions ( options : AppCheckTokenOptions ) : { [ key : string ] : any } {
121
+ if ( ! validator . isNonNullObject ( options ) ) {
122
+ throw new FirebaseAppCheckError (
123
+ 'invalid-argument' ,
124
+ 'AppCheckTokenOptions must be a non-null object.' ) ;
125
+ }
126
+ if ( typeof options . ttlMillis !== 'undefined' ) {
127
+ if ( ! validator . isNumber ( options . ttlMillis ) ) {
128
+ throw new FirebaseAppCheckError ( 'invalid-argument' ,
129
+ 'ttlMillis must be a duration in milliseconds.' ) ;
130
+ }
131
+ // ttlMillis must be between 30 minutes and 7 days (inclusive)
132
+ if ( options . ttlMillis < ( ONE_MINUTE_IN_MILLIS * 30 ) || options . ttlMillis > ( ONE_DAY_IN_MILLIS * 7 ) ) {
133
+ throw new FirebaseAppCheckError (
134
+ 'invalid-argument' ,
135
+ 'ttlMillis must be a duration in milliseconds between 30 minutes and 7 days (inclusive).' ) ;
136
+ }
137
+ return { ttl : transformMillisecondsToSecondsString ( options . ttlMillis ) } ;
138
+ }
139
+ return { } ;
140
+ }
101
141
}
102
142
103
143
/**
@@ -123,7 +163,7 @@ export function appCheckErrorFromCryptoSignerError(err: Error): Error {
123
163
code = APP_CHECK_ERROR_CODE_MAPPING [ status ] ;
124
164
}
125
165
return new FirebaseAppCheckError ( code ,
126
- `Error returned from server while siging a custom token: ${ description } `
166
+ `Error returned from server while signing a custom token: ${ description } `
127
167
) ;
128
168
}
129
169
return new FirebaseAppCheckError ( 'internal-error' ,
0 commit comments