# Secure authentication with tokens (/en/realtime-media/iot/build/authenticate-and-secure-channels/authentication-workflow)

> For AI agents: see the complete documentation index at [llms.txt](/llms.txt).

To protect your business, it is best practice to authenticate every client that joins a channel. This guide explains how to fetch an authentication token from your token server, use it to join a channel, and handle token errors.

## Understand the tech

When a device attempts to connect to an Agora channel, your app retrieves a token from the token server in your security infrastructure. Your app then sends this token to Agora SDRTN® for authentication. Agora SDRTN® reads the information stored in the token to validate the request.

**Token authentication flow**

![token authentication flow](https://assets-docs.agora.io/images/video-sdk/token-authentication.svg)

## Prerequisites

Before starting, ensure that you have:

* Implemented the basic IoT SDK functionality. See [Build from scratch](../../build-from-scratch.mdx).

* Deployed a token server using either of the following guides:

  * [Deploy a token server](/en/realtime-media/rtc/build/authenticate-users/deploy-token-server)
  * [Deploy a middleware server](/en/realtime-media/rtc/build/authenticate-users/middleware-token-server)

## Implement basic authentication

This section shows you how to implement basic authentication by acquiring a token and using it to join a channel.

### Use a token to join a channel

The device requests a token from your authentication server corresponding to the user ID and the channel name it intends to join. You use the received token to join a channel by passing it to `agora_rtc_join_channel`, along with the connection ID, channel name, and user ID:

```c
// Join a channel
rval = agora_rtc_join_channel(g_conn_id, DEFAULT_CHANNEL_NAME, DEFAULT_USER_ID, p_token, &channel_options);
if (rval < 0) {
    printf("Failed to join channel \"%s\", reason: %s\n", DEFAULT_CHANNEL_NAME, agora_rtc_err_2_str(rval));
    return -1;
}
```

<CalloutContainer type="info">
  <CalloutDescription>
    The user ID and channel name used to join a channel must be consistent with the values used to generate the token.
  </CalloutDescription>
</CalloutContainer>

### Handle token errors

If a token is invalid, expired, or missing when required, IoT SDK triggers the `on_error` callback with a token-related error code. Handle these codes to detect authentication failures and fetch a fresh token from your token server:

```c
static void __on_error(connection_id_t conn_id, int code, const char *msg) {
    if (code == ERR_INVALID_TOKEN || code == ERR_TOKEN_EXPIRED) {
        printf("Invalid token. Please double check. Error msg \"%s\"\n", msg);
    } else if (code == ERR_DYNAMIC_TOKEN_BUT_USE_STATIC_KEY) {
        printf("Dynamic token is enabled but is not provided. Error msg \"%s\"\n", msg);
    } else {
        printf("Error %d is captured. Error msg \"%s\"\n", code, msg);
    }
}
```

To recover from a token error, request a new token from your token server, then call `agora_rtc_join_channel` again with the fresh token. For a full list of token and other error codes, see [Error codes](../../reference/error-codes.mdx#transmission-related-errors).

### Renew a token

To renew a token before it expires, without rejoining the channel, request a new token from your token server and pass it to `agora_rtc_renew_token`.

## Reference

This section contains content that completes the information on this page, or points you to documentation that explains other aspects to this product.

### API reference

* [agora\_rtc\_join\_channel](https://api-ref.agora.io/en/iot-sdk/linux/1.x/agora__rtc__api_8h.html#a6c29ff27f04623526a164cf6e5dcd738)
* [agora\_rtc\_renew\_token](https://api-ref.agora.io/en/iot-sdk/linux/1.x/agora__rtc__api_8h.html#a87c94a2d518278926807e24592b41336)
* [on\_error](https://api-ref.agora.io/en/iot-sdk/linux/1.x/structagora__rtc__event__handler__t.html)
