---
schema_version: "1"
id: b723bedbba745a70e1903cdfe4e5d73039904d35c92e94b3fda814fbe11693fe
title: OAuth 2.0 Device Flow for IoT Integration
collection: admin
version: current
locale: en-us
portal_url: https://docs.salesforce.com/admin/current/en-us/xcloud/264-0-0/remoteaccess_oauth_device_flow.md
canonical_url: https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_device_flow.htm&release=264.0.0&type=5
source_site: help.salesforce.com
content_type: documentation
content_hash: c60ddb64f0218b4c641a507b722aacd37d0c2e1310793e60361eab8b47dec304
description: To integrate apps that run on devices with limited input or display capabilities, such as Smart TVs, appliances, and other IoT devices, use the OAuth 2.0 device flow. Command-line apps can also use this flow. Users can connect these apps to Salesforce by accessing a browser on a device with more advanced input capabilities, such as a desktop or mobile device.
products:
  - Security
  - Cross Cloud
release: "264"
---
# OAuth 2.0 Device Flow for IoT Integration

To integrate apps that run on devices with limited input or display capabilities, such as Smart TVs, appliances, and other IoT devices, use the OAuth 2.0 device flow. Command-line apps can also use this flow. Users can connect these apps to Salesforce by accessing a browser on a device with more advanced input capabilities, such as a desktop or mobile device.

### Required Editions

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small edition"><colgroup><col></colgroup><tbody><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: both Salesforce Classic and Lightning Experience</td></tr><tr class=""><td class="slds-cell-wrap" style="vertical-align:top;">Available in: <strong>All</strong> Editions</td></tr></tbody></table>

For example, a customer uses a bluetooth device to control their house lights while they’re away for the evening. You can create an external client app for the bluetooth device to enable this flow.

1. The user opens the bluetooth app on their mobile device and clicks Turn On Lights.
2. The external client app posts a request to the Salesforce token endpoint.
3. Salesforce verifies the request and returns a human-readable user code, verification URL, and device code.
4. The bluetooth app displays the device code and instructs the user to enter it at the specified verification URL. The app also begins polling the Salesforce token endpoint for authorization.
5. The user clicks the link to the verification URL and enters the code.
6. The user then authorizes the app to access their protected data, in this case their home’s location.
7. Salesforce sends an access and refresh token to the external client app.
8. The bluetooth app can access the user’s home location and turn on the lights.

Let’s walk through the steps in this authorization flow.

1. [Device Requests Authorization](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_device_flow.htm&release=264.0.0&type=5)
2. [Salesforce Returns Verification Codes](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_device_flow.htm&release=264.0.0&type=5)
3. [User Authenticates and Authorizes While Device Polls the Token Endpoint](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_device_flow.htm&release=264.0.0&type=5)
4. [Salesforce Grants Access Token](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_device_flow.htm&release=264.0.0&type=5)

![Important](https://sf-zdocs-cdn-prod.zoominsoftware.com/tdta-xcloud-identity-264-0-0-production-enus/699dab6f-8f1a-4662-b24f-6521d5b747f7/images/icon_important.png)

Important Salesforce is restricting the device flow to local external client apps with a localhost callback URL. With this restriction, you can no longer use the device flow with any app that isn't compliant with the new criteria. Non-compliant apps include connected apps, packaged external client apps, and local external client apps that don't have a localhost callback URL.

- Starting in August 2026: if a non-compliant app has the device flow enabled, you get an error if you try to update the app. For example, if the device flow is enabled for a connected app, you can't make any updates to the app. You must disable the device flow or switch to a local external client app with a localhost callback URL.
- Starting in November 2026: Salesforce blocks the device flow for all non-compliant apps.

For more information, review the “Restrict the OAuth 2.0 Device Flow to Local External Client Apps” release update on the Release Updates page in Setup.

## Device Requests Authorization

To initiate the authorization flow, the device—via an external client app—posts an authorization request to the Salesforce token endpoint.

For example:

```
POST /services/oauth2/token  HTTP/1.1
Host: login.salesforce.com
Content-Type: application/x-www-form-urlencoded

response_type=device_code&
client_id=3MVG9PhR6g6B7ps7TTI4cP7Mppg3l7tu.​MRAYULyVqcmA9hGLpHiiS.Q7rO9yjlmffiBUM6tFpYAlXEkRjHb9&scope=api
```

Include these parameters in the request.

![Important](https://sf-zdocs-cdn-prod.zoominsoftware.com/tdta-xcloud-identity-264-0-0-production-enus/699dab6f-8f1a-4662-b24f-6521d5b747f7/images/icon_important.png)

Important When developing OAuth integrations, always pass sensitive information in the body of a POST request or in a request header. Don't use GET parameters in the URL query string to pass sensitive information. Sensitive information includes but isn't limited to usernames, passwords, OAuth tokens, client secrets, and any personally identifiable information. For more information on security best practices, see [Storing Sensitive Data](https://developer.salesforce.com/docs/atlas.en-us.secure_coding_guide.meta/secure_coding_guide/secure_coding_storing_sensitive_data.htm) in the Secure Coding Guide.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col>
          <col>
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d84980e168">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d84980e171">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e168 ">
              <code>Request Header</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e171 ">
              <p>The request header contains this information:</p>
              <ul>
                <li>The Salesforce OAuth 2.0 token endpoint. External client apps send OAuth token
                  requests to this endpoint.</li>

                <li>The authorization server.</li>

                <li>Content type of the request.</li>

              </ul>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e168 ">
              <code>response_type</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e171 ">The OAuth 2.0 grant type that the external client app requests. The value for
              this flow must be <samp class="parmname">device_code</samp>.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e168 ">
              <code>client_id</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e171 ">The consumer key of the external client app. To access the consumer key, from
              the External Client App Manager, find the external client app and select
              <strong class="uicontrol">Edit Settings</strong> from the dropdown. Then expand the OAuth
              Settings section, and click <strong class="uicontrol">Consumer Key and Secret</strong>. You're
              sometimes prompted to verify your identity before you can view the consumer
              key.
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e168 ">
              <code>scope</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e171 ">Optional. Permissions that define the type of protected resources an external
              client app can access. You assign scopes to an external client app when you build
              it, and they’re included with the OAuth tokens during the authorization flow. If you
              don’t include this parameter, all scopes assigned to the external client app are
              requested. The scopes passed in this parameter must be a subset of the registered
              scopes. For valid parameters, see <a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_tokens_scopes.htm&amp;type=5&amp;language=en_US" target="_blank">OAuth Scopes</a>.</td>

          </tr>
        </tbody>
      </table>

## Salesforce Returns Verification Codes

After verifying the request, Salesforce returns a human-readable user code, verification URL, and device code.

This sample response returns these values with a 200 success status code.

```
HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store

{
"device_code":"M01WRzlQaFI2ZzZCN3BzN1RUSTRjUDdNcHBnM2w3dHUuTVJBWVVMeVZxY21BOWhHTHBIaWlTLlE3ck​85eWpsbWZmaUJVTTZ0RnBZQWxYRWtSakhiOTsxMC4yMi4zNC45MjsxNDc3Njc0NDg3NTA1O1gxRDlTRUVU",
"user_code":"X1D9SEET",
"verification_uri":"https://acme.my.salesforce.com/setup/connect",
"interval":5
}
```

The following parameters are included in the response.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col>
          <col>
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d84980e280">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d84980e283">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e280 ">
              <code>Response Header</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e283 ">
              <p>The response header contains this information:</p>
              <ul>
                <li>200 success status code</li>

                <li>Content type of the response</li>

                <li>Indication that data isn’t stored in a cache</li>

              </ul>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e280 ">
              <code>device_code</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e283 ">
              <p>A verification code for the device. This code is valid for 10 minutes. The code
                can include uppercase letters, lowercase letters, and numbers. </p>
              <p style="margin-bottom:0;">Salesforce can
                change how we format device codes at any time. When you develop integrations that
                use this flow, don’t make assumptions about the device code format and
                length.</p>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e280 ">
              <code>user_code</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e283 ">A verification code for the end user, sent as an 8-digit alphanumeric code. The
              user must enter this code at the verification URL. This code is valid for 10
              minutes.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e280 ">
              <code>verification_uri</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e283 ">
              <p>The end-user verification URL on the authorization server. The user must enter
                the user code at this verification URL.</p>
              <p>With verification through a My Domain
                login URL or Experience Cloud site URL, the <samp class="parmname">verification_uri</samp>
                reflects the URL that initiated the flow. For example, if the device sends a token
                request to
                <samp class="filepath">https://acme.my.salesforce.com/services/oauth2/token</samp>, the
                <samp class="parmname">verification_uri</samp> is
                <samp class="filepath">https://acme.my.salesforce.com/setup/connect</samp>.
              </p>
              <p style="margin-bottom:0;">Optionally,
                you can add the <code>prompt</code> parameter to your
                <samp class="parmname">verification_uri</samp>.
              </p>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e280 ">
              <code>interval</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e283 ">The minimum number of seconds that we recommend waiting between polling
              requests to the token endpoint.</td>

          </tr>
        </tbody>
      </table>

## User Authenticates and Authorizes While Device Polls the Token Endpoint

The client app on the device instructs the user to visit the verification URL on a computer or mobile device and enter the user code. The user opens a browser, navigates to the verification URL, and enters the code displayed on the device. If the code is valid, the user logs in, as needed. After successful login, the user allows the device to access Salesforce account data.

Meanwhile, after displaying the user code and verification URL, the client application on the device polls the token endpoint repeatedly. Polling checks whether the user has authorized access and whether the authorization server has issued the access token.

Here’s an example of a polling request:

```
POST /services/oauth2/token  HTTP/1.1
Host: login.salesforce.com
Content-Type: application/x-www-form-urlencoded

grant_type=device&
client_id=3MVG9PhR6g6B7ps7TTI4cP7Mppg3l7tu.MRAYULyVqcmA9hGLpHiiS.​Q7rO9yjlmffiBUM6tFpYAlXEkRjHb9&code=M01WRzlQaFI2ZzZCN3BzN1RUSTRjUDdNcHBnM2w3dHUu​TVJBWVVMeVZxY21BOWhHTHBIaWlTLlE3ck85eWpsbWZmaUJVTTZ0RnBZQWxYRWtSakhiOTsxMC4yMi4zNC45MjsxNDc3N​jc0NDg3NTA1O1gxRDlTRUVU
```

The polling request contains these parameters, in addition to the header.

 
| Parameter | Description |
| --- | --- |
| `grant_type` | The value must be device for this flow. |
| `client_id` | The consumer key of the external client app. To access the consumer key, from the External Client App Manager, find the external client app and select **Edit Settings** from the dropdown. Then expand the OAuth Settings section, and click **Consumer Key and Secret**. You're sometimes prompted to verify your identity before you can view the consumer key. |
| `code` | The authorization server creates an authorization code, which is a short-lived token, and passes it to the client after successful authentication. The client sends the authorization code to the authorization server to obtain an access token and, optionally, a refresh token. |

## Salesforce Grants Access Token

If the user has authorized access, the Salesforce responds with a success message and the access token along with other values. For example:

```
{
"access_token": "00DD00000008Uw2!ARkAQGppKf6n.VwG.EnFSvi731qWh.7vKfaJjL7h49yutIC84gAsxM​rqcE81GjpTjQbDLkytl2ZwosNbIJwUS0X8ahiILj3e"
"refresh_token": "your token here"
"signature": "hJuYICd2IHsjyTcFqTYiOr8THmgDmrcjgWaMp13X6dY="
"scope": "api"
"instance_url": "https://yourInstance.salesforce.com"
"id": "https://login.salesforce.com/id/00DD00000008Uw2MAE/005D0000001cAGmIAM"
"token_type": "Bearer"
"issued_at": "1477674717112"
}
```

These parameters are included in the example response.

<table class="slds-table slds-table_bordered slds-max-medium-table_stacked slds-m-bottom_small ">
        <colgroup>
          <col>
          <col>
        </colgroup>
        <thead class="slds-text-title_caps ">
          <tr class="">
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d84980e507">Parameter</th>
            <th scope="col" class="slds-cell-wrap" style="vertical-align:top;" id="d84980e510">Description</th>
          </tr>
        </thead>
        <tbody>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>access_token</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">OAuth token that an external client app uses to request access to a protected
              resource on behalf of the client application. Additional permissions in the form of
              scopes can accompany the access token.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>refresh_token</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">Token obtained from the web server, user-agent, or hybrid app token flow. This
              value is a secret. Take appropriate measures to protect it. This parameter is
              returned only if your external client app is set up with a
              <code>refresh_token</code> scope.
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>signature</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">Base64-encoded HMAC-SHA256 signature signed with the
              <code>client_secret</code>. The signature can include the concatenated ID and
              <code>issued_at value</code>, which you can use to verify that the identity
              URL hasn’t changed since the server sent it.
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>scope</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">
              <p>The scopes associated with the access token.</p>
              <p>Scopes further define the type
                of protected resources that the client can access. You assign scopes to an
                external client app when you build it, and they’re included with the OAuth tokens
                during the authorization flow. </p>
              <p style="margin-bottom:0;">For valid parameters, see <a href="https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_tokens_scopes.htm&amp;type=5&amp;language=en_US" target="_blank">OAuth Scopes</a>.</p>
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>instance_url</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">A URL indicating the instance of the user’s org. For example:
              <code>https://<var>yourInstance</var>.salesforce.com/</code>.
            </td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>id</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">An identity URL that can be used to identify the user and to query for more
              information about the user. See <a href="https://help.salesforce.com/articleView?id=remoteaccess_using_openid.htm&amp;language=en_US" target="_blank">Identity URLs</a>.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>token_type</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">A <code>Bearer</code> token type, which is used for all responses that
              include an access token.</td>

          </tr>
          <tr class="">
            <td data-label="Parameter" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e507 ">
              <code>issued_at</code>
            </td>

            <td data-label="Description" class="slds-cell-wrap" style="vertical-align:top;" headers="d84980e510 ">Time stamp of when the signature was created in milliseconds.</td>

          </tr>
        </tbody>
      </table>

#### See Also

- [OAuth 2.0 Authorization Errors](https://help.salesforce.com/s/articleView?id=xcloud.remoteaccess_oauth_flow_errors.htm&release=264.0.0&type=5 "Errors can occur during OAuth authorization. For example, a user denies access to the connected app or request parameters are incorrect. When errors occur, the authorizing server sends an error to the callback URL with an error code.")