diff --git a/js/.gitignore b/js/.gitignore index c2007f6b..6b0f23ca 100644 --- a/js/.gitignore +++ b/js/.gitignore @@ -29,4 +29,9 @@ yarn-error.log* protoc-gen-grpc-web src/android_emulation_control/* docker/certs/jwt_secrets_pub.jwks -src/config.js \ No newline at end of file + +# Generated by config_gen.py — see js/README.md (Authentication section) +firebase_config.json +src/config.js +develop/envoy.yaml +docker/envoy.yaml \ No newline at end of file diff --git a/js/README.md b/js/README.md index 330ee68e..ef7b6ad7 100644 --- a/js/README.md +++ b/js/README.md @@ -72,6 +72,37 @@ The most important thing to is to figure out if you need a [Turn Server](https:/ Most of the time there is no need for a turn server. If you do have needs for a turn server you can follow the steps in the [README](turn/README.MD). +# Authentication + +The web demo supports two authentication backends: + +## Self-hosted JWT (recommended for deployments) + +The `docker/` stack ships a token service (`jwt-provider/`) that mints JWTs signed by a keypair you control. This is the production path — no third-party identity provider, no shared sign-in quota, no external dependency. See `docker/` and `jwt-provider/` for the setup. + +## Firebase / Google sign-in (quick-start demo only) + +A lightweight on-ramp for "click sign-in, see the emulator." You bring your own Firebase project — this repo does not ship a usable one. Setup: + +1. **Create a Firebase project** at https://console.firebase.google.com. Any name works (e.g. `my-aemu-demo`). +2. **Enable Google sign-in**: Authentication → Sign-in method → Google → Enable, set a project support email, Save. +3. **Add a web app**: Project settings (gear icon) → Your apps → Web → register the app, copy the `firebaseConfig` object. +4. **Save the config locally**: + ```sh + cp firebase_config.example.json firebase_config.json + ``` + Open `firebase_config.json` and replace each placeholder value with the matching field from your project's web app config. +5. **Generate the derived files**: + ```sh + python3 config_gen.py firebase_config.json + ``` + This writes `src/config.js` and renders `develop/envoy.yaml` + `docker/envoy.yaml` from their `.template.yaml` siblings, substituting your project ID into the envoy JWT issuer/audience. +6. **Start the dev stack** as described under *As a Developer* below. + +`firebase_config.json`, `src/config.js`, and the rendered `envoy.yaml` files are gitignored — each contributor configures their own project. The `.template.yaml` files are the source of truth; rerun `config_gen.py` after changing them. + +Firebase web API keys are not secrets — they're public identifiers, see [Firebase docs](https://firebase.google.com/docs/projects/api-keys). But your project *owns* every sign-in (PII, quota, abuse). Don't paste someone else's config, and don't commit yours. + # Internal Organization This sample is based on ReactJS and uses the android-emulator-webrtc module to display the emulator. diff --git a/js/config_gen.py b/js/config_gen.py index f0a3afc2..dd22bc7d 100644 --- a/js/config_gen.py +++ b/js/config_gen.py @@ -1,41 +1,64 @@ #!/usr/bin/env python3 -import sys +"""Render Firebase web SDK config and envoy proxy configs from firebase_config.json. + +Run from the js/ directory: + + python3 config_gen.py firebase_config.json + +Generates (all gitignored — see js/.gitignore): + src/config.js Firebase web SDK config imported by login_firebase.js + develop/envoy.yaml dev envoy proxy, rendered from develop/envoy.template.yaml + docker/envoy.yaml prod envoy proxy, rendered from docker/envoy.template.yaml + +See js/README.md (Authentication section) for the full BYO-Firebase setup walkthrough. +""" import json +import sys +from pathlib import Path +SENTINEL = "__FIREBASE_PROJECT_ID__" +PLACEHOLDER_VALUES = { + "YOUR_API_KEY", + "YOUR_APP_ID", + "YOUR_PROJECT_ID", + "YOUR_SENDER_ID", +} -def replace(fname, search, replace): - if search == replace: - return - txt = "" - with open(fname, "r") as inp: - txt = inp.read() - txt.replace(search, replace) - with open(fname, "w") as out: - out.write(txt) +def render(template_path: Path, output_path: Path, project_id: str) -> None: + text = template_path.read_text() + if SENTINEL not in text: + sys.exit(f"error: sentinel {SENTINEL!r} not found in {template_path}") + output_path.write_text(text.replace(SENTINEL, project_id)) -def main(): +def main() -> None: if len(sys.argv) != 2: - sys.exit(1) + sys.exit("usage: config_gen.py ") - config = {} - with open(sys.argv[1], "r") as cfg: - config = json.load(cfg) + config_path = Path(sys.argv[1]) + config = json.loads(config_path.read_text()) + project_id = config.get("projectId", "") - with open("src/config.js", "w") as cfg: - lines = [ - "// Do not edit this file!", - "// This file was autogenerated by running:", - "// {}".format(" ".join(sys.argv)), - "export const config = {};".format(json.dumps(config, sort_keys=True, indent=4)), - ] - cfg.write("\n".join(lines)) + if not project_id or project_id in PLACEHOLDER_VALUES: + sys.exit( + f"error: {config_path} still has placeholder values. " + "Copy firebase_config.example.json to firebase_config.json and " + "fill in your Firebase project's web app config." + ) - replace("develop/envoy.yaml", "android-emulator-webrtc-demo", config["projectId"]) - replace("docker/envoy.yaml", "android-emulator-webrtc-demo", config["projectId"]) + Path("src/config.js").write_text("\n".join([ + "// Do not edit this file!", + "// Generated by: {} {}".format(Path(sys.argv[0]).name, config_path), + "export const config = {};".format( + json.dumps(config, sort_keys=True, indent=4) + ), + "", + ])) + + render(Path("develop/envoy.template.yaml"), Path("develop/envoy.yaml"), project_id) + render(Path("docker/envoy.template.yaml"), Path("docker/envoy.yaml"), project_id) if __name__ == "__main__": main() - diff --git a/js/develop/envoy.yaml b/js/develop/envoy.template.yaml similarity index 97% rename from js/develop/envoy.yaml rename to js/develop/envoy.template.yaml index 9cc1c0aa..a52568de 100644 --- a/js/develop/envoy.yaml +++ b/js/develop/envoy.template.yaml @@ -56,9 +56,9 @@ static_resources: "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication providers: firebase_jwt: - issuer: https://securetoken.google.com/android-emulator-webrtc-demo + issuer: https://securetoken.google.com/__FIREBASE_PROJECT_ID__ audiences: - - android-emulator-webrtc-demo + - __FIREBASE_PROJECT_ID__ remote_jwks: http_uri: uri: https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com diff --git a/js/docker/envoy.yaml b/js/docker/envoy.template.yaml similarity index 97% rename from js/docker/envoy.yaml rename to js/docker/envoy.template.yaml index 8883718f..1ae179f3 100644 --- a/js/docker/envoy.yaml +++ b/js/docker/envoy.template.yaml @@ -56,9 +56,9 @@ static_resources: "@type": type.googleapis.com/envoy.extensions.filters.http.jwt_authn.v3.JwtAuthentication providers: firebase_jwt: - issuer: https://securetoken.google.com/android-emulator-webrtc-demo + issuer: https://securetoken.google.com/__FIREBASE_PROJECT_ID__ audiences: - - android-emulator-webrtc-demo + - __FIREBASE_PROJECT_ID__ remote_jwks: http_uri: uri: https://www.googleapis.com/robot/v1/metadata/x509/securetoken@system.gserviceaccount.com diff --git a/js/firebase_config.example.json b/js/firebase_config.example.json new file mode 100644 index 00000000..688dfde9 --- /dev/null +++ b/js/firebase_config.example.json @@ -0,0 +1,9 @@ +{ + "_comment": "Copy this file to firebase_config.json and fill in the values from your own Firebase project's web app config (Project settings → Your apps → Web → SDK setup and configuration → Config). See js/README.md for the full setup walkthrough.", + "apiKey": "YOUR_API_KEY", + "authDomain": "YOUR_PROJECT_ID.firebaseapp.com", + "projectId": "YOUR_PROJECT_ID", + "storageBucket": "YOUR_PROJECT_ID.firebasestorage.app", + "messagingSenderId": "YOUR_SENDER_ID", + "appId": "YOUR_APP_ID" +} diff --git a/js/firebase_config.json b/js/firebase_config.json deleted file mode 100644 index 95cdeae2..00000000 --- a/js/firebase_config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - "apiKey": "AIzaSyApOjVnuRBNO7x5UP-Cq7VSzcylCMjDBng", - "authDomain": "android-emulator-webrtc-demo.firebaseapp.com", - "databaseURL": "https://android-emulator-webrtc-demo.firebaseio.com", - "projectId": "android-emulator-webrtc-demo", - "storageBucket": "android-emulator-webrtc-demo.appspot.com", - "messagingSenderId": "431559312508", - "appId": "1:431559312508:web:c9aaa2a9ac2f48657e35f9", - "measurementId": "G-MTCT6N00FV" -} \ No newline at end of file diff --git a/js/src/components/login_firebase.js b/js/src/components/login_firebase.js index 5612826b..05607f1b 100644 --- a/js/src/components/login_firebase.js +++ b/js/src/components/login_firebase.js @@ -21,6 +21,15 @@ import Typography from "@mui/material/Typography"; import { config } from "../config"; +if (!config.apiKey || !config.projectId || config.projectId === "YOUR_PROJECT_ID") { + throw new Error( + "Firebase config is missing or unconfigured. " + + "Copy firebase_config.example.json to firebase_config.json, " + + "fill in your Firebase project's web app config, then run: " + + "python3 config_gen.py firebase_config.json" + ); +} + // Initialize the Firebase app once at module load. const firebaseApp = initializeApp(config); const firebaseAuth = getAuth(firebaseApp);