├── .gitattributes ├── .gitignore └── src ├── authproviders └── Twitter.authprovider ├── classes ├── TwitterClientCredentialsAuth.cls └── TwitterClientCredentialsAuth.cls-meta.xml ├── customMetadata └── TwitterAuth.Twitter.md ├── layouts └── TwitterAuth__mdt-Twitter Auth Layout.layout ├── namedCredentials ├── TwitterAPI.namedCredential └── TwitterTokenService.namedCredential ├── objects └── TwitterAuth__mdt.object └── package.xml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | 7 | # Standard to msysgit 8 | *.doc diff=astextplain 9 | *.DOC diff=astextplain 10 | *.docx diff=astextplain 11 | *.DOCX diff=astextplain 12 | *.dot diff=astextplain 13 | *.DOT diff=astextplain 14 | *.pdf diff=astextplain 15 | *.PDF diff=astextplain 16 | *.rtf diff=astextplain 17 | *.RTF diff=astextplain 18 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Windows image file caches 2 | Thumbs.db 3 | ehthumbs.db 4 | 5 | # Folder config file 6 | Desktop.ini 7 | 8 | # Recycle Bin used on file shares 9 | $RECYCLE.BIN/ 10 | 11 | # Windows Installer files 12 | *.cab 13 | *.msi 14 | *.msm 15 | *.msp 16 | 17 | # Windows shortcuts 18 | *.lnk 19 | 20 | # ========================= 21 | # Operating System Files 22 | # ========================= 23 | 24 | # OSX 25 | # ========================= 26 | 27 | .DS_Store 28 | .AppleDouble 29 | .LSOverride 30 | 31 | # Thumbnails 32 | ._* 33 | 34 | # Files that might appear in the root of a volume 35 | .DocumentRevisions-V100 36 | .fseventsd 37 | .Spotlight-V100 38 | .TemporaryItems 39 | .Trashes 40 | .VolumeIcon.icns 41 | 42 | # Directories potentially created on remote AFP share 43 | .AppleDB 44 | .AppleDesktop 45 | Network Trash Folder 46 | Temporary Items 47 | .apdisk 48 | -------------------------------------------------------------------------------- /src/authproviders/Twitter.authprovider: -------------------------------------------------------------------------------- 1 | 2 | 3 | TwitterAuth__mdt.Twitter 4 | user@example.com 5 | Twitter 6 | false 7 | TwitterClientCredentialsAuth 8 | Custom 9 | false 10 | false 11 | 12 | -------------------------------------------------------------------------------- /src/classes/TwitterClientCredentialsAuth.cls: -------------------------------------------------------------------------------- 1 | public class TwitterClientCredentialsAuth extends Auth.AuthProviderPluginClass { 2 | 3 | // Name for this auth provider. 4 | private String authProvider = 'Twitter'; 5 | 6 | // Api name for the custom metadata type created for this auth provider. 7 | private String customMetadataTypeApiName = 'TwitterAuth__mdt'; 8 | 9 | // Name for the generic username displayed in the named credential configuration. 10 | private String userName = 'Anonymous User'; 11 | 12 | // Type of token. 13 | private String tokenType = 'secret'; 14 | 15 | // Structure of a token response from Twitter 16 | public class TwitterResponse { 17 | public String token_type; 18 | public String access_token; 19 | } 20 | 21 | //name of custom metadata type to embed configuration fields in auth provider 22 | public String getCustomMetadataType() { 23 | return customMetadataTypeApiName; 24 | } 25 | 26 | //directly redirect the user back to the callback URL, as we already have the client key and secret 27 | //to be used for authenticating directly 28 | public PageReference initiate(Map authProviderConfiguration, String stateToPropagate) { 29 | return new PageReference(authProviderConfiguration.get('Callback__c') +'?state=' +stateToPropagate); 30 | } 31 | 32 | //handle instantaneous callback and then use named credetial (username and password / client key and secret) to retrieve access token 33 | public Auth.AuthProviderTokenResponse handleCallback(Map authProviderConfiguration, Auth.AuthProviderCallbackState state ) { 34 | return new Auth.AuthProviderTokenResponse(authProvider, retrieveToken(authProviderConfiguration.get('NamedCredential__c')), tokenType, state.queryParameters.get('state')); 35 | } 36 | 37 | //if access token expired, reuse named credetial 4to retrieve access token 38 | public override Auth.OAuthRefreshResult refresh(Map authProviderConfiguration, String refreshToken) { 39 | return new Auth.OAuthRefreshResult(retrieveToken(authProviderConfiguration.get('NamedCredential__c')), tokenType); 40 | } 41 | 42 | //return fake username, as client_credential auth is not tied to a specific user 43 | public Auth.UserData getUserInfo(Map authProviderConfiguration, Auth.AuthProviderTokenResponse response) { 44 | return new Auth.UserData(null, null, null, null, null, null, userName, null, authProvider, null, new Map()); 45 | } 46 | 47 | //make HTTP call to token endpoint using client credentials to retrieve access token 48 | private String retrieveToken(String namedCredential) { 49 | 50 | HttpRequest req = new HttpRequest(); 51 | 52 | req.setEndpoint('callout:'+namedCredential); 53 | req.setHeader('Content-Type','application/x-www-form-urlencoded;charset=UTF-8'); 54 | req.setMethod('POST'); 55 | req.setBody('grant_type=client_credentials'); 56 | 57 | HTTPResponse res = new Http().send(req); 58 | 59 | return deserialiseToken(res); 60 | } 61 | 62 | //deserialise response and return token 63 | private String deserialiseToken(HTTPResponse res) { 64 | 65 | String responseBody = res.getBody(); 66 | 67 | TwitterResponse parsedResponse = (TwitterResponse) System.JSON.deserialize(responseBody, TwitterResponse.class); 68 | 69 | return parsedResponse.access_token; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/classes/TwitterClientCredentialsAuth.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 40.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /src/customMetadata/TwitterAuth.Twitter.md: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | Callback__c 7 | https://test.salesforce.com/services/authcallback/OrgId/AuthProviderName 8 | 9 | 10 | NamedCredential__c 11 | TwitterTokenService 12 | 13 | 14 | -------------------------------------------------------------------------------- /src/layouts/TwitterAuth__mdt-Twitter Auth Layout.layout: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | false 6 | true 7 | 8 | 9 | 10 | Required 11 | MasterLabel 12 | 13 | 14 | Required 15 | DeveloperName 16 | 17 | 18 | Edit 19 | Callback__c 20 | 21 | 22 | Edit 23 | NamedCredential__c 24 | 25 | 26 | 27 | 28 | Edit 29 | IsProtected 30 | 31 | 32 | Required 33 | NamespacePrefix 34 | 35 | 36 | 37 | 38 | 39 | false 40 | false 41 | true 42 | 43 | 44 | 45 | Readonly 46 | CreatedById 47 | 48 | 49 | 50 | 51 | Readonly 52 | LastModifiedById 53 | 54 | 55 | 56 | 57 | 58 | false 59 | false 60 | false 61 | 62 | 63 | 64 | false 65 | false 66 | false 67 | false 68 | false 69 | 70 | -------------------------------------------------------------------------------- /src/namedCredentials/TwitterAPI.namedCredential: -------------------------------------------------------------------------------- 1 | 2 | 3 | Twitter 4 | https://api.twitter.com 5 | 6 | NamedUser 7 | Oauth 8 | 9 | -------------------------------------------------------------------------------- /src/namedCredentials/TwitterTokenService.namedCredential: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://api.twitter.com/oauth2/token 4 | 5 | NamedUser 6 | Password 7 | ClientKey 8 | 9 | -------------------------------------------------------------------------------- /src/objects/TwitterAuth__mdt.object: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Callback__c 5 | false 6 | DeveloperControlled 7 | 8 | 255 9 | false 10 | Text 11 | false 12 | 13 | 14 | NamedCredential__c 15 | false 16 | DeveloperControlled 17 | 18 | 255 19 | false 20 | Text 21 | false 22 | 23 | 24 | Twitter Authentication Configurations 25 | Public 26 | 27 | -------------------------------------------------------------------------------- /src/package.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | TwitterClientCredentialsAuth 5 | ApexClass 6 | 7 | 8 | Twitter 9 | AuthProvider 10 | 11 | 12 | TwitterAuth__mdt.Callback__c 13 | TwitterAuth__mdt.NamedCredential__c 14 | CustomField 15 | 16 | 17 | TwitterAuth.Twitter 18 | CustomMetadata 19 | 20 | 21 | TwitterAuth__mdt 22 | CustomObject 23 | 24 | 25 | TwitterAuth__mdt-Twitter Auth Layout 26 | Layout 27 | 28 | 29 | TwitterAPI 30 | TwitterTokenService 31 | NamedCredential 32 | 33 | 40.0 34 | 35 | --------------------------------------------------------------------------------