SFDC Stop - Always the latest about Salesforce


Full Tutorial Series with videos, free apps, live sessions, salesforce consulting and much more.


Telegram logo   Join our Telegram Channel

Thursday 29 November 2018

Create custom authentication provider and forget the hassle of managing tokens - IndiaDreamin 2018

Hello Trailblazers,

Thank you for coming and attending my session at India Dreamin 2018. In this post, I'll give the gist of my session which I presented there. The topic of my session was:- Create custom Authentication Provider plugin and forget the hassle of managing tokens. So, before moving forward, let's have a look at the presentation first.



I suggest you to have a look till slide number 10 so that you have a brief idea about what we're going to talk about. The whole code I used in my session is available on my github repository here.

We're going to talk about integrations and custom authentication providers in this post. So, let's start from the basics:-

API

An API stands for Application Programming Interface. When we're going to connect two systems with each other over the network, then there must be a link or endpoint or port which is exposed by one system and consumed by other system. In simple terms, that endpoint or URL is called an API. As, it is the interface between two systems using which one system can send some data to the other system as well as receive some data in response from that system. This sending and receiving of data is done by sending an HTTP Request from one system to the other system. Some APIs are public i.e. they can be accessed by anyone without authentication whereas some APIs are private i.e. each user have to identify their identity while requesting data from the server.

Authentication Providers

Authentication Providers have the responsibility to prove the identity for the user using which we're hitting the API on the 3rd party server. Whenever, we're trying to hit a 3rd party API which is authenticated by any mechanisms like:- simple auth. or OAuth. 2.0, we need to send some extra data like:- access token to the server to prove our identity as a user of that server.

For ex:- If I have am a user of twitter and I want to make a post using the api, then I need to send some data like:- access token or username and password to the twitter api to make sure that twitter knows that this api call is requesting to make a post on behalf of @rahulcoder.

In salesforce, We can go to setup and search for auth and you'll see that we have the option of Auth. Providers there. Click on that and you'll see a screen where you can create a new authentication provider.


To create a new authentication provider, click on new and select a provider type. You'll see that there are a number of authentication providers already provided by salesforce to connect with most common APIs like:- facebook, google, github, twitter, salesforce etc. and when you select a provider type, you'll see a number of fields available below it to store various data which is linked to that api. The below fields may change according to the provider type selected. Some of the common fields are:- consumer key, consumer secret, token endpoint URL etc.

We mainly use authentication providers to connect with external API using OAuth flow.

OAuth 2.0

In this post, we'll be making a custom authentication provider and we'll be implementing OAuth 2.0 flow with the 3rd party API. OAuth 2.0 is called a 3 legged authentication flow and we need to follow the below steps to implement this:-
  1. Create a connected app on the third party server and note down the client id and client secret generated there.
  2. Use the client id and client secret along with the other required parameters to hit the Authorization URL. While hitting the authorization URL, you also need to send the callback URL to the third party API.
  3. As you hit the Authorization URL, you'll be taken to the login screen of third party server and once you login, the connected app will ask for the required permissions like:- Post on your behalf or anything else depending on the functionality. As you agree, The 3rd party server will redirect the request to the callback URL along with a unique authorization code appended as a query parameter to the callback URL.
  4. The callback URL is mainly the URL of your own server, in our case it'll be a URL of our salesforce org which the 3rd party server will direct our request to after login, appending the query parameter code to the end of the URL.
  5. Our authentication provider will get that code from the callback URL of our org and send that code along with other required information to the access token URL in order to get the Access Token. That access token will be saved in our authentication provider.
  6. In all subsequent requests, we'll send the access token along with each request to the third party server in order to prove our identity and we'll be able to interact with the APIs and do the required task.

Creating our own Custom Authentication Provider

It's time to jump on to code and create our own custom authentication provider in the salesforce org. In our sample, we'll be dealing with GitHub API and will be creating a custom authentication provider to connect with your own github account and post on your behalf. We'll follow the same steps as mentioned above.

To do so, the first step is to create a connected app in our GitHub account. If you don't have a GitHub account, go to this link and create an account. Once you're logged in into your GitHub account, click on your profile picture on the top right hand site and click on settings.


On the settings page, click on developer settings it's the last option on the left hand side.


As you click on developer settings, you'll be taken to this URL:- https://github.com/settings/developers. Click on Register a new application under the OAuth Apps section as given below:-


You'll be taken to the below screen:-


Here you need to enter an application name, a homepage URL (you can enter your org's URL here), an application description and leave the Authorization callback URL empty for now. We'll fill this later. and Click on Register application

Once your application is registered, you'll see that you'll get a client id and client secret as shown below:-


Now we need to use the client id and client secret generated here in our custom authentication provider. Let's get back to our Salesforce Org and create a custom authentication provider now. Have a look at the below code:-

As you can see above, I have made a global class named GithubAuth which is extending the Auth.AuthProviderPluginClass in which I have implemented 4 methods:-

  • global String getCustomMetadataType()
  • global PageReference initiate(Map<String,String> authProviderConfiguration, String stateToPropagate)
  •  global Auth.AuthProviderTokenResponse handleCallback(Map<String, String> authProviderConfiguration, Auth.AuthProviderCallbackState state)
  • global Auth.UserData getUserInfo(Map<String, String> authProviderConfiguration, Auth.AuthProviderTokenResponse response)
Now we'll be learning about all methods one by one.

global String getCustomMetadataType()

Starting from the first method getCustomMetadataType(), In this method we need to return a string that mainly consists of the API name of custom metadata. As you have seen in standard authentication providers that when you select a provider type, there are some fields which are displayed in that section relating to the API like:- client id, client secret etc. So, same is the case of our custom authentication provider. In this case too, we need to store some information regarding the API like:- Authorization URL, Access Token URL, client id, client secret etc. and any other information which I need to store to make request to 3rd party API.

For our GitHub API, I have made a custom metadata named GithubCredential__mdt as shown below:-


As you can see above, in the custom fields, I have made a number of fields which will be used to store the information regarding the GitHub API. These fields are used to store the information which is as follows:-
  1. Access Token URL
  2. Authorization URL
  3. Client Id
  4. Client Secret
  5. Redirect URI
  6. Scope
  7. User Info URL
If you see the GithubAuth class, you'll notice that I have created a private String variable for each of the fields that are in my custom metadata. All this information is required to successfully connect with GitHub API using OAuth flow and return the access token which will be used in subsequent requests. All these fields will be automatically visible when I'll try to create a new authentication provider by the standard way and the name of the class will be the in the provider type picklist.

global PageReference initiate(Map<String, String> authProviderConfiguration, String stateToPropagate)

This method is mainly used to initiate the authorization code flow. It's the same flow in which we call the authorization code URL with the required data and callback URL and it appends the authorization code to the query parameter of our callback URL after login.

This method is mainly providing the custom metadata as a map of string and string the first parameter and the state as the second parameter. Using the map, we're getting the values from our custom metadata which also includes the Authorization Code URL and appending the other required information to this URL. Finally, we're returning a new PageReference Object by passing this newly formed URL.

The second parameter in this method is mainly the state which is nothing but a unique string which is passed to the access token URL for more security. This state is returned as a query parameter while the 3rd party server is redirecting the request to the callback URL so that our server can confirm at it's end that the response is coming from the correct server to which we sent the request. Using this state our salesforce server is confirmed that it's getting the response from the same server it's sending the request to.

global Auth.AuthProviderTokenResponse handleCallback(Map<String, String> authProviderConfiguration, Auth.AuthProviderCallbackState state)

Once we've hit the authorization code URL using our code, we'll be taken to the login page by salesforce followed by the permissions page according to the features of 3rd party app we're going to use and finally, we're redirected to our callback URL with the unique code embedded. That callback is handled by our handleCallback method in which we get the authProviderConfiguration which we know is the metadata as a map of string and string. and the second parameter is the state using which we can get the query parameters which if you remember consists of the state and unique authorization code.

In this method, after getting the values from our custom metadata map which is required to form the access token URL, we're getting the query parameters using state.queryParameters which will  give us a Map<String,String> in which we've a key of code and another key of state as these are the two query parameters we're getting as back at our callback URL.

For more clarification, if your callback URL is:- https://www.google.com then you'll get the callback response as:- https://www.google.com?code=123123123&state=909909090

So, we're getting this code and state using the map returned by state.queryParameters. Now, we're forming another HTTP request to the Access Token URL and we're sending the state as well as code in the request body as it's a POST request. The other parameters depend on the 3rd party service provider. So, as we're sending the same state again to the 3rd party URL, then the 3rd party server is also confirmed that it's the same client which is trying to connect again to this URL as it's using the same state value again. Congratulations..!! Two way authentication is properly implemented.

Moving ahead we've sent the request by making an Object of HTTP class and passing the HTTPRequest class instance to the send method which is returning back the instance of HTTPResponse. We're getting the response body as string using res.getBody() as res is the object of HTTPResponse class. I am further deserializing this JSON response from GitHub API using a wrapper class named GitHubResponse which is made at the end of the code.

The JSON.deserialize is a method which takes the JSON String as the first parameter and the Wrapper.class as the second parameter and return you the instance of wrapper class.

You can have a look at the JSON response shown in the second point of GitHub API documentation here. Which is shown as given below:-

{
          "access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a", 
          "scope":"repo,gist", 
          "token_type":"bearer"
}

Here you can see that we've 3 keys access_token, scope and token_type. That we're deserializing using GithubResponse class which consists of the same 3 keys as String variables. Finally you're returning an instance of Auth.AuthProviderTokenResponse method which takes  the 3rd party API name as the first parameter, access token as the second parameter, refresh token as the third parameter and state as the fourth parameter. In case of GitHub, we don't have a refresh token as the access token has a lifetime validity so we're passing null there. 

getUserInfo(Map<String, String> authProviderConfiguration, Auth.AuthProviderTokenResponse response)

The final method is getUserInfo method which will get the custom metadata as the first parameter and access token response as the second parameter. This method is not important and is only used to store some user information so that our Salesforce server is aware that this particular user's account is used to make a request to 3rd Party API.

In this method, the things to notice is that we're getting the actual access token using response.oauthToken and we're setting it in the header while sending the request to User Info API of GitHub as:- req.setHeader('Authorization', 'Bearer ' + token); we've got from the previous JSON that the token type is bearer so, we're using it here.

This is the only time we need to manually set the access token in the request header as this request is a part of custom auth. provider itself. In all the other requests, Salesforce will take care of adding the request access token to the header because you'll be using this auth. provider. You can thank me later for this..!! :P

The further code is familiar to you as we're just sending the request and I have made another wrapper according to the JSON response, I am receiving from the User Info API. I am returning an instance of return new Auth.UserData whose format is as follows:-

        UserData (
          identifier, => user id
          firstName, => first name of authenticated user
          lastName, => last name of authenticated user
          fullName, => full name of authenticated user
          email, => email of authenticated user
          link, => link of authenticated user profile (Ex:- https://www.facebook.com/sfdcstop)
          userName, => username of authenticated user
          locale, => standard locale of authenticated user
          provider, => name of your 3rd party provider
          siteLoginUrl, => 3rd party login page url
          attributeMap => any other data from 3rd party that you can store as map of <string, string>
        )

If you don't have any User Info API, you can also hard code these values like:- firstname and lastname to return the proper instance or the better option is to store them in custom metadata fields and use them from there.

Congratulations..!! Your custom authentication provider is complete. Now it's time to use that.

Follow the following steps to setup your own Custom Authentication Provider:-

In your org, go to setup and search for auth, click on Auth. Providers as we have done before, you'll be taken to the below screen:-




Click on new and you'll see that under the provider type, you've a new option of GitHub Auth as this is our class name of custom authentication provider code.


Select that and you'll see that it has all the fields that we added in our custom metadata as shown below:-


So, this means that you don't need to create a separate record of your custom metadata. It'll automatically be created from here. You can automatically create a registration handler by clicking on Automatically create a registration handler template near the registration handler field and you can also keep it blank as I have kept it as shown below:-


Once you save this authentication provider, you'll see the Salesforce Configuration section as shown in the above image. Here, you can see that the 4th point is Callback URL so this is the URL which you need to store in your connected app and also send in the request and the unique code will be appended as a query parameter to this URL.

So, you need to copy this URL and edit your auth provider again to update the Redirect URI field with this URL as I am using this field to store my callback URL. You also need to go back to your Github Connected application and update the authorization calback URL with this URL as shown below:-


Add this URL and click on update application.

One Last Step

In this last step, go to setup and search for Named Credentials you'll be taken to the below screen:-


What is Named Credential ?

A named credential is a better way to hit third party APIs as you need to store the URL only once, and then use the name of the created Named Credential whenever you're making a request to the third party API.

Click on New Named Credential button and in the form choose Identity Type as Named Principal and Authentication Protocol as OAuth 2.0. You'll have a lookup of Authentication Provider in which your custom authentication provider will be there that you just created as shown below:-


Add the label and name as you like, I have used it as GithubAPI and for the URL add the base URL of the 3rd party API. I have added:- https://api.github.com/ as all the GitHub APIs start from this URL. Make sure that Start Authentication Flow on Save checkbox is checked as given below:-


Notice that I have added gist in the scope field, as I need permission only to interact with Github Gists. Github Gists are simply files consisting of some code. When you need to share a code snippet or single file, you can create gists instead of repo.

Click on save and you'll be taken to the login screen of GitHub and once you login or if you're already logged in, you'll be taken to the permissions screen as given below:-


Notice in the above image that it is asking for read and write access for gists as I have added only gist in the scope. It mainly depends on the scopes you're using out of those provided by the third party API. Click on Authorize button and you'll be taken back to the named credential and you'll see the below screen:-


As you can see above, The Authentication Status is:- Authenticated as rahulmalhotra. It is also showing a warning:- The authentication provider didn't provide a refresh token. If the access token expires, your org won't be able to access this named credential. But in case of GitHub we don't need to worry as we know that the access token has a lifetime validity.

As we're going to deal with gist now, go to your github account and click on new gist.


In the next page, you just need to give a file name with extension, a description and a file body and click on Create Public Gist.


I have already created a gist to use for my India Dreamin session that you can view here:- https://gist.github.com/rahulmalhotra/66508cf3e51801c01994ce2cbf2e5dde

The random number after rahulmalhotra is my gist id. Now, I am going to add a comment in this gist by hitting an api from my salesforce org. Open the anonymous window in your org's developer console and paste the below code.

As you can see above, I have made a simple HTTP request to the GitHub API, there are two things to notice:-

  1. There is no authentication header as Salesforce will take care of adding the access token.
  2. The endpoint URL is set as:-

    req1.setEndpoint('callout:GithubAPI/gists/66508cf3e51801c01994ce2cbf2e5dde/comments');

In the GitHub API, when we need to make a comment on gist, the standard URL is:- https://api.github.com/gists/<gist-id>/comment I have used the gist id of the same IndiaDreamin gist. When we're calling an API using named credential, we just need to use callout:<NamedCredentialName>. The name of my named credential is GithbAPI so, I have used that. According to the github api, I need to send the comment I want to make as a JSON like:-

{
          "body": "<My comment>"
}

So, for this I have made a map of string, string and for the body key, I have added the value as:- Welcome to India Dreamin Session Blog and I have set the request body by using the setBody method and I have created a JSON from map using JSON.serialize method.

Replace the gist id with your own gist id and execute this code, in my case, there is another comment on my gist as shown below:-


As you can see above, the last gist comment is Welcome to India Dreamin Session Blog which was in the body of my code and this comment is made on my behalf as the application was authenticated using my credentials.

Note:- You need to add the base URL of 3rd party API in Remote Site Settings of your org. Go to setup and search for remote site settings and create a new record for your 3rd party site there.

Now, I can use the GitHub API as many times as I want using named credentials and I don't need to care about authentication and neither do you. I have also made a template that'll help you to create your own authentication provider in Salesforce that you can access here:- https://github.com/rahulmalhotra/Salesforce-Custom-Authentication-Provider-Template/blob/master/src/classes/CustomAuthProviderTemplate.cls

I hope you liked this blog, my session was also repeated online with Motihari Salesforce Developer Group that was broadcasted live on facebook along with the other sessions. You can access the video here:- https://www.facebook.com/MotihariSFDC/videos/1016194645248473

Sharing a picture of my session below:-


Hope to see you too in my next session.

Happy Trailblazing..!!

16 comments:

  1. Great Article Rahul. Much appreciated!!

    ReplyDelete
    Replies
    1. Thank you for your feedback. Make sure to share it and hope to see you in my next session too :-)

      Delete
    2. Hi Rahul,
      I tried implementing this. But when i save the Named credential (checking the Start Authentication Flow on Save), page is not redirecting to Github page. Named credential is just saving and Authentication Status is pending.
      Any thoughts please?

      Delete
    3. I think you've resolved it by yourself by now. Let me know if you have any queries.

      Delete
  2. Hi Rahul,

    Quick question.. my client is planning to use oauth 'Username-Password Flow' to make callouts from salesforce, which means we will be directly getting token in the first call rather than getting code and then token (as per your example).

    In this case, will be the above code work in building auth. provider?
    Thanks for letting me know.

    ReplyDelete
    Replies
    1. Hi,

      You don't need to implement this thing in username password flow. However, there is a password option available when you create named credential. Try that and let me know if that worked for you :-)

      Delete
  3. I am getting this error : We can’t log you in because of the following error. For more information, contact your Salesforce administrator.

    Remote_Error: The remote service returned an error

    ReplyDelete
    Replies
    1. Hi, there should be an error code along with the error, make sure you've followed all the steps and check if there's something in debug logs.

      Delete
    2. Hi,

      I am getting the same error. Even I already have url added to remote settings.
      Plus, I want to make callback url dynamic. How can I do that?

      Delete
    3. Hi Hamza, can you describe me the error in detail. And usually we can set only one callback URL in any connected app. So, and it's automatically generated by salesforce in authentication provider. Can you tell me the scenario when you need a dynamic callback url ?

      Delete
  4. Hi Rahul,

    Thanks for the awesome blog. I want to skip showing the permissions page like (Post on your behalf). Is it possible to do that?

    Thanks

    ReplyDelete
    Replies
    1. Hi Chirag,

      I think it depends on the 3rd party system you're connecting to and I think it should be in the standards to show that page so that the user is aware of the information he/she is sharing. There is no particular way in my knowledge to skip that page.

      Delete
  5. Hi Rahul,

    You hardcoded id in the URL. What if I want to make it dynamic i.e. adding a recordId in the URL to get information about it?

    ReplyDelete
    Replies
    1. Hi Prashant, if you're talking about the gist id, then you can simply call another github api to query a list of your gists and then call api for that particular gist that you want to add a comment on. So, the id was just for a demo in the last step as we don't have any hardcoded ids while setting up the authentication provider. I could have called any github api there. It depends on your use case.

      Delete
  6. Can you help with No_Oauth_State: State was not valid, I see the token returning successfully with state yet the above error

    ReplyDelete
    Replies
    1. Hi Nishant, Can you please make sure that you've set the callback URL properly in your connected app and try again.

      Delete