> For the complete documentation index, see [llms.txt](https://docs.tryterra.co/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.tryterra.co/unified-api/authentication-flow.md).

# User authentication

Connect an end user's wearable or health app to Terra, then store the user ID from the auth event.

Connecting a user lets them grant your app access to a provider such as Fitbit, Garmin or Oura. Once they connect, Terra pushes their data to your destination as events, with no polling. Terra stores and refreshes the provider tokens for you.

## Before you begin

* Get your **API key** and **dev ID** from the [Terra dashboard](https://dashboard.tryterra.co/).
* [Set up a destination](/destinations/setting-up-data-destinations.md) where Terra sends events.
* [Enable the data sources](/unified-api/setting-up-data-sources.md) your users can connect.

## How authentication works

1. The user clicks **Connect device** in your app.
2. Your frontend asks your backend for a widget URL.
3. Your backend creates a widget session with `POST /api/widget/session`. See [Terra authentication widget](/unified-api/implementation-terra-widget.md).
4. Your frontend opens the widget URL in a new tab, or an in-app browser on mobile.
5. The user picks their provider and logs in.
6. The widget redirects to your success or failure URL with `user_id` and `reference_id` as query parameters.
7. Terra sends an `auth` event to your destination.

From then on, the user's data reaches your destination automatically.

## Try it from the dashboard

To see the flow before writing any code, connect your own device from the [Terra dashboard](https://dashboard.tryterra.co/). Your end users don't connect this way: live users go through the API.

{% prompt description="Run a user through auth and read the auth event" icon="terminal" openInAIProviders="true" defaultExpanded="full" %}

```markdown
Generate a widget session for reference_id test-1 and open it for me. Once I have connected, check whether the connection is active and show me the auth event.

Use the Terra CLI: brew install tryterra/tap/terra, or npm install -g @tryterra/cli, then terra login. Run terra docs ask --question "<question>" for anything you need from the Terra docs, and terra <command> --help for usage.
```

{% endprompt %}

{% @supademo/embed url="<https://app.supademo.com/demo/clzvfyvwy08lmo0qpw5yadcf8>" demoId="clzvfyvwy08lmo0qpw5yadcf8" %}

## Handle the auth event

Terra sends an [authentication event](https://docs.tryterra.co/reference/unified-api/event-types#authentication) to your destination whenever a user connects, disconnects, fails to connect, reconnects, or has their access revoked. Each event carries the `user_id`, the auth `status` and a `message`.

A successful `auth` event:

```json
{
    "type": "auth",
    "status": "success",
    "widget_session_id": "a9a0c7a4-c05e-4550-a973-f87a31a7c558",
    "user": {
        "scopes": "COURSE_IMPORT,WORKOUT_IMPORT,ACTIVITY_EXPORT,HEALTH_EXPORT",
        "active": true,
        "last_webhook_update": null,
        "created_at": "2024-10-07T03:45:00.000000+00:00",
        "user_id": "4735eac4-31a2-47ef-8b11-51e98d84c680",
        "reference_id": "tony_stark",
        "provider": "GARMIN"
    },
    "reference_id": "tony_stark",
    "message": "User has successfully authenticated",
    "version": "2022-03-16"
}
```

Every authentication event format is listed under [Event types](https://docs.tryterra.co/reference/unified-api/event-types#authentication). All events you have received are in [Dashboard > Events](https://dashboard.tryterra.co/dashboard/debug).

{% prompt description="Inspect auth events and why one failed" icon="terminal" openInAIProviders="true" defaultExpanded="full" %}

```markdown
Show me the auth events for reference_id test-1 and explain why the last one failed.

Use the Terra CLI: brew install tryterra/tap/terra, or npm install -g @tryterra/cli, then terra login. Run terra docs ask --question "<question>" for anything you need from the Terra docs, and terra <command> --help for usage.
```

{% endprompt %}

## Store the user ID

Save the `user_id` from the auth event against your own user. Every data event carries this `user_id`, so without it you can't tell whose activity or sleep data has arrived.

The `user_id` is also on the success redirect:

```
{auth_success_redirect_url}?user_id=...&reference_id=...&resource=...
```

### When the user ID changes

Connecting the same provider again with the same `reference_id` and `dev_id` deletes the previous user record and creates a new `user_id`. This is per provider: reconnecting Fitbit doesn't affect an Apple Health connection under the same `reference_id`.

The old `user_id` disappears from the Users tab in your dashboard, though Payload History still shows data sent to it. Terra sends a `user_reauth` event with both the old and new `user_id`. Handle it and update the stored `user_id`, or new data won't match your records.

## Check your setup

Your setup is working when:

1. Your backend gets a `201` from `POST /api/widget/session` and reads the `url` field. With a custom UI, it gets a `200` from `POST /auth/authenticateUser` and reads `auth_url`.
2. The end user lands on your success redirect after connecting.
3. Your destination receives an `auth` event with `status: success`.
4. You store the `user_id` from that event against your own user.

## Next steps

* [Terra authentication widget](/unified-api/implementation-terra-widget.md): create a widget session and open the hosted connect screen.
* [Custom authentication UI](/unified-api/implementation-custom-ui.md): build your own provider buttons and generate login links.
* [Authentication redirect pages](/unified-api/customising-authentication-redirects.md): send the end user back to your app after they connect.
