├── .forceignore ├── LICENSE ├── PlayByPlay-soapui-project.xml ├── README.md ├── config └── project-scratch-def.json ├── enterprise_wsdl.xml ├── force-app └── main │ └── default │ ├── applications │ └── Play_by_Play.app-meta.xml │ ├── aura │ └── CalloutWidget │ │ ├── CalloutWidget.cmp │ │ ├── CalloutWidget.cmp-meta.xml │ │ ├── CalloutWidgetController.js │ │ └── CalloutWidgetHelper.js │ ├── authproviders │ ├── Custom.authprovider-meta.xml │ └── DemoAuth.authprovider-meta.xml │ ├── classes │ ├── CustomAuthProvider.cls │ ├── CustomAuthProvider.cls-meta.xml │ ├── NamedCredentialInvoker.cls │ └── NamedCredentialInvoker.cls-meta.xml │ ├── customMetadata │ └── Auth.Custom.md-meta.xml │ ├── layouts │ └── Auth__mdt-Auth Layout.layout-meta.xml │ ├── namedCredentials │ ├── Basic.namedCredential-meta.xml │ ├── Custom.namedCredential-meta.xml │ └── OAuth.namedCredential-meta.xml │ ├── objects │ └── Auth__mdt │ │ ├── Auth__mdt.object-meta.xml │ │ └── fields │ │ ├── Client_Id__c.field-meta.xml │ │ ├── Client_Secret__c.field-meta.xml │ │ └── Token_Endpoint__c.field-meta.xml │ ├── profiles │ └── Demo.profile-meta.xml │ └── tabs │ └── Callout.tab-meta.xml └── sfdx-project.json /.forceignore: -------------------------------------------------------------------------------- 1 | # List files or directories below to ignore them when running force:source:push, force:source:pull, and force:source:status 2 | # More information: https://developer.salesforce.com/docs/atlas.en-us.sfdx_dev.meta/sfdx_dev/sfdx_dev_exclude_source.htm 3 | # 4 | 5 | package.xml -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 grekker 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Play by Play: Authenticating External App and Service Integrations with Salesforce # 2 | By Don Robins and Chuck Liddell 3 | 4 | https://www.pluralsight.com/courses/play-by-play-authenticating-external-app-service-integrations-salesforce 5 | 6 | ## What's In This Repo 7 | 8 | Companion code that was demonstrated during the video course. Code was created for demonstration purposes only and is provided here as-is with no warranty. 9 | -------------------------------------------------------------------------------- /config/project-scratch-def.json: -------------------------------------------------------------------------------- 1 | { 2 | "orgName": "Alpha", 3 | "edition": "Developer", 4 | "orgPreferences" : { 5 | "enabled": ["S1DesktopEnabled"] 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /force-app/main/default/applications/Play_by_Play.app-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | #0070D2 5 | false 6 | 7 | Large 8 | 9 | Standard 10 | standard-Account 11 | Callout 12 | Lightning 13 | 14 | -------------------------------------------------------------------------------- /force-app/main/default/aura/CalloutWidget/CalloutWidget.cmp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /force-app/main/default/aura/CalloutWidget/CalloutWidget.cmp-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 42.0 4 | CalloutWidget 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/aura/CalloutWidget/CalloutWidgetController.js: -------------------------------------------------------------------------------- 1 | ({ 2 | /** 3 | * Called when the component is first loaded. 4 | */ 5 | doInit : function(component, event, helper) { 6 | 7 | // call the server and fetch named credentials 8 | helper.getNamedCredentials(component); 9 | }, 10 | 11 | /** 12 | * Send a message to the selected NamedCredential. 13 | */ 14 | sendMessage : function(component, event, helper) { 15 | 16 | // call the server and pass it a message and a NamedCredential 17 | helper.sendMessage(component); 18 | } 19 | }); -------------------------------------------------------------------------------- /force-app/main/default/aura/CalloutWidget/CalloutWidgetHelper.js: -------------------------------------------------------------------------------- 1 | ({ 2 | /** 3 | * Fetch a list of NamedCredential records from the server. 4 | */ 5 | getNamedCredentials : function(component) { 6 | 7 | var action = component.get('c.serverGetNamedCredentials'); 8 | action.setCallback(this, function(response){ 9 | component.set('v.availableCredentials', response.getReturnValue()); 10 | }); 11 | $A.enqueueAction(action); 12 | }, 13 | 14 | /** 15 | * Send a message to the endpoint for the selected NamedCredential. 16 | */ 17 | sendMessage : function(component) { 18 | 19 | var action = component.get('c.serverSendMessage'); 20 | action.setParams({ 21 | 'selectedCredential' : component.get('v.selectedCredential'), 22 | 'message' : component.get('v.textToSend') 23 | }); 24 | $A.enqueueAction(action); 25 | } 26 | }); -------------------------------------------------------------------------------- /force-app/main/default/authproviders/Custom.authprovider-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Auth__mdt.Custom 4 | test-qjrlpp2r3g73@example.com 5 | Custom 6 | false 7 | CustomAuthProvider 8 | Custom 9 | false 10 | false 11 | 12 | -------------------------------------------------------------------------------- /force-app/main/default/authproviders/DemoAuth.authprovider-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | https://putsreq.com/DgpQxTzXAcEV0zfiKN0u 4 | thisIsMyKey 5 | gkzH3bNPuyd2aLpjlMxUUA== 6 | DemoAuth 7 | false 8 | OpenIdConnect 9 | true 10 | false 11 | https://putsreq.com/90UcOqafPS9yeytdmHN7 12 | 13 | -------------------------------------------------------------------------------- /force-app/main/default/classes/CustomAuthProvider.cls: -------------------------------------------------------------------------------- 1 | public with sharing class CustomAuthProvider extends Auth.AuthProviderPluginClass { 2 | 3 | private static String redirectUrl = 'https://fun-connect-1108-dev-ed.cs65.my.salesforce.com/services/authcallback/Custom'; 4 | 5 | public String getCustomMetadataType() { 6 | return 'Auth__mdt'; 7 | } 8 | 9 | public PageReference initiate(Map config, String stateToPropagate) { 10 | System.debug(LoggingLevel.WARN, 'initiate-config: ' + config); 11 | System.debug(LoggingLevel.WARN, 'initiate-stateToPropagate: ' + stateToPropagate); 12 | String url = config.get('Token_Endpoint__c'); 13 | url += '?redirect_uri=' + redirectUrl + '&state=' + stateToPropagate; 14 | return new PageReference(url); 15 | } 16 | 17 | public Auth.AuthProviderTokenResponse handleCallback(Map config, Auth.AuthProviderCallbackState callbackState) { 18 | System.debug(LoggingLevel.WARN, 'handleCallback-config: ' + config); 19 | System.debug(LoggingLevel.WARN, 'handleCallback-callbackState: ' + callbackState); 20 | System.debug(LoggingLevel.WARN, 'handleCallback-token: ' + callbackState.queryParameters.get('token')); 21 | return new Auth.AuthProviderTokenResponse('PbP', callbackState.queryParameters.get('token'), null, callbackState.queryParameters.get('state')); 22 | } 23 | 24 | public Auth.UserData getUserInfo(Map config, Auth.AuthProviderTokenResponse response) { 25 | System.debug(LoggingLevel.WARN, 'getUserInfo-config: ' + config); 26 | System.debug(LoggingLevel.WARN, 'getUserInfo-response: ' + response); 27 | return new Auth.UserData('fakeId', 'first', 'last', 'full', 'email', 'link', null, null, null, null, null); 28 | } 29 | 30 | public override Auth.OAuthRefreshResult refresh(Map config, String refreshToken) { 31 | System.debug(LoggingLevel.WARN, 'refresh-config: ' + config); 32 | System.debug(LoggingLevel.WARN, 'refresh-refreshToken: ' + refreshToken); 33 | return null; 34 | } 35 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/CustomAuthProvider.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 42.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/classes/NamedCredentialInvoker.cls: -------------------------------------------------------------------------------- 1 | /** 2 | * This class supports a Lightning component that allows the user to call arbitrary NamedCredential endpoints. 3 | */ 4 | public with sharing class NamedCredentialInvoker { 5 | 6 | @AuraEnabled 7 | public static List serverGetNamedCredentials() { 8 | 9 | List credentials = new List(); 10 | 11 | for(NamedCredential credential : [SELECT DeveloperName, MasterLabel FROM NamedCredential]) { 12 | credentials.add( 13 | new Map{ 14 | 'value' => credential.DeveloperName, 15 | 'label' => credential.MasterLabel 16 | } 17 | ); 18 | } 19 | 20 | return credentials; 21 | } 22 | 23 | @AuraEnabled 24 | public static void serverSendMessage(String selectedCredential, String message) { 25 | 26 | HttpRequest req = new HttpRequest(); 27 | req.setMethod('POST'); 28 | req.setEndpoint('callout:' + selectedCredential); 29 | req.setBody(message); 30 | 31 | new Http().send(req); 32 | } 33 | 34 | } -------------------------------------------------------------------------------- /force-app/main/default/classes/NamedCredentialInvoker.cls-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 42.0 4 | Active 5 | 6 | -------------------------------------------------------------------------------- /force-app/main/default/customMetadata/Auth.Custom.md-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | false 5 | 6 | Client_Id__c 7 | fizz 8 | 9 | 10 | Client_Secret__c 11 | buzz 12 | 13 | 14 | Token_Endpoint__c 15 | https://putsreq.com/52sR6FcxdK2vaYtIgwek 16 | 17 | 18 | -------------------------------------------------------------------------------- /force-app/main/default/layouts/Auth__mdt-Auth Layout.layout-meta.xml: -------------------------------------------------------------------------------- 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 | Required 19 | Token_Endpoint__c 20 | 21 | 22 | Required 23 | Client_Id__c 24 | 25 | 26 | Required 27 | Client_Secret__c 28 | 29 | 30 | 31 | 32 | Edit 33 | IsProtected 34 | 35 | 36 | Required 37 | NamespacePrefix 38 | 39 | 40 | 41 | 42 | 43 | false 44 | false 45 | true 46 | 47 | 48 | 49 | Readonly 50 | CreatedById 51 | 52 | 53 | 54 | 55 | Readonly 56 | LastModifiedById 57 | 58 | 59 | 60 | 61 | 62 | false 63 | false 64 | false 65 | 66 | 67 | 68 | false 69 | false 70 | false 71 | false 72 | false 73 | 74 | -------------------------------------------------------------------------------- /force-app/main/default/namedCredentials/Basic.namedCredential-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | false 5 | https://putsreq.com/PDwpXlZATHb0Q0dSOqy0 6 | true 7 | 8 | Anonymous 9 | NoAuthentication 10 | 11 | -------------------------------------------------------------------------------- /force-app/main/default/namedCredentials/Custom.namedCredential-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | false 5 | Custom 6 | https://putsreq.com/dA1cUNDz4WgkIIztMYWf 7 | true 8 | 9 | NamedUser 10 | Oauth 11 | 12 | -------------------------------------------------------------------------------- /force-app/main/default/namedCredentials/OAuth.namedCredential-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | false 4 | false 5 | DemoAuth 6 | https://putsreq.com/ozwm4tE5aeLpkEVVrldn 7 | true 8 | 9 | PerUser 10 | Oauth 11 | 12 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Auth__mdt/Auth__mdt.object-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Auths 5 | Public 6 | 7 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Auth__mdt/fields/Client_Id__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Client_Id__c 4 | false 5 | DeveloperControlled 6 | 7 | 255 8 | true 9 | Text 10 | false 11 | 12 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Auth__mdt/fields/Client_Secret__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Client_Secret__c 4 | false 5 | DeveloperControlled 6 | 7 | 255 8 | true 9 | Text 10 | false 11 | 12 | -------------------------------------------------------------------------------- /force-app/main/default/objects/Auth__mdt/fields/Token_Endpoint__c.field-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | Token_Endpoint__c 4 | The URL where client credentials can be exchanged for an access token. 5 | false 6 | DeveloperControlled 7 | The URL where client credentials can be exchanged for an access token. 8 | 9 | 255 10 | true 11 | Text 12 | false 13 | 14 | -------------------------------------------------------------------------------- /force-app/main/default/profiles/Demo.profile-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Play_by_Play 5 | false 6 | true 7 | 8 | 9 | CustomAuthProvider 10 | false 11 | 12 | 13 | NamedCredentialInvoker 14 | false 15 | 16 | true 17 | 18 | Auth__mdt-Auth Layout 19 | 20 | 21 | standard-Account 22 | DefaultOn 23 | 24 | Salesforce 25 | 26 | true 27 | AddDirectMessageMembers 28 | 29 | 30 | true 31 | AllowUniversalSearch 32 | 33 | 34 | true 35 | AllowViewKnowledge 36 | 37 | 38 | true 39 | ApexRestServices 40 | 41 | 42 | true 43 | ApiEnabled 44 | 45 | 46 | true 47 | AssignTopics 48 | 49 | 50 | true 51 | ChatterEditOwnPost 52 | 53 | 54 | true 55 | ChatterFileLink 56 | 57 | 58 | true 59 | ChatterInternalUser 60 | 61 | 62 | true 63 | ChatterInviteExternalUsers 64 | 65 | 66 | true 67 | ChatterOwnGroups 68 | 69 | 70 | true 71 | ContentWorkspaces 72 | 73 | 74 | true 75 | ConvertLeads 76 | 77 | 78 | true 79 | CreateCustomizeFilters 80 | 81 | 82 | true 83 | CreateCustomizeReports 84 | 85 | 86 | true 87 | CreateTopics 88 | 89 | 90 | true 91 | DistributeFromPersWksp 92 | 93 | 94 | true 95 | EditEvent 96 | 97 | 98 | true 99 | EditOppLineItemUnitPrice 100 | 101 | 102 | true 103 | EditTask 104 | 105 | 106 | true 107 | EditTopics 108 | 109 | 110 | true 111 | EmailMass 112 | 113 | 114 | true 115 | EmailSingle 116 | 117 | 118 | true 119 | EnableCommunityAppLauncher 120 | 121 | 122 | true 123 | EnableNotifications 124 | 125 | 126 | true 127 | ExportReport 128 | 129 | 130 | true 131 | FieldServiceAccess 132 | 133 | 134 | true 135 | ImportPersonal 136 | 137 | 138 | true 139 | LightningConsoleAllowedForUser 140 | 141 | 142 | true 143 | LightningExperienceUser 144 | 145 | 146 | true 147 | ListEmailSend 148 | 149 | 150 | true 151 | MassInlineEdit 152 | 153 | 154 | true 155 | RemoveDirectMessageMembers 156 | 157 | 158 | true 159 | RunReports 160 | 161 | 162 | true 163 | SelectFilesFromSalesforce 164 | 165 | 166 | true 167 | SendExternalEmailAvailable 168 | 169 | 170 | true 171 | SendSitRequests 172 | 173 | 174 | true 175 | ShowCompanyNameAsUserBadge 176 | 177 | 178 | true 179 | SubmitMacrosAllowed 180 | 181 | 182 | true 183 | SubscribeToLightningReports 184 | 185 | 186 | true 187 | UseWebLink 188 | 189 | 190 | true 191 | ViewHelpLink 192 | 193 | 194 | true 195 | ViewRoles 196 | 197 | 198 | true 199 | ViewSetup 200 | 201 | 202 | -------------------------------------------------------------------------------- /force-app/main/default/tabs/Callout.tab-meta.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | CalloutWidget 4 | 5 | false 6 | Custom88: Sailboat 7 | 8 | -------------------------------------------------------------------------------- /sfdx-project.json: -------------------------------------------------------------------------------- 1 | { 2 | "packageDirectories": [ 3 | { 4 | "path": "force-app", 5 | "default": true 6 | } 7 | ], 8 | "namespace": "", 9 | "sfdcLoginUrl": "https://login.salesforce.com", 10 | "sourceApiVersion": "42.0" 11 | } 12 | --------------------------------------------------------------------------------