0

This is in continuation of a question posted by me on LinkedIn Job Posting API implementation in Java. I'm new to this OAuth based authentication of posting Jobs and am also learning as well during this process. Please bear with me if my questions are very basic/naive.

Am trying to use JOAuth library for OAuth authentication and post jobs to LinkedIn. I'm using OAuth2 calls. I've the following questions with JOAuth library:

  1. In the example shown at the JOAuth link, how do I get Request Token for LinkedIn? I don't find any statement for requesting Request Token. But I could see consumer.generateRequestAuthorizationUrl(ResponseType.CODE, redirectUri, null, (String[])null));
  2. If I want to use oob based callback redirection, then what do I need to pass/set in redirectUri?
  3. If everything is successful and if I've the Access Token, how do I finally submit/send my Job data XML at http://api.linkedin.com/v1/jobs?
Community
  • 1
  • 1
Gnanam
  • 10,613
  • 19
  • 54
  • 72
  • Sorry, the old `joauth_1_2_1.jar` had flaws when creating `Authorization` header for OAuth 1. The new `joauth_1_3_1.jar` contains the fix and follows the strict implementation of RFC 5849. – Buhake Sindi Mar 02 '12 at 14:21

2 Answers2

1

You're confused. LinkedIn uses OAuth 1 protocol and not OAuth 2 protocols. Here's how you would do Oauth 1 authorization to/with LinkedIn.

If you're creating a Web application, under WEB-INF folder, create a oauth-config.xml file and have a configuration similar to this:

<?xml version="1.0" encoding="UTF-8"?>
<oauth-config>
    <!-- LinkedIn OAuth Config -->
        <oauth name="linkedIn" version="1">
                <consumer key="API_KEY" secret="API_SECRET" />
                <provider requestTokenUrl="https://api.linkedin.com/uas/oauth/requestToken" authorizationUrl="https://api.linkedin.com/uas/oauth/authorize" accessTokenUrl="https://api.linkedin.com/uas/oauth/accessToken" />
        </oauth>

        <service path="/authorize_ready" class="com.neurologic.example.LinkedInOAuthService" oauth="linkedIn">
                <success path="/start.htm" />
        </service>
</oauth-config>

LinkedIn uses OAuth version 1 (hence the version).

Under your WEB-INF\web.xml, add the following:

<servlet>
    <description>An OAuth Servlet Controller</description>
    <display-name>OAuthServlet</display-name>
    <servlet-name>OAuthServlet</servlet-name>
    <servlet-class>com.neurologic.oauth.servlet.OAuthServlet</servlet-class>
    <init-param>
        <param-name>config</param-name>
        <param-value>/WEB-INF/oauth-config.xml</param-value>
    </init-param>
    <load-on-startup>3</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>OAuthServlet</servlet-name>
    <url-pattern>/oauth/*</url-pattern>
</servlet-mapping>

Now, we need to create a service that will receive the authorized token from Linked In.

package com.neurologic.example;

import javax.servlet.http.HttpServletRequest;

import net.oauth.signature.OAuthSignature;
import net.oauth.signature.impl.OAuthHmacSha1Signature;
import net.oauth.token.v1.AccessToken;
import net.oauth.token.v1.RequestToken;

import com.neurologic.oauth.service.impl.OAuth1Service;

/**
 * @author Buhake Sindi
 * @since 31 May 2011
 *
 */
public class LinkedInOAuthService extends OAuth1Service {

    public static final String LINKED_IN_REQUEST_TOKEN_SESSION = "LINKED_IN_REQUEST_TOKEN_SESSION";
    public static final String LINKED_IN_ACCESS_TOKEN_SESSION = "LINKED_IN_ACCESS_TOKEN_SESSION";

    /* (non-Javadoc)
     * @see com.neurologic.oauth.service.impl.OAuth1Service#getOAuthSignature()
     */
    @Override
    protected OAuthSignature getOAuthSignature() {
        // TODO Auto-generated method stub
        return new OAuthHmacSha1Signature();
    }

    /* (non-Javadoc)
     * @see com.neurologic.oauth.service.impl.OAuth1Service#getRealm()
     */
    @Override
    protected String getRealm() {
        // TODO Auto-generated method stub
        return null;
    }

    /* (non-Javadoc)
     * @see com.neurologic.oauth.service.impl.OAuth1Service#getRequestToken(javax.servlet.http.HttpServletRequest)
     */
    @Override
    protected RequestToken getRequestToken(HttpServletRequest request) {
        // TODO Auto-generated method stub
        return (RequestToken) request.getSession().getAttribute(LINKED_IN_REQUEST_TOKEN_SESSION);
    }

    /* (non-Javadoc)
     * @see com.neurologic.oauth.service.OAuthService#saveAccessToken(javax.servlet.http.HttpServletRequest, java.lang.Object)
     */
    @Override
    public void saveAccessToken(HttpServletRequest request, AccessToken accessToken) {
        // TODO Auto-generated method stub
        request.getSession().setAttribute(LINKED_IN_ACCESS_TOKEN_SESSION, accessToken);
    }
}

Now, use the following example:

package com.neurologic.example;

import net.oauth.consumer.OAuth1Consumer;
import net.oauth.exception.OAuthException;
import net.oauth.provider.OAuth1ServiceProvider;
import net.oauth.signature.impl.OAuthHmacSha1Signature;
import net.oauth.token.v1.AccessToken;
import net.oauth.token.v1.AuthorizedToken;
import net.oauth.token.v1.RequestToken;

/**
 * @author Buhake Sindi
 * @since 14 June 2011
 *
 */
public class LinkedInExample {

    private static final String LINKEDIN_API_URL = "https://api.linkedin.com";
    private static final String API_KEY = "";
    private static final String API_SECRET  = "";
    private static final String CALLBACK_URL = "http://localhost:8080/myapp/oauth/authorize_ready";
    private OAuth1Consumer consumer;


    /**
     * 
     */
    public LinkedInExample() {
        super();
        // TODO Auto-generated constructor stub
        consumer = new OAuth1Consumer(API_KEY, API_SECRET, new OAuth1ServiceProvider(LINKEDIN_API_URL + "/uas/oauth/requestToken", LINKEDIN_API_URL + "/uas/oauth/authorize", LINKEDIN_API_URL + "/uas/oauth/accessToken"));
    }

    public RequestToken requestUnauthorizedRequestToken() throws OAuthException {
        return consumer.requestUnauthorizedToken(null, CALLBACK_URL, null, new OAuthHmacSha1Signature());
    }

    public String getAuthorizationUrl(RequestToken token) throws OAuthException {
        return consumer.createOAuthUserAuthorizationUrl(token, null);
    }

    public AccessToken requestAccessToken(AuthorizedToken authorizedToken, RequestToken token) throws OAuthException {
        return consumer.requestAccessToken(null, authorizedToken, token.getTokenSecret(), new OAuthHmacSha1Signature());
    }


    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub
        try {
            LinkedInExample example = new LinkedInExample();
            RequestToken rt = example.requestUnauthorizedRequestToken();

            //Now that we have request token, let's authorize it....
            String url = example.getAuthorizationUrl(rt);

            //Copy the URL to your browser and make sure that OAuth 1 Servlet is running....
        } catch (OAuthException e) {
            // TODO Auto-generated catch block
            e.printStackTrace();
        }
    }

}

As you can see, the CALLBACK_URL is set to point to the OAuth Servlet configured to JOAuth, as you'll receive an Authorized Token.

You must make sure that you return the unauthorized request token back to the service on the RequestToken getRequestToken(HttpServletRequest request) method as you'll need it to retrieve the access token.

The service saveAccessToken() method is called when LinkedIn returns an access token. You can log to verify if the access token is returned.

Once you have an access token, you can use a LinkedIn API and Post Jobs using the Access Token. JOAuth is designed to retrieve access tokens only and not communicate with other APIs that exists.

Buhake Sindi
  • 87,898
  • 29
  • 167
  • 228
  • @TheEliteGentleman: As per @Adam, OAuth dance is not required & no need for oob or callback for posting of Jobs in LinkedIn, since it uses 2-legged OAuth 1.0a calls. Do JOAuth supports 2-legged OAuth calls? – Gnanam Jun 14 '11 at 10:25
  • @Gnanam, yes, set callback to `oob`, and you will have to retrieve the Authorized token by yourself. – Buhake Sindi Jun 14 '11 at 10:27
  • @TheEliteGentleman: Can you provide me a working example to make 2-legged OAuth calls to LinkedIn using JOAuth? BTW, we're not integrating this in web application, hence it would be _too good_ if you can provide me a standalone Java application for this. – Gnanam Jun 14 '11 at 10:32
  • I have no API key to LinkedIn. How do I register for it? – Buhake Sindi Jun 14 '11 at 10:35
  • @TheEliteGentleman: You need to sign-up here https://www.linkedin.com/secure/developer?newapp to get API Key and Secret Key. – Gnanam Jun 14 '11 at 10:45
  • @TheEliteGentleman: Had a chance to sign-up and make 2-legged OAuth call to post Jobs? – Gnanam Jun 15 '11 at 10:02
  • @Gnanam, Twitter uses 3-Legged OAuth, and LinkedIn gives me a "signature_invalid" when requesting a request token. I've logged this issues to [LinkedIn](http://developers.linkedin.com/thread/3232?tstart=0) as the HTTP Authorization header is valid on my side. – Buhake Sindi Jun 15 '11 at 10:07
  • @Gnanam, Have you tried Scribe and see if you can authenticate to LinkedIn? The full tutorial can be found [here](http://developer.linkedin.com/message/4568) (written by the creator of Scribe) and [here](http://developer.linkedin.com/message/6482#6482) (newer version). – Buhake Sindi Jun 15 '11 at 10:11
  • @TheEliteGentleman: In fact, I tried the older version of Scribe which uses 3-legged OAuth call. It was indeed successful, but as @Adam said, it looks like LinkedIn has to finally grant access to post Jobs finally. Reason to say this is that in response I'm receiving ` 403 1308133608919 C4UOZP830D 0 Write access to job posting denied. `. – Gnanam Jun 15 '11 at 10:30
  • @TheEliteGentleman: As you can see there is 403 error reporting _Write access to job posting denied_. Meantime, I'm also trying to implement the same in 2-legged OAuth call, so that the manual user authorization is not required at the time of posting Jobs. – Gnanam Jun 15 '11 at 10:31
  • Ok, Try doing 2-Legged OAuth with Scribe then. :) – Buhake Sindi Jun 15 '11 at 10:38
  • @TheEliteGentleman: I'm finding from Scribe tutorial that it is designed *only* for making 3-legged OAuth call. I even tried on my own to the extent of signing request with an "empty token", but still it is working based on 3-legged OAuth call. This is the response I received: ` 401 1308112304177 UYJM4ESP5J 0 [unauthorized]. The token used in the OAuth request is not valid. ` – Gnanam Jun 15 '11 at 10:52
  • @TheEliteGentleman: As you can see above, it says `The token used in the OAuth request is not valid`, which it seems to me, making 3-legged OAuth call. – Gnanam Jun 15 '11 at 10:54
  • @TheEliteGentleman: I'm really surprised even after googling, I couldn't find suitable library/code to make 2-legged OAuth call in Java (and then posting Jobs to LinkedIn). – Gnanam Jun 15 '11 at 10:58
  • @Gnanam, you misread @Adam Trachtenberg's post (He said `these are 2-legged OAuth 1.0a calls` and **not** `there are 2-legged OAuth 1.0a calls`). LinkedIn OAuth is still a 3-legged OAuth as it follows the RFC 5849 specification. Read the [documentation](http://developer.linkedin.com/docs/DOC-1243) carefully. Scribe is correct. Also, – Buhake Sindi Jun 15 '11 at 11:22
  • @TheEliteGentleman: Yes, I read that link also. But LinkedIn Job Posting API uses 2-legged OAuth. Please read here [Using OAuth with the Jobs API](http://developer.linkedin.com/docs/DOC-1298). `The Jobs API requires authentication so we know which partner is working with the particular job and can verify that the job's poster matches up with the contract being sent. For this purpose, we are using two-legged OAuth.` Also this ...`Most OAuth libraries are tuned for three-legged requests so you'll need to find out how to make two-legged requests.` – Gnanam Jun 15 '11 at 11:35
  • @TheEliteGentleman: Also, at here [Code Sample: Posting a Job in C#](http://developer.linkedin.com/docs/DOC-1300), 2-legged OAuth call is written in C#. As you can see here, only CONSUMER_KEY and CONSUMER_SECRET is used to post Jobs. – Gnanam Jun 15 '11 at 11:37
  • @Gnanam, No it doesn't. The code examples shows you how do Post Job to LinkedIn with an already established Access token. What the code never shows is how they received the Access Token (hence the 2 fields `TOKEN` and `TOKEN_SECRET` which are access token token and token secret). Download the source code and see the full implementation. I just did, hence I know. – Buhake Sindi Jun 15 '11 at 11:49
  • @TheEliteGentleman: Immediately, this came to my mind. I really wanted to appreciate you for your patience in finding solution on this. – Gnanam Jun 15 '11 at 11:56
  • @Gnanam, what solution? I did nothing. – Buhake Sindi Jun 15 '11 at 12:00
  • @TheEliteGentleman: Yes, in fact, I've also already seen the implementation. If you read completely, you'll come to know that Access Token is never obtained from the provider, and that's the reason it is always checked for `if (this.Token != "")` at line#271 in `oAuthLinkedInSimple.cs`. What I could understand is that, a signature is generated by `HMAC-SHA1` method using only nonce, timestamp, consumer key, etc. Hope this makes clear. – Gnanam Jun 15 '11 at 12:03
  • @TheEliteGentleman: By finding solution, I mean that your continued effort along with me in trying to find alternatives, exploring other libraries, etc. – Gnanam Jun 15 '11 at 12:07
  • @Gnanam, no...what this means is that to invoke LinkedIn API, you have to pass your access token to LinkedIn using `HMAC-SHA1` signature and populate the HTTP `Authorization` header. – Buhake Sindi Jun 15 '11 at 12:07
  • @TheEliteGentleman: OK. But I don't see any statement/call to get Access Token. Can you point me to line# where Access Token is obtained, so that I could clearly understand this? – Gnanam Jun 15 '11 at 12:20
  • The access token is already obtained and populated via the `Token` and `TokenSecret` variable. See the `Main()` method in `LinkedInSample.cs` – Buhake Sindi Jun 15 '11 at 12:28
  • @TheEliteGentleman: Yes, but `LinkedInSample.cs` is written for getting profiles from LinkedIn. If you look at `PostJob.cs` (which is demonstrated for LinkedIn Job Posting API), both `Token` and `TokenSecret` variables are not used/passed. – Gnanam Jun 15 '11 at 13:22
  • @Gnanam, The code on Job Posting is a sample program, just use the fully working code as example. – Buhake Sindi Jun 15 '11 at 13:28
  • @TheEliteGentleman: I believe Job Posting is also a working code and not a sample program. Because, with the help of my .Net Team colleague, even tried this `PostJob.cs` working code example. In my opinion, it seems to be working, but it is throwing 403 error, which I believe this error is because that Job Posting has to be granted access by LinkedIn ultimately. – Gnanam Jun 15 '11 at 13:39
  • @Gnanam, regardless, OAuth 1 is a 3-Legged Authorization protocol. :) – Buhake Sindi Jun 15 '11 at 13:42
  • @TheEliteGentleman: In [Job Posting Guidelines](http://developer.linkedin.com/docs/DOC-1302), under **Authentication** section, you can read this line - `The Jobs API uses two-legged OAuth for all calls.` And in [LinkedIn's Legacy Authentication Model](http://developer.linkedin.com/docs/DOC-1147), it' been mentioned that it is `deprecated legacy authorization format for the LinkedIn APIs`. – Gnanam Jun 16 '11 at 07:31
  • @TheEliteGentleman: You can also read more about the new API changes made in LinkedIn Job Posting API at [What's New with the Jobs API](http://developer.linkedin.com/docs/DOC-1301). – Gnanam Jun 16 '11 at 09:54
  • @TheEliteGentleman: I just wanted to update you that the new Job Posting API is based on 2-legged OAuth. So, `No member authentication required (i.e. no member access token or agreement key)` – Gnanam Jun 17 '11 at 08:10
  • Then do a request token and post your jobs to LinkedIn – Buhake Sindi Jun 17 '11 at 08:16
0

To clarify a few things:

1) These are two-legged OAuth 1.0a calls. You don't need to do the OAuth dance. 2) These is no need for oob or anything like this. 3) You generate the Authorization header using the OAuth library of your choice and send an HTTP POST.

Here is a quick start: http://developer.linkedin.com/docs/DOC-1299

This being said, I will repeat again that this is a closed program, so unless you are on a special whitelisted set of partners, your calls will fail even if you solve all the above.

Furthermore, there is additional authorization necessary of your application by a job poster within their LinkedIn account to make your request succeed and related data needs to be in the XML POST body.

Based on the amount of questions you've had about this, if you are a whitelisted partner, please follow up with the LinkedIn Jobs API team, and we'll arrange for additional assistance.

Adam Trachtenberg
  • 2,231
  • 1
  • 14
  • 18
  • Yes, I've already signed-up for this program. Can you provide me some pointers/resource written in Java that `generate the Authorization header using the OAuth library of your choice and send an HTTP POST`? Am really finding difficult to get thro' this. Also, even I followed-up with LinkedIn Jobs API team at `jobsapi@linkedin.com` but no response yet. If possible could you please arrange for further assistance? – Gnanam Jun 14 '11 at 07:57
  • Unfortunately, signing up doesn't mean you'll be approved. I only provide tech support, not make those decisions. :(The Jobs team will need to get back to you before it makes sense to do more here. – Adam Trachtenberg Jun 14 '11 at 19:22
  • It's been about 2 weeks, I haven't yet received any response from Jobs team to grant access for me to post Jobs. I've also logged a request - [Grant access to post test Jobs](http://developers.linkedin.com/thread/3229?tstart=0). Can you help me in stepping up this? Or is there any alternate email/phone that I can reach at? – Gnanam Jun 16 '11 at 08:01
  • @Gnanam: I have pinged the team to ask for them to provide you with a status update. – Adam Trachtenberg Jun 17 '11 at 06:42
  • Am really surprised. I have not received any response yet from Jobs team. – Gnanam Jun 28 '11 at 13:06
  • is oauth-config.xml is correct? i am getting "cant find declaration oauth" error – pradeep cs Jul 10 '12 at 19:09