Skip to content
351 changes: 218 additions & 133 deletions docs/06-concepts/11-authentication/04-providers/06-firebase/01-setup.md

Large diffs are not rendered by default.

Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,13 @@

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

## Configuration options
## Loading credentials with FirebaseIdpConfig

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.
The [setup guide](./setup) uses `FirebaseIdpConfigFromPasswords`, which loads the service account key from `passwords.yaml` for you. When you need to load credentials from a different source (a file path, a secrets manager, or just a project ID), use `FirebaseIdpConfig` directly and pass a `FirebaseServiceAccountCredentials` instance.

### Loading Firebase Credentials
`FirebaseServiceAccountCredentials` provides four constructors. These are the only supported ways to construct it:

You can load Firebase service account credentials in several ways:

**From JSON string (recommended for production):**
**From a JSON string** (use this when reading the JSON from a secrets manager or environment variable):

```dart
final firebaseIdpConfig = FirebaseIdpConfig(
Expand All @@ -20,7 +18,7 @@ final firebaseIdpConfig = FirebaseIdpConfig(
);
```

**From JSON file:**
**From a JSON file** (useful for local development or when secrets are mounted as files):

```dart
import 'dart:io';
Expand All @@ -32,7 +30,7 @@ final firebaseIdpConfig = FirebaseIdpConfig(
);
```

**From JSON map:**
**From a JSON map** (useful when credentials are assembled programmatically):

```dart
final firebaseIdpConfig = FirebaseIdpConfig(
Expand All @@ -49,31 +47,28 @@ final firebaseIdpConfig = FirebaseIdpConfig(
);
```

### 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:
**Project ID only** (only token verification, no admin operations like deleting Firebase accounts):

```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();
}
}
final firebaseIdpConfig = FirebaseIdpConfig(
credentials: const FirebaseServiceAccountCredentials(
projectId: 'your-project-id',
),
);
```

:::note
Only `projectId` is required to verify Firebase ID tokens. The full service account JSON is only needed if you also use the [admin operations](./admin-operations) on the server.
:::

## 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 without an email).

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

```dart
final firebaseIdpConfig = FirebaseIdpConfig(
credentials: FirebaseServiceAccountCredentials.fromJsonString(
pod.getPassword('firebaseServiceAccountKey')!,
),
final firebaseIdpConfig = FirebaseIdpConfigFromPasswords(
firebaseAccountDetailsValidation: (accountDetails) {
// Require verified email (even for phone auth)
if (accountDetails.verifiedEmail != true) {
Expand All @@ -89,64 +84,63 @@ final firebaseIdpConfig = FirebaseIdpConfig(
);
```

### FirebaseAccountDetails
### FirebaseAccountDetails properties

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');
}
},
```
- `userIdentifier` (`String`): Firebase UID.
- `email` (`String?`): Email address, or `null` for phone-only sign-in.
- `fullName` (`String?`): Display name from Firebase.
- `image` (`Uri?`): Profile image URL.
- `verifiedEmail` (`bool?`): Whether the email is verified.
- `phone` (`String?`): Phone number, only populated for phone authentication.

:::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.
:::
Which properties are populated depends on the Firebase sign-in method the user chose. For example, `phone` is only populated for phone authentication, and `email` may be `null` if the user signed in with phone only.

### Reacting to account creation
## Reacting to auth user creation

You can use the `onAfterFirebaseAccountCreated` callback to run logic after a new Firebase account has been created and linked to an auth user. This callback is only invoked for new accounts, not for returning users.
[`onBeforeAuthUserCreated`](https://pub.dev/documentation/serverpod_auth_idp_server/latest/core/AuthUsersConfig/onBeforeAuthUserCreated.html) and [`onAfterAuthUserCreated`](https://pub.dev/documentation/serverpod_auth_idp_server/latest/core/AuthUsersConfig/onAfterAuthUserCreated.html) are global callbacks on `AuthUsersConfig`. They fire for every identity provider, not just Firebase. See [Working with users](../../working-with-users#reacting-to-the-user-created-event) for full details.

This callback is complimentary to the [core `onAfterAuthUserCreated` callback](../../working-with-users#reacting-to-the-user-created-event) to perform side-effects that are specific to a login on this provider - like storing analytics, sending a welcome email, or storing additional data.
The example below uses Firebase phone numbers as the trigger for assigning a `phone-verified` scope at sign-up, and persists the Firebase UID for later admin lookups:

```dart
final firebaseIdpConfig = FirebaseIdpConfigFromPasswords(
onAfterFirebaseAccountCreated: (
session,
authUser,
firebaseAccount, {
required transaction,
}) async {
// e.g. store additional data, send a welcome email, or log for analytics
},
pod.initializeAuthServices(
tokenManagerBuilders: [
JwtConfigFromPasswords(),
],
identityProviderBuilders: [
FirebaseIdpConfigFromPasswords(),
],
authUsersConfig: AuthUsersConfig(
onBeforeAuthUserCreated: (
session,
scopes,
blocked, {
required transaction,
}) {
return (
scopes: {...scopes, Scope('user')},
blocked: blocked,
);
},
onAfterAuthUserCreated: (
session,
authUser, {
required transaction,
}) async {
// e.g. send a welcome email, log for analytics
},
),
);
```

:::info
This callback runs inside the same database transaction as the account creation. Throwing an exception inside this callback will abort the process. If you perform external side-effects, make sure to safeguard them with a try/catch to prevent unwanted failures.
:::warning
Both callbacks run inside the same database transaction as the account creation. Throwing an exception inside either callback aborts the sign-up. Wrap external side-effects (email sending, analytics) in `try`/`catch` so a third-party outage does not block new sign-ups.
:::

:::caution
If you need to assign Serverpod scopes based on provider account data, note that updating the database alone (via `AuthServices.instance.authUsers.update()`) is **not enough** for the current login session. The token issuance uses the in-memory `authUser.scopes`, which is already set before this callback runs. You would need to update `authUser.scopes` as well for the scopes to be reflected in the issued tokens. For assigning scopes at creation time, consider using `onBeforeAuthUserCreated` to set scopes based on data collected earlier in the flow.
:::
## FirebaseIdpConfig parameter reference

| Parameter | Type | Required | Description |
| --- | --- | --- | --- |
| `credentials` | `FirebaseServiceAccountCredentials` | Yes | Firebase service account credentials for verifying ID tokens. Can be loaded via `fromJsonString`, `fromJsonFile`, or `fromJson`. When using `FirebaseIdpConfigFromPasswords`, this is loaded automatically from the `firebaseServiceAccountKey` key in `passwords.yaml` or the `SERVERPOD_PASSWORD_firebaseServiceAccountKey` environment variable. |
| `firebaseAccountDetailsValidation` | `FirebaseAccountDetailsValidation?` | No | Custom validation callback for Firebase account details before allowing sign-in. By default, validates that email is verified when present (phone-only auth is allowed). |
Loading
Loading