Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,258 @@
# Setup

Firebase authentication works differently from other identity providers in Serverpod. Instead of handling authentication directly, Serverpod's Firebase integration acts as a bridge between Firebase Authentication and your Serverpod backend. Firebase handles the actual sign-in process through its own SDKs and UI components, while Serverpod syncs the authenticated user and manages the server-side session.

This approach allows you to use any authentication method supported by Firebase (email/password, phone, Google, Apple, Facebook, etc.) while maintaining a unified user system in your Serverpod backend.

:::caution
You need to install the auth module before you continue, see [Setup](../../setup).
:::

## Prerequisites

Before setting up Firebase authentication, ensure you have:

- A Firebase project (create one at [Firebase Console](https://console.firebase.google.com/))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add ending dot to all entries on lists. This is a general suggestion for all lists (numbered or not) to make it more syntactically correct and consistent with other lists throughout the authentication docs. We usually have lists that are either an introductory stem (like this one) or a sequence of sentences, which both require punctuation.

- Firebase CLI installed (`npm install -g firebase-tools`)
- FlutterFire CLI installed (`dart pub global activate flutterfire_cli`)
- At least one authentication method enabled in your Firebase project
Comment on lines +11 to +18
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Instead of a "Prerequisites" session, can we introduce each one along the way as steps on the guide? For example, the last one (having methods enabled) is already shown below, so it is not actually a prerequisite for the guide.


## Create your credentials

### Generate Service Account Key

The server needs service account credentials to verify Firebase ID tokens. To create a new key:

1. Go to the [Firebase Console](https://console.firebase.google.com/)
2. Select your project
3. Navigate to **Project settings** > **Service accounts**
4. Click **Generate new private key**, then **Generate key**

![Service account](/img/authentication/providers/firebase/1-server-key.png)

This downloads a JSON file containing your service account credentials.

### Enable Authentication Methods

In the Firebase Console, enable the authentication methods you want to support:

1. Go to **Authentication** > **Sign-in method**
2. Enable your desired providers (Email/Password, Phone, Google, Apple, etc.)
3. Configure each provider according to Firebase's documentation

![Auth provider](/img/authentication/providers/firebase/2-auth-provider.png)

## Server-side configuration

### Store the Service Account Key

This can be done by pasting the contents of the JSON file into the `firebaseServiceAccountKey` key in the `config/passwords.yaml` file or setting as value of the `SERVERPOD_PASSWORD_firebaseServiceAccountKey` environment variable. Alternatively, you can read the file contents directly using the `FirebaseServiceAccountCredentials.fromJsonFile()` method.

```yaml
development:
firebaseServiceAccountKey: |
{
"type": "service_account",
"project_id": "your-project-id",
"private_key_id": "...",
"private_key": "-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n",
"client_email": "firebase-adminsdk-xxxxx@your-project-id.iam.gserviceaccount.com",
"client_id": "...",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token"
}
```
Comment on lines +51 to +64
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Can we add a similar example to the Google Sign-In setup guide? This is very good for users to visualize.


:::warning
The service account key gives admin access to your Firebase project and should not be version controlled. Store it securely using environment variables or secret management.
:::

### Configure the Firebase Identity Provider

In your main `server.dart` file, configure the Firebase identity provider:

```dart
import 'package:serverpod/serverpod.dart';
import 'package:serverpod_auth_idp_server/core.dart';
import 'package:serverpod_auth_idp_server/providers/firebase.dart';

void run(List<String> args) async {
final pod = Serverpod(
args,
Protocol(),
Endpoints(),
);

pod.initializeAuthServices(
tokenManagerBuilders: [
JwtConfigFromPasswords(),
],
identityProviderBuilders: [
FirebaseIdpConfig(
credentials: FirebaseServiceAccountCredentials.fromJsonString(
pod.getPassword('firebaseServiceAccountKey')!,
),
),
],
);

await pod.start();
}
```

:::tip
You can use `FirebaseIdpConfigFromPasswords()` to automatically load credentials from `config/passwords.yaml` or the `SERVERPOD_PASSWORD_firebaseServiceAccountKey` environment variable:

```dart
identityProviderBuilders: [
FirebaseIdpConfigFromPasswords(),
],
```

:::

### Expose the Endpoint

Create an endpoint that extends `FirebaseIdpBaseEndpoint` to expose the Firebase authentication API:

```dart
import 'package:serverpod_auth_idp_server/providers/firebase.dart';

class FirebaseIdpEndpoint extends FirebaseIdpBaseEndpoint {}
```

### Generate and Migrate

Run the following commands to generate client code and create the database migration:

```bash
serverpod generate
```

Then create and apply the migration:

```bash
serverpod create-migration
docker compose up --build --detach
dart run bin/main.dart --role maintenance --apply-migrations
```

More detailed instructions can be found in the general [identity providers setup section](../../setup#identity-providers-configuration).
Comment on lines +124 to +140
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Suppress this details and keep only the reference to the providers configuration, as we do on the other provider setup guides. This is intentional to avoid making specific guides more verbose with steps that users might already be familiar while also simplifying future evolution of the docs (like when introducing new migrations UX).

For consistency, I would suggest having the same wording we use on others:

Suggested change
### Generate and Migrate
Run the following commands to generate client code and create the database migration:
```bash
serverpod generate
```
Then create and apply the migration:
```bash
serverpod create-migration
docker compose up --build --detach
dart run bin/main.dart --role maintenance --apply-migrations
```
More detailed instructions can be found in the general [identity providers setup section](../../setup#identity-providers-configuration).
Finally, run `serverpod generate` to generate the client code and create a migration to initialize the database for the provider. More detailed instructions can be found in the general [identity providers setup section](../../setup#identity-providers-configuration).


### Basic configuration options

- `credentials`: Required. Firebase service account credentials for verifying ID tokens. See the [configuration section](./configuration) for different ways to load credentials.
- `firebaseAccountDetailsValidation`: Optional. Validation function for Firebase account details. By default, this validates that the email is verified when present (phone-only authentication is allowed). See the [configuration section](./configuration#custom-account-validation) for customization options.

## Client-side configuration

Comment on lines +147 to +148
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: Add a brief summary of the session here, like saying that we will use the official Firebase packages and that the below steps follow standard Firebase usage guide. If there is one, it is also worth referencing an official Firebase troubleshooting guide. Although this is slightly redundant with the general description of the setup, it is here that the information is useful, so it is worth repeating.

### Install Required Packages

Add the Firebase and Serverpod authentication packages to your Flutter project:

```bash
flutter pub add firebase_core firebase_auth serverpod_auth_idp_flutter_firebase
```

If you want to use Firebase's pre-built UI components, also add:

```bash
flutter pub add firebase_ui_auth
```

### Configure FlutterFire

Run the FlutterFire CLI to configure Firebase for your Flutter project:

```bash
flutterfire configure
```

This generates a `firebase_options.dart` file with your platform-specific Firebase configuration.

### Initialize Firebase and Serverpod

In your `main.dart`, initialize both Firebase and the Serverpod client:

```dart
import 'package:firebase_core/firebase_core.dart';
import 'package:flutter/material.dart';
import 'package:serverpod_flutter/serverpod_flutter.dart';
import 'package:serverpod_auth_idp_flutter_firebase/serverpod_auth_idp_flutter_firebase.dart';
import 'package:your_client/your_client.dart';
import 'firebase_options.dart';

late Client client;

void main() async {
WidgetsFlutterBinding.ensureInitialized();

// Initialize Firebase
await Firebase.initializeApp(
options: DefaultFirebaseOptions.currentPlatform,
);

// Create the Serverpod client
client = Client('http://localhost:8080/')
..connectivityMonitor = FlutterConnectivityMonitor()
..authSessionManager = FlutterAuthSessionManager();

// Initialize Serverpod auth
await client.auth.initialize();

// Initialize Firebase sign-in service (enables automatic sign-out sync)
client.auth.initializeFirebaseSignIn();

runApp(const MyApp());
}
```

## The authentication flow

Understanding the Firebase authentication flow helps when building custom integrations:

1. **User initiates sign-in** with Firebase using `firebase_auth` or `firebase_ui_auth`
2. **Firebase authenticates** the user and returns a `firebase_auth.User` object
3. **Your app calls** `FirebaseAuthController.login(user)` with the Firebase user
4. **The controller extracts** the Firebase ID token from the user
5. **Token is sent** to your server's `firebaseIdp.login()` endpoint
6. **Server validates** the JWT using the service account credentials
7. **Server creates or updates** the user account and issues a Serverpod session token
8. **Client session is updated** and the user is authenticated with Serverpod
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
8. **Client session is updated** and the user is authenticated with Serverpod
8. **Client session is updated** and the user is authenticated with Serverpod in the Flutter app


:::info
The `initializeFirebaseSignIn()` call in the client setup automatically signs out from Firebase when the user signs out from Serverpod, keeping both systems in sync.
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: The way I read this is that the call in itself will sign out (once), but not that it will keep monitoring changes. Just a small wording suggestion to make it clearer:

Suggested change
The `initializeFirebaseSignIn()` call in the client setup automatically signs out from Firebase when the user signs out from Serverpod, keeping both systems in sync.
The `initializeFirebaseSignIn()` call in the client setup will ensure that the user gets automatically signed out from Firebase when signing out from Serverpod to keep both systems in sync.

:::

## Present the authentication UI

### Using FirebaseAuthController

The `FirebaseAuthController` handles syncing Firebase authentication state with your Serverpod backend. After a user signs in with Firebase, pass the Firebase user to the controller:

```dart
import 'package:firebase_auth/firebase_auth.dart' as firebase_auth;
import 'package:serverpod_auth_idp_flutter_firebase/serverpod_auth_idp_flutter_firebase.dart';

// Create the controller
final controller = FirebaseAuthController(
client: client,
onAuthenticated: () {
// User successfully synced with Serverpod
// NOTE: Do not navigate here - use client.auth.authInfoListenable instead
},
onError: (error) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(content: Text('Error: $error')),
);
},
);

// After user signs in with Firebase
final firebaseUser = firebase_auth.FirebaseAuth.instance.currentUser;
if (firebaseUser != null) {
await controller.login(firebaseUser);
}
```

For details on building custom authentication UIs and integrating with `firebase_ui_auth`, see the [customizing the UI section](./customizing-the-ui).
Comment on lines +227 to +258
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggestion: This session is about Presenting the UI, but we are only showing how to use the controller. I suggest flipping the importance here: have the SignInScreen example directly here and showing more details of the controller in the other session. Because the goal of the guide is for the user to finish having the provider fully integrated and usable from the UI.

Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
# Configuration

This page covers configuration options for the Firebase identity provider beyond the basic setup.

## Configuration options

Below is a non-exhaustive list of some of the most common configuration options. For more details on all options, check the `FirebaseIdpConfig` in-code documentation.

### Loading Firebase Credentials

You can load Firebase service account credentials in several ways:

**From JSON string (recommended for production):**

```dart
final firebaseIdpConfig = FirebaseIdpConfig(
credentials: FirebaseServiceAccountCredentials.fromJsonString(
pod.getPassword('firebaseServiceAccountKey')!,
),
);
```

**From JSON file:**

```dart
import 'dart:io';

final firebaseIdpConfig = FirebaseIdpConfig(
credentials: FirebaseServiceAccountCredentials.fromJsonFile(
File('config/firebase_service_account_key.json'),
),
);
```

**From JSON map:**

```dart
final firebaseIdpConfig = FirebaseIdpConfig(
credentials: FirebaseServiceAccountCredentials.fromJson({
'type': 'service_account',
'project_id': 'your-project-id',
'private_key_id': '...',
'private_key': '-----BEGIN PRIVATE KEY-----\n...\n-----END PRIVATE KEY-----\n',
'client_email': 'firebase-adminsdk-xxxxx@your-project-id.iam.gserviceaccount.com',
'client_id': '...',
'auth_uri': 'https://accounts.google.com/o/oauth2/auth',
'token_uri': 'https://oauth2.googleapis.com/token',
}),
);
```

### Custom Account Validation

You can customize the validation for Firebase account details before allowing sign-in. By default, the validation requires the email to be verified when present (phone-only authentication is allowed).

The default validation logic:

```dart
static void validateFirebaseAccountDetails(
final FirebaseAccountDetails accountDetails,
) {
// Firebase accounts may not have email if using phone auth
// Only validate verifiedEmail if email is present
if (accountDetails.email != null && accountDetails.verifiedEmail != true) {
throw FirebaseUserInfoMissingDataException();
}
}
```

To customize validation, provide your own `firebaseAccountDetailsValidation` function:

```dart
final firebaseIdpConfig = FirebaseIdpConfig(
credentials: FirebaseServiceAccountCredentials.fromJsonString(
pod.getPassword('firebaseServiceAccountKey')!,
),
firebaseAccountDetailsValidation: (accountDetails) {
// Require verified email (even for phone auth)
if (accountDetails.verifiedEmail != true) {
throw Exception('Email must be verified');
}

// Restrict to specific email domain
if (accountDetails.email != null &&
!accountDetails.email!.endsWith('@example.com')) {
throw Exception('Only @example.com emails allowed');
}
},
);
```

### FirebaseAccountDetails

The `firebaseAccountDetailsValidation` callback receives a `FirebaseAccountDetails` record with the following properties:

| Property | Type | Description |
|----------|------|-------------|
| `userIdentifier` | `String` | The Firebase user's unique identifier (UID) |
| `email` | `String?` | The user's email address (null for phone-only auth) |
| `fullName` | `String?` | The user's display name from Firebase |
| `image` | `Uri?` | URL to the user's profile image |
| `verifiedEmail` | `bool?` | Whether the email is verified |
| `phone` | `String?` | The user's phone number (for phone auth) |

Example of accessing these properties:

```dart
firebaseAccountDetailsValidation: (accountDetails) {
print('Firebase UID: ${accountDetails.userIdentifier}');
print('Email: ${accountDetails.email}');
print('Email verified: ${accountDetails.verifiedEmail}');
print('Display name: ${accountDetails.fullName}');
print('Profile image: ${accountDetails.image}');
print('Phone: ${accountDetails.phone}');

// Custom validation logic
if (accountDetails.email == null && accountDetails.phone == null) {
throw Exception('Either email or phone is required');
}
},
```

:::info
The properties available depend on the Firebase authentication method used. For example, `phone` is only populated for phone authentication, and `email` may be null if the user signed in with phone only.
:::
Loading