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:
When the user becomes inactive in the org
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.