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

# Identify users

> identify, login, and logout attach your own user ID to the events of both SDKs.

Every event carries a user ID. Until your code sets one, the ID is anonymous, and the SDK
generates it. After `identify`, every later event carries the ID from your own system, and the
dashboard shows the events of one user as one journey.

## Methods

| Method | SDK | What it does |
| - | - | - |
| `identify(userId)` | Grain Tag | Sets the user ID for every later event. A call before `init` is queued and replayed after `init`. |
| `identify(userId)` | Analytics Web | Sets the user ID for every later event. |
| `setUserId(userId)` | Analytics Web | Same as `identify`. `setUserId(null)` clears the user ID and returns to an anonymous ID. |
| `getUserId()` | Analytics Web | Returns the user ID that your code set, or `null`. |
| `getEffectiveUserId()` | Analytics Web | Returns the ID that the next event carries: your user ID, or the anonymous ID. |
| `login({ userId?, authToken?, authStrategy? })` | Analytics Web | Sets the user ID. With `authToken`, it also switches the client to `JWT` authentication. |
| `logout()` | Analytics Web | Clears the user ID, resets `authStrategy` to `NONE`, and returns to an anonymous ID. |

## Grain Tag

Grain Tag has one method. Call it after the user logs in.

<CodeGroup>
  ```javascript Script tag theme={null}
  GrainTag.identify('user_8f3a2c1b');
  GrainTag.track('dashboard_viewed');
  ```

  ```javascript npm theme={null}
  import { identify, track } from '@grainql/tag';

  identify('user_8f3a2c1b');
  track('dashboard_viewed');
  ```
</CodeGroup>

Grain Tag has no method that clears the user ID. Read [Track an event](/core/event-tracking).

## Analytics Web

### Set the user ID

`identify` and `setUserId` do the same thing for a string. Use the one that reads better in your
code.

```typescript theme={null}
grain.identify('user_8f3a2c1b');
grain.track('dashboard_viewed');
grain.track('report_generated');
```

When your code knows the user at construction time, pass `userId` in the configuration. The first
event then carries it.

```typescript theme={null}
const grain = createGrainAnalytics({
  tenantId: 'your-tenant-id',
  userId: 'user_8f3a2c1b',
});

grain.track('app_opened');
```

### Log in and log out

`login` sets the user ID and the authentication in one call. `logout` reverses both.

```typescript theme={null}
grain.login({ userId: 'user_8f3a2c1b' });

grain.login({ userId: 'user_8f3a2c1b', authToken: 'eyJhbGciOi...' });

grain.logout();
grain.track('logged_out');
```

| Option | Effect |
| - | - |
| `userId` | Sets the user ID. |
| `authToken` | Stores the token as the `authProvider`. If `authStrategy` is `NONE`, changes it to `JWT`. |
| `authStrategy` | Overrides the strategy: `NONE`, `SERVER_SIDE`, or `JWT`. |

After `logout`, `authStrategy` is `NONE`, the `authProvider` is removed, and events carry an
anonymous ID. Read [Authentication](/authentication).

### Read the ID

```typescript theme={null}
const userId = grain.getUserId();
const effectiveUserId = grain.getEffectiveUserId();
```

`getUserId` returns `null` for an anonymous visitor. `getEffectiveUserId` always returns a string.
Use it when you need the ID that Grain stores on the next event.

## Anonymous IDs

When no user ID is set, Analytics Web generates one. The consent mode decides which kind.

| Consent mode | Anonymous ID | Lifetime |
| - | - | - |
| `COOKIELESS` | Daily rotating ID, prefix `daily_` | One calendar day. The SDK stores no cookie for it. |
| `GDPR_STRICT` before `grantConsent()` | Daily rotating ID | One calendar day. |
| `GDPR_STRICT` after `grantConsent()` | Permanent ID, a UUID v4 | Stored in a cookie or `localStorage` until `revokeConsent()`. |
| `GDPR_OPT_OUT` | Permanent ID, a UUID v4 | Stored in a cookie or `localStorage` until `revokeConsent()`. |

The daily rotating ID is computed from the date, the tenant alias, and a random seed. Two visits
on different days get different IDs. Grain Tag uses the same two kinds of ID. Its `auto` consent
mode is cookieless. Read [Privacy and consent](/essentials/privacy-and-compliance).

Events before `identify` keep their anonymous ID. Only the events after the call carry your user
ID.

```typescript theme={null}
grain.track('landing_page_viewed');
grain.track('signup_button_clicked');

grain.identify('user_8f3a2c1b');
grain.track('account_created');
```

## Page loads

The SDK does not store your user ID across page loads. Your authentication system owns the
session, so Grain does not keep a second copy. On every load, set the ID from your own session.

```typescript theme={null}
const currentUser = getCurrentUser();

if (currentUser) {
  grain.setUserId(currentUser.id);
} else {
  grain.setUserId(null);
}
```

## Servers

On a server, one client serves many users. Set the user ID before each request, and flush the
event inside the request.

```typescript theme={null}
app.post('/api/action', async (req, res) => {
  grain.setUserId(req.user.id);
  await grain.track('api_action_performed', { endpoint: '/api/action' }, { flush: true });
  res.sendStatus(200);
});
```

In a serverless function, create one client per invocation. Two requests then cannot share a user
ID.

```typescript theme={null}
export async function handler(event) {
  const grain = createGrainAnalytics({
    tenantId: 'your-tenant-id',
    userId: event.userId,
  });

  await grain.track('function_ran', {}, { flush: true });
  return { statusCode: 200 };
}
```

Read [Node.js and servers](/quickstart/nodejs).

## React

`GrainProvider` takes `userId` in its `config`. When the value changes, the provider updates the
client.

```tsx theme={null}
import { GrainProvider } from '@grainql/analytics-web/react';

function App() {
  const { user } = useAuth();

  return (
    <GrainProvider config={{ tenantId: 'your-tenant-id', userId: user?.id }}>
      <YourApp />
    </GrainProvider>
  );
}
```

Read [React hooks](/react/overview).

## Rules for the client

1. Use an ID that does not change: a database ID or a UUID. An email address changes, and a
   session ID changes on every session.
2. Set the user ID on every page load, before the first event. The SDK does not store it.
3. If the user logs out, call `logout()` or `setUserId(null)`. Otherwise the next user's events go
   to the previous user.
4. Do not switch between many user IDs from one client. Grain rate-limits a source that sends
   many distinct IDs. Read [Security](/advanced/security).
5. With `JWT` authentication, the user ID must equal the subject of the token. The API rejects
   an event with a different ID.
6. On a server, set the user ID before each request, or create one client per request.

After you identify a user, you can store attributes on the user. Read
[User properties](/core/user-properties).


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