# How the Omi Backend Handles Authentication with Firebase and API Keys

> Learn how the Omi backend authenticates users with Firebase and API keys. Discover how it exchanges codes for tokens and verifies them for secure access to resources.

- Repository: [omi/omi](https://github.com/basedhardware/omi)
- Tags: how-to-guide
- Published: 2026-02-26

---

**Omi’s backend authenticates users by initializing the Firebase Admin SDK at startup, exchanging OAuth codes or email credentials for Firebase ID tokens via REST endpoints protected by API keys, and verifying every token with `firebase_admin.auth.verify_id_token` before serving protected resources.**

The Omi open-source AI wearable platform uses Firebase Authentication as its identity backbone for both the core mobile app and third-party integrations. The backend implements a hybrid authentication model that combines server-side Firebase Admin SDK operations with client-facing REST API calls secured by Firebase API keys. This architecture supports multiple login methods—including Google, Apple, and email/password—while ensuring that all protected routes validate JWTs against Firebase’s certificate chain.

## Firebase Admin SDK Initialization

The authentication stack begins with a singleton Firebase Admin instance initialized at application startup. In [`backend/main.py`](https://github.com/basedhardware/omi/blob/main/backend/main.py), the system checks for a `SERVICE_ACCOUNT_JSON` environment variable containing service account credentials.

If present, the backend loads the JSON and creates a certificate-based credential:

```python

# backend/main.py

if os.environ.get('SERVICE_ACCOUNT_JSON'):
    service_account_info = json.loads(os.environ["SERVICE_ACCOUNT_JSON"])
    credentials = firebase_admin.credentials.Certificate(service_account_info)
    firebase_admin.initialize_app(credentials)
else:
    firebase_admin.initialize_app()

```

This singleton instance handles server-side operations like `auth.create_user` and `auth.verify_id_token` throughout the application lifecycle. When `SERVICE_ACCOUNT_JSON` is omitted, the SDK falls back to Google’s default credentials mechanism, useful for local development or GCP environments.

## OAuth Authentication Flow for Google and Apple

The Omi backend implements a complete OAuth 2.0 server-side flow for social providers, culminating in Firebase token generation via the `signInWithIdp` REST endpoint.

### Starting the Authorization Flow

When a client requests authentication via `GET /v1/auth/authorize`, the backend generates a unique session ID and stores the requested provider (`google` or `apple`) in Redis using `set_auth_session`. The user is then redirected to the provider’s OAuth URL with the session ID embedded in the `state` parameter.

This logic resides in `auth_authorize` within [`backend/routers/auth.py`](https://github.com/basedhardware/omi/blob/main/backend/routers/auth.py) (lines 31-58).

### Handling Provider Callbacks

After the user authenticates with the provider, the backend handles callbacks at `/v1/auth/callback/google` or `/v1/auth/callback/apple`. The system retrieves the original session using the `state` value, then exchanges the authorization code for provider tokens:

- **Google**: `_exchange_google_code_for_oauth_credentials` exchanges the code for an ID token and access token using the `GOOGLE_CLIENT_ID` and `GOOGLE_CLIENT_SECRET` environment variables (lines 62-104).
- **Apple**: `_exchange_apple_code_for_oauth_credentials` performs the same operation using Apple’s JWT-based client secret generation with `APPLE_CLIENT_ID`, `APPLE_TEAM_ID`, `APPLE_KEY_ID`, and `APPLE_PRIVATE_KEY` (lines 107-165).

The raw OAuth credentials are temporarily stored in Redis as an auth code via `set_auth_code` for the final token exchange step.

### Exchanging Tokens with Firebase

The client completes authentication by posting to `/v1/auth/token` with the temporary auth code. If `use_custom_token=true`, the backend calls `_generate_custom_token` (lines 74-101), which invokes Firebase’s **signInWithIdp** REST endpoint:

```python
sign_in_url = f"https://identitytoolkit.googleapis.com/v1/accounts:signInWithIdp?key={firebase_api_key}"
post_body = f"id_token={id_token}&providerId=google.com"

# ... includes access_token if present

```

The `FIREBASE_API_KEY` environment variable authorizes this REST call. Firebase returns a JWT `idToken` that the client can use for subsequent authenticated requests to Omi’s backend.

## Email and Password Authentication

For traditional credential-based access, Omi provides `/v1/signup` and `/v1/signin` endpoints in [`backend/routers/custom_auth.py`](https://github.com/basedhardware/omi/blob/main/backend/routers/custom_auth.py).

**Sign-up** calls `firebase_admin.auth.create_user` directly to provision a Firebase user with the provided email and password.

**Sign-in** uses the Firebase Auth REST API’s `signInWithPassword` endpoint:

```python

# Endpoint construction

sign_in_url = f"https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword?key={api_key}"

```

This call uses the `CUSTOM_AUTH_FIREBASE_API_KEY` environment variable (which may differ from the standard `FIREBASE_API_KEY`). After receiving the `idToken` from Firebase, the backend immediately validates it using `firebase_admin.auth.verify_id_token` before returning the UID and token details to the client (lines 20-60).

## Third-Party App Authentication via OAuth

Omi supports third-party app integration through a dedicated OAuth flow defined in [`backend/routers/oauth.py`](https://github.com/basedhardware/omi/blob/main/backend/routers/oauth.py).

The authorization page (`/v1/oauth/authorize`) renders an HTML page embedding the Firebase configuration (API key, auth domain, and project ID) required by the client-side JavaScript SDK. After the user authenticates with Firebase on the client side, the app posts the resulting ID token to `/v1/oauth/token`.

The backend verifies this token using `firebase_admin.auth.verify_id_token`, optionally enables the requested app for the user, and returns a redirect URL to the third-party application (lines 12-78).

## Token Verification and Security Model

All authentication paths converge on **`firebase_admin.auth.verify_id_token`**. Whether the token originates from Google OAuth, Apple OAuth, or email/password sign-in, protected routes extract the Bearer token from the Authorization header and validate it against Firebase’s public certificates.

The verification process guarantees three security properties:
- **Authenticity**: The token was signed by Firebase’s private keys.
- **Integrity**: The token payload has not been tampered with.
- **Validity**: The token has not expired and matches the Omi Firebase project.

Upon successful verification, the backend extracts the Firebase UID (`uid`) and uses it as the primary key for user-specific data in Redis, Firestore, and other data stores.

## Summary

- **Firebase Admin SDK** initializes as a singleton in [`backend/main.py`](https://github.com/basedhardware/omi/blob/main/backend/main.py) using either service account JSON or default credentials.
- **OAuth flows** for Google and Apple exchange provider codes for Firebase ID tokens via the `signInWithIdp` REST endpoint, protected by the `FIREBASE_API_KEY`.
- **Email/password auth** uses the `signInWithPassword` REST endpoint with a separate `CUSTOM_AUTH_FIREBASE_API_KEY`, followed by server-side token verification.
- **Third-party apps** authenticate through a browser-based OAuth flow that validates client-side Firebase JWTs at the `/v1/oauth/token` endpoint.
- **Universal verification** via `firebase_admin.auth.verify_id_token` ensures only valid Firebase UIDs access protected resources.

## Frequently Asked Questions

### How does Omi verify that a Firebase ID token is legitimate?

The backend calls `firebase_admin.auth.verify_id_token` on every protected request. This function checks the JWT signature against Firebase’s public certificates, validates the issuer and audience claims, and ensures the token has not expired. Only tokens issued by the specific Omi Firebase project are accepted.

### What is the difference between `FIREBASE_API_KEY` and `CUSTOM_AUTH_FIREBASE_API_KEY`?

`FIREBASE_API_KEY` is the standard Web API key used for OAuth provider sign-ins via the `signInWithIdp` endpoint. `CUSTOM_AUTH_FIREBASE_API_KEY` is an optional separate key used specifically for the email/password `signInWithPassword` endpoint in [`backend/routers/custom_auth.py`](https://github.com/basedhardware/omi/blob/main/backend/routers/custom_auth.py). This separation allows different rate limits or security policies for custom authentication versus social login.

### Where are temporary OAuth sessions stored during the authentication flow?

Temporary sessions and authorization codes are stored in Redis using functions defined in [`backend/database/redis_db.py`](https://github.com/basedhardware/omi/blob/main/backend/database/redis_db.py). The `set_auth_session` function stores the provider choice associated with a session ID, while `set_auth_code` temporarily caches OAuth credentials before they are exchanged for Firebase tokens. These entries have short TTL values to prevent replay attacks.

### Can the Omi backend work without a service account JSON file?

Yes. If the `SERVICE_ACCOUNT_JSON` environment variable is not set, the backend calls `firebase_admin.initialize_app()` without arguments in [`backend/main.py`](https://github.com/basedhardware/omi/blob/main/backend/main.py). This triggers the Firebase Admin SDK’s default credentials discovery, which works automatically when running on Google Cloud Platform or when the `GOOGLE_APPLICATION_CREDENTIALS` environment variable points to a valid service account file.