Skip to content

Commit 18482d6

Browse files
docs: Add docs on the anonymous IDP feature (#415)
1 parent 5216b9e commit 18482d6

4 files changed

Lines changed: 313 additions & 0 deletions

File tree

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
# Setup
2+
3+
:::warning
4+
The Anonymous identity provider is **experimental** and can not be completely used yet due to the missing support for account linking. The missing parts will be added in the next releases.
5+
:::
6+
7+
To properly configure Anonymous authentication, you must allow anonymous access in your Serverpod auth configuration.
8+
9+
:::caution
10+
You need to install the auth module before you continue, see [Setup](../../setup).
11+
:::
12+
13+
## Server-side configuration
14+
15+
In your main `server.dart` file, configure the anonymous identity provider using the `AnonymousIdpConfig` object and add it to your `pod.initializeAuthServices()` configuration:
16+
17+
```dart
18+
import 'package:serverpod/serverpod.dart';
19+
import 'package:serverpod_auth_idp_server/core.dart';
20+
import 'package:serverpod_auth_idp_server/providers/anonymous.dart';
21+
22+
void run(List<String> args) async {
23+
final pod = Serverpod(
24+
args,
25+
Protocol(),
26+
Endpoints(),
27+
);
28+
29+
pod.initializeAuthServices(
30+
tokenManagerBuilders: [
31+
JwtConfigFromPasswords(),
32+
],
33+
identityProviderBuilders: [
34+
// Configure the Anonymous Identity Provider
35+
AnonymousIdpConfig(),
36+
],
37+
);
38+
39+
await pod.start();
40+
}
41+
```
42+
43+
Then, extend the abstract endpoint to expose it on the server:
44+
45+
```dart
46+
import 'package:serverpod_auth_idp_server/providers/anonymous.dart';
47+
48+
class AnonymousIdpEndpoint extends AnonymousIdpBaseEndpoint {}
49+
```
50+
51+
Then, run `serverpod generate` to generate the client code and create a migration to initialize the database for the provider. More detailed instructions can be found in the general [identity providers setup section](../../setup#identity-providers-configuration).
52+
53+
### Basic configuration options
54+
55+
Although the Anonymous IDP can be used directly with no other configuration, it is recommended to add some form of app attestation to prevent abuse on production environments. See the [Using a token for app attestation section](./configuration#using-a-token-for-app-attestation) for more details.
56+
57+
For other configuration options such as callbacks (before/after account creation) and rate limiting, see the [configuration section](./configuration).
58+
59+
## Client-side configuration
60+
61+
If you have configured the `SignInWidget` as described in the [setup section](../../setup#present-the-authentication-ui), the Anonymous identity provider will be automatically detected and displayed in the sign-in widget as a "Continue without account" option.
62+
63+
You can also use the `AnonymousSignInWidget` to include anonymous sign-in in your own custom UI:
64+
65+
```dart
66+
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';
67+
68+
AnonymousSignInWidget(
69+
client: client,
70+
onAuthenticated: () {
71+
// Do something when the user is authenticated.
72+
//
73+
// NOTE: You should not navigate to the home screen here, otherwise
74+
// the user will have to sign in again every time they open the app.
75+
},
76+
onError: (error) {
77+
// Handle errors
78+
ScaffoldMessenger.of(context).showSnackBar(
79+
SnackBar(content: Text('Error: $error')),
80+
);
81+
},
82+
)
83+
```
84+
85+
The widget displays a "Continue without account" button that creates an anonymous session when pressed. For details on customizing the button (size, shape), using a custom widget with `SignInWidget`, or building a fully custom UI with `AnonymousAuthController`, see the [customizing the UI section](./customizing-the-ui).
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Configuration
2+
3+
This page covers configuration options for the anonymous identity provider beyond the basic setup.
4+
5+
## Using a token for app attestation
6+
7+
The anonymous `login` endpoint accepts an optional **token** that is forwarded to your `onBeforeAnonymousAccountCreated` callback. This lets you tie anonymous sign-in to an app attestation or app-check provider (e.g. [Firebase App Check](https://firebase.google.com/docs/app-check)) so only requests from your real app can create anonymous accounts.
8+
9+
:::warning
10+
Using the anonymous provider without a token for app attestation is not recommended due to the risk of abuse. Make sure to configure an attestation before releasing your app to the public.
11+
:::
12+
13+
### Configuring the Flutter app
14+
15+
Obtain a token from your app-check provider and pass it to the login call by setting `createAnonymousToken` on `AnonymousSignInWidget` or `AnonymousAuthController`. That callback is invoked when the user taps "Continue without account". The returned token is sent to the server as the `token` argument of the anonymous login endpoint.
16+
17+
```dart
18+
import 'package:firebase_app_check/firebase_app_check.dart';
19+
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';
20+
21+
AnonymousSignInWidget(
22+
client: client,
23+
createAnonymousToken: () async {
24+
// Get a Firebase App Check token (or similar) to prove the request comes
25+
// from your app to prevent abuse.
26+
final appCheckToken = await FirebaseAppCheck.instance.getToken();
27+
return appCheckToken;
28+
},
29+
onAuthenticated: () { /* ... */ },
30+
onError: (error) { /* ... */ },
31+
)
32+
```
33+
34+
### Configuring the Server
35+
36+
In `onBeforeAnonymousAccountCreated`, receive the optional `token` and verify it with your app-check provider. If verification fails or the token is missing (when you require it), throw an `AnonymousAccountBlockedException` with reason `denied` to block account creation.
37+
38+
```dart
39+
AnonymousIdpConfig(
40+
onBeforeAnonymousAccountCreated: (
41+
Session session, {
42+
String? token,
43+
required Transaction? transaction,
44+
}) async {
45+
if (token == null || token.isEmpty) {
46+
throw AnonymousAccountBlockedException(
47+
reason: AnonymousAccountBlockedExceptionReason.denied,
48+
);
49+
}
50+
// Verify the token with your app-check provider (e.g. Firebase App Check).
51+
// Example: call Firebase's verifyAppCheckToken REST API or your provider's
52+
// verification endpoint. If invalid, throw AnonymousAccountBlockedException.
53+
final isValid = await _verifyAppCheckToken(session, token);
54+
if (!isValid) {
55+
throw AnonymousAccountBlockedException(
56+
reason: AnonymousAccountBlockedExceptionReason.denied,
57+
);
58+
}
59+
},
60+
)
61+
```
62+
63+
For Firebase App Check, you can verify the token from a custom backend using the [Firebase App Check REST API](https://firebase.google.com/docs/app-check/custom-resource-backend) (`verifyAppCheckToken`). Other app-check or attestation providers can be integrated the same way: client sends a token, server validates it in the callback and denies creation if invalid.
64+
65+
## Reacting to anonymous account creation
66+
67+
Beside the `onBeforeAnonymousAccountCreated` callback to allow or deny creation, you can also use the `onAfterAnonymousAccountCreated` callback to run logic after a new anonymous account has been created (e.g. analytics or side effects).
68+
69+
```dart
70+
AnonymousIdpConfig(
71+
onAfterAnonymousAccountCreated: (
72+
Session session, {
73+
required UuidValue authUserId,
74+
required Transaction? transaction,
75+
}) async {
76+
// e.g. track creation for analytics or send to your logging service
77+
},
78+
)
79+
```
80+
81+
## Rate limiting
82+
83+
The anonymous provider includes built-in rate limiting per IP address to prevent abuse. The default is 100 anonymous account creations per hour per IP. You can customize the rate limit in the `AnonymousIdpConfig` using the `perIpAddressRateLimit` parameter:
84+
85+
```dart
86+
AnonymousIdpConfig(
87+
perIpAddressRateLimit: const RateLimit(
88+
maxAttempts: 50,
89+
timeframe: Duration(hours: 1),
90+
),
91+
)
92+
```
93+
94+
When the limit is exceeded, the provider throws an `AnonymousAccountBlockedException` with reason `tooManyAttempts`.
Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
# Customizing the UI
2+
3+
When using the anonymous identity provider, you can customize the UI to your liking. You can use the `AnonymousSignInWidget` to display the anonymous sign-in button in your own layout, or you can use the `AnonymousAuthController` to build a completely custom authentication interface.
4+
5+
:::info
6+
The `SignInWidget` uses the `AnonymousSignInWidget` internally when the anonymous provider is enabled. You can supply a custom `AnonymousSignInWidget` to the `SignInWidget` to override the default (e.g. to pass `createAnonymousToken` or change size and shape).
7+
8+
```dart
9+
SignInWidget(
10+
client: client,
11+
anonymousSignInWidget: AnonymousSignInWidget(
12+
client: client,
13+
createAnonymousToken: () async => await getAppCheckToken(),
14+
size: AnonymousButtonSize.medium,
15+
shape: AnonymousButtonShape.rectangular,
16+
),
17+
)
18+
```
19+
:::
20+
21+
## Using the `AnonymousSignInWidget`
22+
23+
The `AnonymousSignInWidget` displays a single "Continue without account" button that starts the anonymous sign-in flow when pressed. You can customize the widget's behavior and appearance using its constructor parameters:
24+
25+
```dart
26+
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';
27+
28+
AnonymousSignInWidget(
29+
client: client,
30+
createAnonymousToken: () async {
31+
// Optional: provide a token for app attestation (e.g. Firebase App Check)
32+
return await getAppCheckToken();
33+
},
34+
onAuthenticated: () {
35+
// Do something when the user is authenticated.
36+
//
37+
// NOTE: You should not navigate to the home screen here, otherwise
38+
// the user will have to sign in again every time they open the app.
39+
},
40+
onError: (error) {
41+
// Handle errors
42+
},
43+
size: AnonymousButtonSize.large, // large (default), medium, or small
44+
shape: AnonymousButtonShape.pill, // pill (default) or rectangular
45+
)
46+
```
47+
48+
Optionally, you can provide an externally managed `AnonymousAuthController` instance to the widget. When a controller is provided, `client`, `onAuthenticated`, and `onError` are ignored in favor of the controller's configuration.
49+
50+
```dart
51+
AnonymousSignInWidget(
52+
controller: controller,
53+
size: AnonymousButtonSize.medium,
54+
shape: AnonymousButtonShape.rectangular,
55+
)
56+
```
57+
58+
### Customizing the button appearance
59+
60+
The widget renders a single **TextButton** with the label "Continue without account". The button uses Flutter's material design system, so it reacts to your app's `Theme`. You can wrap the widget in a `Theme` (or `ThemeData`) to change colors and typography:
61+
62+
```dart
63+
Theme(
64+
data: Theme.of(context).copyWith(
65+
colorScheme: ColorScheme.fromSeed(seedColor: Colors.blue),
66+
textButtonTheme: TextButtonThemeData(
67+
style: TextButton.styleFrom(
68+
foregroundColor: Colors.blue,
69+
),
70+
),
71+
),
72+
child: AnonymousSignInWidget(client: client),
73+
)
74+
```
75+
76+
The widget constrains the button to a minimum width of 240 and maximum width of 400; you can place it in a `SizedBox`, `Expanded`, or `Flex` to control layout.
77+
78+
## Building a custom UI with the `AnonymousAuthController`
79+
80+
For full control over the UI, use the `AnonymousAuthController` class. It provides the anonymous sign-in logic without any built-in widget, so you can trigger login from your own button or flow and build a completely custom layout.
81+
82+
```dart
83+
import 'package:serverpod_auth_idp_flutter/serverpod_auth_idp_flutter.dart';
84+
85+
final controller = AnonymousAuthController(
86+
client: client,
87+
createAnonymousToken: () async => await getAppCheckToken(),
88+
onAuthenticated: () {
89+
// Do something when the user is authenticated.
90+
},
91+
onError: (error) {
92+
// Handle errors
93+
},
94+
);
95+
```
96+
97+
### AnonymousAuthController state management
98+
99+
The controller notifies listeners when its state changes. Use these properties to drive your UI:
100+
101+
```dart
102+
// Check if a request is in progress
103+
final isLoading = controller.isLoading;
104+
105+
// Check current state (idle, loading, error, authenticated)
106+
final state = controller.state;
107+
108+
// Listen to state changes
109+
controller.addListener(() {
110+
setState(() {
111+
// Rebuild when controller state changes
112+
});
113+
});
114+
```
115+
116+
### AnonymousAuthController methods
117+
118+
The controller exposes a single action for anonymous sign-in:
119+
120+
```dart
121+
// Start anonymous sign-in.
122+
// Obtains token if `createAnonymousToken` is set, then calls the login endpoint.
123+
await controller.login();
124+
```
125+
126+
Call `controller.login()` from your custom button's `onPressed`, or from any other trigger (e.g. after a delay or when the user performs an action). The controller handles loading state, success, and errors and invokes `onAuthenticated` or `onError` as appropriate.
127+
128+
:::tip
129+
Remember to dispose the controller when it is no longer needed (e.g. in your widget's `dispose`), unless the widget manages its own controller and disposes it for you.
130+
:::
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
{
2+
"label": "Anonymous",
3+
"collapsed": true
4+
}

0 commit comments

Comments
 (0)