> ## Documentation Index
> Fetch the complete documentation index at: https://docs.portalhq.io/llms.txt
> Use this file to discover all available pages before exploring further.

# handleRedirect

> Complete a sign-in from the redirect URL the app received, or return null when the URL is not a Portal sign-in.

## Function Signature

```dart theme={null}
Future<PortalAuthResult?> handleRedirect(String url)
```

## Description

A [`PortalAuth`](./portalauth) method, and the single completion path for every sign-in method: magic links and the Google and Apple callbacks. It exchanges the grant on the redirect for a session, which is persisted on the device before the result is returned.

It returns `null` for any URL that is not a Portal redirect for the configured `redirectUrl`, so every deep link your app receives can be passed here.

A redirect the OS delivers twice returns the same result instead of failing the backend's single-use check. Only successful exchanges are remembered, and the memory lives in the native instance and this process: [`configure`](./portalauthconfigure) and [`clearPersistedSession`](./portalauthclearpersistedsession) reset it, and it does not survive the app being terminated.

Available starting from `portal_flutter` 2.0.0.

## Parameters

| Parameter | Type | Required | Description |
| - | - | - | - |
| `url` | `String` | Yes | The URL the app received, for example from `app_links`. |

## Returns

**`PortalAuthResult?`**:

* **`null`**: the URL is not a Portal sign-in for this `redirectUrl`. Let your other routes handle it.
* **`PortalAuthenticated`**: the sign-in is complete. Pass `session` to `Portal().initialize(session:)`.
* **`PortalTotpRequired`**: a TOTP code is required. Nothing is persisted yet; complete it with [`verifyTotp`](./portalauthverifytotp).

See [PortalAuth](./portalauth#portalauthresult) for the fields of each case.

## Example

```dart theme={null}
import 'package:portal_flutter/portal_flutter.dart';

Future<void> completeSignIn(Uri uri) async {
  try {
    final result = await PortalAuth().handleRedirect(uri.toString());

    switch (result) {
      case null:
        return; // Not a Portal sign-in redirect.
      case PortalTotpRequired():
        showTotpPrompt(result);
      case PortalAuthenticated():
        await Portal().initialize(session: result.session, rpcConfig: { /* ... */ });
    }
  } on PortalException catch (e) {
    print('Sign-in failed (${e.code}): ${e.message}');
  }
}
```

## Errors

| Code | Description |
| - | - |
| `AUTH_NOT_CONFIGURED` | Called before [`configure`](./portalauthconfigure) |
| `AUTH_AUTHENTICATION_FAILED` | The redirect carried an `error` parameter: the user did not complete the provider sign-in |
| `AUTH_INVALID_GRANT_RESPONSE` | The exchange returned neither a session token nor a `userJwt` |
| `AUTH_MALFORMED_RESPONSE` | The response was missing a required field |
| `AUTH_SESSION_STORAGE_FAILURE` | The session could not be saved to secure storage. On iOS, retry with the same URL; on Android, start a new sign-in. See [Handle errors](../guide/client-auth#handle-errors) |
| `AUTH_HANDLE_REDIRECT_ERROR` | A network or server error, including a grant that was already used, expired (15 minutes for a magic link, 10 for OAuth), or was issued for another environment |

## Related

* [Complete the sign-in guide](../guide/client-auth#complete-the-sign-in)
* [Register your redirect](../guide/client-auth#register-your-redirect)
* [verifyTotp](./portalauthverifytotp)
* [restoreSession](./portalauthrestoresession)


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.