LinkedIn API :: how to obtain the bearer access token
Asked Answered
P

3

6

It's not easy to use the official LinkedIn API and I cannot find a valid documentation.

Following the official documentation I created a new application in order to obtain the Client ID and Client Secret

When I now make a POST call through Postman to https://www.linkedin.com/oauth/v2/accessToken this is what I obtain:

{
    "error": "invalid_grant_type",
    "error_description": "The passed in grant_type is invalid"
}

enter image description here

Where am I wrong?

EDIT AFTER HELP FROM @Amit Singh

Thanks to @AmitSingh I was able to create 2 different applications, the test with the Client Credentials flow gave me as a result an error retrieving the token:

enter image description here

{
    "error": "access_denied",
    "error_description": "This application is not allowed to create application tokens"
}

When I try to use the LinkedIn 3-legged workflow I receive Unauthorized

enter image description here

EDIT 3: GETTING THERE THROUGH POSTMAN

I now see that I can ask Postman to do the job, however when I press on Get New Access Token it opens an error page. I believe the error might be in these 4 elements:

enter image description here

  • Token name: maybe I have to give a special token name?
  • Auth URL: I set https://www.getpostman.com/oauth2/callback as explained here but maybe I have to set something else?
  • Access Token URL: I left it blank, maybe I have to put something here?
  • State: I set a random string like Hello123Boy but maybe I have to put something else. Maybe is too long. Maybe is too short. Maybe it has to contain symbols, etc... ?

...Also, in the guide you linked it says that the applicatoin needs to have:

  • r_liteprofile
  • rw_company_admin
  • w_member_social

mine has nothing:

enter image description here

Being recently created is still under review. It says it can take up to 90 days. Is that true?

enter image description here

4th EDIT: I WANT TO BELIEVE!

Here we are, at least now I'm getting the error: Bummer, something went wrong. The redirect_uri does not match the registered value. This is amazing: finally an error that says where the problem is!

enter image description here

On the app the, on the Products tab, I choose Sign In with LinkedIn. As Authorized redirect URLs for your app I set https://www.getpostman.com/oauth2/callback

enter image description here

In Postman I setup Auth URL and Access Token URL as you said:

enter image description here

Paryavi answered 20/12, 2020 at 21:45 Comment(16)
if you want to browse documentation in general - here's the official source: linkedin.com/developersRover
@Rover thank you, the documentation is very unpractical, that's why I'm asking this question. I will now add the specific error.Paryavi
you don't seem to be passing redirect_uri and code to https://www.linkedin.com/oauth/v2/accessToken - i believe both are requiredRover
@timur, those are for step 3, I'm still stuck in step 2 with the bearer token. If you have a solution just post itParyavi
Has this been resolved?Merrileemerrili
@AmitSingh, I'm sure timur's code should work. Probably the problem is on my side that I don't know how to use Postman properly. That's why I need someone that teach me step by step how to use Postman to obtain the access and query LinkedIn API. I can import the JSON to Postman and I see the 4 queries that timur wrote for me but still I don't know ow to make the code run. Any help would be much appreciatedParyavi
One clear issue that I see here is that your grant_type is set to your email ID where as the documentation explicitly says that it should be set to client_credentials and nothing else.Merrileemerrili
Documentation : learn.microsoft.com/en-us/linkedin/shared/authentication/… Check the first row of the table.Merrileemerrili
Thank you @AmitSingh but it would be easier if you post a solution. Because now I have several questions 1) what client_credentials means? Should I put email:password or email&password or just email or just password? 2) What about client_id? An example would be easier and will be less back and forward for everyone. I just want to have some code that I can paste and worksParyavi
Auth URL should be set to https://www.linkedin.com/oauth/v2/authorization and access token URL should be set to https://www.linkedin.com/oauth/v2/accessToken. Token name and state can be anything no issues with that.Merrileemerrili
LinkedIn OAuth's are added based on what LinkedIn products are integrated with your app. Select the (Sign In with LinkedIn)[learn.microsoft.com/en-us/linkedin/consumer/integrations/… for access to r_liteprofile and r_emailaddress. Marketing APIs need approval from LinkedIn before they can be used by you. Request for the Sign In with LinkedIn for those scopes.Merrileemerrili
Note that Sign In With LinkedIn and Share on LinkedIn do not require approval and they have the basic scopes that you might want. For more info, check their docs.Merrileemerrili
Thank you @AmitSingh, Sign in with LinkedIn requires approval snipboard.io/SRoLEz.jpg . Anyway, I feel I'm getting there. I will let you knowParyavi
I updated my question again. Take your time @AmitSingh, I will relaunch the bounty if it expires. Thank you for your kind helpParyavi
I think it will be better if we move this chat into a chat room instead of addressing it here. That will save someone coming to this thread for some help.Merrileemerrili
Let us continue this discussion in chat.Merrileemerrili
M
14

LinkedIn Credential workflows

LinkedIn offers 2 different credential workflows.

  1. LinkedIn 3-legged workflow - When you want to use an API that will access LinkedIn member's data. Authorization Code grant type needed.
  2. LinkedIn Client Credentials flow - When you want to use an API that will access non-member resources. Client credentials grant needed.

What are grant types?

"Grant type" refers to how you have acquired an access token in an OAuth workflow.

Several grant types are supported. Some of them are:

  1. Client Credentials - Used when you want to access your own resources and not any other users

  2. Authorization Code - Used when an app wants to access a client's data

  3. Refresh token - Exchange an expired access token for a valid access token, used to avoid repeated user involvement

  4. Password - Used when there is high trust between the app and the user e.g. LinkedIn mobile app, you provide your username and password

Client Credentials flow

What you need to know

  • Grant type used here is Client credentials - client_credentials.
  • Remember to set your Content-Type to application/x-www-form-urlencoded for all POST requests in OAuth.

Steps

  1. Create an App and get your Client ID and Client Secret. Steps are shown in the respective docs linked above. Let's say they have values - <client_id> and <client_secret>.

  2. Send a POST required to https://www.linkedin.com/oauth/v2/accessToken with following information.

    Parameters

    grant_type : client_credentials
    client_id  : <client_id>
    client_secret : <client_secret>
    

    NOTE : client_credentials is the literal text to be entered for grant_type.

    Response will return a JSON Object containing your access token and its expiry duration in seconds.

    Response

    {
       "access_token" : <access_token>,
       "expires_in" : "1800"
    }
    
  3. Use the <access_token> obtained in Step 2 make API requests.

    Example

    Request URL: https://www.linkedin.com/v2/jobs
    Request type: GET
    
    Parameters
    Authorization: Bearer <access_token>
    

LinkedIn 3-legged workflow

What you need to know

  • Grant type will be Authorization code - code, since you want to access a user's data.

  • Your Content-Type should be application/x-www-form-urlencoded for all POST requests in OAuth.

  • Redirect URLs are URLs where you OAuth server will redirect the user after successful authorization.

    • These are verified against your provided redirect URLs to ensure that it's not fraudulent.
    • These should be absolute URLs.
    • URL arguments are ignored and cannot include a #.

Steps

  1. Create app and provide the Redirect URLs, if not already provided. Check docs for information regarding how to do this.

  2. Get your Client ID and Client Secret. Let's say the values are <client_id> and <client_secret>.

  3. Generate a random, hard to guess string. Let's say it's <random-string>.

  4. Choose one of the redirect URLs provided in Step 1, where you want user to be redirected after authorization. Let's say it is <redirect_uri>.

  5. Let's suppose you want to:

    • r_emailaddress - Get his email address
    • w_member_social - Post, comment and like posts on behalf of the user.

    These are referred as "permission scopes", as in what permissions is the user authenticating you for. When sending these scopes in your request, they should be URL-encoded and space-delimited. In this particular instance, our scope will be scope: r_emailaddress%20w_member_social. We have URL-encoded the scopes mentioned above.

    Adding more information regarding scopes from the Microsoft docs:

    The scopes available to your app depend on which Products or Partner Programs your app has access to. Your app's Auth tab will show current scopes available. You can apply for new Products under the Products tab. If approved, your app will have access to new scopes.

  6. Send a POST request to https://www.linkedin.com/oauth/v2/authorization with following information.

    Parameters

    response_type : code
    client_id  : <client_id>
    redirect_uri : <redirect_uri>
    state : <random_string>
    scope : r_emailaddress%20w_member_social
    
  7. After the request, the user will be presented with LinkedIn's Auth screen and asked to approve the request.

  8. After user approves the request and the <redirect_uri> has been verified, user will be redirected to provided <redirect_uri> along with the access code <access_code> and a value in state argument. Let's say in the state argument is <state_value>.

  9. Verify that the <state_value> is equal to the <random_string> before working with the <access_code> to get access token, for security purposes. Also, use the <access_code> within 30 minutes of being issued, for security reasons.

  10. Next, send a POST request to https://www.linkedin.com/oauth/v2/accessToken with following information to get the access token.

    Parameters

    grant_type : authorization_code
    client_id  : <client_id>
    client_secret : <client_secret>
    redirect_uri : <redirect_uri>
    code : <access_code>
    

    NOTE : authorization_code is the literal text to be passed in grant_type.

    You should get a similar response as in the Client Credentials workflow containing your access token and expiry duration.

    Response

    {
       "access_token" : <access_token>,
       "expires_in" : "1800"
    }
    
  11. Use the <access_token> obtained in Step 9 make API requests.

    Example

    Request URL: `https://www.linkedin.com/v2/me`
    Request type: GET
    
    Parameters:
    Authorization: Bearer <access_token>
    

How to do this in Postman?

  1. Create a new Collection.
  2. Right click, select edit collection and move to authorization tab.
  3. In "Type", select "OAuth2.0", click on "Get New Access Token".
  4. You will see a screen where all the familiar terms mentioned above are there. Fill those, check the "Authorize via Browser" checkbox for authorization.
  5. Now you have the access token and can proceed to make your API calls.

Postman has been designed to make such operations easier, but you have to know how to do it. For more details, you can read their official docs.

Merrileemerrili answered 1/1, 2021 at 20:4 Comment(8)
Thank you, is all much clear now. My goal is to retrieve informations from LinkedIn users profile. Cannot believe LinkedIn API is so hard to use. I created 2 different API but none of them works. Maybe the problem is the redirect URL I'm using https://www.jeeja.biz ? Or maybe the Products tab in the LinkedIn application?Paryavi
What I did see was that the grant type is incorrect. What step are you getting stuck in? Is the redirection happening? Ideally, I would expect it to redirect and then throw the error even if it's an incorrect redirection. @FrancescoMantovaniMerrileemerrili
Since your redirect URI is https://www.jeeja.biz, you would be getting a request like https://www.jeeja.biz?code=dkafjkakf&state=dkfkafa and your web server listening at the endpoint should process it from Step 10.Merrileemerrili
Can you confirm that what I need in order to dig the users' profiles is LinkedIn 3-legged workflow and not the LinkedIn Client Credentials flow. Because I want to scrap usersParyavi
If you want to get person's profile data, you need his approval and for that you need the 3-legged workflow. This is mentioned in the Profile API doc which provides you this information. Just click on the authentication call link and it redirects to the 3-legged workflow. learn.microsoft.com/en-us/linkedin/shared/integrations/people/…Merrileemerrili
@FrancescoMantovani Did not know that you had added further details in the question. Have updated the answer to show the steps for Postman too. This flow is common to both the Client Credentials flow and the 3-legged workflow.Merrileemerrili
I also found a gist showing this workflow now that might help you out. gist.github.com/1951FDG/bf589ac915dfa9a3c14cb284c73093cdMerrileemerrili
When I hit the "authorization" API and login with user credentials. I got the error "Not found: checkpoint/lg/login-submit", Do you have any idea why I am getting that?Dividend
P
2

Thanks to @timur and @AmitSingh I finally arrived to authenticate to LinkedIn API.

A brief step by step solution in pictures:

  • Authorized redirect URLs for your app = https://oauth.pstmn.io/v1/callback

  • OAuth 2.0 scopes = must have r_emailaddress and r_liteprofile

  • In the Products Tab set Sign In with LinkedIn

enter image description here

Now open Postman > Collections > New Collection > Authorization and set the parameters as in picture:

enter image description here

  • TYPE = OAUTH 2.0
  • Token Name = put whatever you want
  • Callback URL = https://oauth.pstmn.io/v1/callback (should be greyed out once you tick Authorize using browser)
  • Tick Authorize using browser
  • Auth URL = https://www.linkedin.com/oauth/v2/authorization
  • Access Token URL = https://www.linkedin.com/oauth/v2/accessToken
  • Client ID = The Client ID you find on your LinkedIn App
  • Client Secret = The Client Secret you find on your LinkedIn App
  • Scope = r_liteprofile r_emailaddress
  • State = put whatever you like

Now click on Get New Access Token, a page will open on your browser and you will be able to login using your LinkedIn account. Once done you are authenticated.

Now use the code provided by @timur and on Postman go to Import > Upload File and import that .JSON file. You will now have the 4 queries and you can drag and drop them in your collection.

Paryavi answered 3/1, 2021 at 21:52 Comment(0)
R
0

Assuming you've created your app, added correct redirect URL and enabled "Sign In with LinkedIn" product for your app, the issue you are having is probably that the first call returns a login page where your users are supposed to authenticate.

  1. submit the request to https://www.linkedin.com/oauth/v2/authorization (you seem to have done that)
  2. parse response of step 1 and extract all form values, add username and password to simulate user login
  3. make POST request and use values from previous step as x-www-form-urlencoded data
  4. manually follow the redirect header from step 3
  5. make note of second redirect header but do not follow it - instead extract the code
  6. POST code from previous step to https://www.linkedin.com/oauth/v2/accessToken and get access_token in response

From here, I was able to successfully transition to the auth code by following the steps. I am not sure if you use the online Postman, but here's my complete collection export file for reference:

{
    "info": {
        "_postman_id": "397761c9-4287-43f2-860a-3c34cb710d50",
        "name": "Linkedin oAuth",
        "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
    },
    "item": [
        {
            "name": "01 request Login form",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "exec": [
                            "const $ = cheerio.load(pm.response.text());\r",
                            "var inputs = $('form').serializeArray();\r",
                            "var payload = '';\r",
                            "inputs.forEach(i => {\r",
                            "    payload += encodeURIComponent(i.name)+ '=' + encodeURIComponent(i.value) + '&';\r",
                            "})\r",
                            "payload += 'session_key='+ encodeURIComponent(pm.collectionVariables.get('username')) + '&'\r",
                            "payload += 'session_password='+ encodeURIComponent(pm.collectionVariables.get('password'))\r",
                            "\r",
                            "pm.collectionVariables.set(\"form_data\", payload);"
                        ],
                        "type": "text/javascript"
                    }
                }
            ],
            "request": {
                "method": "GET",
                "header": [],
                "url": {
                    "raw": "https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_id={{client_id}}&redirect_uri={{redirect_uri}}&scope=r_liteprofile&state={{$guid}}",
                    "protocol": "https",
                    "host": [
                        "www",
                        "linkedin",
                        "com"
                    ],
                    "path": [
                        "oauth",
                        "v2",
                        "authorization"
                    ],
                    "query": [
                        {
                            "key": "response_type",
                            "value": "code"
                        },
                        {
                            "key": "client_id",
                            "value": "{{client_id}}"
                        },
                        {
                            "key": "redirect_uri",
                            "value": "{{redirect_uri}}"
                        },
                        {
                            "key": "scope",
                            "value": "r_liteprofile"
                        },
                        {
                            "key": "state",
                            "value": "{{$guid}}"
                        }
                    ]
                }
            },
            "response": []
        },
        {
            "name": "02 Submit login form",
            "event": [
                {
                    "listen": "prerequest",
                    "script": {
                        "exec": [
                            ""
                        ],
                        "type": "text/javascript"
                    }
                },
                {
                    "listen": "test",
                    "script": {
                        "exec": [
                            "var url = 'https://www.linkedin.com'+ pm.response.headers.get(\"Location\");\r",
                            "pm.collectionVariables.set('first_redirect', url);\r",
                            "//console.log(pm.collectionVariables.get('first_redirect'));"
                        ],
                        "type": "text/javascript"
                    }
                }
            ],
            "protocolProfileBehavior": {
                "followRedirects": false
            },
            "request": {
                "method": "POST",
                "header": [
                    {
                        "key": "Content-Type",
                        "value": "application/x-www-form-urlencoded",
                        "type": "text"
                    }
                ],
                "body": {
                    "mode": "raw",
                    "raw": "{{form_data}}",
                    "options": {
                        "raw": {
                            "language": "text"
                        }
                    }
                },
                "url": {
                    "raw": "https://www.linkedin.com/checkpoint/lg/login-submit",
                    "protocol": "https",
                    "host": [
                        "www",
                        "linkedin",
                        "com"
                    ],
                    "path": [
                        "checkpoint",
                        "lg",
                        "login-submit"
                    ]
                }
            },
            "response": []
        },
        {
            "name": "03 handle login-success redirect",
            "event": [
                {
                    "listen": "test",
                    "script": {
                        "exec": [
                            "var sdk = require('postman-collection');\r",
                            "var redirect = new sdk.Url(pm.response.headers.get(\"Location\"));\r",
                            "pm.collectionVariables.set('code', redirect.query.filter(q => q.key === 'code').map(k => k.value)[0]);\r",
                            "//console.log(pm.collectionVariables.get('code'));"
                        ],
                        "type": "text/javascript"
                    }
                },
                {
                    "listen": "prerequest",
                    "script": {
                        "exec": [
                            "console.log(pm.variables.get('first_redirect'));\r",
                            "pm.request.url.update(pm.variables.get('first_redirect'));"
                        ],
                        "type": "text/javascript"
                    }
                }
            ],
            "protocolProfileBehavior": {
                "followRedirects": false
            },
            "request": {
                "method": "GET",
                "header": [],
                "url": {
                    "raw": "{{first_redirect}}",
                    "host": [
                        "{{first_redirect}}"
                    ]
                }
            },
            "response": []
        },
        {
            "name": "04 Get Auth Code",
            "request": {
                "method": "POST",
                "header": [],
                "url": {
                    "raw": "https://www.linkedin.com/oauth/v2/accessToken?grant_type=authorization_code&code={{code}}&redirect_uri={{redirect_uri}}&client_id={{client_id}}&client_secret={{client_secret}}",
                    "protocol": "https",
                    "host": [
                        "www",
                        "linkedin",
                        "com"
                    ],
                    "path": [
                        "oauth",
                        "v2",
                        "accessToken"
                    ],
                    "query": [
                        {
                            "key": "grant_type",
                            "value": "authorization_code"
                        },
                        {
                            "key": "code",
                            "value": "{{code}}"
                        },
                        {
                            "key": "redirect_uri",
                            "value": "{{redirect_uri}}"
                        },
                        {
                            "key": "client_id",
                            "value": "{{client_id}}"
                        },
                        {
                            "key": "client_secret",
                            "value": "{{client_secret}}"
                        }
                    ]
                }
            },
            "response": []
        }
    ],
    "event": [
        {
            "listen": "prerequest",
            "script": {
                "type": "text/javascript",
                "exec": [
                    ""
                ]
            }
        },
        {
            "listen": "test",
            "script": {
                "type": "text/javascript",
                "exec": [
                    ""
                ]
            }
        }
    ],
    "variable": [
        {
            "key": "client_id",
            "value": "your app id"
        },
        {
            "key": "client_secret",
            "value": "your app secret"
        },
        {
            "key": "redirect_uri",
            "value": "your urlencoded redirect uri such as https%3A%2F%2Flocalhost%3A8080"
        },
        {
            "key": "username",
            "value": "user login"
        },
        {
            "key": "password",
            "value": "user password"
        }
    ]
}
Rover answered 28/12, 2020 at 9:46 Comment(19)
I don't have any app, I have to do this through Postman. The problem I believe is in step 1: I haven't queried linkedin.com/oauth/v2/authorization . Can you just give me a step by step in Postman, please? From point 1 to point 6. Thank youParyavi
well, these are the steps - can you import the json into your postman?Rover
It says I cannot import JSON into PostmanParyavi
what version postman are you using? mine happily picked it up with File->Import dialogRover
This is my problem on the latest Postaman: snipboard.io/mw1eqv.jpgParyavi
don't you have this item in your "File" menu? snipboard.io/JP4ZeO.jpgRover
yeah but the result is the same, it opens up the same upload page, in which I upload a test.json file and it returns me the same errorParyavi
Hi, I arrived to import, I had to do Import > Raw text. Now where to add the value to the variables?Paryavi
you probably want to create an environment and set all variables under thereRover
OK, i set client_id and client_secret but what about redirect_uri and code? What about first_redirect and $guid?Paryavi
You need to set client_id, client_secret, redirect_uri, username and password. Other variables should get created by steps when you run themRover
Thank you for your patience @timur. What should I put as redirect_uri? I don't find the variables username and password by the wayParyavi
You define redirect_uri in developer console, https:// localhost is totally acceptable for development purposesRover
Very blurry, let me know if you need an higher quality youtu.be/sIVyl2FnGEEParyavi
You need to supply user login and password to authenticate to LinkedIn on second step. As I said in the answer, it sends you a login form that users would normally use to authenticate. Since that's you doing the test - it might as well be your linkedin credsRover
Also, you might want to get a general overview of oAuth protocol. I believe Auth0 have good tutorials on the topicRover
But there are no variables for user/passwordParyavi
There are, check the end of JSON file and test code for step 02Rover
Thank you. I edited that part but I receive the same error. What if I pay you 50NZD and you teach me through a Skype session how to do it? It will no take more than 10 minutes I believe. Let me knowParyavi

© 2022 - 2024 — McMap. All rights reserved.