openrtc-trust-firebase-app-check 2.0.0-rc.15
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +3 -0
- package/README.md +22 -0
- package/dist/index.d.ts +40 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +77 -0
- package/dist/index.js.map +1 -0
- package/package.json +35 -0
package/LICENSE
ADDED
package/README.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# openrtc-trust-firebase-app-check
|
|
2
|
+
|
|
3
|
+
Optional Firebase App Check evidence for OpenRTC 2.0 device enrollment. The
|
|
4
|
+
consumer app owns Firebase initialization and passes its `AppCheck` instance:
|
|
5
|
+
|
|
6
|
+
```ts
|
|
7
|
+
import { FirebaseAppCheckAttestationProvider } from 'openrtc-trust-firebase-app-check';
|
|
8
|
+
|
|
9
|
+
const trust = new FirebaseAppCheckAttestationProvider({ appCheck });
|
|
10
|
+
const rtc = OpenRTC({ apiKey, trust: { attestation: trust } });
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The adapter uses normal reusable App Check tokens. It never requests
|
|
14
|
+
limited-use tokens, coalesces concurrent fetches, and honors Firebase's returned
|
|
15
|
+
expiry. Call `invalidate()` after a provider rejection if enrollment is retried
|
|
16
|
+
outside the OpenRTC client.
|
|
17
|
+
|
|
18
|
+
Firebase App Check proves application integrity for the reusable token session;
|
|
19
|
+
it does not sign OpenRTC's enrollment challenge. OpenRTC records this adapter as
|
|
20
|
+
`app-session` evidence and combines it with its independently challenge-bound
|
|
21
|
+
install-key proof. Use the consumer-backend assertion path, Apple App Attest, or
|
|
22
|
+
Play Integrity when a policy requires request-bound evidence.
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { type AppCheck } from 'firebase/app-check';
|
|
2
|
+
import type { OpenRTCAttestationProvider } from 'openrtc';
|
|
3
|
+
export interface FirebaseAppCheckTokenResult {
|
|
4
|
+
token: string;
|
|
5
|
+
/** Some host wrappers expose this; the Web SDK token is otherwise decoded. */
|
|
6
|
+
expireTimeMillis?: number;
|
|
7
|
+
}
|
|
8
|
+
export interface FirebaseAppCheckAttestationProviderOptions {
|
|
9
|
+
appCheck: AppCheck;
|
|
10
|
+
/** Refresh this long before Firebase's returned expiry. Defaults to 60s. */
|
|
11
|
+
refreshSkewMs?: number;
|
|
12
|
+
/** Test/integration seam. Production callers should use Firebase's getToken. */
|
|
13
|
+
tokenFetcher?: (appCheck: AppCheck, forceRefresh: boolean) => Promise<FirebaseAppCheckTokenResult>;
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* Enrollment-only Firebase App Check adapter.
|
|
17
|
+
*
|
|
18
|
+
* This intentionally calls the reusable-token API (`getToken`) and never the
|
|
19
|
+
* limited-use/replay-protected API. OpenRTC binds enrollment to its own nonce
|
|
20
|
+
* and device-key proof, so five-minute limited-use polling adds cost without
|
|
21
|
+
* improving the gateway's connection security.
|
|
22
|
+
*/
|
|
23
|
+
export declare class FirebaseAppCheckAttestationProvider implements OpenRTCAttestationProvider {
|
|
24
|
+
private readonly options;
|
|
25
|
+
readonly kind: "firebase-app-check";
|
|
26
|
+
readonly evidenceBinding: "app-session";
|
|
27
|
+
private readonly refreshSkewMs;
|
|
28
|
+
private readonly tokenFetcher;
|
|
29
|
+
private cached;
|
|
30
|
+
private pending;
|
|
31
|
+
private forceRefresh;
|
|
32
|
+
constructor(options: FirebaseAppCheckAttestationProviderOptions);
|
|
33
|
+
invalidate(): void;
|
|
34
|
+
private current;
|
|
35
|
+
private fetch;
|
|
36
|
+
getEvidence(): Promise<{
|
|
37
|
+
token: string;
|
|
38
|
+
}>;
|
|
39
|
+
}
|
|
40
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC7D,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,SAAS,CAAC;AAE1D,MAAM,WAAW,2BAA2B;IACxC,KAAK,EAAE,MAAM,CAAC;IACd,8EAA8E;IAC9E,gBAAgB,CAAC,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,0CAA0C;IACvD,QAAQ,EAAE,QAAQ,CAAC;IACnB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gFAAgF;IAChF,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,QAAQ,EAAE,YAAY,EAAE,OAAO,KAAK,OAAO,CAAC,2BAA2B,CAAC,CAAC;CACtG;AAuBD;;;;;;;GAOG;AACH,qBAAa,mCAAoC,YAAW,0BAA0B;IAStE,OAAO,CAAC,QAAQ,CAAC,OAAO;IARpC,QAAQ,CAAC,IAAI,EAAG,oBAAoB,CAAU;IAC9C,QAAQ,CAAC,eAAe,EAAG,aAAa,CAAU;IAClD,OAAO,CAAC,QAAQ,CAAC,aAAa,CAAS;IACvC,OAAO,CAAC,QAAQ,CAAC,YAAY,CAA0E;IACvG,OAAO,CAAC,MAAM,CAA4B;IAC1C,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,YAAY,CAAS;gBAEA,OAAO,EAAE,0CAA0C;IAKhF,UAAU,IAAI,IAAI;IAKlB,OAAO,CAAC,OAAO;IAOf,OAAO,CAAC,KAAK;IAsBP,WAAW,IAAI,OAAO,CAAC;QAAE,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;CAIlD"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
import { getToken } from 'firebase/app-check';
|
|
2
|
+
function tokenExpiryMillis(result) {
|
|
3
|
+
if (Number.isFinite(result.expireTimeMillis))
|
|
4
|
+
return result.expireTimeMillis;
|
|
5
|
+
try {
|
|
6
|
+
const part = result.token.split('.')[1];
|
|
7
|
+
if (!part)
|
|
8
|
+
throw new Error('missing payload');
|
|
9
|
+
const base64 = part.replace(/-/g, '+').replace(/_/g, '/');
|
|
10
|
+
const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, '=');
|
|
11
|
+
const claims = JSON.parse(atob(padded));
|
|
12
|
+
const expiresAtMs = Number(claims.exp) * 1000;
|
|
13
|
+
if (!Number.isFinite(expiresAtMs))
|
|
14
|
+
throw new Error('missing exp');
|
|
15
|
+
return expiresAtMs;
|
|
16
|
+
}
|
|
17
|
+
catch {
|
|
18
|
+
throw new Error('Firebase App Check returned a token without a valid expiry.');
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
/**
|
|
22
|
+
* Enrollment-only Firebase App Check adapter.
|
|
23
|
+
*
|
|
24
|
+
* This intentionally calls the reusable-token API (`getToken`) and never the
|
|
25
|
+
* limited-use/replay-protected API. OpenRTC binds enrollment to its own nonce
|
|
26
|
+
* and device-key proof, so five-minute limited-use polling adds cost without
|
|
27
|
+
* improving the gateway's connection security.
|
|
28
|
+
*/
|
|
29
|
+
export class FirebaseAppCheckAttestationProvider {
|
|
30
|
+
constructor(options) {
|
|
31
|
+
this.options = options;
|
|
32
|
+
this.kind = 'firebase-app-check';
|
|
33
|
+
this.evidenceBinding = 'app-session';
|
|
34
|
+
this.cached = null;
|
|
35
|
+
this.pending = null;
|
|
36
|
+
this.forceRefresh = false;
|
|
37
|
+
this.refreshSkewMs = Math.max(0, options.refreshSkewMs ?? 60000);
|
|
38
|
+
this.tokenFetcher = options.tokenFetcher ?? getToken;
|
|
39
|
+
}
|
|
40
|
+
invalidate() {
|
|
41
|
+
this.cached = null;
|
|
42
|
+
this.forceRefresh = true;
|
|
43
|
+
}
|
|
44
|
+
current() {
|
|
45
|
+
if (!this.cached || this.cached.expiresAtMs <= Date.now() + this.refreshSkewMs) {
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
return this.cached;
|
|
49
|
+
}
|
|
50
|
+
fetch() {
|
|
51
|
+
if (this.pending)
|
|
52
|
+
return this.pending;
|
|
53
|
+
const forceRefresh = this.forceRefresh;
|
|
54
|
+
this.forceRefresh = false;
|
|
55
|
+
this.pending = this.tokenFetcher(this.options.appCheck, forceRefresh)
|
|
56
|
+
.then((result) => {
|
|
57
|
+
if (!result.token) {
|
|
58
|
+
throw new Error('Firebase App Check returned an invalid token result.');
|
|
59
|
+
}
|
|
60
|
+
const cached = {
|
|
61
|
+
token: result.token,
|
|
62
|
+
expiresAtMs: tokenExpiryMillis(result),
|
|
63
|
+
};
|
|
64
|
+
this.cached = cached;
|
|
65
|
+
return cached;
|
|
66
|
+
})
|
|
67
|
+
.finally(() => {
|
|
68
|
+
this.pending = null;
|
|
69
|
+
});
|
|
70
|
+
return this.pending;
|
|
71
|
+
}
|
|
72
|
+
async getEvidence() {
|
|
73
|
+
const result = this.current() ?? await this.fetch();
|
|
74
|
+
return { token: result.token };
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "openrtc-trust-firebase-app-check",
|
|
3
|
+
"version": "2.0.0-rc.15",
|
|
4
|
+
"private": false,
|
|
5
|
+
"type": "module",
|
|
6
|
+
"sideEffects": false,
|
|
7
|
+
"description": "Optional Firebase App Check attestation adapter for OpenRTC 2.0 device enrollment.",
|
|
8
|
+
"license": "SEE LICENSE IN LICENSE",
|
|
9
|
+
"main": "dist/index.js",
|
|
10
|
+
"types": "dist/index.d.ts",
|
|
11
|
+
"files": ["dist", "LICENSE", "README.md"],
|
|
12
|
+
"exports": {
|
|
13
|
+
".": {
|
|
14
|
+
"types": "./dist/index.d.ts",
|
|
15
|
+
"import": "./dist/index.js"
|
|
16
|
+
},
|
|
17
|
+
"./package.json": "./package.json"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"build": "rm -rf dist && tsc",
|
|
21
|
+
"test": "vitest run",
|
|
22
|
+
"test:deterministic": "vitest run",
|
|
23
|
+
"prepublishOnly": "pnpm run build && pnpm run test"
|
|
24
|
+
},
|
|
25
|
+
"peerDependencies": {
|
|
26
|
+
"firebase": "^9.0.0 || ^10.0.0 || ^11.0.0 || ^12.0.0",
|
|
27
|
+
"openrtc": "2.0.0-rc.15"
|
|
28
|
+
},
|
|
29
|
+
"devDependencies": {
|
|
30
|
+
"firebase": "^12.16.0",
|
|
31
|
+
"openrtc": "workspace:*",
|
|
32
|
+
"typescript": "^5.8.3",
|
|
33
|
+
"vitest": "^4.0.18"
|
|
34
|
+
}
|
|
35
|
+
}
|