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

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.

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.

JavaScript and Google API troubles

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.

Java Idap Authentication for HTML webpage

I want to add Idap authentication to the web applications that I developed with HTML and AngularJS.
But I don't understand how I should do it. I looked online and only found answer to how to set up IDAP in JAVA.
{
String uid = "User";
String password = "Password";
String BASE = "ou=People,dc=objects,dc=com,dc=au";
String userDN = "uid=" + uid + ",ou=people," + BASE;
String HOST = "ldap://ldap.example.com:389";
String INIT_CTX = "com.sun.jndi.ldap.LdapCtxFactory";
// Setup environment for authenticating
Hashtable env = new Hashtable();
env.put(Context.INITIAL_CONTEXT_FACTORY, INIT_CTX);
env.put(Context.PROVIDER_URL, HOST);
env.put(Context.SECURITY_AUTHENTICATION, "simple");
env.put(Context.SECURITY_PRINCIPAL, userDN);
env.put(Context.SECURITY_CREDENTIALS, password);
env.put(Context.REFERRAL, "follow");
try
{
DirContext authContext =
new InitialDirContext(env);
System.out.println("I am authenticated");
}
catch (AuthenticationException ex)
{
System.out.println("authentication failed");
}
catch (NamingException ex)
{
ex.printStackTrace();
}
}
So the above code is just a tryout from me, and it works fine.
I will make it into a function later and i want to use it to authenticate the users that come to use my web application.
I have no idea how to do that though. I mean I know I should create a HTML page that request users to enter ID and Password. then I can use AngularJS to capture those data.
But then what?
How do I use the Java function in AngularJS or HTML? I mean once authenticated, It should jump to the index.html page.
Thanks guys, Sorry i am new to web development.
Java runs on the backend only. You grab the username/password in Angular and POST it back to your Java application. Typically, you'd set this up with Dropwizard or Spring Boot. The backend then returns either success or failure to Angular which then sets the browser location to your index.html.
Read up on Jersey, REST and Dropwizard, there's lots of examples. Alternatively, you can run a Tomcat server and let this do the LDAP auth via container managed security - you only need to supply the LDAP info in the web.xml.

Write cookie using c# and read using javascript

Normally Cookies are stored in the following location:
C:\Users\SomeUser\AppData\Roaming\Microsoft\Windows\Cookies
What i want to do is write cookie in a text file at this location using c# which is quite simple no problem at all, this is ok.
But i am not able to find a way to read cookie from that file using java-script in my web application.
Is it even possible?
JavaScript has no privileges reading files from the disc. You can't make a cross-domain request from a local file because it's not on a domain. You need to host C:\index.html on a local webserver instance in order for it to work. Or start Chrome with the following parameters.
--disable-web-security -–allow-file-access-from-files
You should also add the following to your service response:
response.setHeader("Access-Control-Allow-Origin", "*");
I believe it is possible, but due to the security issues it's won't be straight forward.
Check this jQuery lib: https://github.com/carhartl/jquery-cookie
Note: Some browsers for eg. Chrome, restrict from using coockies from file : Why does Chrome ignore local jQuery cookies?
I think u looking for this answer that u mention on comment..
i want the user to be automatically logged in if user is already logged in my desktop application, using cookies i want to pass some information of logged in user to the browser
. For that you need to generate a token that can be used for a one-time login that is stored in the database and Make sure tokens expire if they haven't been used in e.g. 8 hours. When you start the website, pass the token on the query string to the website. Then the website uses the token to connect the user's session to their account, and the token gets deleted.
Also you can use Single sign on WIKI
After struggling alot and searching, i found a way to do it. We can use InternetSetCookie method which is in wininet.dll to set cookie for a specific url or web application.
Add this code in your class:
[DllImport("wininet.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool InternetSetCookie(string url, string name, string data);
public static bool SetWinINETCookieString(string url, string name, string data)
{
return Form1.InternetSetCookie(url, name, data);
}
Usage:
var cookieOutput = SetWinINETCookieString("http://localhost:49549/Home/Index", "dataToTest", "thisIsTheData;Expires = " + DateTime.Now.AddDays(10).ToString("R"));
For testing purpose i hardcoded my application specific url.
and then in web application i wrote this javascript to get the cookie value:
$(document).ready(function () {
alert(getCookie("dataToTest"));
});
and here is getCookie function:
function getCookie(name) {
var value = "; " + document.cookie;
var parts = value.split("; " + name + "=");
if (parts.length == 2) return parts.pop().split(";").shift();
}
This SO post really helped me out.

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