> ## Documentation Index
> Fetch the complete documentation index at: https://docs-staging-quickstart-revamp.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

> Learn how to revoke a refresh token if it gets compromised using the Authentication API, the Management API, or the Auth0 Dashboard.

# Revoke Refresh Tokens

You can revoke <Tooltip tip="Refresh Token: Token used to obtain a renewed Access Token without forcing users to log in again." cta="View Glossary" href="/docs/glossary?term=refresh+tokens">refresh tokens</Tooltip> in case they become compromised. Auth0 handles token revocation as though the token has been potentially exposed to malicious adversaries.

You can also use [refresh token rotation](/docs/secure/tokens/refresh-tokens/refresh-token-rotation) so that every time a client exchanges a refresh token to get a new <Tooltip tip="Refresh Token: Token used to obtain a renewed Access Token without forcing users to log in again." cta="View Glossary" href="/docs/glossary?term=access+token">access token</Tooltip>, a new refresh token is also returned. Therefore, you no longer have a long-lived refresh token that, if compromised, could provide illegitimate access to resources. As refresh tokens are continually exchanged and invalidated, the threat is reduced.

You can revoke a refresh token in the following ways:

* In the Dashboard
* Post a request to the Authentication API `/oauth/revoke` endpoint
* Post a request to the <Tooltip tip="Management API: A product to allow customers to perform administrative tasks." cta="View Glossary" href="/docs/glossary?term=Management+API">Management API</Tooltip> `/api/v2/device-credentials` endpoint

## Refresh tokens and grants

A grant provides an application with access to a resource on another entity without exposing user credentials. Tokens are issued in the context of a grant, and when a grant is revoked, so are all tokens issued in the context of that grant. When, on the other hand, a token is revoked, this does not necessarily mean that the grant is revoked.

You can choose the revocation behavior in the Dashboard tenant settings when a device is unlinked from a user in Auth0, using either the Dashboard or the Management API.

<Callout icon="file-lines" color="#0EA5E9" iconType="regular">
  For existing tenants, this feature is enabled by default to preserve the existing behavior. For new tenants (as of 13 January 2021), this feature is disabled by default to ensure that the revocation of a refresh token will not revoke the grant. If a grant revocation is needed, a separate request must be sent using a grant revocation endpoint.
</Callout>

1. Go to [Dashboard > Tenant Settings > Advanced](https://manage.auth0.com/#/tenant/advanced) and scroll to the **Settings** section.
2. Enable or disable the **Refresh Token Revocation Deletes Grant** toggle depending on how you want the revocation to work.

   1. Enable the toggle to delete the underlying grant when you revoke the refresh token. Each revocation request invalidates not only the specific token but all other tokens based on the same authorization grant. This means that all refresh tokens that have been issued for the same user, application, and audience will be revoked.
   2. Disable the toggle to retain the underlying grant when you revoke the refresh token. When a device is unlinked, only the associated refresh token is revoked, leaving the grant intact.

## Use the Dashboard

You can use the Dashboard to revoke a user's authorized access to the application that issued the token. This renders the refresh token invalid, which is functionally identical to revoking the token itself.

1. Go to [Dashboard > User Management > Users](https://manage.auth0.com/#/users), and click the name of the user to view.
2. Select the **Authorized Applications** tab. This page lists all the applications to which the user has authorized access.
3. To revoke the user's access to an authorized application, and hence invalidate the refresh token, click **Revoke**.

## Use the Authentication API

To revoke a refresh token, send a `POST` request to `https://{yourDomain}/oauth/revoke`.

The `/oauth/revoke` endpoint revokes the entire grant, not just a specific token. Use the `/api/v2/device-credentials` endpoint to revoke refresh tokens. The API first validates the application credentials and then verifies whether the token was issued to the application making the revocation request. If this validation fails, the request is refused, and the application is informed of the error. Next, the API invalidates the token. The invalidation takes place immediately, and the token cannot be used again after the revocation. Each revocation request invalidates all the tokens that have been issued for the same authorization grant.

<CodeGroup>
  ```bash cURL lines
  curl --request POST \
    --url 'https://{yourDomain}/oauth/revoke' \
    --header 'content-type: application/json' \
    --data '{ "client_id": "{yourClientId}", "client_secret": "{yourClientSecret}", "token": "{yourRefreshToken}" }'
  ```

  ```csharp C# lines
  var client = new RestClient("https://{yourDomain}/oauth/revoke");
  var request = new RestRequest(Method.POST);
  request.AddHeader("content-type", "application/json");
  request.AddParameter("application/json", "{ \"client_id\": \"{yourClientId}\", \"client_secret\": \"{yourClientSecret}\", \"token\": \"{yourRefreshToken}\" }", ParameterType.RequestBody);
  IRestResponse response = client.Execute(request);
  ```

  ```go Go lines expandable
  package main

  import (
  	"fmt"
  	"strings"
  	"net/http"
  	"io/ioutil"
  )

  func main() {

  	url := "https://{yourDomain}/oauth/revoke"

  	payload := strings.NewReader("{ \"client_id\": \"{yourClientId}\", \"client_secret\": \"{yourClientSecret}\", \"token\": \"{yourRefreshToken}\" }")

  	req, _ := http.NewRequest("POST", url, payload)

  	req.Header.Add("content-type", "application/json")

  	res, _ := http.DefaultClient.Do(req)

  	defer res.Body.Close()
  	body, _ := ioutil.ReadAll(res.Body)

  	fmt.Println(res)
  	fmt.Println(string(body))

  }
  ```

  ```java Java lines
  HttpResponse<String> response = Unirest.post("https://{yourDomain}/oauth/revoke")
    .header("content-type", "application/json")
    .body("{ \"client_id\": \"{yourClientId}\", \"client_secret\": \"{yourClientSecret}\", \"token\": \"{yourRefreshToken}\" }")
    .asString();
  ```

  ```javascript Node.JS lines
  var axios = require("axios").default;

  var options = {
    method: 'POST',
    url: 'https://{yourDomain}/oauth/revoke',
    headers: {'content-type': 'application/json'},
    data: {
      client_id: '{yourClientId}',
      client_secret: '{yourClientSecret}',
      token: '{yourRefreshToken}'
    }
  };

  axios.request(options).then(function (response) {
    console.log(response.data);
  }).catch(function (error) {
    console.error(error);
  });
  ```

  ```obj-c Obj-C lines expandable
  #import <Foundation/Foundation.h>

  NSDictionary *headers = @{ @"content-type": @"application/json" };
  NSDictionary *parameters = @{ @"client_id": @"{yourClientId}",
                                @"client_secret": @"{yourClientSecret}",
                                @"token": @"{yourRefreshToken}" };

  NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

  NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@"https://{yourDomain}/oauth/revoke"]
                                                         cachePolicy:NSURLRequestUseProtocolCachePolicy
                                                     timeoutInterval:10.0];
  [request setHTTPMethod:@"POST"];
  [request setAllHTTPHeaderFields:headers];
  [request setHTTPBody:postData];

  NSURLSession *session = [NSURLSession sharedSession];
  NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                              completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                  if (error) {
                                                      NSLog(@"%@", error);
                                                  } else {
                                                      NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                      NSLog(@"%@", httpResponse);
                                                  }
                                              }];
  [dataTask resume];
  ```

  ```php PHP lines expandable
  $curl = curl_init();

  curl_setopt_array($curl, [
    CURLOPT_URL => "https://{yourDomain}/oauth/revoke",
    CURLOPT_RETURNTRANSFER => true,
    CURLOPT_ENCODING => "",
    CURLOPT_MAXREDIRS => 10,
    CURLOPT_TIMEOUT => 30,
    CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
    CURLOPT_CUSTOMREQUEST => "POST",
    CURLOPT_POSTFIELDS => "{ \"client_id\": \"{yourClientId}\", \"client_secret\": \"{yourClientSecret}\", \"token\": \"{yourRefreshToken}\" }",
    CURLOPT_HTTPHEADER => [
      "content-type: application/json"
    ],
  ]);

  $response = curl_exec($curl);
  $err = curl_error($curl);

  curl_close($curl);

  if ($err) {
    echo "cURL Error #:" . $err;
  } else {
    echo $response;
  }
  ```

  ```python Python lines
  import http.client

  conn = http.client.HTTPSConnection("")

  payload = "{ \"client_id\": \"{yourClientId}\", \"client_secret\": \"{yourClientSecret}\", \"token\": \"{yourRefreshToken}\" }"

  headers = { 'content-type': "application/json" }

  conn.request("POST", "/{yourDomain}/oauth/revoke", payload, headers)

  res = conn.getresponse()
  data = res.read()

  print(data.decode("utf-8"))
  ```

  ```ruby Ruby lines
  require 'uri'
  require 'net/http'
  require 'openssl'

  url = URI("https://{yourDomain}/oauth/revoke")

  http = Net::HTTP.new(url.host, url.port)
  http.use_ssl = true
  http.verify_mode = OpenSSL::SSL::VERIFY_NONE

  request = Net::HTTP::Post.new(url)
  request["content-type"] = 'application/json'
  request.body = "{ \"client_id\": \"{yourClientId}\", \"client_secret\": \"{yourClientSecret}\", \"token\": \"{yourRefreshToken}\" }"

  response = http.request(request)
  puts response.read_body
  ```

  ```swift Swift lines expandable
  import Foundation

  let headers = ["content-type": "application/json"]
  let parameters = [
    "client_id": "{yourClientId}",
    "client_secret": "{yourClientSecret}",
    "token": "{yourRefreshToken}"
  ] as [String : Any]

  let postData = JSONSerialization.data(withJSONObject: parameters, options: [])

  let request = NSMutableURLRequest(url: NSURL(string: "https://{yourDomain}/oauth/revoke")! as URL,
                                          cachePolicy: .useProtocolCachePolicy,
                                      timeoutInterval: 10.0)
  request.httpMethod = "POST"
  request.allHTTPHeaderFields = headers
  request.httpBody = postData as Data

  let session = URLSession.shared
  let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
    if (error != nil) {
      print(error)
    } else {
      let httpResponse = response as? HTTPURLResponse
      print(httpResponse)
    }
  })

  dataTask.resume()
  ```
</CodeGroup>

Where:

<table class="table">
  <thead>
    <tr>
      <th>Attribute</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td><code>client\_id</code> <br /><span class="label label-danger">Required</span></td>
      <td>Your application's Client ID. The application should match the one the Refresh Token was issued for.</td>
    </tr>

    <tr>
      <td><code>client\_secret</code></td>
      <td>Your application's Client Secret. Required for <a href="/docs/get-started/applications/confidential-and-public-applications#confidential-applications">confidential applications</a>.</td>
    </tr>

    <tr>
      <td><code>token</code> <br /><span class="label label-danger">Required</span></td>
      <td>The Refresh Token you want to revoke.</td>
    </tr>
  </tbody>
</table>

The application should match the one for which the refresh token was issued.

### Revoke tokens without the client secret

For applications that cannot keep the <Tooltip tip="Client Secret: Secret used by a client (application) to authenticate with the Authorization Server; it should be known to only the client and the Authorization Server and must be sufficiently random to not be guessable." cta="View Glossary" href="/docs/glossary?term=client+secret">client secret</Tooltip> safe (such as native apps), the `/oauth/revoke` endpoint supports access without the client secret. However, the application itself must have the property `tokenEndpointAuthMethod` set to `none`. You can change the `tokenEndpointAuthMethod` value, either from the [Dashboard > Applications > Applications](https://manage.auth0.com/#/applications), or using the Management API.

If the request is valid, the refresh token is revoked, and the response is `HTTP 200`, with an empty response body. Otherwise, the response body contains the error code and description.

```json lines
{
      "error": "invalid_request|invalid_client",
      "error_description": "Description of the error"
    }
```

The possible responses are:

<table class="table">
  <thead>
    <tr>
      <th>HTTP Status</th>
      <th>Description</th>
    </tr>
  </thead>

  <tbody>
    <tr>
      <td>200</td>
      <td>The Refresh Token is revoked, does not exist, or was not issued to the application making the revocation request. The response body is empty.</td>
    </tr>

    <tr>
      <td>400</td>
      <td>The required parameters were not sent in the request (<code>"error": "invalid\_request"</code>).</td>
    </tr>

    <tr>
      <td>401</td>
      <td>The request is not authorized (<code>"error": "invalid\_client"</code>). Check that the application credentials (<code>client\_id</code> and <code>client\_secret</code>) are present in the request and hold valid values.</td>
    </tr>
  </tbody>
</table>

## Use the Management API

To revoke a refresh token using the Auth0 Management API, you need the `id` of the refresh token you wish to revoke. To obtain a list of existing refresh tokens, call the `/api/v2/device-credentials` [endpoint](https://auth0.com/docs/api/management/v2#!/Device_Credentials/get_device_credentials), specifying `type=refresh_token` and `user_id` with an access token containing `read:device_credentials` scope. To narrow the results, you can also specify the `client_id` associated with the token (if known).

```json lines
GET https://{yourDomain}/api/v2/device-credentials?
    type=refresh_token
    &client_id=
    &user_id=
  
  {
    "Authorization":   "Bearer {your_access_token}"
  }
```

Response body:

```json lines
[
    {
  "id": "dcr_dFJiaAxbEroQ5xxx",
  "device_name": "my-device" // the value of 'device' provided in the /authorize call when creating the token
    }
  ]
```

To revoke a refresh token, call the `/api/v2/device-credentials` endpoint with an access token containing `delete:device_credentials` scope and the value of ID obtained above:

```json lines
DELETE https://{yourDomain}/api/v2/device-credentials/{id}

{
  "Authorization":   "Bearer {your_access_token}"
}
```

The response will be `HTTP 204: The credential no longer exists.`

## Considerations and limitations

With the [Device Authorization Flow](/docs/get-started/authentication-and-authorization-flow/device-authorization-flow), the only way to force a device to reauthorize is to revoke the refresh token assigned to the device. See [Unlink Devices from Users](/docs/manage-users/user-accounts/unlink-devices-from-users) for details. The device will not be forced to reauthorize until the current access token expires and the application tries to use the revoked refresh token.

When using [Refresh Token Rotation](/docs/secure/tokens/refresh-tokens/refresh-token-rotation), if a previously invalidated token is used, the entire set of refresh tokens issued since that invalidated token was issued will immediately be revoked, requiring the end-user to re-authenticate.

* Use the Authentication API `/oauth/revoke` [endpoint](https://auth0.com/docs/api/authentication#revoke-refresh-token) to revoke a refresh token. This endpoint does not delete the underlying grant. You can change this behavior to also delete the underlying grant in the Dashboard: [Dashboard > Tenant Settings > Advanced](https://manage.auth0.com/#/tenant/advanced). Scroll to **Settings** and enable the **Refresh Token Revocation Deletes Grant** toggle.
* Use the Management API `/api/v2/device-credentials` [endpoint](https://auth0.com/docs/api/management/v2#!/Device_Credentials/get_device_credentials) to revoke refresh tokens configured for rotation.

## Learn more

* [Get Refresh Tokens](/docs/secure/tokens/refresh-tokens/get-refresh-tokens)
* [Use Refresh Tokens](/docs/secure/tokens/refresh-tokens/use-refresh-tokens)
* [Refresh Token Rotation](/docs/secure/tokens/refresh-tokens/refresh-token-rotation)
* [Configure Refresh Token Expiration](/docs/secure/tokens/refresh-tokens/configure-refresh-token-expiration)
