diff --git a/en/asgardeo/docs/assets/img/sdks/flutter/banner.png b/en/asgardeo/docs/assets/img/sdks/flutter/banner.png new file mode 100644 index 0000000000..44d278ea14 Binary files /dev/null and b/en/asgardeo/docs/assets/img/sdks/flutter/banner.png differ diff --git a/en/asgardeo/docs/sdks/flutter/apis/account-management.md b/en/asgardeo/docs/sdks/flutter/apis/account-management.md new file mode 100644 index 0000000000..a45cc2ad11 --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/apis/account-management.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Managing Accounts +--- +{% include "../../../../../includes/sdks/flutter/apis/account-management.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/apis/handle-push-notifications.md b/en/asgardeo/docs/sdks/flutter/apis/handle-push-notifications.md new file mode 100644 index 0000000000..4aecfbf9a3 --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/apis/handle-push-notifications.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Handling Push Notifications +--- +{% include "../../../../../includes/sdks/flutter/apis/handle-push-notifications.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/apis/register-device.md b/en/asgardeo/docs/sdks/flutter/apis/register-device.md new file mode 100644 index 0000000000..28f2b24493 --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/apis/register-device.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Registering the Device +--- +{% include "../../../../../includes/sdks/flutter/apis/register-device.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/apis/send-auth-response.md b/en/asgardeo/docs/sdks/flutter/apis/send-auth-response.md new file mode 100644 index 0000000000..70683bf237 --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/apis/send-auth-response.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Sending the Auth Response +--- +{% include "../../../../../includes/sdks/flutter/apis/send-auth-response.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/apis/unregister-device.md b/en/asgardeo/docs/sdks/flutter/apis/unregister-device.md new file mode 100644 index 0000000000..309a77c41c --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/apis/unregister-device.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Unregistering the Device +--- +{% include "../../../../../includes/sdks/flutter/apis/unregister-device.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/apis/update-device.md b/en/asgardeo/docs/sdks/flutter/apis/update-device.md new file mode 100644 index 0000000000..252433af98 --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/apis/update-device.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Updating the Device +--- +{% include "../../../../../includes/sdks/flutter/apis/update-device.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/guides/configure-notification-handlers.md b/en/asgardeo/docs/sdks/flutter/guides/configure-notification-handlers.md new file mode 100644 index 0000000000..d3e40a7cfa --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/guides/configure-notification-handlers.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Configuring Notification Handlers +--- +{% include "../../../../../includes/sdks/flutter/guides/configure-notification-handlers.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/guides/set-up-push-providers.md b/en/asgardeo/docs/sdks/flutter/guides/set-up-push-providers.md new file mode 100644 index 0000000000..abcf3c56ba --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/guides/set-up-push-providers.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Setting Up Push Providers +--- +{% include "../../../../../includes/sdks/flutter/guides/set-up-push-providers.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/overview.md b/en/asgardeo/docs/sdks/flutter/overview.md new file mode 100644 index 0000000000..6ae09bd02b --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/overview.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Overview +--- +{% include "../../../../includes/sdks/flutter/overview.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/references/builder-options.md b/en/asgardeo/docs/sdks/flutter/references/builder-options.md new file mode 100644 index 0000000000..f16eeccd9d --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/references/builder-options.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Builder Options +--- +{% include "../../../../../includes/sdks/flutter/references/builder-options.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/references/custom-managers.md b/en/asgardeo/docs/sdks/flutter/references/custom-managers.md new file mode 100644 index 0000000000..bc4329d005 --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/references/custom-managers.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Custom Managers +--- +{% include "../../../../../includes/sdks/flutter/references/custom-managers.md" %} diff --git a/en/asgardeo/docs/sdks/flutter/references/error-handling.md b/en/asgardeo/docs/sdks/flutter/references/error-handling.md new file mode 100644 index 0000000000..d7aed6207f --- /dev/null +++ b/en/asgardeo/docs/sdks/flutter/references/error-handling.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Error Handling +--- +{% include "../../../../../includes/sdks/flutter/references/error-handling.md" %} diff --git a/en/asgardeo/mkdocs.yml b/en/asgardeo/mkdocs.yml index f82c8677e2..7d07e09ea4 100644 --- a/en/asgardeo/mkdocs.yml +++ b/en/asgardeo/mkdocs.yml @@ -92,6 +92,12 @@ extra: description: An SDK to integrate {{ product_name }} into JS/TS-based frameworks such as ExpressJS registry_url: https://registry.npmjs.org/@asgardeo/auth-node/latest source_link: https://github.com/asgardeo/asgardeo-auth-node-sdk + - name: Flutter SDK + artifact_id: asgardeo_flutter + icon: assets/img/logo/flutter-logo.svg + description: Official {{ product_name }} Flutter SDK. Currently supports push authentication. + documentation_link: sdks/flutter/overview + source_link: https://github.com/asgardeo/flutter/tree/main/packages/flutter - name: Tomcat OIDC Agent artifact_id: io.asgardeo.tomcat.oidc.agent icon: assets/img/logo/java-logo.svg @@ -810,6 +816,21 @@ nav: - Guides: - Accessing Protected APIs: sdks/nextjs/guides/accessing-protected-apis.md - Protecting Routes: sdks/nextjs/guides/protecting-routes.md + - Flutter: + - Overview: sdks/flutter/overview.md + - APIs: + - Registering the Device: sdks/flutter/apis/register-device.md + - Handling Push Notifications: sdks/flutter/apis/handle-push-notifications.md + - Sending the Auth Response: sdks/flutter/apis/send-auth-response.md + - Unregistering the Device: sdks/flutter/apis/unregister-device.md + - Managing Accounts: sdks/flutter/apis/account-management.md + - References: + - Builder Options: sdks/flutter/references/builder-options.md + - Custom Managers: sdks/flutter/references/custom-managers.md + - Error Handling: sdks/flutter/references/error-handling.md + - Guides: + - Setting Up Push Providers: sdks/flutter/guides/set-up-push-providers.md + - Configuring Notification Handlers: sdks/flutter/guides/configure-notification-handlers.md - Asgardeo MCP Server: sdks/asgardeo-mcp-server.md - APIs: - APIs - Overview: apis/index.md diff --git a/en/base.yml b/en/base.yml index db993801f4..e97b75db2a 100644 --- a/en/base.yml +++ b/en/base.yml @@ -353,6 +353,12 @@ extra: description: An SDK to integrate {{ product_name }} into JS/TS-based frameworks such as ExpressJS registry_url: https://registry.npmjs.org/@asgardeo/auth-node/latest source_link: https://github.com/asgardeo/asgardeo-auth-node-sdk + - name: Flutter SDK + artifact_id: asgardeo_flutter + icon: assets/img/logo/flutter-logo.svg + description: Official {{ product_name }} Flutter SDK. Currently supports push authentication. + documentation_link: sdks/flutter/overview + source_link: https://github.com/asgardeo/flutter/tree/main/packages/flutter - name: Tomcat OIDC Agent artifact_id: io.asgardeo.tomcat.oidc.agent icon: assets/img/logo/java-logo.svg diff --git a/en/identity-server/next/docs/assets/img/sdks/flutter/banner.png b/en/identity-server/next/docs/assets/img/sdks/flutter/banner.png new file mode 100644 index 0000000000..44d278ea14 Binary files /dev/null and b/en/identity-server/next/docs/assets/img/sdks/flutter/banner.png differ diff --git a/en/identity-server/next/docs/sdks/flutter/apis/account-management.md b/en/identity-server/next/docs/sdks/flutter/apis/account-management.md new file mode 100644 index 0000000000..e9d4520582 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/apis/account-management.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Managing Accounts +--- +{% include "../../../../../../includes/sdks/flutter/apis/account-management.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/apis/handle-push-notifications.md b/en/identity-server/next/docs/sdks/flutter/apis/handle-push-notifications.md new file mode 100644 index 0000000000..fe39d7fec0 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/apis/handle-push-notifications.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Handling Push Notifications +--- +{% include "../../../../../../includes/sdks/flutter/apis/handle-push-notifications.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/apis/register-device.md b/en/identity-server/next/docs/sdks/flutter/apis/register-device.md new file mode 100644 index 0000000000..b142539281 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/apis/register-device.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Registering the Device +--- +{% include "../../../../../../includes/sdks/flutter/apis/register-device.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/apis/send-auth-response.md b/en/identity-server/next/docs/sdks/flutter/apis/send-auth-response.md new file mode 100644 index 0000000000..79618117bb --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/apis/send-auth-response.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Sending the Auth Response +--- +{% include "../../../../../../includes/sdks/flutter/apis/send-auth-response.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/apis/unregister-device.md b/en/identity-server/next/docs/sdks/flutter/apis/unregister-device.md new file mode 100644 index 0000000000..29020f5505 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/apis/unregister-device.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Unregistering the Device +--- +{% include "../../../../../../includes/sdks/flutter/apis/unregister-device.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/guides/configure-notification-handlers.md b/en/identity-server/next/docs/sdks/flutter/guides/configure-notification-handlers.md new file mode 100644 index 0000000000..cb16dedf8c --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/guides/configure-notification-handlers.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Configuring Notification Handlers +--- +{% include "../../../../../../includes/sdks/flutter/guides/configure-notification-handlers.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/guides/set-up-push-providers.md b/en/identity-server/next/docs/sdks/flutter/guides/set-up-push-providers.md new file mode 100644 index 0000000000..3b312478f4 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/guides/set-up-push-providers.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Setting Up Push Providers +--- +{% include "../../../../../../includes/sdks/flutter/guides/set-up-push-providers.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/overview.md b/en/identity-server/next/docs/sdks/flutter/overview.md new file mode 100644 index 0000000000..68dcecb262 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/overview.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Overview +--- +{% include "../../../../../includes/sdks/flutter/overview.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/references/builder-options.md b/en/identity-server/next/docs/sdks/flutter/references/builder-options.md new file mode 100644 index 0000000000..c38cc6313c --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/references/builder-options.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Builder Options +--- +{% include "../../../../../../includes/sdks/flutter/references/builder-options.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/references/custom-managers.md b/en/identity-server/next/docs/sdks/flutter/references/custom-managers.md new file mode 100644 index 0000000000..ce4a1971a9 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/references/custom-managers.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Custom Managers +--- +{% include "../../../../../../includes/sdks/flutter/references/custom-managers.md" %} diff --git a/en/identity-server/next/docs/sdks/flutter/references/error-handling.md b/en/identity-server/next/docs/sdks/flutter/references/error-handling.md new file mode 100644 index 0000000000..e7db1223f8 --- /dev/null +++ b/en/identity-server/next/docs/sdks/flutter/references/error-handling.md @@ -0,0 +1,5 @@ +--- +template: templates/sdk.html +heading: Error Handling +--- +{% include "../../../../../../includes/sdks/flutter/references/error-handling.md" %} diff --git a/en/identity-server/next/mkdocs.yml b/en/identity-server/next/mkdocs.yml index 3cd18f74f7..09b9f094c3 100644 --- a/en/identity-server/next/mkdocs.yml +++ b/en/identity-server/next/mkdocs.yml @@ -1211,6 +1211,21 @@ nav: - Guides: - Accessing Protected APIs: sdks/nextjs/guides/accessing-protected-apis.md - Protecting Routes: sdks/nextjs/guides/protecting-routes.md + - Flutter: + - Overview: sdks/flutter/overview.md + - APIs: + - Registering the Device: sdks/flutter/apis/register-device.md + - Handling Push Notifications: sdks/flutter/apis/handle-push-notifications.md + - Sending the Auth Response: sdks/flutter/apis/send-auth-response.md + - Unregistering the Device: sdks/flutter/apis/unregister-device.md + - Managing Accounts: sdks/flutter/apis/account-management.md + - References: + - Builder Options: sdks/flutter/references/builder-options.md + - Custom Managers: sdks/flutter/references/custom-managers.md + - Error Handling: sdks/flutter/references/error-handling.md + - Guides: + - Setting Up Push Providers: sdks/flutter/guides/set-up-push-providers.md + - Configuring Notification Handlers: sdks/flutter/guides/configure-notification-handlers.md - Connectors: - Connectors: connectors/index.md - Sift: diff --git a/en/includes/sdks/flutter/apis/account-management.md b/en/includes/sdks/flutter/apis/account-management.md new file mode 100644 index 0000000000..f075dac14d --- /dev/null +++ b/en/includes/sdks/flutter/apis/account-management.md @@ -0,0 +1,96 @@ +After registration, the SDK keeps a local record of every enrolled account. The account-management APIs let you list and look up these accounts and read each account's push authentication history. + +All operations are accessed through the `Asgardeo.instance` singleton. + +## getAccounts() + +Returns all locally registered accounts. + +```dart +Future> getAccounts() +``` + +```dart title="List all registered accounts" +final accounts = await Asgardeo.instance.pushAuth.getAccounts(); +for (final account in accounts) { + print('${account.username} @ ${account.organizationName ?? account.tenantDomain}'); +} +``` + +## getAccount() + +Returns a single account by its local identifier, or `null` if not found. + +```dart +Future getAccount(String accountId) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `accountId` | `String` | ✅ | Local identifier of the account to look up | + +```dart title="Look up an account by local ID" +final account = await Asgardeo.instance.pushAuth.getAccount(accountId); +``` + +## getAccountByDeviceId() + +Returns a single account by the server-assigned device identifier, or `null` if not found. + +```dart +Future getAccountByDeviceId(String deviceId) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `deviceId` | `String` | ✅ | Device identifier assigned by {{product_name}} during registration | + +```dart title="Look up an account by device ID" +final account = await Asgardeo.instance.pushAuth.getAccountByDeviceId(deviceId); +``` + +## getAuthHistory() + +Returns the push authentication history for a given account. + +```dart +Future> getAuthHistory(String accountId) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `accountId` | `String` | ✅ | Local identifier of the account whose history to retrieve | + +```dart title="Read push authentication history" +final records = await Asgardeo.instance.pushAuth.getAuthHistory(accountId); +for (final record in records) { + print('${record.applicationName} — ${record.status}'); +} +``` + +## PushAuthAccount fields + +| Field | Type | Description | +|-------|------|-------------| +| `id` | `String` | Local account identifier | +| `username` | `String` | Username associated with the account | +| `displayName` | `String` | Display name for the account | +| `deviceId` | `String` | Device identifier assigned by the server | +| `host` | `String` | {{product_name}} server host URL | +| `tenantDomain` | `String?` | Tenant domain of the root organization user | +| `organizationId` | `String?` | Sub-organization identifier, if applicable | +| `organizationName` | `String?` | Sub-organization name, if applicable | +| `userStoreDomain` | `String?` | User store domain | + +## PushAuthRecord fields + +| Field | Type | Description | +|-------|------|-------------| +| `pushAuthId` | `String` | Unique identifier of the push authentication request | +| `applicationName` | `String` | Application that requested authentication | +| `status` | `String` | `APPROVED` or `DENIED` | +| `respondedTime` | `int` | Epoch-millisecond timestamp when the response was sent | +| `ipAddress` | `String` | IP address from which the login attempt originated | +| `deviceOS` | `String` | OS of the requesting device | +| `browser` | `String` | Browser or client that initiated the login | +| `accountId` | `String` | Local ID of the account that handled the request | diff --git a/en/includes/sdks/flutter/apis/handle-push-notifications.md b/en/includes/sdks/flutter/apis/handle-push-notifications.md new file mode 100644 index 0000000000..4e5ec13287 --- /dev/null +++ b/en/includes/sdks/flutter/apis/handle-push-notifications.md @@ -0,0 +1,51 @@ +When {{product_name}} sends a push authentication request, your app receives a raw notification payload through FCM or APNs. The SDK provides a single helper to convert that payload into a typed request your code can act on. + +For details on wiring up FCM and APNs handlers in your app, see the [Configure Notification Handlers]({{base_path}}/sdks/flutter/guides/configure-notification-handlers/) guide. + +## parsePushNotification() + +Converts a raw push notification payload into a typed `PushAuthRequest`. Returns `null` for notifications that did not originate from {{product_name}}, making it safe to call inside any shared notification handler. + +```dart +PushAuthRequest? parsePushNotification( + Map data, { + int? sentTime, +}) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `data` | `Map` | ✅ | Raw notification payload (`message.data` from FCM, or the APNs userInfo map) | +| `sentTime` | `int?` | ❌ | Epoch-millisecond timestamp when the notification was sent | + +```dart title="Parse a push notification" +final request = Asgardeo.instance.pushAuth.parsePushNotification( + message.data, + sentTime: message.sentTime?.millisecondsSinceEpoch, +); + +if (request != null) { + // Present the auth request to the user. +} +``` + +## PushAuthRequest fields + +| Field | Type | Description | +|-------|------|-------------| +| `pushId` | `String` | Unique identifier for this push authentication request | +| `challenge` | `String` | Server-issued challenge string | +| `numberChallenge` | `String?` | Optional number for number-matching flows | +| `relativePath` | `String?` | Resolved relative path for the authentication endpoint | +| `deviceId` | `String` | Device identifier this notification targets | +| `username` | `String` | Username of the authenticating user | +| `tenantDomain` | `String` | Tenant domain of the user's organization | +| `organizationId` | `String?` | Sub-organization identifier, if applicable | +| `organizationName` | `String?` | Sub-organization name, if applicable | +| `userStoreDomain` | `String` | User store domain | +| `applicationName` | `String` | Name of the application requesting authentication | +| `notificationScenario` | `String` | Scenario type (e.g., `AUTHENTICATION`) | +| `ipAddress` | `String` | IP address from which the login attempt originated | +| `deviceOS` | `String` | OS of the requesting device | +| `browser` | `String` | Browser or client that initiated the login | +| `sentTime` | `int` | Epoch-millisecond timestamp when the notification was sent | diff --git a/en/includes/sdks/flutter/apis/register-device.md b/en/includes/sdks/flutter/apis/register-device.md new file mode 100644 index 0000000000..f77ac71280 --- /dev/null +++ b/en/includes/sdks/flutter/apis/register-device.md @@ -0,0 +1,139 @@ +Device registration enrolls a Flutter app instance with {{product_name}} so it can later receive and respond to push authentication requests. The SDK supports two registration methods: a QR-based flow for unauthenticated apps, and a token-based flow for apps that already hold a valid OAuth2 access token. + +All operations are accessed through the `Asgardeo.instance` singleton. + +## registerDevice() + +Registers the device using a QR code JSON payload. + +```dart +Future registerDevice( + String qrCodeJson, + String deviceToken, + AsgardeoPushNotificationProvider provider, +) +``` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `qrCodeJson` | `String` | Raw JSON string scanned from the registration QR code | +| `deviceToken` | `String` | Push notification token from FCM or APNs | +| `provider` | `AsgardeoPushNotificationProvider` | Push notification provider for this device. See [Push Notification Providers](#push-notification-providers) | + +Returns the local `accountId` string on success. + +```dart title="Register a device with FCM" +try { + + final accountId = await Asgardeo.instance.pushAuth.registerDevice( + qrCodeJson, + deviceToken, + FCMPushProvider(), + ); + +} on AsgardeoValidationException catch (e) { + // Invalid or malformed QR code data. +} on AsgardeoDeviceAlreadyRegisteredException catch (e) { + // This user already has a registered account on this device. +} on AsgardeoRegistrationException catch (e) { + // Server rejected the registration request. +} on AsgardeoNetworkException catch (e) { + // Network failure after all retries were exhausted. +} +``` + +## registerDeviceWithToken() + +Registers the device using an OAuth2 access token. The SDK fetches the registration payload from the {{product_name}} push discovery-data endpoint. + +```dart +Future registerDeviceWithToken( + String baseUrl, + String accessToken, + String deviceToken, + AsgardeoPushNotificationProvider provider, +) +``` + +| Parameter | Type | Description | +|-----------|------|-------------| +| `baseUrl` | `String` | {{product_name}} server base URL{% if product_name == "Asgardeo" %} (e.g., `https://api.asgardeo.io/t/myorg`){% else %} (e.g., `https://localhost:9443/t/myorg`){% endif %} | +| `accessToken` | `String` | OAuth2 Bearer token for the authenticated user | +| `deviceToken` | `String` | Push notification token from FCM or APNs | +| `provider` | `AsgardeoPushNotificationProvider` | Push notification provider for this device. See [Push Notification Providers](#push-notification-providers) | + +!!! note + The user must be authenticated before calling this method. The access token must include the `internal_login` scope. + +```dart title="Register with an access token" +try { + + final accountId = await Asgardeo.instance.pushAuth.registerDeviceWithToken( + {% if product_name == "Asgardeo" %}'https://api.asgardeo.io/t/myorg'{% else %}'https://localhost:9443/t/myorg'{% endif %}, + accessToken, + deviceToken, + AmazonSNSPushProvider(AmazonSNSPlatform.fcm), + ); + +} on AsgardeoDeviceAlreadyRegisteredException catch (e) { + // This user already has a registered account on this device. +} on AsgardeoRegistrationException catch (e) { + // Discovery-data fetch or registration request failed. +} on AsgardeoNetworkException catch (e) { + // Network failure. +} +``` + +## Push Notification Providers + +Pass the appropriate provider when registering a device. + +!!! important + The push notification provider must be configured in {{product_name}} before registering a device. See [Server-side Push Provider Configuration]({{base_path}}/sdks/flutter/guides/set-up-push-providers/#server-side-push-provider-configuration). + +### Firebase Cloud Messaging (FCM) + +Use FCM to deliver push notifications on Android, or on iOS when routing through Firebase instead of APNs directly. + +| Constructor | When to use | +|-------------|-------------| +| `FCMPushProvider()` | Firebase Cloud Messaging (Android, or iOS via FCM) | + +### Amazon SNS + +Use Amazon SNS when your {{product_name}} organization is configured with SNS as the push provider. Select the platform that matches your target device. + +| Constructor | When to use | +|-------------|-------------| +| `AmazonSNSPushProvider(AmazonSNSPlatform.fcm)` | Amazon SNS with FCM as the underlying platform | +| `AmazonSNSPushProvider(AmazonSNSPlatform.apns)` | Amazon SNS with APNs as the underlying platform | + +!!! note + A platform application for the relevant platform must be configured in {{product_name}} before using it. **`AmazonSNSPlatform.apns` is for the native APNs approach and should not be used for FCM on iOS.** + +### Custom Provider + +Use `CustomPushProvider` when your organization uses a push delivery service not covered by the built-in providers. Supply the provider name and any metadata required by your server configuration. + +| Constructor | When to use | +|-------------|-------------| +| `CustomPushProvider(name: '...', metadata: {...})` | Any other push provider | + +```dart title="Example: Use a custom push provider" +await Asgardeo.instance.pushAuth.registerDevice( + qrCodeJson, + deviceToken, + CustomPushProvider(name: 'MyProvider', metadata: {'key': 'value'}), +); +``` + +!!! tip "Tip: Selecting a provider based on platform" + ```dart + import 'dart:io'; + + final provider = Platform.isIOS + ? AmazonSNSPushProvider(AmazonSNSPlatform.apns) + : FCMPushProvider(); + + await Asgardeo.instance.pushAuth.registerDevice(qrCodeJson, deviceToken, provider); + ``` diff --git a/en/includes/sdks/flutter/apis/send-auth-response.md b/en/includes/sdks/flutter/apis/send-auth-response.md new file mode 100644 index 0000000000..d97c3dd0f7 --- /dev/null +++ b/en/includes/sdks/flutter/apis/send-auth-response.md @@ -0,0 +1,66 @@ +After the user reviews a push authentication request and approves or denies it, your app submits the decision back to {{product_name}} to complete (or reject) the login flow. + +All operations are accessed through the `Asgardeo.instance` singleton. + +## sendAuthResponse() + +Sends the user's approve or deny decision for a pending push authentication request. + +```dart +Future sendAuthResponse( + PushAuthRequest request, + PushAuthResponseStatus status, { + int? selectedNumber, +}) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `request` | `PushAuthRequest` | ✅ | The parsed push authentication request | +| `status` | `PushAuthResponseStatus` | ✅ | `PushAuthResponseStatus.approved` or `.denied` | +| `selectedNumber` | `int?` | ❌ | Number selected by the user; required only when `request.numberChallenge` is present | + +```dart title="Approve an authentication request" +try { + + await Asgardeo.instance.pushAuth.sendAuthResponse( + request, + PushAuthResponseStatus.approved, + ); + +} on AsgardeoAccountNotFoundException catch (e) { + // No local account found for the device ID in the notification. +} on AsgardeoAuthResponseException catch (e) { + // Server rejected the response. +} on AsgardeoNetworkException catch (e) { + // Network failure. +} +``` + +### Denying a request + +```dart title="Example: Deny an authentication request" +await Asgardeo.instance.pushAuth.sendAuthResponse( + request, + PushAuthResponseStatus.denied, +); +``` + +### Approving with a number challenge + +When `request.numberChallenge` is non-null, prompt the user to select the matching number and pass their selection as `selectedNumber`. + +```dart title="Example: Approve with number challenge" +await Asgardeo.instance.pushAuth.sendAuthResponse( + request, + PushAuthResponseStatus.approved, + selectedNumber: userSelectedNumber, +); +``` + +## PushAuthResponseStatus + +| Value | Description | +|-------|-------------| +| `PushAuthResponseStatus.approved` | User approved the request | +| `PushAuthResponseStatus.denied` | User denied the request | diff --git a/en/includes/sdks/flutter/apis/unregister-device.md b/en/includes/sdks/flutter/apis/unregister-device.md new file mode 100644 index 0000000000..f124fa137f --- /dev/null +++ b/en/includes/sdks/flutter/apis/unregister-device.md @@ -0,0 +1,44 @@ +Unregistering a device removes it from {{product_name}} so it no longer receives push authentication requests, and clears all associated local data (account record, key pair, and history). For cases where the device has already been removed server-side and only local cleanup is required, use `removeLocalAccount()`. + +All operations are accessed through the `Asgardeo.instance` singleton. + +## unregisterDevice() + +Removes the device from {{product_name}} and deletes all associated local data. + +```dart +Future unregisterDevice(String accountId) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `accountId` | `String` | ✅ | Local identifier of the account to unregister | + +```dart title="Unregister a device" +try { + await Asgardeo.instance.pushAuth.unregisterDevice(accountId); +} on AsgardeoDeviceNotFoundException catch (e) { + // Already removed server-side — clean up locally. + await Asgardeo.instance.pushAuth.removeLocalAccount(accountId); +} on AsgardeoUnregistrationException catch (e) { + // Server rejected the unregistration request. +} on AsgardeoNetworkException catch (e) { + // Network failure. +} +``` + +## removeLocalAccount() + +Deletes the local account, key pair, and history without contacting {{product_name}}. Use this when the device has already been removed server-side. + +```dart +Future removeLocalAccount(String accountId) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `accountId` | `String` | ✅ | Local identifier of the account to remove | + +```dart title="Remove a local account" +await Asgardeo.instance.pushAuth.removeLocalAccount(accountId); +``` diff --git a/en/includes/sdks/flutter/apis/update-device.md b/en/includes/sdks/flutter/apis/update-device.md new file mode 100644 index 0000000000..9aecaac165 --- /dev/null +++ b/en/includes/sdks/flutter/apis/update-device.md @@ -0,0 +1,58 @@ +Use the device update API to refresh the device token (after FCM or APNs rotates it) or to change the device's display name on {{product_name}}. + +All operations are accessed through the `Asgardeo.instance` singleton. + +## updateDevice() + +Updates the device's display name or device token. At least one of `name` or `deviceToken` must be provided. + +```dart +Future updateDevice( + String accountId, { + String? name, + String? deviceToken, +}) +``` + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `accountId` | `String` | ✅ | Local identifier of the account whose device is being updated | +| `name` | `String?` | ❌ | New display name for the device | +| `deviceToken` | `String?` | ❌ | New push notification token from FCM or APNs | + +```dart title="Refresh device token after rotation" +try { + + await Asgardeo.instance.pushAuth.updateDevice( + accountId, + deviceToken: newToken, + ); + +} on AsgardeoDeviceNotFoundException catch (e) { + // Device was removed server-side — clean up locally. + await Asgardeo.instance.pushAuth.removeLocalAccount(accountId); +} on AsgardeoDeviceUpdateException catch (e) { + // Server rejected the update request. +} +``` + +### Renaming a device + +```dart title="Example: Rename a registered device" +await Asgardeo.instance.pushAuth.updateDevice( + accountId, + name: "Alice's Phone", +); +``` + +### Updating the device token + +```dart title="Example: Update device token" +await Asgardeo.instance.pushAuth.updateDevice( + accountId, + deviceToken: newToken, +); +``` + +!!! note + Use this to update the device token when FCM or APNs rotates it. diff --git a/en/includes/sdks/flutter/guides/configure-notification-handlers.md b/en/includes/sdks/flutter/guides/configure-notification-handlers.md new file mode 100644 index 0000000000..7f0190fb9d --- /dev/null +++ b/en/includes/sdks/flutter/guides/configure-notification-handlers.md @@ -0,0 +1,113 @@ +This guide explains how to integrate push notification handlers into your Flutter app so that incoming {{product_name}} push authentication requests are routed to the SDK. + +The core method is `parsePushNotification`, which accepts the raw notification data map and returns a typed `PushAuthRequest` (or `null` for unrelated notifications). It is safe to call in any shared notification handler — it silently filters out payloads that don't originate from {{product_name}}. + +!!! note + Make sure you have completed [Set Up Push Providers]({{base_path}}/sdks/flutter/guides/set-up-push-providers/) before continuing. + + + +--- + +## FCM Foreground Handler + +When the app is open and in the foreground, use `FirebaseMessaging.onMessage` to receive notifications. + +```dart +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:asgardeo_flutter/asgardeo.dart'; + +void setupForegroundHandler() { + FirebaseMessaging.onMessage.listen((RemoteMessage message) { + final request = Asgardeo.instance.pushAuth.parsePushNotification( + message.data, + sentTime: message.sentTime?.millisecondsSinceEpoch, + ); + + if (request != null) { + // Display the authentication request to the user. + // e.g. show an in-app dialog or navigate to an approval screen. + } + }); +} +``` + +--- + +## FCM Background and Terminated Handler + +When the app is in the background or not running, FCM invokes a top-level background handler. This function must be a top-level (non-anonymous) function so that Flutter can isolate it. + +```dart +import 'package:firebase_messaging/firebase_messaging.dart'; +import 'package:asgardeo_flutter/asgardeo.dart'; + +@pragma('vm:entry-point') +Future firebaseMessagingBackgroundHandler(RemoteMessage message) async { + // Ensure the SDK is initialized before using it in the background isolate. + AsgardeoBuilder().build(); + + final request = Asgardeo.instance.pushAuth.parsePushNotification( + message.data, + sentTime: message.sentTime?.millisecondsSinceEpoch, + ); + + if (request != null) { + // Store the request locally or show a system notification + // so the user can act on it when they open the app. + } +} +``` + +Register the handler early in `main()`: + +```dart +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await Firebase.initializeApp(options: DefaultFirebaseOptions.currentPlatform); + FirebaseMessaging.onBackgroundMessage(firebaseMessagingBackgroundHandler); + + AsgardeoBuilder().build(); + runApp(const MyApp()); +} +``` + +!!! important + The background handler runs in a **separate Dart isolate**. It cannot update UI state directly. Use local storage, shared preferences, or a stream to pass the request to the main isolate when the user opens the app. + +--- + +## APNs Handler (iOS) + +If your iOS app communicates directly through Apple Push Notification service (APNs) without going through FCM, implement the native APNs delegate methods via a Flutter platform channel or a native plugin. + +**Obtaining the device token:** + +In your `AppDelegate.swift`, capture the APNs device token and pass it to Flutter: + +```swift +func application( + _ application: UIApplication, + didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data +) { + let tokenString = deviceToken.map { String(format: "%02.2hhx", $0) }.joined() + // Send tokenString to your Flutter layer via a MethodChannel or EventChannel. +} +``` + +Pass this token as the `deviceToken` argument when calling `registerDevice`. + +**Receiving notifications:** + +When a notification arrives, the system calls `didReceiveRemoteNotification`. Extract the payload and pass it to `parsePushNotification`: + +```swift +func application( + _ application: UIApplication, + didReceiveRemoteNotification userInfo: [AnyHashable: Any], + fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void +) { + // Forward userInfo to your Flutter layer, then call parsePushNotification. + completionHandler(.newData) +} +``` diff --git a/en/includes/sdks/flutter/guides/set-up-push-providers.md b/en/includes/sdks/flutter/guides/set-up-push-providers.md new file mode 100644 index 0000000000..534e33763d --- /dev/null +++ b/en/includes/sdks/flutter/guides/set-up-push-providers.md @@ -0,0 +1,109 @@ +Before the push authenticator app can send or receive notifications, three external systems must be configured: the push delivery service (Firebase or APNs), the Apple Developer account (for iOS), and the {{product_name}} push provider. + +## Firebase Cloud Messaging (FCM) + +FCM is required for Android push notifications and optionally for iOS. + +### Step 1: Create a Firebase project + +1. Go to the [Firebase Console](https://console.firebase.google.com/) and click **Add project**. +2. Follow the setup wizard to create or select a project. + +### Step 2: Add your Android and iOS apps + +In **Project Settings > General**, add an Android app and an iOS app. Note the package name / bundle ID — these must match the values in your Flutter project. + +### Step 3: Generate configuration files + +Choose one of the following methods to generate your Firebase configuration files. + +#### Option A (recommended) — FlutterFire CLI + +The FlutterFire CLI registers your apps, downloads `google-services.json` and `GoogleService-Info.plist`, and generates `firebase_options.dart` in a single command. + +```bash +npm install -g firebase-tools +firebase login + +dart pub global activate flutterfire_cli +flutterfire configure +``` + +!!! tip + Official guide: [FlutterFire CLI](https://firebase.flutter.dev/docs/cli) + +#### Option B — Manual + +1. Download `google-services.json` from Firebase Console and place it in `android/app/`. +2. Download `GoogleService-Info.plist` and add it to your Xcode project: + 1. Place the file in `ios/Runner/`. + 2. Open `ios/Runner.xcworkspace` in Xcode. + 3. Right-click **Runner** → **Add Files to "Runner"...**, select the plist file, and ensure the Runner target is checked. +3. Copy the example options file and fill in your project values: + + ```bash + cp lib/firebase_options.dart.example lib/firebase_options.dart + ``` + +### Step 4: Upload your APNs authentication key to Firebase (iOS only) + +For FCM to deliver push notifications to iOS devices, it needs your APNs authentication key. + +!!! note + Complete the [Apple Developer Account](#apple-developer-account-ios) section first to generate your `.p8` APNs key, Key ID, and Team ID. + +1. In Firebase Console, go to **Project Settings > Cloud Messaging**. +2. Under **Apple app configuration**, upload your `.p8` APNs key file. +3. Enter the **Key ID** and your **Team ID**. + +!!! tip + Official guide: [Set up APNs with FCM](https://firebase.google.com/docs/cloud-messaging/ios/certs) + +--- + +## Apple Developer Account (iOS) + +iOS push notifications require a registered App ID with the Push Notifications capability, a provisioning profile, and an APNs authentication key. + +!!! important + End-to-end push notification testing on iOS requires a **physical device**, though UI testing can be simulated. + +### Step 1: Create an App ID with Push Notifications + +1. Sign in to the [Apple Developer Portal](https://developer.apple.com/account/). +2. Go to **Certificates, Identifiers & Profiles > Identifiers > +**. +3. Select **App IDs**, then **App**. +4. Enter a description and your **Bundle ID** (must match your Flutter project and Firebase configuration). +5. Under **Capabilities**, check **Push Notifications**. +6. Click **Continue > Register**. + +### Step 2: Register your test device + +1. Go to **Devices > +**. +2. Enter the device name and UDID. + - To find the UDID: connect the device, open Finder, click the device, then click the serial number to reveal the UDID. + +### Step 3: Create a provisioning profile + +1. Go to **Profiles > +**. +2. Select **iOS App Development > Continue**. +3. Select your **App ID**, **Certificate**, and **Devices**. +4. Download and install the `.mobileprovision` file (double-click or drag into Xcode). + +### Step 4: Generate an APNs authentication key + +1. Go to **Keys > +**. +2. Enter a key name and check **Apple Push Notifications service (APNs)**. +3. Click **Continue > Register**, then **Download** the `.p8` file. +4. Note the **Key ID** and your **Team ID** — you will need these for Firebase (Step 4 above) or for configuring Amazon SNS. + +!!! important + You can only download the `.p8` file once. Store it securely. + +--- + +## Server-side Push Provider Configuration + +The {{product_name}} server must be configured with a push notification provider before it can deliver notifications to your app. + +Refer to the official guide for step-by-step instructions: [Configure Push Provider]({{base_path}}/guides/notification-channels/configure-push-provider/). diff --git a/en/includes/sdks/flutter/overview.md b/en/includes/sdks/flutter/overview.md new file mode 100644 index 0000000000..ebd5723033 --- /dev/null +++ b/en/includes/sdks/flutter/overview.md @@ -0,0 +1,98 @@ +![Asgardeo Flutter SDK]({{base_path}}/assets/img/sdks/flutter/banner.png){: width="auto" style="display: block; margin-bottom: 20px;"} + +The `asgardeo_flutter` package is the official {{product_name}} Flutter SDK. The current release supports push-notification-based multi-factor authentication and handles the full lifecycle — device registration, cryptographic challenge signing, approve/deny responses, and local auth history — using RSA-2048 key pairs stored in the platform's secure hardware storage. + +## Features + +| Feature | Description | +|---------|-------------| +| **Device Registration** | Register devices via QR code scan or OAuth2 access token | +| **RSA-2048 Signing** | Cryptographic request signing with platform-secure key storage (Android Keystore / iOS Keychain) | +| **Push Authentication** | Parse incoming push notifications and send approve/deny responses | +| **Number Challenge** | Support for number-matching push authentication flows | +| **Biometric Gating** | Optional biometric/device-credential authentication before key operations | +| **Device Management** | Update device name, refresh push token, and unregister devices | +| **Auth History** | Local tracking of all push authentication events per account | +| **Automatic Retries** | Built-in retry logic with exponential backoff for transient failures | +| **Pluggable Architecture** | Replace HTTP, storage, crypto, logging, and device-info implementations | + +## Getting Started + +This guide gets the `asgardeo_flutter` SDK running in the minimum number of steps. For push notification delivery to work end-to-end, complete the [Set Up Push Providers]({{base_path}}/sdks/flutter/guides/set-up-push-providers/) and [Configure Notification Handlers]({{base_path}}/sdks/flutter/guides/configure-notification-handlers/) guides. + +### Installation + +Add the dependency to your `pubspec.yaml`: + +```yaml +dependencies: + asgardeo_flutter: +``` + +Then run: + +```bash +flutter pub get +``` + +### Initialize the SDK + +Call `AsgardeoBuilder().build()` once at app startup, before any other SDK calls. The best place is in `main()` after `WidgetsFlutterBinding.ensureInitialized()`. + +```dart title="lib/main.dart" +import 'package:asgardeo_flutter/asgardeo.dart'; +import 'package:flutter/material.dart'; + +void main() { + WidgetsFlutterBinding.ensureInitialized(); + + AsgardeoBuilder().build(); + + runApp(const MyApp()); +} +``` + +After `build()` returns, access push-auth operations through `Asgardeo.instance.pushAuth` from anywhere in your app: + +```dart +final pushAuth = Asgardeo.instance.pushAuth; +``` + +### Register a device + +Once you have a QR code JSON string (scanned from the {{product_name}} My Account portal) and a push token from FCM or APNs, call `registerDevice`: + +```dart +import 'package:asgardeo_flutter/asgardeo.dart'; + +Future registerDevice(String qrCodeJson, String deviceToken) async { + + try { + + final accountId = await Asgardeo.instance.pushAuth.registerDevice( + qrCodeJson, + deviceToken, + FCMPushProvider(), + ); + + print('Device registered. Account ID: $accountId'); + + } on AsgardeoDeviceAlreadyRegisteredException { + // This user already has a registered account on this device. + } on AsgardeoRegistrationException catch (e) { + print('Registration failed: ${e.message}'); + } on AsgardeoNetworkException catch (e) { + print('Network error: ${e.message}'); + } +} +``` + +!!! note + `FCMPushProvider` is used in this example. Choose the provider that matches your push notification service configuration. See [Push Notification Providers]({{base_path}}/sdks/flutter/apis/register-device/#push-notification-providers) for all options. + +## Next steps + +- [Set Up Push Providers]({{base_path}}/sdks/flutter/guides/set-up-push-providers/) — Configure Firebase, Apple Developer, and server-side push provider. +- [Configure Notification Handlers]({{base_path}}/sdks/flutter/guides/configure-notification-handlers/) — Wire up FCM and APNs handlers in your app. +- [API Reference]({{base_path}}/sdks/flutter/apis/register-device/) — Full documentation for every method. +- [Customization]({{base_path}}/sdks/flutter/references/builder-options/) — Customize the SDK with builder options. diff --git a/en/includes/sdks/flutter/references/builder-options.md b/en/includes/sdks/flutter/references/builder-options.md new file mode 100644 index 0000000000..bd9414b791 --- /dev/null +++ b/en/includes/sdks/flutter/references/builder-options.md @@ -0,0 +1,53 @@ +`AsgardeoBuilder` configures the SDK before `build()` is called. All options have sensible defaults — only override what you need. + +## Builder properties + +```dart +(AsgardeoBuilder() + ..logLevel = LogLevel.debug + ..maxHistoryRecords = 50 + ..maxRetries = 2 + ..biometricPolicy = BiometricPolicy.mandatory + ..biometricLocalizedReason = 'Verify your identity') + .build(); +``` + +| Property | Type | Default | Description | +|----------|------|---------|-------------| +| `httpManager` | `AsgardeoHttpManager?` | `HttpClientManager()` | Custom HTTP client for network requests | +| `storageManager` | `AsgardeoStorageManager?` | `SharedPreferencesStorageManager()` | Persistent storage for accounts and history | +| `cryptoEngine` | `AsgardeoCryptoEngine?` | `SecureCryptoEngine()` | RSA key generation and signing engine | +| `deviceInfoProvider` | `AsgardeoDeviceInfoProvider?` | `PlatformDeviceInfoProvider()` | Device name and model provider | +| `logger` | `AsgardeoLogger?` | `DefaultLogger()` | Custom logger implementation | +| `logLevel` | `LogLevel?` | `LogLevel.none` | Log verbosity (`none`, `error`, `info`, `debug`) | +| `biometricPolicy` | `BiometricPolicy` | `BiometricPolicy.enabled` | Biometric gate for private-key operations | +| `biometricLocalizedReason` | `String` | `'Authenticate to confirm this action'` | Text shown in the biometric prompt | +| `maxHistoryRecords` | `int` | `20` | Maximum auth history records per account | +| `maxRetries` | `int` | `1` | Retry attempts for transient network/5xx errors (0 to disable) | + +!!! note + Calling `build()` more than once throws `AsgardeoAlreadyInitializedException`. `Asgardeo.reset()` exists for development and testing only — it disposes the singleton and allows re-initialization. Do not use `reset()` in production code. + +For pluggable interfaces (`httpManager`, `storageManager`, `cryptoEngine`, `logger`, `deviceInfoProvider`), see [Custom Managers]({{base_path}}/sdks/flutter/references/custom-managers/). + +--- + +## Biometric Policy + +`BiometricPolicy` controls whether biometric or device-credential authentication is required before any private-key operation (key generation, registration signing, JWT signing for auth responses, device updates, unregistration). + +| Policy | Behavior | Throws | +|--------|----------|--------| +| `BiometricPolicy.disabled` | No biometric prompt. Operations proceed immediately. | — | +| `BiometricPolicy.enabled` | Prompts when biometrics are available; skips silently if the device doesn't support it. **(Default)** | `AsgardeoBiometricAuthFailedException` if the user cancels or fails the prompt | +| `BiometricPolicy.mandatory` | Requires biometric or device-credential authentication. | `AsgardeoBiometricUnavailableException` if the device doesn't support it; `AsgardeoBiometricAuthFailedException` on cancel or failure | + +```dart +(AsgardeoBuilder() + ..biometricPolicy = BiometricPolicy.mandatory + ..biometricLocalizedReason = 'Authenticate to approve login') + .build(); +``` + +!!! important + The biometric gate is enforced on **every** private-key operation — not only at app launch. This means the user is prompted each time they register, approve/deny a request, update the device, or unregister. diff --git a/en/includes/sdks/flutter/references/custom-managers.md b/en/includes/sdks/flutter/references/custom-managers.md new file mode 100644 index 0000000000..667c279041 --- /dev/null +++ b/en/includes/sdks/flutter/references/custom-managers.md @@ -0,0 +1,77 @@ +The SDK exposes abstract interfaces for every external dependency, so you can plug in custom implementations for HTTP, storage, crypto, logging, and device info. + +## Pluggable interfaces + +| Interface | Default Implementation | Purpose | +|-----------|------------------------|---------| +| `AsgardeoHttpManager` | `HttpClientManager` | HTTP GET / POST operations | +| `AsgardeoStorageManager` | `SharedPreferencesStorageManager` | Persistent storage for accounts and history | +| `AsgardeoCryptoEngine` | `SecureCryptoEngine` | RSA-2048 key generation and signing | +| `AsgardeoLogger` | `DefaultLogger` | Log output | +| `AsgardeoDeviceInfoProvider` | `PlatformDeviceInfoProvider` | Device name and model resolution | + +Inject custom implementations on the builder before calling `build()`: + +```dart +(AsgardeoBuilder() + ..httpManager = MyHttpManager() + ..storageManager = MyStorageManager() + ..logger = MyLogger()) + .build(); +``` + +--- + +## Custom HTTP client + +For example, to trust a self-signed certificate when connecting to a local server, provide a custom `HttpClientManager`: + +```dart +import 'dart:io'; +import 'package:asgardeo_flutter/asgardeo.dart'; +import 'package:flutter/services.dart'; +import 'package:http/io_client.dart'; + +final certBytes = await rootBundle.load('assets/certs/server.pem'); +final context = SecurityContext(withTrustedRoots: true) + ..setTrustedCertificatesBytes(certBytes.buffer.asUint8List()); + +final httpClient = IOClient(HttpClient(context: context)); + +(AsgardeoBuilder() + ..httpManager = HttpClientManager(client: httpClient)) + .build(); +``` + +--- + +## Custom logger + +Implement `AsgardeoLogger` to forward SDK logs to your crash-reporting or analytics service: + +```dart +import 'package:asgardeo_flutter/asgardeo.dart'; + +class MyLogger implements AsgardeoLogger { + @override + void error(String message, [Object? error, StackTrace? stackTrace]) { + // Send to Sentry, Crashlytics, etc. + } + + @override + void info(String message) => print('[INFO] $message'); + + @override + void debug(String message) => print('[DEBUG] $message'); +} + +(AsgardeoBuilder()..logger = MyLogger()).build(); +``` + +--- + +## Custom storage + +Implement `AsgardeoStorageManager` to back the SDK with an alternative storage layer (encrypted database, custom keystore, etc.). The default `SharedPreferencesStorageManager` is usually sufficient — accounts and history records are non-sensitive metadata; the private keys live in platform-secure storage regardless of this manager. + +Refer to the `AsgardeoStorageManager` interface in the package source for the full method contract. diff --git a/en/includes/sdks/flutter/references/error-handling.md b/en/includes/sdks/flutter/references/error-handling.md new file mode 100644 index 0000000000..3a26f06d23 --- /dev/null +++ b/en/includes/sdks/flutter/references/error-handling.md @@ -0,0 +1,68 @@ +All exceptions thrown by the SDK extend `AsgardeoException`. Catch the most specific subclass first, then fall back to broader categories. + +## Base exception + +```dart +class AsgardeoException implements Exception { + final String message; + final String? code; + final String? traceId; + final Object? cause; +} +``` + +| Field | Type | Description | +|-------|------|-------------| +| `message` | `String` | Human-readable description of the error | +| `code` | `String?` | Error code identifying the specific failure (server codes or SDK codes prefixed with `ASGPA-`) | +| `traceId` | `String?` | Server trace ID for correlating with server-side logs | +| `cause` | `Object?` | The underlying error that caused this failure, if any | + +## Exception catalog + +| Exception | Thrown when | +|-----------|-------------| +| `AsgardeoNotInitializedException` | `Asgardeo.instance` is accessed before `build()` was called | +| `AsgardeoAlreadyInitializedException` | `build()` is called twice without an intervening `reset()` | +| `AsgardeoValidationException` | Input validation fails (invalid QR data, empty parameters) | +| `AsgardeoRegistrationException` | Server rejects a device registration request | +| `AsgardeoDeviceAlreadyRegisteredException` | The user already has a registered account on this device (subclass of `AsgardeoRegistrationException`) | +| `AsgardeoAuthResponseException` | Server rejects an approve/deny response | +| `AsgardeoAccountNotFoundException` | No local account is found for the given ID or device ID | +| `AsgardeoDeviceNotFoundException` | Server reports the device no longer exists (server code `PDH-15009`) | +| `AsgardeoDeviceUpdateException` | Server rejects a device update request | +| `AsgardeoUnregistrationException` | Server rejects a device unregistration request | +| `AsgardeoNetworkException` | Transport-level failure after retries are exhausted | +| `AsgardeoCryptoException` | A cryptographic operation fails | +| `AsgardeoBiometricUnavailableException` | Biometric is required (`BiometricPolicy.mandatory`) but the device does not support it | +| `AsgardeoBiometricAuthFailedException` | Biometric prompt was shown but the user cancelled or failed | +| `AsgardeoStorageException` | Local storage read or write fails | + +## Structured error handling + +```dart +try { + await Asgardeo.instance.pushAuth.registerDevice(qrCodeJson, deviceToken, FCMPushProvider()); +} on AsgardeoValidationException catch (e) { + // Client-side input error — fix and retry. + print('Invalid input: ${e.message}'); +} on AsgardeoDeviceAlreadyRegisteredException catch (e) { + // Account already exists — prompt user to remove the existing account first. + print('Already registered: ${e.message}'); +} on AsgardeoRegistrationException catch (e) { + // Server rejected the registration. + print('Server error [${e.code}]: ${e.message} (traceId: ${e.traceId})'); +} on AsgardeoBiometricAuthFailedException catch (e) { + // User cancelled or failed the biometric prompt. + print('Biometric auth failed: ${e.message}'); +} on AsgardeoNetworkException catch (e) { + // Transport failure after retries. + print('Network error: ${e.message}'); +} on AsgardeoException catch (e) { + // Any other SDK error. + print('SDK error: ${e.message}'); +} +``` + +!!! tip + When reporting issues, include both `code` and `traceId` from the caught exception. The `traceId` correlates the failed request with server-side logs and significantly speeds up debugging.