I am trying to retrieve the video ID (eventually in JavaScript) of the YouTube video that just started streaming from our church. In testing to see what works and what doesn't, when I try calling
curl 'https://www.googleapis.com/youtube/v3/search?eventType=live&part=snippet&channelId={CHANNEL_ID}&type=video&key={API_KEY}'
I get back an empty list until I go into Studio for the channel and change the setting on the now live video (which is already Public) to something else and then Public again and click Publish. It is worth noting that in the Live Streaming portion of Studio where the video is set up that it is also marked Public. This is not something that we could have church volunteers running the broadcast on Sunday do. Is there either some way to either get the video ID (for certain reasons I don't want to use the https://www.youtube.com/embed/live_stream?channel={CHANNEL_ID} option) or to publish the new live video either automatically or without requiring an OAuth token?
I am looking to do this so that from our web page, which is on a hosted site that doesn't allow programmatic updates, we would dynamically be able to look up the video ID of the pending (or now actively streaming) live stream being broadcast from the church. OAuth access to the YouTube APIs isn't a good option, as there are security issues, so that rules out certain options. Some API key searches let you see the ID, but only after the video is published from YouTube Studio, but depending on church volunteers to go into the YouTube channel and make updates there as the video is starting is also not a valid option in this case.
We could do the video ID lookup and update the web page manually if that is the only option, but my hope was that there would be some way that this could be done from JavaScript so that we could always know that the web page was pointing to the right video so that we don't have to rely on someone remembering to (or being able to) update the web page or do some similar update each week.
According to the official documentation, the LiveBroadcasts.list API endpoint has a request parameter that allows you to retrieve a set of LiveBroadcast resource objects pertaining to the live broadcasts that belong to the authenticated user (which objects contain, among other info, the IDs of your interest):
mine (boolean)
The mine parameter can be used to instruct the API to only return broadcasts owned by the authenticated user. Set the parameter value to true to only retrieve your own broadcasts.
Note that to invoke this endpoint, you have to have proper authorization; that is to say that you have to have obtained valid credentials upon running to successful completion an OAuth 2 authentication/authorization flow. Hence you have to pass on to the endpoint a valid access token that has among its scopes at least one of those specified by the official documentation.
The curl call to LiveBroadcast.list at a bash command line prompt would look like:
$ curl \
--header "Authorization: Bearer $ACCESS_TOKEN" \
'https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id&fields=items/id&maxResults=50&mine=true'
where $ACCESS_TOKEN is an environment variable that is set to a valid access token (as mentioned above).
For what concerns a JavaScript implementation of this endpoint call, there are several possible solutions, mainly depending on the type of your app: a desktop app or a web app.
If using the Fetch API, you'll have to mimic closely the curl call above:
fetch('https://www.googleapis.com/youtube/v3/liveBroadcasts?part=id&fields=items/id&maxResults=50&mine=true', {
headers: {
'Authorization': 'Bearer ' + access_token
}
})
where the variable access_token contains your valid access token.
For what concerns a Javascript GAPI (i.e. Google’s Client Library for Browser-side JavaScript) implementation, the code would look like shown below (for a broader context look into this sample source file from Google: analytics_codelab.js):
function loadAPIClientInterfaces() {
gapi.client.load('youtube', 'v3', function() {
getMyLiveBroadcasts();
});
}
function getMyLiveBroadcasts() {
var request = gapi.client.youtube.liveBroadcasts.list({
part: 'id',
fields: 'items/id',
mine: true,
maxResults: 50
});
request.execute(function(response) {
if ('error' in response) {
displayMessage(response.error.message);
} else {
// process each response.items[k].id
...
}
});
}
Note that each piece of code above uses the fields request parameter for to obtain from LiveBroadcasts.list only the broadcast's ID info (it is always good to ask from the API only the info that is of actual use).
Related
I've been working with this code that builds a custom SoundCloud player from hrefs to the tracks via their API:
https://codepen.io/nicholaspetersen/pen/yyVYMY
It was working fine until SoundCloud changed the way their API authorises as described here:
https://developers.soundcloud.com/blog/security-updates-api
They said in that post:
Currently, to access the public resources of the platform, server-side integrations with our API only require a client_id in the URL’s query parameter. We’ll be strengthening our authorization here by making all public resources on the API only accessible to apps that have been authorized with the client_credentials grant. This will enable the app to capture both the access_token and the refresh_token to then fetch the resources from the API. Please note that the use of client_id will be deprecated and deleted soon (from July 2021). Developers should provide the Authentication header for all their requests to the SoundCloud API going forward.*
So, my understanding from the above is that I need to update the Authentication header as currently the code above is authenticating using this code:
// convert a SoundCloud resource URL to an API URL
scApiUrl = function(url, apiKey) {
var resolver = ( secureDocument || (/^https/i).test(url) ? 'https' : 'http') + '://api.' + domain + '/resolve?url=',
params = 'format=json&consumer_key=' + apiKey +'&callback=?';
However, I'm completely stuck as to how to do that... can anyone take a look at the JS in that codepen and let me know what I should be doing to fix the issue? I thought it might involve updating the code above to include a callback URL, but then that blog post says the use of client_id is now deprecated.
Any help / pointers hugely welcome.
After the SoundCloud security updates, the playlist on my website also stopped working. It wouldn't load any of the tracks in my playlist. I did a lot of research, finally realizing that I would need to register an app on SoundCloud to get access to the API. This was not possible due to the app registration page being down since early this year and with no promises of it ever going back up. So, I needed to find an alternative. Which brought me to the Widget API page.
https://developers.soundcloud.com/docs/api/html5-widget
The Widget API allows you to externally control the widget that is embedded on your website through javascript. You set the width and height of the widget to 0 and it hides the widget. I tinkered with this on codepen this week and I am pleased that I now have a working playlist ready to be utilized on my website. See the link below.
https://codepen.io/mdcrowcodes/pen/eYEMyzY
enter code here
Not sure if the title summarises my question well.
Basically, I am trying to authenticate routes such as checking if user exists etc. I only want to allow
requests coming from my frontend application to be approved, but, since no user is signed in there is no token to send.
Api request -
mywebiste/checkUser/email
This route is unprotected on my backend because no user is logged in.
BUT I want to protect this route, in such a way that it's accessible only from the frontend.
Some ideas I came up with were adding specific headers tag from the frontend and check them on the backend, but that could be easily replicated, is there something more secure like using tokens etc.
I am using React and Node.js
Same origin policy is going to give you some basic protection, but basically if an API endpoint is exposed publicly, it's exposed publicly. If you don't want that route to be publicly accessible you need to add access control.
If you use that route to check if a user is already registered, you could, for example, merge it with the user registration route and send a different error code if the user already exists (which is not a great idea because it leaks which emails are registered on your system).
You can verify that a request was originated by a user (by authenticating him) but you cannot verify that a request comes from a particular client because of these two reasons :
If you include some API key in your client (web page or other), it's easily retrievable by everyone (the best thing you could do is offuscate it which makes things slightly harder but still possible)
If you send an API key over the network it's easily retrievable as well
The only thing you could do is prevent other web pages from calling your backend on behalf of the user, by using CORS (which is actually active by default if you dont specify an Access-Control-Allow-Origin header)
I ended up creating a kind of working solution, so basically, I create a new base64 string on my frontend and attach that to the header while making a request to the backend. The base64 string is different every minute, so even if the header is copied, it differs every minute and is combined with your secret key.
I have made a package so that people can use it if they want - https://github.com/dhiraj1site/ncrypter
You can use it like so
var ncrypter = require('ncrypter');
//use encode on your frontend with number of seconds and secret key
var encodedString = ncrypter.encrypt(2, 'mysecret1')
//use decode on your backend with same seconds and secret
var decodedString = ncrypter.decrypt(encodedString, 2, 'mysecret1');
console.log('permission granted -->', decodedString);
I use below function to get the Continent Code from the api which works fine on localhost but fail in live environment which is website
$.getJSON('//www.geoplugin.net/json.gp?jsoncallback=?', function (data) {
// console.log(JSON.stringify(data, null, 2));
console.log(JSON.stringify(data.geoplugin_continentCode));
});
Warning which i see in Console is
Loading failed for the with source
“https://www.geoplugin.net/json.gp?jsoncallback=jQuery16407901144106031991_1537089290623&_=1537089292750”.
I am not sure why it fails on website https://www.example.com
could SSL version some problem as i am not sure as i tried it on fiddle & it works fine http://jsfiddle.net/om8ahkp3/
UPDATE
Since problem was due to crossdomain issue which as this api used a different url for ssl version. i was not able to use this ssl version as it was not free.
So ended up using another api which had free option also limited to 50k request on monthly basis.
$.ajax({
url: 'https://api.ipgeolocation.io/ipgeo?fields=is_eu& excludes=ip&apiKey=YOURKEY',
dataType: 'json',
success: function (json) {
console.log("json.is_eu " + json.is_eu);
}
});
What is the whole problem?
You want to access to a third site (crossDomain). So, That site decides that you can access to it, or not. When a site provides a service (similar geo service that you have used it), it determines which part of it's services are free.
In your case, if your source site's protocol is http (like as localhost) and dest site (service provider site) is http too, you can access to this geo service with your above code (because this third site allows this now). But if you want to access to this service from a https site (I think you are trying this now) the geoPlugin don't allow you easily or free!
In this cases, the destination sites, provide another urls and define user levels (to getting money for special services.).
In act, if your dest site was for yourself too(which it is not in this case), you could add needed access to specific referer sites, but now...
I look at its site to be sure. You must use this url in this case:
https://ssl.geoplugin.net/json.gp?k=yourAPICode
But this is not all of things! What is k in above url? This site writes:
"For SSL access, an API Key is required to offset certificate prices and costs €12 per year."
I don't know, but if you need it, you should search for free plugins (if exists) or buy it.
So I read through the example which Apple gave us (CloudKit catalog) and I noticed that everytime you want to write or read you need to put your API token into the script.
Now Javascript is clientbased which means every user can read the API token and can read and write into my containers?!
This code would be in one of the Javascript files
CloudKit.configure({
locale: 'en-us',
containers: [{
// Change this to a container identifier you own.
containerIdentifier: 'com.example.apple-samplecode.cloudkit-catalog',
apiTokenAuth: {
// And generate a web token through CloudKit Dashboard.
apiToken: '<insert your token here>',
persist: true, // Sets a cookie.
signInButton: {
id: 'apple-sign-in-button',
theme: 'black' // Other options: 'white', 'white-with-outline'.
},
signOutButton: {
id: 'apple-sign-out-button',
theme: 'black'
}
},
environment: 'development'
}]
});
Now the question is: am I missing something or is the solution to user a server-to-server communication over Node?
Here's my understanding:
API Tokens are not really meant to be private, and can't be since they're designed to be used in client-side JavaScript. Even if you tried to obfuscate the token in your code, it would be easily discovered by examining the URLs that are called during the sign in process.
The important thing to understand is that they can't do much by themselves. They allow a user to sign in to your container, and then the signed in user can read and write their own data—the same things they'd have access to if they signed in to iCloud on their iPhone or Mac and used your app there.
There's not much of a security concern because even if they take your token and write their own JavaScript, they're only messing with their own data. That said, you can use the "Allowed Origins" option to make this harder to do. (I put it that way because they could conceivably use a browser extension or something to alter the JS on your site. In general it seems wise to treat a user's CloudKit data as untrusted, even when it's coming from the API.)
Server to Server Keys are very different, and have a private key that is of course meant to be private. In that scenario anyone with the private key has read and write access to your public database. As the name implies, this is not something you'd use directly from JavaScript—you'd write your own server-side code that contacts the CloudKit API directly.
Unfortunately, while Apple has a nice red warning when you create a private Server to Server key, they don't seem to offer any security guidance on API Tokens. I'm 99% confident that this is because it's not a concern, and working on getting confirmation for that last 1%.
The init.js runs at client side, in the browser, you can easily notice that from the code:
<script>
window.addEventListener('cloudkitloaded',CKCatalog.init);
</script>
This will disclose the API token to the user ...
But you can always mitigate the risk of dangerous usage on the API token by:
Set "Allowed Origins" of the API token to only the domain of your site;
Set "Sign In Callback" to your URL only;
etc
In short, running in client side will disclose your API token, while it is still OK if you take actions to prevent dangerous usage of your token.
I need to retrieve a facebook page's list of posts (feed) using their javascript SDK, just like they explain in their docs: https://developers.facebook.com/docs/graph-api/reference/v2.4/page/feed
/* make the API call */
FB.api(
"/{page-id}/posts",
function (response) {
if (response && !response.error) {
/* handle the result */
}
}
);
I need it to be my website's "news section", so users should see it even if they are not connected to facebook.
The problem
Cool, but there is a problem... It returns: An access token is required to request this resource.
Holy cow... I'd like to get some access token for you #facebook, but my app doesn't make use of your authentication tools/plugins.
ANYWAY, I tried with FB.getLoginStatus(); but doesn't work, because the only way it can return an access_token is if the user is actually connected to the application. My users may not even be logged to facebook!
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
I've already payed a look to this SO question, but it doesn't solves my problem, simply because there are no such "generic tokens".
I've also read https://developers.facebook.com/docs/facebook-login/access-tokens/ and that also relies on tokens generated through facebook login methods... So, can't I display a list of fb page's posts in my website, without being connected into facebook, hence an application?
ADD: My app is build with angularJS, I'm not dealing with server-side code. I shall rely purely on javascript methods.
You could either use an page or an app access token, but as you'd be using them on the client-side, neither of them are an option.
See
https://developers.facebook.com/docs/facebook-login/access-tokens#apptokens
https://developers.facebook.com/docs/facebook-login/access-tokens#pagetokens
Note that because this request uses your app secret, it must never be made in client-side code or in an app binary that could be decompiled. It is important that your app secret is never shared with anyone. Therefore, this API call should only be made using server-side code.
I'd strongly recommend to build a simple server-side script (PHP for example) to proxy the request to the Graph API. You could then call this via AJAX for example and load the posts asynchronously (and alse get rid of the FB JS SDK!). There is NO way to handle this in a secure manner if you don't want to use FB Login for all of your users (which also doesn't make much sense IMHO).
I think it's straightforward :)
Since pages' posts are always public, it only needs a valid access token to retrieve page posts.
Quoting what you've written:
So, ¿How can I get an access_token to be stored into a variable, and later be used to get /{my-page}/posts?
You only require an access token.
My suggestion would be;
- Generate an access token for yourself (no extra permission needed)
- request page-id/posts
This way you don't require other users to be connected to facebook, you can simply requests page-id/posts to retrieve posts with access token you generated for yourself.
I hope it solves your problem :D
TIP: As long as posts are public, you only require a valid access token, it doesn't need to be user or page specific.