Skip to content

Firebase Configuration

Detailed guide for configuring Firebase for the Developer Dashboard.

Firebase Project Setup

1. Create Project

  1. Go to Firebase Console
  2. Click "Add project"
  3. Enter project name (e.g., "scry-dashboard")
  4. Disable Google Analytics (optional)
  5. Click "Create project"

2. Enable Authentication

  1. Go to Authentication → Sign-in method
  2. Enable "GitHub" provider
  3. Configure GitHub OAuth (see below)
  4. Save

3. Enable Firestore

  1. Go to Firestore Database
  2. Click "Create database"
  3. Choose "Production mode"
  4. Select a location (choose closest to your users)
  5. Click "Create"

4. Get Configuration

  1. Go to Project Settings → General
  2. Scroll to "Your apps"
  3. Click the web icon (</>)
  4. Register app with a nickname
  5. Copy the configuration object

Configuration Values

javascript
const firebaseConfig = {
  apiKey: "AIzaSy...",
  authDomain: "your-project.firebaseapp.com",
  projectId: "your-project-id",
  storageBucket: "your-project.appspot.com",
  messagingSenderId: "123456789",
  appId: "1:123456789:web:abc123",
  measurementId: "G-XXXXXXX" // optional
};

Map these to environment variables:

Config KeyEnvironment Variable
apiKeyNEXT_PUBLIC_FIREBASE_API_KEY
authDomainNEXT_PUBLIC_FIREBASE_AUTH_DOMAIN
projectIdNEXT_PUBLIC_FIREBASE_PROJECT_ID
storageBucketNEXT_PUBLIC_FIREBASE_STORAGE_BUCKET
messagingSenderIdNEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID
appIdNEXT_PUBLIC_FIREBASE_APP_ID
measurementIdNEXT_PUBLIC_FIREBASE_MEASUREMENT_ID

Firestore Data Structure

Collections

firestore/
├── users/
│   └── {userId}/
│       ├── email: string
│       ├── displayName: string
│       ├── photoURL: string
│       └── createdAt: Timestamp

└── projects/
    └── {projectId}/
        ├── name: string
        ├── description: string
        ├── ownerId: string
        ├── createdAt: Timestamp

        ├── builds/
        │   └── {buildId}/
        │       ├── versionId: string
        │       ├── buildNumber: number
        │       ├── zipUrl: string
        │       ├── status: string
        │       └── createdAt: Timestamp

        ├── apiKeys/
        │   └── {keyId}/
        │       ├── name: string
        │       ├── prefix: string
        │       ├── hash: string
        │       ├── status: string
        │       └── createdAt: Timestamp

        └── counters/
            └── builds/
                └── currentBuildNumber: number

Security Rules

Basic Rules

javascript
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    // Helper functions
    function isSignedIn() {
      return request.auth != null;
    }

    function isOwner(ownerId) {
      return request.auth.uid == ownerId;
    }

    // Users collection
    match /users/{userId} {
      allow read: if isSignedIn();
      allow write: if isSignedIn() && request.auth.uid == userId;
    }

    // Projects collection
    match /projects/{projectId} {
      allow read: if isSignedIn() && isOwner(resource.data.ownerId);
      allow create: if isSignedIn();
      allow update, delete: if isSignedIn() && isOwner(resource.data.ownerId);

      // Nested builds collection
      match /builds/{buildId} {
        allow read: if isSignedIn();
        allow write: if isSignedIn();
      }

      // Nested apiKeys collection
      match /apiKeys/{keyId} {
        allow read: if isSignedIn();
        allow write: if isSignedIn();
      }

      // Nested counters collection
      match /counters/{counterId} {
        allow read, write: if isSignedIn();
      }
    }
  }
}

Production Rules

For stricter security:

javascript
rules_version = '2';
service cloud.firestore {
  match /databases/{database}/documents {

    function isSignedIn() {
      return request.auth != null;
    }

    function isProjectOwner(projectId) {
      return isSignedIn() &&
        get(/databases/$(database)/documents/projects/$(projectId)).data.ownerId == request.auth.uid;
    }

    // Users
    match /users/{userId} {
      allow read: if isSignedIn() && request.auth.uid == userId;
      allow create: if isSignedIn() && request.auth.uid == userId;
      allow update: if isSignedIn() && request.auth.uid == userId;
    }

    // Projects
    match /projects/{projectId} {
      allow read: if isProjectOwner(projectId);
      allow create: if isSignedIn() &&
        request.resource.data.ownerId == request.auth.uid;
      allow update, delete: if isProjectOwner(projectId);

      // Builds - service account can write, owners can read
      match /builds/{buildId} {
        allow read: if isProjectOwner(projectId);
        allow create: if isSignedIn(); // Upload service creates builds
      }

      // API Keys
      match /apiKeys/{keyId} {
        allow read: if isProjectOwner(projectId);
        allow create: if isProjectOwner(projectId);
        allow update: if isProjectOwner(projectId) &&
          request.resource.data.diff(resource.data).affectedKeys()
            .hasOnly(['status', 'revokedAt', 'revokedBy']);
      }

      // Counters
      match /counters/{counterId} {
        allow read: if isProjectOwner(projectId);
        allow update: if isSignedIn(); // Upload service increments
      }
    }
  }
}

Indexes

Create these indexes in Firestore:

Projects by Owner

Collection: projects
Fields: ownerId (Ascending), createdAt (Descending)

Builds by Project

Collection: projects/{projectId}/builds
Fields: createdAt (Descending)

API Keys by Status

Collection: projects/{projectId}/apiKeys
Fields: status (Ascending), createdAt (Descending)

Service Account

For server-side access (Upload Service, CDN Service):

1. Generate Key

  1. Go to Project Settings → Service accounts
  2. Click "Generate new private key"
  3. Save the JSON file securely

2. Use in Services

Node.js:

bash
export GOOGLE_APPLICATION_CREDENTIALS=/path/to/serviceAccount.json

Cloudflare Workers:

Extract and set as secrets:

bash
wrangler secret put FIREBASE_PROJECT_ID
wrangler secret put FIREBASE_CLIENT_EMAIL
wrangler secret put FIREBASE_PRIVATE_KEY

GitHub OAuth Setup

1. Create GitHub OAuth App

  1. Go to GitHub → Settings → Developer settings → OAuth Apps
  2. Click "New OAuth App"
  3. Fill in:
    • Name: Scry Dashboard
    • Homepage URL: Your dashboard URL
    • Callback URL: From Firebase Console

2. Configure in Firebase

  1. Go to Authentication → Sign-in method → GitHub
  2. Enable GitHub
  3. Enter Client ID and Client Secret
  4. Note the callback URL
  5. Add callback URL to GitHub OAuth app
  6. Save

Troubleshooting

"Permission denied"

  • Check Firestore rules
  • Verify user is authenticated
  • Check ownerId matches authenticated user

"Invalid API key"

  • Verify NEXT_PUBLIC_FIREBASE_API_KEY
  • Check API key restrictions in Google Cloud Console

"Auth domain not authorized"

  • Add your domain to Firebase → Authentication → Settings → Authorized domains

Next Steps

Released under the MIT License.