Create a Token Exchange Handler Apex Class
A token exchange handler consists of an Apex class that extends the Oauth2TokenExchangeHandler abstract class and a token
exchange handler definition. To get started, create an Apex class to reference in the
handler definition.
Required Editions
| Available in: Enterprise, Performance, Unlimited, and Developer Editions |
Note You can skip this step by autogenerating an Apex class template when you define a handler in Setup. Make sure you
customize the autogenerated handler.
- From Setup, in the Quick Find box, enter Apex, and then select Apex Classes.
- Click New.
- Create an Apex class that extends the
Auth.Oauth2TokenExchangeHandlerabstract class.Here’s what the abstract class looks like. It contains two methods. Use the first method to validate the incoming token from the identity provider. Use the second method to map the subject of the token to a Salesforce user.//Abstract Class that a developer must extend for a token exchange handler global abstract class Oauth2TokenExchangeHandler { //First method called in the handler //This method must be overriden by the extending class global virtual Auth.TokenValidationResult validateIncomingToken(String appDeveloperName, Auth.IntegratingAppType appType, String incomingToken, Auth.OAuth2TokenExchangeType tokenType) return null; } //Second method called in the handler //This method must be overriden by the extending class global virtual User getUserForTokenSubject(Id networkId, Auth.TokenValidationResult result, Boolean canCreateUser, String appDeveloperName, Auth.IntegratingAppType appType) { return null; } }Here’s an example implementation that extends theAuth.Oauth2TokenExchangeHandlerabstract class.
Important This code sample is for demonstration only. Use it as a starting point, but make sure you evaluate, customize, and test it carefully./*Token Exchange Handler Implementation Example*/ public class MyTokenExchangeClass extends Auth.Oauth2TokenExchangeHandler{ public override Auth.TokenValidationResult validateIncomingToken(String appDeveloperName, Auth.IntegratingAppType appType, String incomingToken, Auth.OAuth2TokenExchangeType tokenType) { //Depending on your incoming token, you validate it in different ways //If the incoming token is an opaque access token or refresh token, validate it with a callout to the identity provider //If it is a SAML assertion, validate it by checking the XML //If it is an ID Token or JWT, try using our JWT validation methods //The example below assumes that the incoming token is a JWT and that there is a public keys endpoint on the identity provider //Be very careful with any logic in this method and test carefully before using Boolean isValid = false; Auth.JWT jwt; //Custom data structure CustomStructuredUserData customData; //Standard user data structure Auth.UserData userData; if (tokenType == Auth.OAuth2TokenExchangeType.JWT || tokenType == Auth.OAuth2TokenExchangeType.ID_TOKEN) { try { jwt = Auth.JWTUtil.validateJWTWithKeysEndpoint(incomingToken, 'https://your-idp.com/keys', true); isValid = true; //These values would be sourced from the JWT or ID Token userData = new Auth.UserData('identifier', 'firstName', 'lastName', 'fullName', 'customer@email.com', 'link url', 'remote username', 'local', 'Provider (IDP Name)', '', new Map<String,String>()); //You can also pass data as generic object customData = new CustomStructuredUserData(); } catch (Exception e) { isValid = false; } } else if (tokenType == Auth.OAuth2TokenExchangeType.ACCESS_TOKEN || tokenType == Auth.OAuth2TokenExchangeType.REFRESH_TOKEN) { //Logic for validating a opaque access token or refresh token can go here //This validation typically involves a callout to the introspect or user info endpoints //If you call out to the user info endpoint, make sure to pass the data from the validation into the getUserForTokenSubject method using an Apex class or the user data class isValid = false; } else if (tokenType == Auth.OAuth2TokenExchangeType.SAML_2) { //Logic for validating a SAML assertion can go here //This validation involves XML parsing isValid = false; } else { //You can add new token types. If you don’t know how to validate the token, always check the type and return false isValid = false; } if(isValid){ return new Auth.TokenValidationResult(true, (object)customData, userData, incomingToken, tokenType, 'CustomErrorMessage'); } else { return new Auth.TokenValidationResult(isValid); } } public override User getUserForTokenSubject(Id networkId, Auth.TokenValidationResult result, Boolean canCreateUser, String appDeveloperName, Auth.IntegratingAppType appType) { //If you passed data from the validation method, grab it now. Remember to cast back for the custom data CustomStructuredUserData customData = (CustomStructuredUserData)result.data; Auth.UserData userData = result.userData; //If you don’t have any data from the token, you can perform a callout using the incoming token String userToken = result.token; //Now, search for a user User u; try { u = [SELECT Id, IsActive FROM User WHERE email =: userData.email]; } catch (Exception e) { //No user existed for this email address, or there were too many. Try looking harder } // If you didn’t find a user, check to see if you can create one if (canCreateUser && (u == null)) { u = new User(); u.firstName = userData.firstName; u.lastName = userData.lastName; //... Finish setting user attributes. For external users, make sure you set up the contact/account/person account //If you assign permission sets, do it in a future method to avoid mixed DML //Returning the user from this method handles the insertion, so it’s not necessary to manually insert } return u; } //This class gives you a way to pass stuctured data between the validateIncomingToken and getUserForTokenSubject methods //This example is for demonstration only. Implement this class in a way that matches the data that you are passing private class CustomStructuredUserData { public String customAttribute1; public Integer customAttribute2; public Map<String,Object> customAttribute3; } } - Save the class.
- When you’re ready, customize the class’s validation and subject mapping methods. Depending on your development process, you can complete this step after you finish creating the handler.
To finish creating the handler, define a token exchange handler. You can define the handler in Setup or use Metadata API.
For more information about customizing your Apex handler, see these resources.
- Apex Developer Guide: OAuth 2.0 Token Exchange Handler Examples
- Apex Reference Guide: Oauth2TokenExchangeHandler Class
- Apex Reference Guide: TokenValidationResult Class
- Apex Reference Guide: JWTUtil Class
- Apex Reference Guide: OAuth2TokenExchangeType Enum
- Apex Reference Guide: IntegratingAppType Enum
Did this article solve your issue?
Let us know so we can improve!

