Revoke Salesforce OAuth Tokens with a POST request in Python

The Salesforce user with API access can directly access Salesforce data by using an external client via a connected app. there are the two scenarios below that I need to revoke the user OAuth tokens in Salesforce:

  1. When the user becomes inactive in the org

  2. When I don’t trust the external client

Here is the Salesforce document how we can use Salesforce rest API to revoke the tokens, I am going to revoke tokens with a POST Request by using Python.

Solution:

  • To check all the tokens you need to revoke, you can run the SQOL below to query Salesforce standard system object OAuthToken to get all the OAuth Token Usage by inactive users

    SELECT Id, UserId, AppName, DeleteToken FROM OAuthToken WHERE User.IsActive = false

  • To call Salesforce rest API to revoke the tokens, you need an access token or your session id, there are 2 options to get an access token or your session Id only if you’re handling your own credentials

    • Set up Salesforce Rest API authorization

      curl https://MyDomainName.my.salesforce.com/services/oauth2/token -d 'grant_type=password' -d 'client_id=consumer-key' -d 'client_secret=consumer-secret' -d 'username=my-login@domain.com' -d 'password=my-password'

    • Run the Apex script once you log in using your own credentials

      String sessionId = UserInfo.getOrganizationId()+''+UserInfo.getSessionId().SubString(15);

  • Python Code Example to query all the OAuth Token Usage by inactive users

import requests
import urllib.parse

accessToken: str = 'your access token or your session id'
targetOrgURL: str = 'your target org url'
""" 
     issue a COUNT() query in a SELECT clause for OauthToken. This query gives you the total number of records.
     If there are more than 2500 records, divide your query by filtering on fields, like UserId,
     to return subsets of less than 2500 records.
"""
query: str = 'SELECT Id, UserId, AppName, DeleteToken FROM OauthToken WHERE User.IsActive = false'


def queryAll(instanceUrl: str = targetOrgURL, accessToken: str = accessToken, apiVersion: str = 'v55.0', 
            query: str = query) -> str:
    
    headers = {
        'Authorization': 'Bearer %s' % accessToken
    }
    
    r = requests.get(instanceUrl+'/services/data/' +
                     apiVersion+'/queryAll', params={'q': query}, headers=headers)
    
    if r.status_code < 300:
        return r.json()
    else:
        raise Exception('API error when calling %s : %s' % (r.url, r.content))

records = queryAll()
for obj in records.get('records', []):
    deleteToken: str = obj.get('DeleteToken')
    print(deleteToken)
  • Once you have all the tokens to revoke, we need to construct a Salesforce post request to revoke each of them

    • API endpoint: https://MyDomainName.my.salesforce.com/services/oauth2

    • Content Type: application/x-www-form-urlencoded

    • Request Body: revoke?token=(the Delete Token), make sure the token is URL encoded.

def revokeToken(deleteToken: str, instanceUrl: str = targetOrgURL, accessToken: str = accessToken, 
                apiVersion: str = 'v55.0') -> str:
    
    headers = {
        'Content-type': 'application/x-www-form-urlencoded',
        'Authorization': 'Bearer %s' % accessToken
    }
    
    r = requests.post(instanceUrl+'/services/oauth2/revoke',
                      data='token='+urllib.parse.quote(deleteToken), headers=headers)
    
    if r.status_code >= 300:
        raise Exception('API error when calling %s : %s' % (r.url, r.content))


records = queryAll()
for obj in records.get('records', []):
    deleteToken: str = obj.get('DeleteToken')
    print(deleteToken)
    revokeToken(deleteToken)

If you have any questions or suggestions, please feel free to contact me.

Previous
Previous

Replace/Remove the first substring of a string that matches the search term or expression using Salesforce Formula.

Next
Next

How Existing User Linking Works with Open ID Connect and Third-Party Account link in Salesforce Single Sign-on