JavaScript and Google API troubles - javascript

I'm trying to adapt a simple search script from this question. However, I'm trying to determine why the method loadApi is undefined. I'm also having trouble tracking down the source of the problem, because the JS supplied by Google is... difficult to read.
Here is the script, once again:
<script>
googleApiClientReady=function() {
loadApi() = function() {
return new Promise(function(resolve,reject){
gapi.client.setApiKey('<redacted>');
gapi.client.load('youtube', 'v3', resolve);
});
};
loadApi().then(function() {
var q = 'pink floyd';
var request = gapi.client.youtube.channels.list({
part: 'statistics',
forUsername : 'GameSprout'
});
request.execute(function(response) {
var str = JSON.stringify(response.result);
alert(str);
});
});
};
</script>
And here is the error in question:

It may not require authentication to preform a search but your application still needs to be reregistered and you will need to at the very least use an API key.
Kindly note I a not a JS dev I just copied this example from the documentation. There are several that show how to access public apis.
gapi.client.init({
'apiKey': 'YOUR_API_KEY',
...
}).then(...)
Note: channels.list is a part public part private method as far as I can see. Some of the parameters will only work if you are authenticated .
mine boolean
This parameter can only be used in a properly authorized request. Set this parameter's value to true to instruct the API to only return channels owned by the authenticated user.

Related

Positively identify the current user from my javascript in SharePoint

When logging into SharePoint a cookie called "AADAuth" is set on ".office.com". The contents of this cookie is a JWT and it positively identifies the current user. If I could get this cookie from my javascript in SharePoint, I could send the JWT to my custom API and with Microsofts public certificate, I would be able to positively verify the identity of the user.
However, since this cookie is on ".office.com" which is obviously not my SharePoint domain, I cannot access that cookie.
So is there a way to get the JWT on SharePoint?
Otherwise, is there any other way I can find any content on the client side javascript to positively identify the current user?
I know I can initiate a new authentication process from my javascript in SharePoint, but that takes time and I'm looking for a solution that doesn't take much time for the end user, so hopefully I can use some of the information already in SharePoint.
I'm going to include a couple of different methods as it seems you may be looking for a specialized solution rather than a general one.
General Solution
I'm going to expand on DevBot's answer using _spPageContextInfo.userId, which is a number, not the person's user name.
<script type="text/javascript">
var spContextUserId;
$(document).ready(function() {
spContextUserId = _spPageContextInfo.userId;
console.log('user context id: ' + spContextUserId);
console.log('Executing sp.js...');
SP.SOD.executeOrDelayUntilScriptLoaded(loadSPUserProfiles, 'sp.js');
});
function loadSPUserProfiles() {
// ..wait for sp.js to load
console.log('sp.js loaded. Loading sp.userprofiles.js...');
SP.SOD.executeOrDelayUntilScriptLoaded(getUserProperties, 'SP.UserProfiles.js');
}
</script>
<script type="text/ecmascript">
var userProfileProperties;
function getUserProperties() {
try {
console.log('sp.userprofiles.js loaded...');
console.log('Getting user properties...');
var clientContext = new SP.ClientContext.get_current();
var peopleManager = new SP.UserProfiles.PeopleManager(clientContext);
userProfileProperties = peopleManager.getMyProperties();
clientContext.load(userProfileProperties);
clientContext.executeQueryAsync(onRequestSuccess, onRequestFail);
}
catch (err) {
console.log(err.message);
}
}
function onRequestSuccess() {
console.log('in request success routine');
var accountName = "";
try {
//console.log(userProfileProperties);
//accountName = userProfileProperties.get_accountName; // also works
accountName = userProfileProperties.get_userProfileProperties()['AccountName'];
console.log('accountName from svc: ' + accountName);
// Now see if that account name matches the ID
getUserInfo(accountName, function(userInfo){
console.log('User Id: ' + userInfo.ID);
if (spContextUserId == userInfo.ID) {
alert('Verified');
} else {
alert('Not verified.');
}
},
function(sender,args){
console.log(args.get_message());
});
catch(ex) {
console.log(ex.message);
}
function getUserInfo(userName, Success, Error)
{
var context = new SP.ClientContext.get_current();
var userInfoList = context.get_web().get_siteUserInfoList();
var query = new SP.CamlQuery();
var viewXml = "<View> \
<Query> \
<Where> \
<Eq><FieldRef Name='UserName' /><Value Type='Text'>" + userName + "</Value></Eq> \
</Where> \
</Query> \
<RowLimit>1</RowLimit> \
</View>";
query.set_viewXml(viewXml);
var items = userInfoList.getItems(query);
context.load(items,'Include(Deleted,Department,EMail,FirstName,ID,IsActive,IsSiteAdmin,JobTitle,LastName,MobilePhone,Name,Notes,Office,Picture,SipAddress,UserName,WebSite,WorkPhone)');
context.executeQueryAsync(function() {
if(items.get_count() > 0) {
var item = items.itemAt(0);
Success(item.get_fieldValues());
}
else {
Success(null);
}
}, Error);
}
</script>
This getUserInfo function was posted at https://sharepoint.stackexchange.com/questions/31457/get-user-via-javascript-client-object-model , but I've used a similar construct, myself.
This takes the numeric ID assigned to a user by the page context and compares this against the People Manager version after getting the current web context. To me, this is about as effective as you're going to get using just the JSOM to verify that a user matches their login context, if we assume they are already on a SharePoint page and have been authenticated via Windows, as they should have been.
Tokens
If you are still trying to pursue getting the JWT or using a token method, or just otherwise need access to this path for some reason, say to make an add-in work or because they're accessing SharePoint from outside it, you're going to want to review Microsoft's documentation on what the tokens look like and how to create one, because using Context Token flow requires using the CSOM, not JSOM. There, you have to get a context token to get an access token:
SharePointContextToken contextToken =
TokenHelper.ReadAndValidateContextToken(contextTokenString,
Request.Url.Authority);
string sharePointUrl = WebConfigurationManager.AppSettings.Get("RedirectUri");
Response.Redirect(TokenHelper.GetAppContextTokenRequestUrl(sharePointUrl, Server.UrlEncode(Request.Url.ToString())));
where you have set up the RedirectUri in the web.config:
<configuration>
<appSettings>
<add key="RedirectUri" value="https://contoso.com/RedirectAccept.aspx" />
</appSettings>
<configuration>
Instead, you'd have to translate all of this into a JSOM method to match what you say you're trying to achieve, creating your own using "Authorization flow", and then you should be able to use it against your API.
In the Authorization Code flow, there is no context token, so you cache the refresh token itself, and the user gets a new refresh token each time he or she launches the add-in (https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/handle-security-tokens-in-provider-hosted-low-trust-sharepoint-add-ins#CacheKey). It must redirect the user to the SharePoint's OAuthAuthorize.aspx:
/oauthauthorize.aspx?IsDlg=1&client_id=c78d058c-7f82-44ca-a077-fba855e14d38&scope=list.read&response_type=code&redirect_uri=https%3A%2F%2Fcontoso%2Ecom%2Fredirectaccept.aspx
IsDlg=1 would launch an authentication dialog.
The client_id has to be the same as when your add-in got registered with SharePoint.
Perhaps this would work as a GET inside an AJAX call. Microsoft talks about redirecting the user to this URL, which indicates to me it would be a call to actually change the URL:
window.location.href = 'http://www.mysharepointsite.com/somesite/oauthorize.aspx?...';
My suggestion would be to review this documentation thoroughly and construct what they say you need/perform the redirects required using this information:
https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/handle-security-tokens-in-provider-hosted-low-trust-sharepoint-add-ins
https://learn.microsoft.com/en-us/sharepoint/dev/sp-add-ins/authorization-code-oauth-flow-for-sharepoint-add-ins
Without more information on how you are accessing SharePoint, and why you think the token method is the only way to verify an account, this is a difficult question to answer as it seems like you may have a specific scenario you are trying to build for, and there's often a lot of trial-and-error involved to ensure all permissions, GUIDs, etc. are registered and accurate.
You can grab _spPageContextInfo.userId or _spPageContextInfo.userLoginName.
More info about that object here.

View Facebook events outside of Facebook - where to put the access token?

Unfortunately, there are many snippets, but no complete code examples. I'm trying to allow my group's events to be seen in a webpage outside of FB, and following the Javascript SDK example quick start I'm able to put the login and share buttons. But when I try to access my group's events, I get the error - "An access token is required to request this resource." Where do I put the access token in this code?
window.fbAsyncInit = function() {
FB.init({
appId : '{myappID}',
xfbml : true,
version : 'v2.8'
});
FB.AppEvents.logPageView();
FB.api(
'/myGroupID/events',
'GET',
{},
function(response) {
// Insert your code here
}
);
};
A couple of things:
I don't need my users to log in, as my group is public; my events are public. If someone is casually browsing through my website I want them to be able to see the events.
One of the other things that I've had trouble with is extremely short answers. Coming from the .NET community, I'm used to seeing lots of tutorials, and lots of complete code examples for how to do things. There are few "long form" answers or tutorials - even stackoverflow answers (like this one) don't contain enough detail on how to do this stuff. Especially the access token thing.
Is there a complete example of how to do this?
Thanks in advance.
You COULD add the Token like this:
FB.api(
'/myGroupID/events',
'GET',
{access_token: 'xxx'},...
...but that would expose your Token (which is always meant to be kept secret) to everyone visiting the website. You have to do that API call server side. Check out the PHP SDK or just use PHP cURL. The Token is just a GET parameter.
Ok, I figured it out. The point of all of this is to get the Access Token that Facebook says it wants. The Access Token is really the appsecret_proof (and not the access_token - the access_token is a different thing. See later in this post), so be aware of that. I cobbled together some different code examples (like this one, and a super, very careful reading of the Facebook graph api docs, to reach an answer. I coded in C# rather than Javascript because a lot of this needs to be done server side, and I'm more comfortable there anyway.
I created a console app as a proof of concept. A few notes:
the page_id is, in my case, for a group, not a (capital P) Page,
which is a different thing.
The access_token and app_secret are from the app
you've (hopefully) already created. (If you need to get the access_token in the first place, there
are some docs out there to help you get this part started.) You should also
make sure to use (as of .NET 1.0) the dotnet core secret manager
to protect your secrets when you develop so you don't pass them
around via source control.
This last one is big - the appsecret_proof is a combination of your access_token and your app_secret hashed together (with the app_secret being the key) and then made part of the query string. This is what Facebook wants when it says "An access token is required to request this resource."
public static string page_id = {your_page_id};
public static string access_token = {your_app_access_token};
public static string app_secret = {your_app_secret};
public static string appsecret_proof = FaceBookSecret(access_token, app_secret);
static void Main(string[] args)
{
Console.WriteLine("Making API Call...");
using (var client = new HttpClient(new HttpClientHandler { AutomaticDecompression = DecompressionMethods.GZip | DecompressionMethods.Deflate }))
{
client.BaseAddress = new Uri("https://graph.facebook.com/" + page_id + "/");
HttpResponseMessage response = client.GetAsync("events?access_token=" + access_token + "&appsecret_proof=" + appsecret_proof).Result;
response.EnsureSuccessStatusCode();
string result = response.Content.ReadAsStringAsync().Result;
Console.WriteLine("Result: " + result);
}
Console.ReadLine();
}
internal static string FaceBookSecret(string content, string key)
{
byte[] keyBytes = Encoding.UTF8.GetBytes(key);
byte[] messageBytes = Encoding.UTF8.GetBytes(content);
byte[] hash;
using (HMACSHA256 hmacsha256 = new HMACSHA256(keyBytes))
{
hash = hmacsha256.ComputeHash(messageBytes);
}
StringBuilder sbHash = new StringBuilder();
for (int i = 0; i < hash.Length; i++)
{
sbHash.Append(hash[i].ToString("x2"));
}
Console.WriteLine(sbHash);
return sbHash.ToString();
}
So after all of this runs, I get a nice json string with all of my events.

Authorizing xAPI interactions through Javascript triggers against my LRS

I've constructed a course in Storyline2, and defined several triggers via javascript as xAPI (tincan) activities.
I have an LRS endpoint link and authorization token, but I'm yet to understand where these credentials should be embedded in order for the whole thing to function properly.
I was given this at another message board:
var tincan = new TinCan({url: window.location.href});
Which of these needs to be replaced by one of the above mentioned? I'm guessing another should be added via "+" after "href".
That example code (which I guess you got from the article I wrote here: http://tincanapi.com/share-statements-between-courses/ ) is envisaging that you will launch the Storyline content from something like an LMS. In that case you will enter the endpoint and authorization details in the LMS settings, and the LMS will pass those to Storyline.
See: http://tincanapi.com/share-statements-between-courses/
If you want to put the details directly in the package, see the example code here: http://rusticisoftware.github.io/TinCanJS/
(included below for convenience)
var lrs;
try {
lrs = new TinCan.LRS(
{
endpoint: "https://cloud.scorm.com/tc/public/",
username: "<Test User>",
password: "<Test Password>",
allowFail: false
}
);
}
catch (ex) {
console.log("Failed to setup LRS object: " + ex);
// TODO: do something with error, can't communicate with LRS
}

Linkedin API SignUp Button - I am a beginner

I am making a website for the first time. I am sorry for any dumb question but I really have no background. However, I am trying to learn.
I developed the website on Weebly (www.i2i.network) and I am trying to include a LinkedIn SignUp button through their API service to monitor the people who are using the website.
So far I copied and pasted what I have found on the LinkedIn SDK guide
<script type="in/Login"></script>
<script type="text/javascript" src="//platform.linkedin.com/in.js">
IN.User.logout(callbackFunction, callbackScope);
api_key: [API] //I put my API key here
authorize: true
onLoad: onLinkedInLoad
lang: [LANG_LOCALE]
</script>
<script type="text/javascript">
// Setup an event listener to make an API call once auth is complete
function onLinkedInLoad() {
IN.Event.on(IN, "auth", getProfileData);
}
// Handle the successful return from the API call
function onSuccess(data) {
console.log(data);
}
// Handle an error response from the API call
function onError(error) {
console.log(error);
}
// Use the API call wrapper to request the member's basic profile data
function getProfileData() {
IN.API.Raw("/people/~").result(onSuccess).error(onError);
}
</script>
The button appeared the first time and it opens the connection to my LinkedIn profile. That's exciting! :D (Geek)
At this point I still don't know if I retrieve any data from the LinkedIn API service and if "yes" how to manage them and possibly include them in Intercom.io.
As far as I understood I should receive from LinkedIn API the following:
{
"firstName": "Frodo",
"headline": "Jewelery Repossession in Middle Earth",
"id": "1R2RtA",
"lastName": "Baggins",
"siteStandardProfileRequest": {
"url": "https://www.linkedin.com/profile/view?id=…"
}
}
Do I actually receive the answer? How do I read it? How do I use it in the following Intercom.io script?
<script>
window.intercomSettings = {
app_id: "k9sz4pfb",
name: "Nikola Tesla", // Full name
email: "nikola#example.com", // Email address
created_at: 1312182000 // Signup date as a Unix timestamp
};
</script>
<script>(function(){var w=window;var ic=w.Intercom;if(typeof ic==="function"){ic('reattach_activator');ic('update',intercomSettings);}else{var d=document;var i=function(){i.c(arguments)};i.q=[];i.c=function(args){i.q.push(args)};w.Intercom=i;function l(){var s=d.createElement('script');s.type='text/javascript';s.async=true;s.src='https://widget.intercom.io/widget/k92zopfb';var x=d.getElementsByTagName('script')[0];x.parentNode.insertBefore(s,x);}if(w.attachEvent){w.attachEvent('onload',l);}else{w.addEventListener('load',l,false);}}})()</script>
I know this might be a very basic question but I am starting from zero and I like to learn by practicing...
Furthermore, if you have any suggestion about website or tutorial where I could learn step-by-step it will be really appreciated.
Thank you for your help,
Giacomo
Double check the syntax but this should get you started.
// Use the API call wrapper to request the member's basic profile data
function getProfileData() {
var dataOutput =
// I added the callback event DONE. This assures the data method is called when the this API call is successful and complete.
var dataOutput = IN.API.Raw("/people/~").result(onSuccess).error(onError).done( {
if (dataOutput != null) {
DoSomethingWithYourData(dataOutput);
}
});
}
function DoSomethingWithYourData(dataOutput) {
console.log(dataOutput);
// Parse your data or assign it to a control. do whatever here.
}
And to answer your other question, you can add this to a JS script file and reference it on other pages, but you would have to keep any control IDs/class names added to this script file the same, so my tip would be to keep this file very generic so that it does not depend on anything else and only deals with working with LinkedIn's data.
I recommend these links for further reading so that you can expand on your code.
Jquery Parse JSON
JQuery AJAX
JQuery Callbacks

How to set access token on analytics API?

i was wondering, the documentation has tutorials for implementing use of the Analytics API in several languages.
Now in PHP they show how to store the access token and maintain it , now i assume the JS somehow mentains it in some sort of local storage but i don't wish the user to authenticate each time he visitis so my plan is to save the access & refresh token in my database and simply applying it to the client-side instead of going through the all pop up procress.
According to tutorial this :
gapi.auth.authorize({client_id: clientId, scope: scopes, immediate: false}, result);
Fires the popup which returns the access token but i'll say again i'm intrested in submiting token from database.
How can that be done?
is there some gapi.auth.setToken(token) method which after i could make calls to Core Reporting API?
I arrived here looking for a solution using this after having already written PHP to do the auth using google's PHP client libraries. I wanted to share the stored token as you mentioned, and be able to use javascript without re-authenticating, and/or triggering the popup (there may be a solution to this using the .init(callback) method, see docs/links at end).
Is there some gapi.auth.setToken(token) method
It turns out you can, there is the exact setToken(token) function you mention, and, you can even share the auth token generated earlier in PHP. What I'm not sure about yet, is if we should do it :)
I'm using PHP to do the initial auth, but presumeably in the javascript client you'd be able to call setToken() on something that you'd stored with getToken() in the same manner as this example. There may also be better approaches to this, like CORS (see links at end) mentioned in the API Authentication docs that I haven't had a chance to investigate any of these yet, but, I can give an example to answer the question, and might be useful to others needing the same behaviour
I first found Google developer Dan Holevoet's blog post with some sample JS code.
http://googleappsdeveloper.blogspot.com.au/2011/12/using-new-js-library-to-unlock-power-of.html
It's great to be able to query the API directly with javascript, and dynamically load lists etc, but the thing that worried me about this of course was storing clientid etc in js..
// Snippet from Dan's post
var clientId = 'YOUR_CLIENT_ID';
var apiKey = 'YOUR_API_KEY';
var scopes = 'https://www.googleapis.com/auth/calendar';
function handleClientLoad() {
gapi.client.setApiKey(apiKey);
window.setTimeout(checkAuth,1);
checkAuth();
}
But, according to Dan in an answer to the same question:
The apiKey is used in conjunction with designated referrers, which you
must declare in the APIs Console. If the key is sent from an
unauthorized referrer, it will not work. You should make your accepted
referrer list as limited as possible to ensure that others do not use
your apiKey for their own requests.
Now, my example is for the calendar API, but it all seems pretty consistent with the other APIs as well.
Note: This snippet was for proof-of-concept purposes only, and probably shouldn't be used in production. I assume the referrer protection mentioned makes something like this OK to do, but more thought needs to be given. It could be done by hidden input, AJAX call etc.. But in the end, they're all going to be visible in the javascript.
What I did to test the concept was to:
Authenticate via PHP client libs, using the same script/URL as callback (see [API console][2] for these callbacks)
On sucessful auth, in callback, set a global javascript variable in the page to the stored PHP auth token
Once page is loaded, on your first click event to use the javascript (or even in document ready), call authMe() to set the token
Then proceed as normal, calling any javascript API methods that you have given scope to in the initial PHP authentication process (in this case makeApiCall())
Like so:
In the php callback routine, regardless of whether authenticated yet (assuming that your callback URL is the same script), make this var global
<script type="text/javascript">
// A place to stick PHP's auth token once the auth dance is done
var dodgey_global_access_token = {};
</script>
Now, in php callback routine once we've checked that we're authenticated, and $_SESSION['token'] = $client->getAccessToken(); has been called (IE storing the auth token somewhere for later), or at least $client->getAccessToken() has something meaningful:
<script type="text/javascript">
// Set the js var via PHP here.. Yeck... Note json encode in php and parse in jquery
dodgey_global_access_token = $.parseJSON (<?php echo json_encode ($client->getAccessToken() ); ?>);
// dodgey_global_access_token now contains the auth token structure
// Removed auth-related functions in Dan's code - we'll already have a token
// Dan's orig function to list events in 'primary' calendar
function makeApiCall() {
gapi.client.load('calendar', 'v3', function() {
var request = gapi.client.calendar.events.list({
'calendarId': 'primary'
});
request.execute(function(resp) {
for (var i = 0; i < resp.items.length; i++) {
var li = document.createElement('li');
li.appendChild(document.createTextNode(resp.items[i].summary));
document.getElementById('events').appendChild(li);
}
});
});
}
// My function to setToken with the token injected from PHP authentication
function authMe () {
// Stuff the token into the gapi object
gapi.auth.setToken( dodgey_global_access_token );
// Now call the original 'makeAPICall' function now that we're 'authenticated'
makeApiCall();
}
</script>
Note: I used jquery in my example to quickly parse the JSON, we're already using it in the project, but if not, you'll have to find another library to do so
Relevant/useful docs:
// Sorry, as a new poster I can only post 2 hyperlinks,
// so embedding this in code snippet
//
// http://code.google.com/p/google-api-javascript-client/wiki/ReferenceDocs#gapi
// http://code.google.com/p/google-api-javascript-client/wiki/Authentication
// http://code.google.com/p/google-api-javascript-client/issues/detail?id=22
// http://code.google.com/p/google-api-javascript-client/wiki/CORS
// https://code.google.com/apis/console
Let me know if anything's unclear, and can post a working sample to demo.
Google API documentation has changed a bit from when this answer was first provided. google-api-javascript-client is no longer the recommended library for accessing google api libraries and Google suggests using discovery documents to load specific libraries.
As a result, the workflow that you are looking for in order to load all the libraries and set the access token would look like this:
var token = 'someaccesstokenfromoauth2'
gapi.load('client:auth2', function(){
gapi.client.load(
'https://analyticsreporting.googleapis.com/$discovery/rest',
'v4'
).then(function(){
gapi.auth.setToken({ access_token: token })
// business logic with gapi.client.analyticsreporting()
})
})
Load gapi with:
<script src="https://apis.google.com/js/api.js"></script>

Categories

Resources