I am reading over Creating an efficient REST API and I understand and agree with a lot of what the document has to say.
I'm currently implementing a Twitter clone and each tweet object in the database has a list of readers (user IDs).
According to the document, it seems like the request for this would be something like:
PATCH /tweet
{read: true}
However read is not an actual property of the tweet. Instead this would actually update the list of readers on the tweet with the currently authenticated user. Is this correct?
This also means that the user could potentially perform other operations on the tweet using this same API route, but users cannot actually update/PATCH tweets in any way other than to mark them as read or not.
Additionally, only an authenticated user should be able to do this and it should only update the list of readers for them. The document says that you should not handle state in your API which means no sessions -- however authentication is obviously necessary. It seems to say that you should send the authentication/authorization token each time, but I don't understand how this is substantially different from a session cookie in some cases. Is the implication that the request should actually be:
PATCH /tweet
<Authentication-Header>
{userId: userId, read: true}
i.e. should the API itself not try to use information from the session and force requests to provide logged-in user information? Is there a better way to structure this?
Depends on what level of authentication is necessary.
Keep it simple. No need to complicate things.
Do you really need a "state"? You have a database where you have to check the user authentication. Why not use the database record?
If the API will be used by the client on their web server, then you just have to check the IP address of the request.
The IP address cannot be spoofed and therefore is a very good authentication factor. Add username and you have multi-factor authentication
Instruction for client using PHP
$user = 'joe';
$param = 1;
file_get_contents("http://apisite.com/api/function/?id=$user¶m=$param");
An HTML form, maybe you'd have to restrict access but if they name name the page something not easy to guess, then you have it.:
<form action="http://apisite.com/api/function/">
<input type="textarea" name="tweet"/>
<input type="hidden" name="user" value="joe" />
<input type="hidden" name="param" value="1" />
</form>
If the API were to be done in PHP:
PHP
<?php
header('Content-Type: text/plain; charset=utf-8');
header('Cache-Control: max-age=0');
$ip = $_SERVER['REMOTE_ADDR'];
$results = mysqli_query("SELECT COUNT(*),`user`,`state` FROM `Profile` WHERE `ip` LIKE '$ip' ");
$row = mysql_fetch_array($results, MYSQL_NUM);
if ($row[0] == 1 && $row[1]==$_GET['user']) && $row[2]==1){
$tweet = trim($_GET['tweet']);
$param = $_GET['param'];
include('tweet.php');
echo "Sent\n";
}
else{
echo "Not Sent\n";
}
?>
The problem with using cookies is that you are moving state outside of the URI and into the cookie.
RESTful APIs should have all the state necessary in the URI. Believe it or not but this does make things a lot easier as you don't have to mess around with handling cookies if you are creating a new client library.
Not using cookies also makes for really nice integration with other devices. Say I am viewing a RESTful webpage on my browser, and then I want to show someone something on my account. I can email them the URI /some-uri?authToken=1234 and they will be able to view my account without me giving them my password!!
Once the token expires, they will no longer be able to access the account.
EDIT
If you put the token in the POST body, a user won't be able to visit a page by typing in the address bar. They will keep being told they are not authorised. So yes, you need it in the URI. You can use a cookie if you really want, but except that it is a workaround.
You'd get the login token by exposing a login resource. To login you would send your username and password to:
POST /login
This would create a /login/{token} resource. You can use this token (or even the full URI if you want) to authenticate with the server.
When you want to log out, you call
`DELETE /login/{token}`
If you try to use the token now, the API should return with 'not authenticated' as the token no longer exists.
Related
I am using this method to login with google plus, It's working properly, I need email of user in callback but this method in return did not give email address. what should I do for email, is their any other method which return email or we can get user email using access token?
You need to add an additional scope to the request. Email-providing scopes have recently changed, with older ones being deprecated. The full list is at https://developers.google.com/+/api/oauth#email-scopes, but you can probably safely just use the email scope in addition to the https://www.googleapis.com/auth/plus.login scope. Scopes are specified by listing them with a space between each one, so your button configuration might look something like this:
<span id="signinButton">
<span
class="g-signin"
data-callback="signinCallback"
data-clientid="CLIENT_ID"
data-cookiepolicy="single_host_origin"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-scope="https://www.googleapis.com/auth/plus.login email">
</span>
</span>
You would use the people.get API to access the full profile of the user, which would include the emails array. Each element of the array contains the type of the address (home, work, etc) and the address itself. The email scope just lists the address directly associated with the account, while other scopes provide other publicly available addresses, so they may not provide further information.
Note that the older userinfo.* scopes are deprecated and may stop working in September 2014. See https://developers.google.com/+/api/auth-migration#timetable for details.
Update: To be clear - the sign-in button does not give you the email itself. It requests permission to get the email address and user profile information. What it gives you is an access token that you can use for further requests.
As both #DalmTo and I have pointed out, in order to actually get the information, you need to call the API endpoint of people.get. Go to this page and go to the Examples section and select JavaScript from the pull down for an example. You will also need to load the javascript client library: see https://developers.google.com/api-client-library/javascript/samples/samples for some examples, but in particular you will need a line like
<script src="https://apis.google.com/js/client.js?onload=onLoad"></script>
which loads the client library (and calls the onLoad() function when it has been loaded) and then something like
<script>
function onLoad(){
gapi.client.load('plus', 'v1', onClientLoad);
}
</script>
The onClientLoad() callback will be called when the client is loaded, and should set a marker to indicate things are set to make the call. Then, as part of the signinCallback() function, you would make sure this marker is set (indicating the client library is ready) and use the gapi.client.plus.people.get() method to make the request. Possibly something like this as part of the signed in block:
gapi.client.plus.people.get({
'userId': 'me'
}).execute(function(response){
console.log(response);
});
This does a call to people.get, requesting the profile of a particular user. The special userid of "me" indicates it should be for the user that is signed in. Calling execute() with a callback function executes the request, returning a response object. In this example, we're just printing it, but it will be a JSON object containing the user's profile.
You need to add another scope https://www.googleapis.com/auth/userinfo.email it should let you request the email address then using plus.people.get
<span id="signinButton">
<span
class="g-signin"
data-callback="signinCallback"
data-clientid="CLIENT_ID"
data-cookiepolicy="single_host_origin"
data-requestvisibleactions="http://schemas.google.com/AddActivity"
data-scope="https://www.googleapis.com/auth/plus.login,https://www.googleapis.com/auth/userinfo.email">
</span>
</span>
you can test the API here: plus.people.get
Note: a user can have more then one email you probably want the one with type account
I am working on a library in LUA for an ipad app called Codea. I'm trying to figure out to use OAuth for GitHub Gists. Only part that i can not figure out is how to get an Auth token via code. I used curl in terminal to get myself a token but this seems to be to much work for other users.
I've read through the github api docs multiple times but I cant figure out how to get a Token programmatically. I've tried to duplicate the method I've used to GET and POST gists but it does not seem to work. I'm not sure how to pass the username and password.
I'm creating a table with the needed params then encoding it in json. Everything I try gets a 404 error or 500 error. Thank you all in advance.
local url = "https://api.github.com/authorizations"
local d = {}
d.scopes = {"gist"}
d.note = "AutoGist Codea"
projectAuth = json.encode(d)
opts = { data = projectAuth }
opts.method = "POST"
opts.headers = {Authorization = "basic " .."username:password"}
http.request(url,successCallback,failedCallback,opts)
Scopes are coming, but only in Q4 2013.
See "OAuth changes coming" (October 2013, by Tim Cleam - tclem):
Starting today, we are returning granted scopes as part of the access_token response.
For example, if you are making a POST with the application/json mime-type you’ll see an additional field for the granted scopes.
{
"access_token":"e72e16c7e42f292c6912e7710c838347ae178b4a",
"scope":"repo,gist",
"token_type":"bearer"
}
Right now, these scopes will be identical to what you requested, but we are moving towards a feature set that will allow GitHub users to edit their scopes, effectively granting your application less access than you originally requested.
You should be aware of this possibility and adjust your application behavior accordingly.
Some things to watch out for and keep in mind:
Most third party applications using GitHub OAuth to identify users have the best success in adoption by starting out with a request for the minimum access that the application can possibly get away with.
Something like no scopes or just user:email is very sane.
It is important to handle the error cases where a users chooses to grant you less access than you originally requested.
Now that we are surfacing the granted scopes on the access_token response, applications can warn or otherwise communicate with their users that they will see reduced functionality or be unable to perform some actions.
Applications can always send users back through the flow again to get additional permission, but don’t forget that users can always say no.
I'm developing a Facebook app that will run in canvas. This is the scenario (I think it's very common). This is all server-side implementation.
Being:
APP_URL: https://apps.facebook.com/xxxxxx/
CANVAS_URL: https://myexample.com/facebookApp/
Step 1 (index of the app) has a form. It has action="CANVAS_URL/step2" (note that is not the app Url). In order to use signed_request in the next step, it has an hidden field <input type="hidden" name="signed_request" value="<?php echo $_POST['signed_request'] ?>" />
Step 2: it receives the info of the form and stores it in a Session, then parses the signed_request. This works OK. I store it in the Session because I want to save it to a database after the user is authenticated. If user was logged on to the app, I redirect him to APP_URL/step3; if not, I redirect him to login dialog, with &redirect_uri=APP_URL/step3 . In both cases, note that the step 3 is APP_URL/step3 (as I need signed_request again to check if user has authenticated and another data). All redirections are made with JavaScript: <script type="text/javascript">window.top.location = "URL";</script>
Step 3: now I want to save the data previously stored in the Session. BUT as the user is navigating through FB canvas, the session data is not available.
I tried several combinations. If the form is sent to APP_URL/step2 (instead of CANVAS_URL/step2, in order to create the session for APP_URL), I can't retrieve the posted data (because it is sent to FB, not to the CANVAS_URL).
I thought about using the session_id to recreate the Session in APP_URL, but I'm afraid that it isn't a very secure approach. I'm sure that there must be a better workaround.
I'm using the Javascript SDK inside a node.js (Express) App.
Connecting Users works fine, but the connection does not persist between page reloads.
Is this the default behaviour, or is the User supposed to stay connected in new requests?
Do I have to use OAuth token authentication and if so, how can this be done with the JS-SDK?
Inside the "Permission"-Popup, Users are already logged in with soundlcoud, though.
(just have to click the "connect" button each time)
Figured I'd share my answer for those who are unsatisfied with the current answers for automated oauth:
Retrieving access_token:
I had to define get and set cookie functions and then I use the functions to set and retrieve a function holding the access token. I'm not going to give these functions for conciseness but you can easily find them with a google search. I then use this line of code to get the SC access token (once the user has authenticated for the first time)
SC.accessToken()
Setting token:
So this is kind of just an elephant in the room in my opinion that for some reason no one has mentioned. How in the **** do you connect w/ SC using the access token? Do you set it as oauth param? On each call pass it? Well, after experimenting with putting the parameter in every single place I could think, I found out you have to do something like this:
SC.initialize({
client_id: '[removed for security reasons]',
client_secret: '[removed for security reasons]',
redirect_uri: '[removed for security reasons]',
access_token: getCookie("sc_lm2"),
scope: 'non-expiring'
});
//Where "sc_lm2" is the name of my cookie
Hope the helps! Took me a while to figure this out for such a simple thing
EDIT
Using PHP and Wordpress:
$json = wp_remote_get("http://api.soundcloud.com/users/[user_id]/tracks.json?client_id=[client_id]");
$soundcloudData = json_decode($json['body'],true);
(substitue cURL functionality if you're not using Wordpress). #krafty I assume you would just change the endpoint from "/tracks" to "/users" but I can't say I have ever really needed to grab anything but tracks using the Soundcloud API. Hope this helps, though I'm not sure I fully understand what it is that you are trying to accomplish (or rather, how exactly you're going about it) - are you trying to allow user logins? If you want to explain fully what you're trying to accomplish and the steps you're taking I'd be happy to take a crack at it.
Yep, this is the way to do it, officially. :)
For the Next SoundCloud site, we store the token in localStorage (until the user logs out, of course). In each AJAX request to the API from the front end, we put the oauth token in a request header:
jqXHR.setRequestHeader('Authorization', 'OAuth ' + the_oauth_token);
Well there seems to be quite a bit of confusion on this topic and I am struggling to get a clear answer, so here is my question...
I am using the serverside flow to obtain access tokens for my web app, I previously used offline_access which is now being depreciated so I need a way to refresh the token in the following situations:
1) User changes FB password
2) Token expires naturally
My app posts results to users FB walls so the refresh needs to be done automatically by our server (no cookies or OAuth dialogs)
I thought I could try to use the new endpoint described here
http://developers.facebook.com/roadmap/offline-access-removal/
, with the following piece of code (Java):
public static String refreshFBAccessToken(String existingAccessToken)
throws Exception{
//Currently not working
String refreshUrl = "https://graph.facebook.com/oauth/access_token?
client_id="+FacebookApp.appId+"
&client_secret="+FacebookApp.appSecret+"
&grant_type=fb_exchange_token
&fb_exchange_token="+existingAccessToken;
URL url = new URL(refreshUrl);
URI uri = new URI(url.getProtocol(), url.getHost(), url.getPath(),
url.getQuery(), null);
String result = readURL(uri.toURL());
String[] resultSplited = result.split("&");
return resultSplited[0].split("=")[1];
}
But this doesnt seem to work (I get a response 400), and when I re-read the documentation it seems this endpoint is used for tokens obtained using the client-side flow only...
So what about the serverside flow....?
Can someone tell me if the approach above is correct or there is another way?
Many thanks
From what I understand there is no server side flow for refreshing tokens.
The refresh token call needs to include the response of the user authentication process which is a short lived token.
You will need to include the refresh token process as part of the user login flow or if this doesn't work for your setup you will need to email the user asking them to come back!
I dont know java but syntax is very much like C#, so I can say,you are doing everything right.
But I doubt what does this function readURL do ?
If it works like get_file_contents() of php (i.e. if it does an HTTP get) , I guess thats not a right way to do .
Based on my experience on google's refresh token method, I think you should do an HTTP POST instead of HTTP GET to given url.