Skip to content
Merged
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
2 changes: 1 addition & 1 deletion docs/06-concepts/11-authentication/01-get-started.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ This guide walks you through that, then shows how to test signing up and signing

## Show the sign-in screen

Your app already includes a sign-in screen. It is just turned off by default. Turn it on with two small edits to your app's `main.dart`.
Your app already includes a sign-in screen. It is turned off by default. Turn it on with two small edits to your app's `main.dart`.

First, import the screen:

Expand Down
16 changes: 8 additions & 8 deletions docs/06-concepts/11-authentication/01-setup.md
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,12 @@ Add the auth modules as dependencies to the server project's `pubspec.yaml`.
```yaml
dependencies:
...
serverpod_auth_idp_server: 3.x.x
serverpod_auth_idp_server: 4.0.0-beta.0
```

The `serverpod_auth_idp_server` package contains all components required to configure authentication services.

### Configure Authentication Services
### Configure authentication services

In your main `server.dart` file, configure the authentication system using the `pod.initializeAuthServices()` extension method.

Expand Down Expand Up @@ -72,7 +72,7 @@ import 'package:serverpod_auth_idp_server/core.dart' as core;
class RefreshJwtTokensEndpoint extends core.RefreshJwtTokensEndpoint {}
```

### Token Manager Configuration
### Token manager configuration

The authentication system uses token managers to handle authentication tokens. You need to configure at least one token manager to be used as the primary token manager. Additional token managers can be configured to be used for validation and management operations.

Expand All @@ -83,7 +83,7 @@ Serverpod provides two built-in token manager builders:

For more details on how to configure token managers or create custom ones, see the dedicated [Token Managers](./token-managers/managing-tokens) documentation.

### Identity Providers Configuration
### Identity providers configuration

Identity providers handle authentication with different methods (Email, Google, Apple, etc.). Each provider has its own configuration:

Expand Down Expand Up @@ -128,7 +128,7 @@ By default, endpoints for all providers are disabled. To enable a provider, it i
If this is the first time creating migrations after adding the module, besides the provider tables, all auth module tables will also be created. More detailed migration instructions can be found in the [migration guide](../database/migrations).
:::

### Storing Secrets
### Storing secrets

Secrets like peppers and private keys should be stored securely. The example above uses `pod.getPassword()` which reads from your `config/passwords.yaml` file or environment variables in the format `SERVERPOD_PASSWORD_<key>='value'`.

Expand Down Expand Up @@ -179,7 +179,7 @@ Add the `serverpod_auth_idp_client` package to your client project's `pubspec.ya
```yaml
dependencies:
...
serverpod_auth_idp_client: 3.x.x
serverpod_auth_idp_client: 4.0.0-beta.0
```

## App setup
Expand All @@ -190,8 +190,8 @@ First, add dependencies to your app's `pubspec.yaml` file for the methods of sig
dependencies:
flutter:
sdk: flutter
serverpod_auth_idp_flutter: 3.x.x
serverpod_flutter: 3.x.x
serverpod_auth_idp_flutter: 4.0.0-beta.0
serverpod_flutter: 4.0.0-beta.0
your_client:
path: ../your_client
```
Expand Down
24 changes: 23 additions & 1 deletion docs/06-concepts/11-authentication/02-basics.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ description: Authentication tokens are handled automatically by Serverpod. Learn

Serverpod automatically checks if the user is logged in and if the user has the right privileges to access each endpoint. When using the Serverpod Authentication modules, you will not have to worry about keeping track of tokens, refreshing them or even including them in requests as this all happens automatically under the hood.

The `Session` object provides information about the current user. A unique `userIdentifier` identifies a user as a `UuidValue`. You should use this id whenever you are referring to a user. Access the id of a signed-in user through the `authenticated` asynchronous getter of the `Session` object.
The `Session` object provides information about the current user. Access the current authentication through the synchronous `authenticated` getter of the `Session` object. It exposes a `userIdentifier`, a `String` that uniquely identifies the signed-in user. Use this id whenever you refer to a user.

```dart
Future<void> myMethod(Session session) async {
Expand Down Expand Up @@ -294,6 +294,28 @@ void _onAuthStateChanged() {

The listener is triggered whenever the user's sign-in state changes.

### Validate the session and handle expiry

Serverpod refreshes tokens automatically while the user stays signed in, so most apps never handle tokens directly. Expiry becomes visible only when the refresh token itself has expired or been revoked: the client can no longer refresh, and the stored session is no longer valid.

Call `validateAuthentication` to check the current session against the server and sign the user out if it is no longer valid:

```dart
await client.auth.validateAuthentication(); // throws on transient errors; retry if needed
```

The method force-refreshes the token and confirms with the server that the user is still signed in. If the session is no longer valid, it signs the user out on the current device. A transient problem, such as a network error or timeout, does not sign the user out; the exception is thrown instead, so you can catch it and retry.

At app startup, use `initialize` to restore a stored session and validate it in one step:

```dart
bool validated = await client.auth.initialize();
```

The `initialize` method runs `restore` followed by `validateAuthentication`. If the stored session has expired, the user is signed out. If validation cannot complete for a transient reason (network error, server error, or timeout), `initialize` returns `false` and leaves the stored session in place so you can retry later, which keeps offline users signed in.

Because signing out updates the authentication state, a listener registered on `authInfoListenable` (see [Monitor authentication changes](#monitor-authentication-changes)) fires when a session expires, so you can route the user back to a sign-in screen from one place.

## User authentication

### Signing out users
Expand Down
6 changes: 3 additions & 3 deletions docs/06-concepts/11-authentication/03-working-with-users.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ var userIdString = session.authenticated?.userIdentifier;
var userIdUuidValue = session.authenticated?.authUserId;
```

Further operations on the authenticated user can be performed using the `AuthUsers` class which is provide by the `AuthServices` instance.
Further operations on the authenticated user can be performed using the `AuthUsers` class which is provided by the `AuthServices` instance.

```dart
await AuthServices.instance.authUsers.delete(session, userIdUuidValue);
Expand Down Expand Up @@ -197,7 +197,7 @@ You can also extend the endpoint class to add custom profile editing functionali

```dart
class UserProfileEditEndpoint extends UserProfileEditBaseEndpoint {
Future<UserProfileModel> myCustomProfileEdit(Session session, String bio) async {
Future<UserProfileModel?> myCustomProfileEdit(Session session, String bio) async {
final userProfile = await session.authenticated?.userProfile(session);

// Your custom logic here...
Expand Down Expand Up @@ -268,7 +268,7 @@ When referencing module classes in your model files, you can use a nickname for

The model above creates a relation to the `AuthUser` table and ensures that each user can only have one `MyDomainData` object. The `onDelete=Cascade` ensures that when the `AuthUser` is deleted, the `MyDomainData` object is also deleted.

This makes it easy to query the additional information later based on the user's `authId`.
This makes it easy to query the additional information later based on the user's `authUserId`.

```dart
final authUserId = session.authenticated?.authUserId;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ void _sendRegistrationCode(
required Transaction? transaction,
}) {
// NOTE: Here you call your mail service to send the verification code to
// the user. For testing, we will just log the verification code.
// the user. For testing, we will log the verification code.
session.log('[EmailIDP] Registration code ($email): $verificationCode');
}

Expand All @@ -68,7 +68,7 @@ void _sendPasswordResetCode(
required Transaction? transaction,
}) {
// NOTE: Here you call your mail service to send the verification code to
// the user. For testing, we will just log the verification code.
// the user. For testing, we will log the verification code.
session.log('[EmailIDP] Password reset code ($email): $verificationCode');
}
```
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ final emailIdpConfig = EmailIdpConfigFromPasswords(

### Customizing Password Requirements

By default, the minimum password length is set to 8 characters. If you wish to modify this requirement, you can utilize the `passwordValidationFunction` configuration option.
By default, the minimum password length is set to 8 characters. If you wish to modify this requirement, you can use the `passwordValidationFunction` configuration option.

```dart
final emailIdpConfig = EmailIdpConfigFromPasswords(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ await admin.deletePasswordResetRequestsAttemptsForEmail(

### Cleaning Up Failed Login Attempts

Failed login attempts are tracked for rate limiting should also be cleaned up when no longer useful for auditing purposes:
Failed login attempts, tracked for rate limiting, should also be cleaned up when no longer useful for auditing purposes:

```dart
// Delete all failed login attempts
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ All platforms (iOS, Android, and Web) require a **Web application** OAuth client

### Store your credentials

Your server's `config/passwords.yaml` already has `development:`, `staging:`, and `production:` sections from the project template. Add the `googleClientSecret` key to the `development:` section using the client ID and client secret you just copied:
Your server's `config/passwords.yaml` already has `development:`, `staging:`, and `production:` sections from the project template. Add the `googleClientSecret` key to the `development:` section using the client ID and client secret you copied:

```yaml
development:
Expand Down Expand Up @@ -134,7 +134,7 @@ pod.initializeAuthServices(
);
```

`GoogleIdpConfigFromPasswords()` automatically loads the client secret from the `googleClientSecret` key in `config/passwords.yaml` (or the `SERVERPOD_PASSWORD_googleClientSecret` environment variable).
The `GoogleIdpConfigFromPasswords()` constructor automatically loads the client secret from the `googleClientSecret` key in `config/passwords.yaml` (or the `SERVERPOD_PASSWORD_googleClientSecret` environment variable).

:::tip
If you need more control over how the client secret is loaded, you can use `GoogleIdpConfig(clientSecret: GoogleClientSecret.fromJsonString(...))` instead. See the [customizations](./customizations) page for details.
Expand Down Expand Up @@ -252,7 +252,7 @@ flutter build web --output ../my_project_server/web/app # from your Flutter pro
serverpod start # from your server project
```

Open `http://localhost:8082/app` to test. `flutter run -d chrome` won't work here because Flutter's dev server runs on a different port from Serverpod for hot-reload workflows, use the [separately-hosted Flutter web](./customizations#separately-hosted-flutter-web) flow instead.
Open `http://localhost:8082/app` to test. `flutter run -d chrome` won't work here because Flutter's dev server runs on a different port from Serverpod: for hot-reload workflows, use the [separately-hosted Flutter web](./customizations#separately-hosted-flutter-web) flow instead.

The examples below use port `8082` (Serverpod's default from `config/development.yaml`).

Expand Down Expand Up @@ -399,7 +399,7 @@ body: SignInScreen(
),
```

`SignInWidget` renders the standard Google sign-in button:
The `SignInWidget` renders the standard Google sign-in button:

![Google sign-in button](/img/authentication/providers/google/3-button.png)

Expand All @@ -417,7 +417,7 @@ Before going live, complete the following steps:

Google's **Authorized domains** field on the [Branding](https://console.cloud.google.com/auth/branding) page accepts only the **top private domain** (the root). Once the root is verified, every subdomain under it is automatically authorized, and you do not need to add each project subdomain separately.

If you deploy on Serverpod Cloud under a `*.serverpod.space` subdomain, `serverpod.space` is already verified by Serverpod. Just add `serverpod.space` to **Authorized domains** in the Google Auth Platform, no DNS verification is required on your end.
If you deploy on Serverpod Cloud under a `*.serverpod.space` subdomain, `serverpod.space` is already verified by Serverpod. Add `serverpod.space` to **Authorized domains** in the Google Auth Platform; no DNS verification is required on your end.

For a custom domain, verify ownership of your root domain (e.g., `example.com`) at [Google Search Console](https://search.google.com/search-console) by adding the DNS TXT record Google provides. After verification completes, add the root to **Authorized domains** in the Google Auth Platform.

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ The Google identity provider can be configured using one of two classes:
- **`GoogleIdpConfigFromPasswords`**: Automatically loads the client secret from the `googleClientSecret` key in `passwords.yaml` (or the `SERVERPOD_PASSWORD_googleClientSecret` environment variable). This is the class used in the [setup guide](./setup) and is recommended for most projects.
- **`GoogleIdpConfig`**: Requires you to pass a `GoogleClientSecret` object directly. Use this when you need to load credentials from a custom source, such as a JSON file, a secrets manager, or a programmatically constructed map.

`GoogleIdpConfigFromPasswords` is a convenience wrapper around `GoogleIdpConfig` that handles credential loading for you.
The `GoogleIdpConfigFromPasswords` class is a convenience wrapper around `GoogleIdpConfig` that handles credential loading for you.

Both classes accept the same optional callbacks shown in the sections below. The examples on this page use `GoogleIdpConfigFromPasswords` unless the section specifically demonstrates manual client secret loading.

Expand Down Expand Up @@ -119,9 +119,9 @@ final googleIdpConfig = GoogleIdpConfigFromPasswords(

### Reacting to auth user creation

The `onBeforeAuthUserCreated` and `onAfterAuthUserCreated` hooks are global callbacks configured on `AuthUsersConfig` in `initializeAuthServices`. They are not specific to Google -- they fire for every identity provider. See the [working with users](../../working-with-users#reacting-to-the-user-created-event) page for full details.
The `onBeforeAuthUserCreated` and `onAfterAuthUserCreated` hooks are global callbacks configured on `AuthUsersConfig` in `initializeAuthServices`. They are not specific to Google; they fire for every identity provider. See the [working with users](../../working-with-users#reacting-to-the-user-created-event) page for full details.

`onBeforeAuthUserCreated` receives the default scopes and blocked status for the new user and must return the final values. Use it to assign custom scopes at creation time:
The `onBeforeAuthUserCreated` callback receives the default scopes and blocked status for the new user and must return the final values. Use it to assign custom scopes at creation time:

```dart
pod.initializeAuthServices(
Expand Down Expand Up @@ -156,7 +156,7 @@ pod.initializeAuthServices(

### Lightweight Sign-In on the Flutter app

Lightweight sign-in is a feature that attempts to authenticate users previously logged in with Google automatically with minimal or no user interaction. When enabled, the Google authentication controller will try to sign in users seamlessly using platform-specific lightweight authentication methods. This feature is disabled by default, but can be enabled from the `GoogleSignInWidget` or `GoogleAuthController`.
Lightweight sign-in is a feature that attempts to authenticate users previously logged in with Google automatically with minimal or no user interaction. When enabled, the Google authentication controller will try to sign the user in using platform-specific lightweight authentication methods. This feature is disabled by default, but can be enabled from the `GoogleSignInWidget` or `GoogleAuthController`.

```dart
GoogleSignInWidget(
Expand All @@ -169,7 +169,7 @@ GoogleSignInWidget(
```

:::note
Lightweight sign-in runs automatically when the controller is initialized (typically at app launch). If it fails — e.g., no previous session, or the user dismisses the prompt the regular sign-in button remains available.
Lightweight sign-in runs automatically when the controller is initialized (typically at app launch). If it fails (no previous session, or the user dismisses the prompt), the regular sign-in button remains available.
:::

### Configuring Client IDs on the App
Expand Down Expand Up @@ -246,7 +246,7 @@ Use this flow when your Flutter web app and Serverpod are on different origins.

1. Place a static `auth.html` file in your Flutter project's `web/` folder. A single copy is shared across every identity provider that uses an OAuth2 redirect, so create it once. Follow [Web callback page (`auth.html`)](../../setup#web-callback-page-authhtml) in the authentication setup guide.

2. Run Flutter on a fixed port. The examples use `49660`, but any free port works keep it consistent everywhere:
2. Run Flutter on a fixed port. The examples use `49660`, but any free port works, as long as you keep it consistent everywhere:

```bash
flutter run -d chrome --web-port=49660
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,9 @@ Common mistakes:

**Problem:** You followed [Web setup](./setup#web) and registered `FlutterWebAuth2CallbackRoute`. Sign-in completes at Google, the browser redirects, but the Flutter app never receives the result. Only affects `flutter run -d chrome` local dev.

**Cause:** The integrated route requires Serverpod and your Flutter web app to be on the **same origin** (same scheme, host, AND port). With `flutter run -d chrome`, Flutter runs on its own dev server port (e.g., `49660`) while Serverpod is on `8082` different origins. The browser blocks the callback page's `postMessage` across origins.
**Cause:** The integrated route requires Serverpod and your Flutter web app to be on the **same origin** (same scheme, host, AND port). With `flutter run -d chrome`, Flutter runs on its own dev server port (e.g., `49660`) while Serverpod is on `8082`, so they are different origins. The browser blocks the callback page's `postMessage` across origins.

**Resolution:** Use the [separately-hosted Flutter web](./customizations#separately-hosted-flutter-web) flow for local dev it serves `auth.html` from Flutter's own dev server, same-origin with your Flutter app. For production, the integrated route works once Serverpod serves your Flutter build (template default via `FlutterRoute` on `/app`).
**Resolution:** Use the [separately-hosted Flutter web](./customizations#separately-hosted-flutter-web) flow for local dev; it serves `auth.html` from Flutter's own dev server, same-origin with your Flutter app. For production, the integrated route works once Serverpod serves your Flutter build (template default via `FlutterRoute` on `/app`).

## Sign-in callback never returns to the Flutter app

Expand Down
Loading
Loading