From b4a360579cfd79669ed84e9381fcbcc496f6fd53 Mon Sep 17 00:00:00 2001 From: developerjamiu Date: Tue, 7 Jul 2026 17:39:25 +0100 Subject: [PATCH 1/3] docs: Review and conform the Authentication pages, add expired-auth handling (#654) --- .../11-authentication/01-get-started.md | 2 +- .../06-concepts/11-authentication/01-setup.md | 16 ++++----- .../11-authentication/02-basics.md | 24 +++++++++++++- .../03-working-with-users.md | 6 ++-- .../04-providers/02-email/01-setup.md | 4 +-- .../04-providers/02-email/02-configuration.md | 2 +- .../02-email/04-admin-operations.md | 2 +- .../04-providers/03-google/01-setup.md | 10 +++--- .../03-google/02-customizations.md | 12 +++---- .../03-google/04-troubleshooting.md | 4 +-- .../04-providers/04-apple/01-setup.md | 18 +++++----- .../04-apple/02-customizations.md | 4 +-- .../04-providers/05-facebook/01-setup.md | 6 ++-- .../05-facebook/02-customizations.md | 2 +- .../04-providers/06-firebase/01-setup.md | 4 +-- .../06-firebase/02-customizations.md | 14 ++++---- .../06-firebase/03-customizing-the-ui.md | 14 ++++---- .../04-providers/07-github/01-setup.md | 2 +- .../07-github/02-customizations.md | 4 +-- .../07-github/03-customizing-the-ui.md | 4 +-- .../04-providers/09-passkey/01-setup.md | 2 -- .../10-custom-providers/01-overview.md | 2 +- .../02-oauth2-utility/01-setup.md | 33 +++++++++---------- ...ating-an-oauth2-based-identity-provider.md | 12 +++---- .../05-token-managers/01-managing-tokens.md | 16 ++++----- .../05-token-managers/02-jwt-token-manager.md | 2 +- .../03-server-side-sessions-token-manager.md | 2 +- .../11-authentication/06-ui-components.md | 6 ++-- .../11-authentication/10-custom-overrides.md | 14 ++++---- 29 files changed, 131 insertions(+), 112 deletions(-) diff --git a/docs/06-concepts/11-authentication/01-get-started.md b/docs/06-concepts/11-authentication/01-get-started.md index e0876e1d..7d42d67e 100644 --- a/docs/06-concepts/11-authentication/01-get-started.md +++ b/docs/06-concepts/11-authentication/01-get-started.md @@ -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: diff --git a/docs/06-concepts/11-authentication/01-setup.md b/docs/06-concepts/11-authentication/01-setup.md index 895ebf5e..394e7f9e 100644 --- a/docs/06-concepts/11-authentication/01-setup.md +++ b/docs/06-concepts/11-authentication/01-setup.md @@ -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. @@ -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. @@ -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: @@ -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_='value'`. @@ -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 @@ -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 ``` diff --git a/docs/06-concepts/11-authentication/02-basics.md b/docs/06-concepts/11-authentication/02-basics.md index 8e0e0b95..8e952b93 100644 --- a/docs/06-concepts/11-authentication/02-basics.md +++ b/docs/06-concepts/11-authentication/02-basics.md @@ -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 myMethod(Session session) async { @@ -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 +bool valid = await client.auth.validateAuthentication(); +``` + +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 diff --git a/docs/06-concepts/11-authentication/03-working-with-users.md b/docs/06-concepts/11-authentication/03-working-with-users.md index b526f1b3..2b62e32e 100644 --- a/docs/06-concepts/11-authentication/03-working-with-users.md +++ b/docs/06-concepts/11-authentication/03-working-with-users.md @@ -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); @@ -197,7 +197,7 @@ You can also extend the endpoint class to add custom profile editing functionali ```dart class UserProfileEditEndpoint extends UserProfileEditBaseEndpoint { - Future myCustomProfileEdit(Session session, String bio) async { + Future myCustomProfileEdit(Session session, String bio) async { final userProfile = await session.authenticated?.userProfile(session); // Your custom logic here... @@ -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; diff --git a/docs/06-concepts/11-authentication/04-providers/02-email/01-setup.md b/docs/06-concepts/11-authentication/04-providers/02-email/01-setup.md index ad294225..80a331dc 100644 --- a/docs/06-concepts/11-authentication/04-providers/02-email/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/02-email/01-setup.md @@ -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'); } @@ -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'); } ``` diff --git a/docs/06-concepts/11-authentication/04-providers/02-email/02-configuration.md b/docs/06-concepts/11-authentication/04-providers/02-email/02-configuration.md index 80d97c20..c82505b8 100644 --- a/docs/06-concepts/11-authentication/04-providers/02-email/02-configuration.md +++ b/docs/06-concepts/11-authentication/04-providers/02-email/02-configuration.md @@ -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( diff --git a/docs/06-concepts/11-authentication/04-providers/02-email/04-admin-operations.md b/docs/06-concepts/11-authentication/04-providers/02-email/04-admin-operations.md index 32a65d21..973b9c44 100644 --- a/docs/06-concepts/11-authentication/04-providers/02-email/04-admin-operations.md +++ b/docs/06-concepts/11-authentication/04-providers/02-email/04-admin-operations.md @@ -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 diff --git a/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md b/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md index 750ab9aa..24446632 100644 --- a/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/03-google/01-setup.md @@ -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: @@ -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. @@ -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`). @@ -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) @@ -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. diff --git a/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md b/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md index ca1f78bb..b8ebbb01 100644 --- a/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md +++ b/docs/06-concepts/11-authentication/04-providers/03-google/02-customizations.md @@ -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. @@ -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( @@ -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( @@ -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 @@ -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 diff --git a/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md b/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md index 959eb177..50be5f5a 100644 --- a/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md +++ b/docs/06-concepts/11-authentication/04-providers/03-google/04-troubleshooting.md @@ -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 diff --git a/docs/06-concepts/11-authentication/04-providers/04-apple/01-setup.md b/docs/06-concepts/11-authentication/04-providers/04-apple/01-setup.md index 6a011185..9c0035e1 100644 --- a/docs/06-concepts/11-authentication/04-providers/04-apple/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/04-apple/01-setup.md @@ -23,7 +23,7 @@ All platforms require an App ID and a Sign in with Apple key. Android and Web ad 2. Select **App IDs** and click **Continue**. - ![Register a new identifier — App IDs selected](/img/authentication/providers/apple/4-app-id-create.png) + ![Register a new identifier: App IDs selected](/img/authentication/providers/apple/4-app-id-create.png) 3. Select **App** as the type and click **Continue**. @@ -31,7 +31,7 @@ All platforms require an App ID and a Sign in with Apple key. Android and Web ad 5. Scroll down to **Capabilities**, find **Sign in with Apple**, and check it. Keep it set as a **primary App ID**. - ![App ID capabilities — Sign in with Apple enabled](/img/authentication/providers/apple/5-app-id-capability.png) + ![App ID capabilities: Sign in with Apple enabled](/img/authentication/providers/apple/5-app-id-capability.png) 6. Click **Continue**, then **Register**. @@ -43,7 +43,7 @@ Skip this section if you are building for iOS or macOS only. 2. Select **Services IDs** and click **Continue**. - ![Register a new identifier — Services IDs selected](/img/authentication/providers/apple/6-service-id-create.png) + ![Register a new identifier: Services IDs selected](/img/authentication/providers/apple/6-service-id-create.png) 3. Enter a description and a unique **Identifier** (e.g. `com.example.service`). This value becomes your `serviceIdentifier`. Click **Continue**, then **Register**. @@ -54,7 +54,7 @@ Skip this section if you are building for iOS or macOS only. - **Domains and Subdomains**: your domain (e.g. `example.com`) - **Return URLs**: your server's callback route (e.g. `https://example.com/auth/callback`) - ![Web Authentication Configuration — Primary App ID, domains, and return URLs](/img/authentication/providers/apple/7-service-id-configure.png) + ![Web Authentication Configuration: Primary App ID, domains, and return URLs](/img/authentication/providers/apple/7-service-id-configure.png) 6. Click **Next**, then **Done**, then **Save**. @@ -72,15 +72,15 @@ If you plan to support web sign-in, also register the value you will use for `ap 2. Enter a key name, check **Sign in with Apple**, and click **Configure**. Select your primary App ID and click **Save**. - ![Configure key — Sign in with Apple checked, primary App ID selected](/img/authentication/providers/apple/8-key-create.png) + ![Configure key: Sign in with Apple checked, primary App ID selected](/img/authentication/providers/apple/8-key-create.png) 3. Click **Continue**, then **Register**. - ![Register a New Key — review screen](/img/authentication/providers/apple/8-key-register.png) + ![Register a New Key: review screen](/img/authentication/providers/apple/8-key-register.png) -4. Download the `.p8` key file immediately — **you can only download it once**. Note the **Key ID** shown on this page. +4. Download the `.p8` key file immediately: **you can only download it once**. Note the **Key ID** shown on this page. - ![Download Your Key — one-time download warning with Key ID visible](/img/authentication/providers/apple/8-key-download.png) + ![Download Your Key: one-time download warning with Key ID visible](/img/authentication/providers/apple/8-key-download.png) 5. Find your **Team ID** in your [Apple Developer Account](https://developer.apple.com/account) under Membership. @@ -142,7 +142,7 @@ pod.initializeAuthServices( ); ``` -`AppleIdpConfigFromPasswords()` reads the eight `apple*` keys from `config/passwords.yaml` (or the corresponding `SERVERPOD_PASSWORD_` environment variables), so you do not have to wire up each credential manually. +The `AppleIdpConfigFromPasswords()` constructor reads the eight `apple*` keys from `config/passwords.yaml` (or the corresponding `SERVERPOD_PASSWORD_` environment variables), so you do not have to wire up each credential manually. :::tip If you need more control over how the credentials are loaded, you can use `AppleIdpConfig(...)` with manual `pod.getPassword()` calls instead. See the [customizations](./customizations) page for details. diff --git a/docs/06-concepts/11-authentication/04-providers/04-apple/02-customizations.md b/docs/06-concepts/11-authentication/04-providers/04-apple/02-customizations.md index 8e3d1227..e32c9ccf 100644 --- a/docs/06-concepts/11-authentication/04-providers/04-apple/02-customizations.md +++ b/docs/06-concepts/11-authentication/04-providers/04-apple/02-customizations.md @@ -13,7 +13,7 @@ Below is a non-exhaustive list of some of the most common configuration options. ### Loading Apple credentials -`AppleIdpConfigFromPasswords()` reads the eight `apple*` keys from `config/passwords.yaml` (or the matching `SERVERPOD_PASSWORD_` environment variables) for you. This is the path used in the [setup guide](./setup#add-the-apple-identity-provider) and is the recommended default: +The `AppleIdpConfigFromPasswords()` constructor reads the eight `apple*` keys from `config/passwords.yaml` (or the matching `SERVERPOD_PASSWORD_` environment variables) for you. This is the path used in the [setup guide](./setup#add-the-apple-identity-provider) and is the recommended default: ```dart final appleIdpConfig = AppleIdpConfigFromPasswords(); @@ -105,7 +105,7 @@ These parameters are only used on web and Android. On native Apple platforms (iO #### Using environment variables -`APPLE_SERVICE_IDENTIFIER` and `APPLE_REDIRECT_URI` are the two build variables read by `initializeAppleSignIn()` on web and Android: +The build variables `APPLE_SERVICE_IDENTIFIER` and `APPLE_REDIRECT_URI` are read by `initializeAppleSignIn()` on web and Android: - `APPLE_SERVICE_IDENTIFIER`: your Services ID identifier (e.g. `com.example.service`) - `APPLE_REDIRECT_URI`: the server callback URL (e.g. `https://example.com/auth/callback`) diff --git a/docs/06-concepts/11-authentication/04-providers/05-facebook/01-setup.md b/docs/06-concepts/11-authentication/04-providers/05-facebook/01-setup.md index d04f9fef..af88354e 100644 --- a/docs/06-concepts/11-authentication/04-providers/05-facebook/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/05-facebook/01-setup.md @@ -123,7 +123,7 @@ pod.initializeAuthServices( ); ``` -`FacebookIdpConfigFromPasswords()` automatically loads the App ID and secret from the `facebookAppId` and `facebookAppSecret` keys in `config/passwords.yaml` (or the matching `SERVERPOD_PASSWORD_*` environment variables). +The `FacebookIdpConfigFromPasswords()` constructor automatically loads the App ID and secret from the `facebookAppId` and `facebookAppSecret` keys in `config/passwords.yaml` (or the matching `SERVERPOD_PASSWORD_*` environment variables). :::tip If you need more control over how the credentials are loaded, use `FacebookIdpConfig(appId: ..., appSecret: ...)` instead. See [Customizations](./customizations) for details. @@ -196,7 +196,7 @@ defaultConfig { Replace `YOUR_FACEBOOK_APP_ID` with your App ID and `YOUR_CLIENT_TOKEN` with your Client token (found in **Settings** > **Advanced** > **Client token** in the Facebook App Dashboard). -1. Open `/android/app/src/main/AndroidManifest.xml` and add the following: +2. Open `/android/app/src/main/AndroidManifest.xml` and add the following: **Add internet permission before the `` element:** @@ -406,7 +406,7 @@ This enables Facebook authentication on these domains. Without it, Facebook sign ![Allowed Domains](/img/authentication/providers/facebook/3-allowed-domains.png) :::warning -The facebook javascript SDK is only allowed to use with `https` but you can test the plugin in your localhost with an error message in your web console. +The Facebook JavaScript SDK is only allowed over `https`, but you can test the plugin on `localhost` (you will see an error message in your web console). ::: For more detailed web setup instructions, refer to the [flutter_facebook_auth web documentation](https://facebook.meedu.app/docs/7.x.x/web). diff --git a/docs/06-concepts/11-authentication/04-providers/05-facebook/02-customizations.md b/docs/06-concepts/11-authentication/04-providers/05-facebook/02-customizations.md index d8389ce9..32404f4c 100644 --- a/docs/06-concepts/11-authentication/04-providers/05-facebook/02-customizations.md +++ b/docs/06-concepts/11-authentication/04-providers/05-facebook/02-customizations.md @@ -129,7 +129,7 @@ final facebookIdpConfig = FacebookIdpConfigFromPasswords( You can use the `onAfterFacebookAccountCreated` callback to run logic after a new Facebook account has been created and linked to an auth user. This callback is only invoked for new accounts, not for returning users. -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. +This callback is complementary 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. ```dart final facebookIdpConfig = FacebookIdpConfigFromPasswords( diff --git a/docs/06-concepts/11-authentication/04-providers/06-firebase/01-setup.md b/docs/06-concepts/11-authentication/04-providers/06-firebase/01-setup.md index c4eb5dce..25bcb801 100644 --- a/docs/06-concepts/11-authentication/04-providers/06-firebase/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/06-firebase/01-setup.md @@ -127,7 +127,7 @@ pod.initializeAuthServices( ); ``` -`FirebaseIdpConfigFromPasswords()` automatically loads the service account key from the `firebaseServiceAccountKey` key in `config/passwords.yaml` (or the `SERVERPOD_PASSWORD_firebaseServiceAccountKey` environment variable). For loading credentials from other sources (file, JSON map, project ID only), see the [Customizations](./customizations) page. +The `FirebaseIdpConfigFromPasswords()` constructor automatically loads the service account key from the `firebaseServiceAccountKey` key in `config/passwords.yaml` (or the `SERVERPOD_PASSWORD_firebaseServiceAccountKey` environment variable). For loading credentials from other sources (file, JSON map, project ID only), see the [Customizations](./customizations) page. ### 2. Create the endpoint @@ -164,7 +164,7 @@ flutter pub add firebase_core firebase_auth firebase_ui_auth serverpod_auth_idp_ ``` :::note -`firebase_ui_auth` is the fastest path to a working sign-in screen. If you want to build a fully custom UI on top of `firebase_auth` directly, see [Using firebase_auth directly](./customizing-the-ui#using-firebase_auth-directly). +The `firebase_ui_auth` package is the fastest path to a working sign-in screen. If you want to build a fully custom UI on top of `firebase_auth` directly, see [Using firebase_auth directly](./customizing-the-ui#using-firebase_auth-directly). ::: ### 2. Configure FlutterFire diff --git a/docs/06-concepts/11-authentication/04-providers/06-firebase/02-customizations.md b/docs/06-concepts/11-authentication/04-providers/06-firebase/02-customizations.md index 8150edc0..6c759128 100644 --- a/docs/06-concepts/11-authentication/04-providers/06-firebase/02-customizations.md +++ b/docs/06-concepts/11-authentication/04-providers/06-firebase/02-customizations.md @@ -11,7 +11,7 @@ This page covers additional configuration options for the Firebase identity prov 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. -`FirebaseServiceAccountCredentials` provides four constructors. These are the only supported ways to construct it: +The `FirebaseServiceAccountCredentials` class provides four constructors. These are the only supported ways to construct it: **From a JSON string** (use this when reading the JSON from a secrets manager or environment variable): @@ -149,9 +149,9 @@ Both callbacks run inside the same database transaction as the account creation. ## 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). | -| `onAfterFirebaseAccountCreated` | `AfterFirebaseAccountCreatedFunction?` | No | Callback invoked after a new Firebase account has been created and linked to an auth user. Receives the session, the created `AuthUserModel`, the `FirebaseAccount`, and the active `Transaction`. Runs inside the same database transaction as account creation, so the `transaction` can be used to perform additional database operations atomically with sign-up. | -| `clockSkewTolerance` | `Duration` | No | Tolerance for clock skew when validating Firebase ID token timestamps. Defaults to the framework's default clock skew tolerance. | +| Parameter | Type | Required | Description | +| ---------------------------------- | -------------------------------------- | -------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `credentials` | `FirebaseServiceAccountCredentials` | Yes | Firebase service account credentials for verifying ID tokens. Can be loaded via `fromJsonString`, `fromJsonFile`, `fromJson`, or the default constructor with just `projectId`. 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). | +| `onAfterFirebaseAccountCreated` | `AfterFirebaseAccountCreatedFunction?` | No | Callback invoked after a new Firebase account has been created and linked to an auth user. Receives the session, the created `AuthUserModel`, the `FirebaseAccount`, and the active `Transaction`. Runs inside the same database transaction as account creation, so the `transaction` can be used to perform additional database operations atomically with sign-up. | +| `clockSkewTolerance` | `Duration` | No | Tolerance for clock skew when validating Firebase ID token timestamps. Defaults to the framework's default clock skew tolerance. | diff --git a/docs/06-concepts/11-authentication/04-providers/06-firebase/03-customizing-the-ui.md b/docs/06-concepts/11-authentication/04-providers/06-firebase/03-customizing-the-ui.md index c7aaf0ca..af116a48 100644 --- a/docs/06-concepts/11-authentication/04-providers/06-firebase/03-customizing-the-ui.md +++ b/docs/06-concepts/11-authentication/04-providers/06-firebase/03-customizing-the-ui.md @@ -35,10 +35,10 @@ Do not navigate to a home screen inside `onAuthenticated`. This callback fires e ### Action handlers -`firebase_ui_auth` emits state changes through [`AuthStateChangeAction`](https://pub.dev/documentation/firebase_ui_auth/latest/firebase_ui_auth/AuthStateChangeAction-class.html). Two handlers cover the cases that need a Serverpod sync: +The `firebase_ui_auth` package emits state changes through [`AuthStateChangeAction`](https://pub.dev/documentation/firebase_ui_auth/latest/firebase_ui_auth/AuthStateChangeAction-class.html). Two handlers cover the cases that need a Serverpod sync: -- [`SignedIn`](https://pub.dev/documentation/firebase_ui_auth/latest/firebase_ui_auth/SignedIn-class.html) -- a returning user signed in. -- [`UserCreated`](https://pub.dev/documentation/firebase_ui_auth/latest/firebase_ui_auth/UserCreated-class.html) -- a brand-new account was just created. +- [`SignedIn`](https://pub.dev/documentation/firebase_ui_auth/latest/firebase_ui_auth/SignedIn-class.html): a returning user signed in. +- [`UserCreated`](https://pub.dev/documentation/firebase_ui_auth/latest/firebase_ui_auth/UserCreated-class.html): a brand-new account was created. Both call [`controller.login(user)`](https://pub.dev/documentation/serverpod_auth_idp_flutter_firebase/latest/serverpod_auth_idp_flutter_firebase/FirebaseAuthController/login.html) so the Firebase user is registered with Serverpod: @@ -99,10 +99,10 @@ void initState() { The controller exposes a few properties for your `build` method: -- `controller.isLoading` -- Whether the controller is processing a request. -- `controller.isAuthenticated` -- Whether the user is authenticated. -- `controller.errorMessage` -- The error message string, if any. -- `controller.error` -- The raw error object, for advanced error handling. +- `controller.isLoading`: Whether the controller is processing a request. +- `controller.isAuthenticated`: Whether the user is authenticated. +- `controller.errorMessage`: The error message string, if any. +- `controller.error`: The raw error object, for advanced error handling. Use them in your `build` method to render the right UI for the current state: diff --git a/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md b/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md index 3cbb39fe..4277ce52 100644 --- a/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/07-github/01-setup.md @@ -129,7 +129,7 @@ pod.initializeAuthServices( ); ``` -`GitHubIdpConfigFromPasswords()` automatically loads the client ID and secret from the `githubClientId` and `githubClientSecret` keys in `config/passwords.yaml` (or the matching `SERVERPOD_PASSWORD_*` environment variables). +The `GitHubIdpConfigFromPasswords()` constructor automatically loads the client ID and secret from the `githubClientId` and `githubClientSecret` keys in `config/passwords.yaml` (or the matching `SERVERPOD_PASSWORD_*` environment variables). :::tip If you need more control over how the credentials are loaded, use `GitHubIdpConfig(clientId: ..., clientSecret: ...)` instead. See [Customizations](./customizations) for details. diff --git a/docs/06-concepts/11-authentication/04-providers/07-github/02-customizations.md b/docs/06-concepts/11-authentication/04-providers/07-github/02-customizations.md index 3bab9f87..a0c0590f 100644 --- a/docs/06-concepts/11-authentication/04-providers/07-github/02-customizations.md +++ b/docs/06-concepts/11-authentication/04-providers/07-github/02-customizations.md @@ -16,7 +16,7 @@ The GitHub identity provider can be configured using one of two classes: - **`GitHubIdpConfigFromPasswords`**: Automatically loads the client ID and secret from the `githubClientId` and `githubClientSecret` keys in `passwords.yaml` (or the matching `SERVERPOD_PASSWORD_*` environment variables). This is the class used in the [setup guide](./setup) and is recommended for most projects. - **`GitHubIdpConfig`**: Requires you to pass the client ID and secret directly. Use this when you load credentials from a custom source, such as a secrets manager or a programmatically constructed config. -`GitHubIdpConfigFromPasswords` is a convenience wrapper around `GitHubIdpConfig` that handles credential loading for you. +The `GitHubIdpConfigFromPasswords` class is a convenience wrapper around `GitHubIdpConfig` that handles credential loading for you. Both classes accept the same optional callbacks shown in the sections below. The examples on this page use `GitHubIdpConfigFromPasswords` unless the section specifically demonstrates manual credential loading. @@ -151,7 +151,7 @@ If you need to assign Serverpod scopes based on provider account data, updating The `onBeforeAuthUserCreated` and `onAfterAuthUserCreated` hooks are global callbacks configured on `AuthUsersConfig` in `initializeAuthServices`. They are not specific to GitHub; 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( diff --git a/docs/06-concepts/11-authentication/04-providers/07-github/03-customizing-the-ui.md b/docs/06-concepts/11-authentication/04-providers/07-github/03-customizing-the-ui.md index 92402cb5..67e29756 100644 --- a/docs/06-concepts/11-authentication/04-providers/07-github/03-customizing-the-ui.md +++ b/docs/06-concepts/11-authentication/04-providers/07-github/03-customizing-the-ui.md @@ -86,7 +86,7 @@ final controller = GitHubAuthController( await controller.signIn(); ``` -### GitHubAuthController State Management +### GitHubAuthController state management Your widget should render the appropriate UI based on the `state` property of the controller. You can also use the below state properties to build your UI: @@ -111,7 +111,7 @@ controller.addListener(() { }); ``` -#### GitHubAuthController States +#### GitHubAuthController states - `GitHubAuthState.idle` - Ready for user interaction. - `GitHubAuthState.loading` - Processing a sign-in request. diff --git a/docs/06-concepts/11-authentication/04-providers/09-passkey/01-setup.md b/docs/06-concepts/11-authentication/04-providers/09-passkey/01-setup.md index 64970caa..a8fbbc66 100644 --- a/docs/06-concepts/11-authentication/04-providers/09-passkey/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/09-passkey/01-setup.md @@ -74,8 +74,6 @@ Finally, start the server with `serverpod start` to generate the client code, th - `hostname`: Required. The hostname (relying party ID) to be used on the web and associated with any apps. This should match your application's domain. - `challengeLifetime`: Optional. Maximum time after which a challenge must have been solved. Default is 5 minutes. - - ## Client-side implementation :::info diff --git a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/01-overview.md b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/01-overview.md index 71ad1e08..7bf0e6ac 100644 --- a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/01-overview.md +++ b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/01-overview.md @@ -4,7 +4,7 @@ description: Custom providers extend Serverpod's authentication module with your # Custom providers -Serverpod's authentication module makes it easy to implement custom authentication providers. This allows you to leverage all the existing providers supplied by the module along with the specific providers your project requires. +Serverpod's authentication module lets you implement custom authentication providers. You can use all the existing providers supplied by the module along with the specific providers your project requires. :::note This section is under development and will be updated soon. diff --git a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md index 78ffe287..49fd89ad 100644 --- a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md +++ b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/01-setup.md @@ -20,7 +20,7 @@ The [GitHub IDP](../../github/setup) is built using these utilities, serving as OAuth2 with PKCE is an authorization protocol that allows users to grant your application access to their data without sharing passwords. The PKCE extension adds an additional security layer, particularly important for mobile and public clients. -### The OAuth2 Flow +### The OAuth2 flow Here's how the complete flow works: @@ -35,9 +35,7 @@ Here's how the complete flow works: PKCE ensures that even if an attacker intercepts the authorization code, they cannot exchange it for an access token without the original code verifier. -## Server-Side Implementation - -### Configuration +## Server-side implementation ### Configuration @@ -85,11 +83,11 @@ final config = OAuth2PkceServerConfig( The `credentialsLocation` parameter controls how your client credentials are sent to the OAuth2 provider: - **Header mode (recommended):** Credentials are placed in the `Authorization` header using HTTP Basic authentication. This follows RFC 6749 and is generally more secure, since sensitive values don't appear in the request body or logs. -- **Body mode:** Credentials are sent as form parameters in the request body.Use this only if your provider doesn't support header-based authentication. +- **Body mode:** Credentials are sent as form parameters in the request body. Use this only if your provider doesn't support header-based authentication. ::: -### Exchanging Tokens +### Exchanging tokens Using the previously created `config` object, create the `OAuth2PkceUtil` on your endpoint to exchange the authorization code: @@ -155,14 +153,15 @@ class MyProviderIdpEndpoint extends IdpBaseEndpoint { Future _issueToken( Session session, { - required int authUserId, + required UuidValue authUserId, required Set scopes, }) async { // Issue Serverpod authentication token for the authenticated user } } +``` -### Exception Handling +### Exception handling The server-side utility throws these exceptions: @@ -173,7 +172,7 @@ The server-side utility throws these exceptions: | `OAuth2NetworkErrorException` | Network failure | Timeout, connection issues | | `OAuth2UnknownException` | Unexpected error | Unknown problems | -## Client-Side Implementation +## Client-side implementation ### Configuration @@ -214,7 +213,7 @@ final config = OAuth2PkceProviderClientConfig( ); ``` -### Initiating Authorization +### Initiating authorization Using the previously created `config` object, create an `OAuth2PkceUtil` instance to start the authorization flow: @@ -257,7 +256,7 @@ try { } ``` -### Exception Handling +### Exception handling The client-side utility throws specific exceptions to help you handle different error scenarios: @@ -269,7 +268,7 @@ The client-side utility throws specific exceptions to help you handle different | `OAuth2PkceProviderErrorException` | Provider returned error response | Invalid credentials, rate limiting | | `OAuth2PkceUnknownException` | Unexpected error occurred | Network issues, unknown problems | -### Platform-Specific Configuration +### Platform-specific configuration The OAuth2 utility uses the [flutter_web_auth_2](https://pub.dev/packages/flutter_web_auth_2) package under the hood, which requires platform-specific setup. @@ -339,13 +338,13 @@ This file is shared across all IDPs that use the OAuth2 utility, as long as your Make sure your redirect URI points to the callback file, e.g. `https://yourdomain.com/auth.html` -## Complete Example of a Custom Provider +## Complete example of a custom provider -For a full end‑to‑end implementation of a custom OAuth2 provider — including server configuration, client setup and integration of all components — see the [Complete Example](./creating-an-oauth2-based-identity-provider) page. +For a full end-to-end implementation of a custom OAuth2 provider (server configuration, client setup, and integration of all components), see the [Complete Example](./creating-an-oauth2-based-identity-provider) page. -## Best Practices +## Best practices -### Security Considerations +### Security considerations 1. **Always Use PKCE**: Keep `enablePKCE: true` in your client configuration. PKCE protects against authorization code interception attacks. 2. **Validate State Parameter**: Keep `enableState: true` to prevent CSRF attacks. The state parameter ensures the authorization response matches your request. @@ -353,7 +352,7 @@ For a full end‑to‑end implementation of a custom OAuth2 provider — includi 4. **Use HTTPS**: Always use HTTPS URLs for production endpoints. Only use HTTP for local development. 5. **Validate Redirect URIs**: Ensure redirect URIs in your code exactly match those registered with your OAuth provider. -### Error Handling +### Error handling 1. **Catch Specific Exceptions**: Handle each exception type appropriately rather than using generic catch-all handlers. 2. **Log Securely**: Log errors for debugging but never log sensitive data like tokens or secrets. diff --git a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md index db9421dc..b3c0287f 100644 --- a/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md +++ b/docs/06-concepts/11-authentication/04-providers/10-custom-providers/02-oauth2-utility/02-creating-an-oauth2-based-identity-provider.md @@ -15,9 +15,9 @@ This example implements authentication with a fictional OAuth2 provider called " - Flutter UI integration - Error handling -## Server-Side Implementation +## Server-side implementation -### 1. Data Model +### 1. Data model First, create a data model to store provider accounts: @@ -101,7 +101,7 @@ class MyProviderIdpConfig extends IdentityProviderBuilder { } ``` -### 3. Provider Class +### 3. Provider class Create the main identity provider class: @@ -357,7 +357,7 @@ class MyProviderIdpEndpoint extends IdpBaseEndpoint { } ``` -### 5. Server Registration +### 5. Server registration Register the provider in `server.dart`: @@ -392,7 +392,7 @@ void run(List args) async { } ``` -## Client-Side Implementation +## Client-side implementation ### 1. Configuration @@ -562,7 +562,7 @@ class MyProviderAuthController extends ChangeNotifier { } ``` -### 4. UI Widget +### 4. UI widget Create the sign-in button: diff --git a/docs/06-concepts/11-authentication/05-token-managers/01-managing-tokens.md b/docs/06-concepts/11-authentication/05-token-managers/01-managing-tokens.md index 1c9c5540..235ee79b 100644 --- a/docs/06-concepts/11-authentication/05-token-managers/01-managing-tokens.md +++ b/docs/06-concepts/11-authentication/05-token-managers/01-managing-tokens.md @@ -6,7 +6,7 @@ description: Token managers issue, validate, revoke, and list authentication tok The authentication system uses token managers to handle authentication tokens. Token managers are responsible for issuing, validating, revoking, and listing authentication tokens. -## Default Token Managers +## Default token managers Serverpod provides two built-in token managers: - `JwtTokenManager` for JWT-based authentication. See [JWT Token Manager](./jwt-token-manager) for details. @@ -28,7 +28,7 @@ The `MultiTokenManager` is a composite token manager that is automatically creat - Validates tokens against all managers (primary and additional). - Delegates management operations to all managers. -### Token Validation Flow +### Token validation flow When validating a token, the `MultiTokenManager`: @@ -43,9 +43,9 @@ This allows you to support multiple token types simultaneously, which is useful - Supporting legacy tokens alongside new tokens. - Using different token types for different use cases. -## Token Lifecycle Management +## Token lifecycle management -### Issuing Tokens +### Issuing tokens Tokens are issued automatically by identity providers when users authenticate. You can also issue tokens programmatically: @@ -62,7 +62,7 @@ final authSuccess = await AuthServices.instance.tokenManager.issueToken( It is possible to attach metadata to tokens using either global callbacks configured on each token manager or by inserting a metadata row right after issuing the token. For more details, see the specific configuration sections for [Server-Side Sessions](./server-side-sessions-token-manager#attaching-custom-metadata-to-sessions) and [JWT](./jwt-token-manager#attaching-custom-metadata-to-tokens). -### Validating Tokens +### Validating tokens Tokens are validated automatically by the authentication handler. You can also validate tokens manually: @@ -80,7 +80,7 @@ if (authInfo != null) { } ``` -### Revoking Tokens +### Revoking tokens Revoke specific tokens by token ID: @@ -112,7 +112,7 @@ await AuthServices.instance.tokenManager.revokeAllTokens( ); ``` -### Listing Tokens +### Listing tokens List all tokens for a user: @@ -137,7 +137,7 @@ final tokens = await AuthServices.instance.tokenManager.listTokens( ); ``` -## Accessing Specific Token Managers +## Accessing specific token managers In case more than one token manager is configured, you can access specific token manager types from the `AuthServices` instance using the `getTokenManager()` method. diff --git a/docs/06-concepts/11-authentication/05-token-managers/02-jwt-token-manager.md b/docs/06-concepts/11-authentication/05-token-managers/02-jwt-token-manager.md index 8cf1e747..6ae9f12e 100644 --- a/docs/06-concepts/11-authentication/05-token-managers/02-jwt-token-manager.md +++ b/docs/06-concepts/11-authentication/05-token-managers/02-jwt-token-manager.md @@ -193,7 +193,7 @@ for (final row in tokenMetadata) { #### Attaching metadata when issuing tokens from an endpoint -The `onRefreshTokenCreated` callback is global and runs for every new refresh token (including those created by identity providers). When you create a token from an endpoint—for example, a personal access token (PAT) or CLI token—you often have endpoint-specific parameters (e.g. a token name or label) that the callback cannot see. In that case, issue the token with `AuthServices.instance.tokenManager.issueToken`, then use the returned `AuthSuccess.jwtRefreshTokenId` to insert your metadata with the endpoint's parameters: +The `onRefreshTokenCreated` callback is global and runs for every new refresh token (including those created by identity providers). When you create a token from an endpoint, for example a personal access token (PAT) or CLI token, you often have endpoint-specific parameters (e.g. a token name or label) that the callback cannot see. In that case, issue the token with `AuthServices.instance.tokenManager.issueToken`, then use the returned `AuthSuccess.jwtRefreshTokenId` to insert your metadata with the endpoint's parameters: ```dart final authSuccess = await AuthServices.instance.tokenManager.issueToken( diff --git a/docs/06-concepts/11-authentication/05-token-managers/03-server-side-sessions-token-manager.md b/docs/06-concepts/11-authentication/05-token-managers/03-server-side-sessions-token-manager.md index 3fc78e3b..d7f4f45b 100644 --- a/docs/06-concepts/11-authentication/05-token-managers/03-server-side-sessions-token-manager.md +++ b/docs/06-concepts/11-authentication/05-token-managers/03-server-side-sessions-token-manager.md @@ -131,7 +131,7 @@ for (final row in tokenMetadata) { #### Attaching metadata when issuing tokens from an endpoint -The `onSessionCreated` callback is global and runs for every new session (including those created by identity providers). When you create a token from an endpoint—for example, a personal access token (PAT) or CLI token—you often have endpoint-specific parameters (e.g. a token name or label) that the callback cannot see. In that case, issue the token with `AuthServices.instance.tokenManager.issueToken`, then use the returned `AuthSuccess.serverSideSessionId` to insert your metadata with the endpoint's parameters: +The `onSessionCreated` callback is global and runs for every new session (including those created by identity providers). When you create a token from an endpoint, for example a personal access token (PAT) or CLI token, you often have endpoint-specific parameters (e.g. a token name or label) that the callback cannot see. In that case, issue the token with `AuthServices.instance.tokenManager.issueToken`, then use the returned `AuthSuccess.serverSideSessionId` to insert your metadata with the endpoint's parameters: ```dart final authSuccess = await AuthServices.instance.tokenManager.issueToken( diff --git a/docs/06-concepts/11-authentication/06-ui-components.md b/docs/06-concepts/11-authentication/06-ui-components.md index 31285cb0..7e6202c2 100644 --- a/docs/06-concepts/11-authentication/06-ui-components.md +++ b/docs/06-concepts/11-authentication/06-ui-components.md @@ -58,7 +58,7 @@ It is not possible to forcefully enable a provider, since this depends on the co But, even though the `SignInWidget` automatically detects enabled providers, you can disable specific providers if you want to hide them on the client, but still keep them available on the server. -This is useful if you want gradually disable a provider, but still keep compatibility with older clients. +This is useful if you want to gradually disable a provider, but still keep compatibility with older clients. ```dart SignInWidget( @@ -80,7 +80,7 @@ SignInWidget( You can customize individual provider widgets: ```dart -SignInWidget( +final signInWidget = SignInWidget( client: client, emailSignInWidget: EmailSignInWidget( client: client, @@ -111,7 +111,7 @@ SignInWidget( ), onAuthenticated: _onAuthenticated, onError: _onError, -) +); void _onAuthenticated() { // Handle successful authentication diff --git a/docs/06-concepts/11-authentication/10-custom-overrides.md b/docs/06-concepts/11-authentication/10-custom-overrides.md index 21c64af8..63012047 100644 --- a/docs/06-concepts/11-authentication/10-custom-overrides.md +++ b/docs/06-concepts/11-authentication/10-custom-overrides.md @@ -5,11 +5,11 @@ description: Custom authentication overrides let you implement your own handling # Custom authentication overrides -It is recommended to use the `serverpod_auth_idp` package but if you have special requirements not fulfilled by it, you can implement your authentication module. Serverpod is designed to make it easy to add custom authentication overrides. +It is recommended to use the `serverpod_auth_idp` package, but if you have special requirements it does not fulfill, you can implement your own authentication module. Serverpod is designed to make it easy to add custom authentication overrides. ## Server setup -When running a custom auth integration it is up to you to build the authentication model and issuing auth tokens. +When running a custom auth integration it is up to you to build the authentication model and issue auth tokens. ### Token validation @@ -95,12 +95,12 @@ You are responsible for implementing the endpoints to authenticate/authorize the ```dart class UserEndpoint extends Endpoint { - Future login( + Future login( Session session, String username, String password, ) async { - var identifier = authenticateUser(session, username, password); + var identifier = await authenticateUser(session, username, password); if (identifier == null) return null; return issueMyToken(identifier, scopes: {}); @@ -139,7 +139,7 @@ class SimpleAuthKeyProvider implements ClientAuthKeyProvider { } } -var client = Client('http://$localhost:8080/') +var client = Client('http://localhost:8080/') ..authKeyProvider = SimpleAuthKeyProvider() ..connectivityMonitor = FlutterConnectivityMonitor(); ``` @@ -205,7 +205,7 @@ class MyOAuthKeyProvider implements ClientAuthKeyProvider { } } -var client = Client('http://$localhost:8080/') +var client = Client('http://localhost:8080/') ..authKeyProvider = MyOAuthKeyProvider() ..connectivityMonitor = FlutterConnectivityMonitor(); ``` @@ -220,7 +220,7 @@ final pod = Serverpod( Endpoints(), authenticationHandler: (Session session, String token) async { /// Bearer token validation handler - var (uid, scopes) = myBearerTokenValidator(token) + var (uid, scopes) = myBearerTokenValidator(token); if (uid == null) return null; return AuthenticationInfo(uid, scopes); From 1c65b5afb6e855ecdcbeb60c9dc331675b2bd34d Mon Sep 17 00:00:00 2001 From: developerjamiu Date: Wed, 8 Jul 2026 23:37:42 +0100 Subject: [PATCH 2/3] docs: Clarify validateAuthentication return value on the auth basics page --- docs/06-concepts/11-authentication/02-basics.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/06-concepts/11-authentication/02-basics.md b/docs/06-concepts/11-authentication/02-basics.md index 8e952b93..8c2bb350 100644 --- a/docs/06-concepts/11-authentication/02-basics.md +++ b/docs/06-concepts/11-authentication/02-basics.md @@ -301,10 +301,10 @@ Serverpod refreshes tokens automatically while the user stays signed in, so most Call `validateAuthentication` to check the current session against the server and sign the user out if it is no longer valid: ```dart -bool valid = await client.auth.validateAuthentication(); +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. +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. The return value does not indicate whether the session was valid (it is `true` both when the session is valid and when an invalid session is signed out), so check `client.auth.isAuthenticated` afterward to see the current state. At app startup, use `initialize` to restore a stored session and validate it in one step: From 043109352e9d5daef2a07f079abb16e8fefe54b8 Mon Sep 17 00:00:00 2001 From: developerjamiu Date: Wed, 8 Jul 2026 23:48:32 +0100 Subject: [PATCH 3/3] docs: Keep the validateAuthentication fix to the reviewer's suggestion --- docs/06-concepts/11-authentication/02-basics.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/06-concepts/11-authentication/02-basics.md b/docs/06-concepts/11-authentication/02-basics.md index 8c2bb350..b71cb675 100644 --- a/docs/06-concepts/11-authentication/02-basics.md +++ b/docs/06-concepts/11-authentication/02-basics.md @@ -304,7 +304,7 @@ Call `validateAuthentication` to check the current session against the server an 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. The return value does not indicate whether the session was valid (it is `true` both when the session is valid and when an invalid session is signed out), so check `client.auth.isAuthenticated` afterward to see the current state. +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: