Currently I am working on a website for a company that wants to show their (company) LinkedIn updates on a webpage. In my opinion, this should be possible to do without the need to login for the viewer (oAuth), however, I can't find a working solution.
I've googled quite a lot and read the documentation quite thoroughly, but could not find a satisfying answer. Therefore, I come to you.
Current Case
Without oAuth authorization a 504 Gateway error appears, otherwise the viewer is required to login to LinkedIn to view the company updates. Another weird thing is that a few weeks ago, the API worked fine: the company updates showed without the need to login for the viewer, did anyone else experience this?
Questions
If so, could you show me an example with LinkedIn's own updates? Because the documentation does not make much sense on this point.
[just to confirm] Did anyone else experience a change in the need for authorization of the LinkedIn API?
A small first-version API wrapper that I created for this purpose:
<?php
class Linkedin{
const API_LINK = 'https://api.linkedin.com/v1/'; // Link to API
const AUTH_LINK = 'https://www.linkedin.com/uas/oauth2/authorization?'; // Authorisation link
const ACC_LINK = 'https://www.linkedin.com/uas/oauth2/accessToken?'; // Access token link
private $sApikey;
private $sApisecret;
private $sState;
private $sScope;
private $sRedirectUri;
private $aErrors = array();
public function __construct($aParams = array()){
/* Check input */
if(count($aParams)<1){
logmsg('There where no parameters given to the linkedin api.');
return false;
}
/* Basic settings */
$aSettings = array(
'api_key' => null,
'api_secret' => null,
'scope' => 'r_basicprofile',
'redirect_uri' => 'http://' . $_SERVER['SERVER_NAME']
);
$aSettings = array_merge($aSettings,$aParams);
/* Set variables */
$this->sApikey = $aSettings['api_key'];
$this->sApisecret = $aSettings['api_secret'];
$this->sScope = $aSettings['scope'];
$this->sRedirectUri = $aSettings['redirect_uri'];
}
public function company($iCompanyID, $aSettings = array()){
$aOutput = $this->fetch('GET', 'companies/'.$iCompanyID.'/updates', $aSettings);
logmsg($aOutput);
return $aOutput;
}
public function getErrors($sType = 'array'){
switch($sType){
case 'html':
$sOutput = '<div class="error">';
$sOutput.= '<p>The LinkedIn API failed to connect due to the following error(s):</p>';
$sOutput.= '<ul>';
foreach($this->aErrors as $i => $sError){
$sOutput.= '<li>'.$sError.'</li>';
}
$sOutput.= '</ul></div>';
return $sOutput;
case 'array':
default:
return $this->aErrors;
}
}
private function setError($sString){
$this->aErrors[] = $sString;
}
/* The actual fetch */
private function fetch($sMethod, $sLink, $aParams = array()) {
if($_SESSION['access_code']){
$aHeaders = array(
'Authorization' => 'Bearer ' . $_SESSION['access_token'],
'x-li-format' => 'json', // Comment out to use XML
);
}
// Need to use HTTPS
$sUrl = trim(self::API_LINK,'/').'/' . trim($sLink,'/');
// Append query parameters (if there are any)
if (count($aParams)) {
$sUrl .= '?' . http_build_query($aParams);
}
// Tell streams to make a (GET, POST, PUT, or DELETE) request
// And use OAuth 2 access token as Authorization
$context = stream_context_create(
array('http' =>
array('method' => $sMethod,
'header' => (isset($aHeaders)) ? $aHeaders : null
)
)
);
// Hocus Pocus
$sResponse = file_get_contents($sUrl, false, $context);
// Native PHP object, please
return json_decode($sResponse);
}
/* Authorisation handler */
public function oAuth(){
// OAuth 2 Control Flow
if (isset($_GET['error'])) {
$this->setError(trim(htmlentities($_GET['error'].': '.$_GET['error_description'])));
return false;
} elseif (isset($_GET['code'])) {
// User authorized your application
if ($this->getState() == $_GET['state']) {
// Get token so you can make API calls
if($this-getAccessToken()){
return true;
}
} else {
logmsg('Possible CRFS attack. Wrong state code: "'.$this->getState() .'" != "'.$_GET['state'].'"',2);
$this->setError('Login to Linkedin failed because an attack was detected.');
return false;
}
} else {
if ((empty($_SESSION['expires_at'])) || (time() > $_SESSION['expires_at'])) {
// Token has expired, clear the state
$_SESSION = array();
}
if (!isset($_SESSION['access_token']) || empty($_SESSION['access_token'])) {
// Start authorization process
$this->getAuthorisation();
}
}
}
/* Get Authorisation code */
private function getAuthorisation(){
$aParams = array(
'response_type' => 'code',
'client_id' => $this->sApikey,
'scope' => $this->sScope,
'state' => $this->getState(),
'redirect_uri' => $this->sRedirectUri,
);
// Authentication request
$sUrl = self::AUTH_LINK . http_build_query($aParams);
// Redirect user to authenticate
header("Location: $sUrl");
exit();
}
/* Get access token */
private function getAccessToken(){
$aParams = array(
'grant_type' => 'authorization_code',
'client_id' => $this->sApikey,
'client_secret' => $this->sApisecret,
'code' => $_GET['code'],
'redirect_uri' => $this->sRedirectUri,
);
// Access Token request
$sUrl = self::ACC_LINK . http_build_query($aParams);
// Tell streams to make a POST request
$sContext = stream_context_create(
array('http' => array('method' => 'POST') )
);
// Retrieve access token information
$sResponse = file_get_contents($sUrl, false, $sContext);
// Native PHP object, please
$oToken = json_decode($sResponse);
// Store access token and expiration time
$_SESSION['access_token'] = $oToken->access_token; // guard this!
$_SESSION['expires_in'] = $oToken->expires_in; // relative time (in seconds)
$_SESSION['expires_at'] = time() + $_SESSION['expires_in']; // absolute time
return true;
}
private function getState(){
if(is_null($this->sScope)){
if(!isset($_SESSION['ln-api-scope'])){
$_SESSION['ln-api-scope'] = md5(uniqid(rand(), true));
}
$this->sScope = $_SESSION['ln-api-scope'];
}
return $this->sScope;
}
}
With the scope set to rw_nus
and the API key and secret supplied.
Thanks for any help in advance.
With kind regards,
fps