I'm having some difficulty exchanging my JSAPI token for a REST API token. I'm using this for reference:
https://developer-programs.linkedin.com/documents/exchange-jsapi-tokens-rest-api-oauth-tokens
I've: set up a self signed SSL cert locally, so Linkedin's secure cookie works properly; given my app r_basicprofile and r_emailaddress permissions.
Here's my front end code:
<script type="text/javascript" src="//platform.linkedin.com/in.js">
api_key: **MY_CLIENT_ID**
authorize: true
credentials_cookie: true
</script>
...
$('.linkedin-signin').click(function(e) {
IN.User.authorize( function () {
IN.API.Raw("/people/~").result(function(data) {
$.post(location.origin+'/api/account/create/linkedin', { 'lId': data.id } ).done(function(result) {
console.log(result);
});
});
});
return false;
});
And here is my PHP code, which is almost exactly as in their docs:
$consumer_key = '**MY_CLIENT_ID**';
$consumer_secret = '**MY_CLIENT_SECRET**';
$cookie_name = "linkedin_oauth_${consumer_key}";
$credentials_json = $_COOKIE[$cookie_name];
$credentials = json_decode($credentials_json);
$access_token_url = 'https://api.linkedin.com/uas/oauth/accessToken';
$oauth = new OAuth($consumer_key, $consumer_secret);
$access_token = $credentials->access_token;
// swap 2.0 token for 1.0a token and secret
$oauth->fetch($access_token_url, array('xoauth_oauth2_access_token' => $access_token), OAUTH_HTTP_METHOD_POST);
Everything looks good, but on $oauth->fetch, I get the error:
OAuthException(code: 401): Invalid auth/bad request (got a 401, expected HTTP/1.1 20X or a redirect)
Which leads me to believe that the token is invalid... but it's taken directly from the cookie, so how could it be invalid? Any ideas?
Today we got the weird 401 error too, apperently linkedin was broken, because after an hour it worked again without any changes on our side.
I found this site though and eventhough it's a really old post, i tought i'd share how we fixed it, which works.
JS Front-end
var AppConfig = {
linkedin : {
onLoad : "linkedinLibInit",
api_key : 'YOUR_API_KEY',
authorize : false,
credentials_cookie: true
}
};
window.linkedinLibInit = function ( response ) {
// post init magic
// cleanup window callback function
delete window.linkedinLibInit;
}
$.getScript( "//platform.linkedin.com/in.js?async=true", function success() {
IN.init( AppConfig.linkedin );
} );
function connectToLinkedIn() {
if ( IN.User.isAuthorized() ) {
_linkedinAuthorized();
}
else {
IN.User.authorize( _linkedinAuthorized );
}
}
function _linkedinAuthorized() {
IN.API.Profile( "me" )
.fields( 'id', 'first-name', 'last-name', 'location', 'industry', 'headline', 'picture-urls::(original)', 'email-address' )
.result( function ( response ) {
var accessToken = JSON.parse( $.cookie( 'linkedin_oauth_' + AppConfig.linkedin.api_key ) );
// performApi Call to backend
} )
.error( function ( err ) {
// render error
} );
}
PHP Backend using PECL oAuth
function offlineAuthLinkedIn($accessToken, $linkedinConfig) {
$oAuth = new \OAuth( $linkedinConfig['app_id'], $linkedinConfig['app_secret'] );
$oAuth->fetch(
'https://api.linkedin.com/uas/oauth/accessToken',
array('xoauth_oauth2_access_token' => $accessToken),
OAUTH_HTTP_METHOD_POST
);
$response = null;
parse_str($oAuth->getLastResponse(), $response);
$oAuth->setToken($response['oauth_token'], $response['oauth_token_secret']);
$oAuth->fetch(
'http://api.linkedin.com/v1/people/~:(id,first-name,last-name,formatted-name,headline,location,picture-url,picture-urls::(original),public-profile-url)',
array(),
OAUTH_HTTP_METHOD_GET,
array('x-li-format' => 'json')
);
$profile = json_decode($oAuth->getLastResponse(), true);
$profile['user_id'] = $profile['id'];
if (true == isset($profile['pictureUrl']))
{
$profile['profile_image'] = $profile['pictureUrl'];
unset($profile['pictureUrl']);
}
return $profile;
}
Related
I develope App for CRUD Operation rest api using Http Client. GET and POST work perfectly, but problem come after DELETE Operation.
Console Message show me response code Bad Request 400 **, I Check code rest Api (in my codeigniter) **Bad Request 400 it's means the ID is null or not passing the ID on URL Rest Api.
It's Console Message Image
But I try on POSTMAN and **DELETE* is perfectly work , i dont know why in flutter http.delete not working.
It's Result POSTMAN Working
My Api Url for Delete
http://192.168.43.159/wpu-rest-server/apii/mahasiswa/delete/
It's My API.dart :
Future<bool> checkPost(Map<String, dynamic> id) async {
final client = http.Client();
try {
final response = await client.send(http.Request(
"DELETE", Uri.parse("${Urls.BASE_API_URL}mahasiswa/delete/"))
..headers['Content-type']= 'application/x-www-form-urlencoded'
..body = jsonEncode(id));
print(response.reasonPhrase);
print(response.statusCode.toString());
print(response.request.toString());
return response.statusCode == 204;
} finally {}
}
It's My list.dart :
void _checkPost() async{
final post = {"id":widget.id};
bool result = await api.checkPost(post);
if (result) {
_showSnackBar(
context, 'Success ');
} else {
_showSnackBar(context, 'Failed');
return null;
}
}
If you need Rest Api Code :
Controller
public function delete_delete()
{
$id = $this->delete('id');
$msgDelete = ['id' => $id, 'message' => 'Deleted the resource'];
$msgEmpty = ['status' => false, 'message' => 'ID Not Found'];
$msgBadRequest = ['status' => false, 'message' => 'Provide an ID'];
if ($id === null) {
$this->set_response($msgBadRequest, 400);
} else {
if ($this->mahasiswa->deleteMahasiswa($id) > 0) {
$this->set_response($msgDelete, 204);
} else {
$this->set_response($msgEmpty, 404);
}
}
}
Model
public function deleteMahasiswa($id = null)
{
if ($id === null) {
return false;
} else {
$this->db->delete('mahasiswa', ['id' => $id]);
return $this->db->affected_rows();
}
}
I'm mistake something ?
I ever heard httpclient.delete can't add body , but in POSTMAN need add body for deleting data, how to handle this ?
I'm having a problem using the adal.js library without Angular. (I'm using Vue.js.)
I have an authentication context instance, which is constructed with the following options (exact values have been changed to protect the innocent):
let config = {
tenant: '<tenant id>',
clientId: '<client id>',
redirectUri: 'http://myapplication.com/index.html',
// popUp: true,
cacheLocation: 'localStorage'
}
On my login page, I call authContext.login(), which redirects me first to https://login.microsoftonline.com/, where I log into AAD. Upon successful login, another redirect takes me back to my application, at the URI I've configured above, along with an id_token parameter in the URL. However, no token or other properties are stored by the library in local storage, just a few properties that are the result of the configuration.
On successful login, All I've got in localStorage is
{
adal.access.token.key: "",
adal.error: ""
adal.error.description: ""
adal.expiration.key: "0"
adal.idtoken: ""
adal.login.error: ""
adal.login.request: "http://myapplication.com/#/login"
adal.nonce.idtoken: "<a non-empty string>"
adal.session.state: ""
adal.state.login: "<a non-empty string>"
adal.token.keys: ""
adal.username: ""
}
So, as far as AAD is concerned, I've successfully authenticated, but the library itself seems to have no notion of what user is logged in, what tokens are associated with them, when the token expires, etc. Any advice on how to proceed would be most appreciated. Thank you in advance for reading.
Active Directory Authentication Library for JavaScript (ADAL JS) helps you to use Azure AD for handling authentication in your single page applications. This library is optimized for working together with AngularJS.
It will not save the tokens into the cache unless we code it. You can check the relative code from adal-angular.js. Here is a piece of code for your reference:
The saveTokenFromHash method will also save the tokens into cache and this function will execute after the page redirect back to the Angular app.
adal.js:
AuthenticationContext.prototype.saveTokenFromHash = function (requestInfo) {
this._logstatus('State status:' + requestInfo.stateMatch);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, '');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, '');
// Record error
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ERROR_DESCRIPTION)) {
this._logstatus('Error :' + requestInfo.parameters.error);
this._logstatus('Error description:' + requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
this._saveItem(this.CONSTANTS.STORAGE.FAILED_RENEW, requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
this._saveItem(this.CONSTANTS.STORAGE.ERROR, requestInfo.parameters.error);
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, requestInfo.parameters[this.CONSTANTS.ERROR_DESCRIPTION]);
if (requestInfo.requestType === this.REQUEST_TYPE.LOGIN) {
this._loginInProgress = false;
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, requestInfo.parameters.errorDescription);
} else {
this._renewActive = false;
}
} else {
// It must verify the state from redirect
if (requestInfo.stateMatch) {
// record tokens to storage if exists
this._logstatus('State is right');
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.SESSION_STATE)) {
this._saveItem(this.CONSTANTS.STORAGE.SESSION_STATE, requestInfo.parameters[this.CONSTANTS.SESSION_STATE]);
}
var keys, resource;
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ACCESS_TOKEN)) {
this._logstatus('Fragment has access token');
// default resource
this._renewActive = false;
resource = this.config.loginResource;
if (!this._hasResource(resource)) {
keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
this._saveItem(this.CONSTANTS.STORAGE.TOKEN_KEYS, keys + resource + this.CONSTANTS.RESOURCE_DELIMETER);
}
if (requestInfo.requestType === this.REQUEST_TYPE.RENEW_TOKEN) {
resource = this._getResourceFromState(requestInfo.stateResponse);
}
// save token with related resource
this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + resource, requestInfo.parameters[this.CONSTANTS.ACCESS_TOKEN]);
this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY + resource, this._expiresIn(requestInfo.parameters[this.CONSTANTS.EXPIRES_IN]));
}
if (requestInfo.parameters.hasOwnProperty(this.CONSTANTS.ID_TOKEN)) {
this._loginInProgress = false;
this._user = this._createUser(requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
if (this._user && this._user.profile) {
if (this._user.profile.nonce !== this._getItem(this.CONSTANTS.STORAGE.NONCE_IDTOKEN)) {
this._user = null;
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'Nonce is not same as ' + this._idTokenNonce);
} else {
this._saveItem(this.CONSTANTS.STORAGE.IDTOKEN, requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
// Save idtoken as access token for app itself
resource = this.config.clientId;
if (!this._hasResource(resource)) {
keys = this._getItem(this.CONSTANTS.STORAGE.TOKEN_KEYS) || '';
this._saveItem(this.CONSTANTS.STORAGE.TOKEN_KEYS, keys + resource + this.CONSTANTS.RESOURCE_DELIMETER);
}
this._saveItem(this.CONSTANTS.STORAGE.ACCESS_TOKEN_KEY + resource, requestInfo.parameters[this.CONSTANTS.ID_TOKEN]);
this._saveItem(this.CONSTANTS.STORAGE.EXPIRATION_KEY + resource, this._user.profile.exp);
}
}
}
} else {
this._saveItem(this.CONSTANTS.STORAGE.ERROR, 'Invalid_state');
this._saveItem(this.CONSTANTS.STORAGE.ERROR_DESCRIPTION, 'Invalid_state');
if (requestInfo.requestType === this.REQUEST_TYPE.LOGIN) {
this._saveItem(this.CONSTANTS.STORAGE.LOGIN_ERROR, 'State is not same as ' + requestInfo.stateResponse);
}
}
}
};
And this function will be called in this.$get like below:
// special function that exposes methods in Angular controller
// $rootScope, $window, $q, $location, $timeout are injected by Angular
this.$get = ['$rootScope', '$window', '$q', '$location', '$timeout', function ($rootScope, $window, $q, $location, $timeout) {
var locationChangeHandler = function () {
var hash = $window.location.hash;
if (_adal.isCallback(hash)) {
// callback can come from login or iframe request
var requestInfo = _adal.getRequestInfo(hash);
_adal.saveTokenFromHash(requestInfo);
$window.location.hash = '';
if (requestInfo.requestType !== _adal.REQUEST_TYPE.LOGIN) {
_adal.callback = $window.parent.AuthenticationContext().callback;
}
// Return to callback if it is send from iframe
if (requestInfo.stateMatch) {
if (typeof _adal.callback === 'function') {
// Call within the same context without full page redirect keeps the callback
if (requestInfo.requestType === _adal.REQUEST_TYPE.RENEW_TOKEN) {
// Idtoken or Accestoken can be renewed
if (requestInfo.parameters['access_token']) {
_adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['access_token']);
return;
} else if (requestInfo.parameters['id_token']) {
_adal.callback(_adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION), requestInfo.parameters['id_token']);
return;
}
}
} else {
// normal full login redirect happened on the page
updateDataFromCache(_adal.config.loginResource);
if (_oauthData.userName) {
//IDtoken is added as token for the app
$timeout(function () {
updateDataFromCache(_adal.config.loginResource);
$rootScope.userInfo = _oauthData;
// redirect to login requested page
var loginStartPage = _adal._getItem(_adal.CONSTANTS.STORAGE.START_PAGE);
if (loginStartPage) {
$location.path(loginStartPage);
}
}, 1);
$rootScope.$broadcast('adal:loginSuccess');
} else {
$rootScope.$broadcast('adal:loginFailure', _adal._getItem(_adal.CONSTANTS.STORAGE.ERROR_DESCRIPTION));
}
}
}
} else {
// No callback. App resumes after closing or moving to new page.
// Check token and username
updateDataFromCache(_adal.config.loginResource);
if (!_adal._renewActive && !_oauthData.isAuthenticated && _oauthData.userName) {
if (!_adal._getItem(_adal.CONSTANTS.STORAGE.FAILED_RENEW)) {
// Idtoken is expired or not present
_adal.acquireToken(_adal.config.loginResource, function (error, tokenOut) {
if (error) {
$rootScope.$broadcast('adal:loginFailure', 'auto renew failure');
} else {
if (tokenOut) {
_oauthData.isAuthenticated = true;
}
}
});
}
}
}
$timeout(function () {
updateDataFromCache(_adal.config.loginResource);
$rootScope.userInfo = _oauthData;
}, 1);
}
...
And here is a sample code which could save the tokens into cache for your reference:
<html>
<head>
<script src="https://unpkg.com/vue"></script>
<script src="node_modules\adal-angular\lib\adal.js"> </script>
<script src="config.js"> </script>
</head>
<body>
<div>
<button onclick="login()" >Login</button>
</div>
<script>
var authContext=new AuthenticationContext(config);
function login(){
authContext.login();
}
function init(configOptions){
if (configOptions) {
// redirect and logout_redirect are set to current location by default
var existingHash = window.location.hash;
var pathDefault = window.location.href;
if (existingHash) {
pathDefault = pathDefault.replace(existingHash, '');
}
configOptions.redirectUri = configOptions.redirectUri || pathDefault;
configOptions.postLogoutRedirectUri = configOptions.postLogoutRedirectUri || pathDefault;
// create instance with given config
} else {
throw new Error('You must set configOptions, when calling init');
}
// loginresource is used to set authenticated status
updateDataFromCache(authContext.config.loginResource);
}
var _oauthData = { isAuthenticated: false, userName: '', loginError: '', profile: '' };
var updateDataFromCache = function (resource) {
// only cache lookup here to not interrupt with events
var token = authContext.getCachedToken(resource);
_oauthData.isAuthenticated = token !== null && token.length > 0;
var user = authContext.getCachedUser() || { userName: '' };
_oauthData.userName = user.userName;
_oauthData.profile = user.profile;
_oauthData.loginError = authContext.getLoginError();
};
init(config);
function saveTokenFromHash(){
var hash = window.location.hash;
var requestInfo = authContext.getRequestInfo(hash);
if (authContext.isCallback(hash)) {
// callback can come from login or iframe request
var requestInfo = authContext.getRequestInfo(hash);
authContext.saveTokenFromHash(requestInfo);
window.location.hash = '';
if (requestInfo.requestType !== authContext.REQUEST_TYPE.LOGIN) {
authContext.callback = window.parent.AuthenticationContext().callback;
}
}
}
saveTokenFromHash();
</script>
</body>
</html>
I wonder if deepstream provides ready-to-use solution to make endpoints private/public. If it doesn't I wonder how I can track proper deepstream calls on server side to allow only certain endpoints? I believe I need to provider permissionHandler that implements canPerformAction and check whether it's an RPC call required authorization and whether a caller authorized properly to do that. Is that right thinking? I'm looking at documentation and understand that I'm interested in topic P but I don't know what is a right action to check.
https://deepstream.io/docs/constants.html
Thanks in advance!
You're spot on with your approach. Here's a code sample on how to permission different users for different RPCs. In a real-world use-case you would most likely get the variables users and rpcs from a database.
So now whenever a client calls ds.rpc.make( 'set-user-data',... the server looks up which permission the rpc requires ('canEditUser') and if the user has that permission (mike: true, lisa: false)
var DeepstreamServer = require( 'deepstream.io' );
var server = new DeepstreamServer();
var C = server.constants;
var users = {
'mike': { canEditUser: true },
'lisa': { canEditUser: false }
};
var rpcs = {
'set-user-data': 'canEditUser'
};
server.set( 'permissionHandler', {
isValidUser: function( connectionData, authData, callback ) {
if( !authData.username ) {
callback( 'no username specified' );
}
else if( users[ authData.username ] ) {
callback( null, authData.username );
}
},
canPerformAction: function( username, message, callback ) {
var isIncomingRpc = message.topic === C.TOPIC.RPC && message.action === C.ACTIONS.REQUEST;
if( isIncomingRpc ) {
var rpcName = message.data[ 0 ];
if( rpcs[ rpcName ] === undefined ) {
callback( 'Unknown RPC ' + rpcName );
return;
}
var userPermissions = users[ username ];
var requiredRpcPermissions = rpcs[ rpcName ];
var isPermissioned = userPermissions[ requiredRpcPermissions ];
callback( null, isPermissioned );
}
}
});
server.start();
I´m trying to get some data from analytics, but can´t get authorized. It returns the following error:
I renewed my credentials at google console several times.
The code I´m using:
var clientId = '*****************0m1fnmuae00abaaq.apps.googleusercontent.com';
var apiKey = '********fB9eVMVfQ0oR6';
var scopes = 'https://www.googleapis.com/auth/analytics.readonly';
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth, 1);
}
function checkAuth() {
gapi.auth.authorize({
client_id: clientId, scope: scopes, immediate: true}, handleAuthResult);
}
function handleAuthResult(authResult) {
if (authResult) {
gapi.client.load('analytics', 'v3', handleAuthorized);
} else {
handleUnauthorized();
}
}
function handleAuthorized() {
var authorizeButton = document.getElementById('authorize-button');
var runDemoButton = document.getElementById('run-demo-button');
authorizeButton.style.visibility = 'hidden';
runDemoButton.style.visibility = '';
runDemoButton.onclick = makeApiCall;
outputToPage('Click the Run Demo button to begin.');
}
function handleUnauthorized() {
var authorizeButton = document.getElementById('authorize-button');
var runDemoButton = document.getElementById('run-demo-button');
runDemoButton.style.visibility = 'hidden';
authorizeButton.style.visibility = '';
authorizeButton.onclick = handleAuthClick;
outputToPage('Please authorize this script to access Google Analytics.');
}
function handleAuthClick(event) {
gapi.auth.authorize({
client_id: clientId, scope: scopes, immediate: false}, handleAuthResult);
return false;
}
When I run the code, it returns the following error:
error: {errors:[{domain:usageLimits, reason:keyInvalid, message:Bad Request}], code:400, message:Bad Request}
code: 400
errors: [{domain:usageLimits, reason:keyInvalid, message:Bad Request}]
0: {domain:usageLimits, reason:keyInvalid, message:Bad Request}
domain: "usageLimits"
message: "Bad Request"
reason: "keyInvalid"
message: "Bad Request"
Someone can help with this issue?
Find the error.
In Google Console, you have to create the OAuth ID and the Public API Access ID.
Take the cliendId from the first and the APIKey from the second.
I think it´s very confuse, and it could be more explicit in documentation.
I am trying to use your api in a custom app with imported users.
Everything works fine (auth_token, login, call initiation) , but when the callee should get a response and add the remotestream nothing happens. no errors get shown in the console.
I would appreciate if someone takes a look at the code and tells me what i m missing.
I tried the vline demo at https://freeofcinema.vline.com and it worked with the same browsers and conditions between the two computers. In my app it is a http , but i tried it also with https, and the same problem came up. This is some simplified code i used to test the api.
var Streams = [];
var Vsession = null;
var Vline = (function(){
var Client;
var authToken;
var service_id = 'freeofcinema';
var profile = null;
var Person;
var Calls = [];
var onMessage = function(event){
//alert('message');
var msg = event.message, sender = msg.getSender();
console.log(sender.getDisplayName() +'sais: '+ msg.getBody());
console.log(event);
}
var onMediaSession = function(event){
console.log(event);
var mediaSession = event.target;
InitSession(mediaSession);
}
function Call(mediaSession) {
mediaSession.
on('change', alert_info);
}
function alert_info(b){
console.log(b);
}
function InitSession(mediaSession){
mediaSession.on('mediaSession:addRemoteStream', function(event) {
alert('addRemoteStream');
});
mediaSession.on('mediaSession:addLocalStream', function(event) {
alert('addLocalStream');
});
mediaSession.on('mediaSession:removeLocalStream mediaSession:removeRemoteStream', function(event) {
console.log('removedStream');
});
Calls.push(new Call(mediaSession));
}
return {
init : function(){
if(profile){
return;
}
profile = {
"displayName" : //some getusrname function...
};
$.post('vtoken.php',{//get auth token
id : Comm.Voip_user().id
},function(data){
authToken = data;
Client = vline.Client.create({
"serviceId": service_id,
"ui" : true
});
Client.on('recv:im', onMessage , this);
Client.on('add:mediaSession', onMediaSession, this);
Client.on('login', function(e) {
Vsession = e.target;
//alert('loged in');
});
Client.login(service_id, profile, authToken);
});
},
getPerson : function(id){//id of user to call
if(Vsession){
Vsession.getPerson(id).
done(function(person){
Person = person;
Vsession.startMedia(id);
});
}
}
}
}());
Thank you for your response.
I tried with one user from the app, and another from the https://freeofcinema.vline.com, and the same problem occured. Also the call (in pending state) gets terminated after a short while..
When passing ui:true when creating the client, you do not have to handle the media sessions yourself. Just comment the line Client.on('add:mediaSession', onMediaSession, this); and it should just work.