I have built a web service based on a REST design. Of course some of the operations available on resources require authentication (delete a user for example). I use Basic authentication and so far everything is fine.
I have built a client to consume the web service : a set of Ajax function. Again, everything is fine (also for the Basic authentication).
I want now to create a whole web app that will use the set of Ajax function above to interact with the web service. But, to enhance the user experience, some of the web app functions will require Facebook authentication.
So here is my problem. The web app will require username and password to call the web service via the Basic authentication. But it will also require Facebook credentials to use Facebook API and the user will have to log in twice. Moreover, every time I will have to check if the Facebook user (currently logged in Facebook) corresponds to the user of the web service and it is quite troublesome.
Does anyone have an idea to simplify the process ?
It's a bit related to that post authentication-scheme-for-multi-tiered-web-application-utilizing-rest-api but I did not find any answer I could understand.
In such scenarios, I use only Facebook authentication. If user is logged into Facebook by JS SDK, you can simply get accessToken by FB.getAuthResponse().accessToken. Then, you can pass it into webservice and use it to authenticate on server side.
First, client side authentication with JS SDK:
/* assumed, that you alredy called FB.login() and stuff */
var accessToken = FB.getAuthResponse().accessToken;
$.ajax({
'url' : 'rest.php',
'type' : 'get',
'dataType' : 'json',
'data' : { accessToken : accessToken },
'success' : function(response) {
/* some fancy code, blah blah blah */
}
});
I use PHP SDK, so I'll show example in PHP.
<?php
require 'facebook.php';
$accessToken = $_GET['accessToken'];
$facebook = new Facebook(array(
'appId' => 'xxxxx',
'secret' => 'xxxxx'
));
$facebook->setAccessToken($accessToken);
if ($facebook->getUser()) {
// yaay, user is authenticated
echo json_encode($mySuperDuperSecretContentForLoggedUserOnly);
} else {
// authentication failed
echo json_encode(null);
}
If your main design uses basic authentication, but you want on some cases to be able to connect with facebook, then I suggest that you use an adapter/bridge on your server that will be able to take the facebook auth data and then handle the basic authentication itself.
That way the facebook users won't have to go through 2 authentication processes, and you don't break anything in your original flow.
It should not be hard to implement, when a user signs up just associate his fb account with a user in your system, then when he logs in with facebook take his id and log him into the system using the basic authentication.
One thing though, if you want to implement the facebook authentication using client side then you should send the auth data in https.
Related
We want to implement a SSO like approach in our program where users will register in a third party program and synchronously register in our program and authenticate the login/logout from the third party. Since we don't have access to the third party's authentication. The 3rd party program is our client's program that wants to use our program's services.
Since packages like miniorage is not free, we need to do this the hard way.
Here is what I have done and will try finish:
Already done code:
Install laravel/passport in our program.
Create an registration API that the client in third party can use to register in our program
With passport, it will return a Bearer token that they need to save in their system (Remove the expi`ration date)
Create a URL that will accept the bearer token that was created during the registration
When the URL is opened, I will be able to use the Auth::user() and get user info.
Trying to accomplish:
$credentials = ([
'email' => 'test#gmail.com',
]);
if (Auth::attempt($credentials)) {
Log::info(Auth::user());
}
From the Auth::user(), get the email address and re-login using the code above
However, when I open another page, the Auth::user() is empty.
My question is:
1 Is this approach okay for a makeshift SSO?
2 Why is the Auth::user() empty on a different url even if I manually login the user?
UPDATE
So I think I will continue with the Laravel Passport as a makeshift SSO on our program. However I want to do a Auth::login() when I pass the token in the header:
Here would be the code:
public function authenticate()
{
$user = Auth::user();
Log::info($user);
. . . . . .
$authuser = User::where('email', '=', $user->email)->first();
$authuser = Auth::login($authuser);
Log::info($authuser);
return $user;
}
The reason why I wish to use Auth::Login here is so that the user is authenticated on all pages. Not just in the first page.
The function authenticate is used when I access the program with the Bearer Token. In the $authuser part, I want to re login the user with just the email. Reason why only email is because of the logic before this. Yes we already consider this one. However when I use the Auth::login($authuser);, it returns Method Illuminate\Auth\RequestGuard::login does not exist error message.
Possible data that you wish to see:
'guards' => [
'web' => [
'driver' => 'session',
'provider' => 'users',
],
'api' => [
'driver' => 'passport',
'provider' => 'users',
'hash' => false,
],
],
api => driver was originally token before the laravel passport was implemented
Is this approach okay for a makeshift SSO?
It might work with Passport, but it's not really the intended use (OAuth2) for it. I found it more confusing than just rolling out a custom solution:
Install JWT auth - https://jwt-auth.readthedocs.io/en/develop/laravel-installation/
Set guard in config/auth.php
I'd look into using a manual JWT auth solution (https://jwt-auth.readthedocs.io/en/develop/laravel-installation/)
2 Why is the Auth::user() empty on a different url even if I manually login the user?
Most likely because the route/controller method is missing an auth middleware that says it's an authenticated.
After trial and errors, I decided to use Laravel Passport and fixed the Auth::login not persisting. For the Auth::login issue, I moved \Illuminate\Session\Middleware\StartSession::class, from $middlewareGroups to $middleware
I have Phonegap's Facebook plugin working perfectly within my Android app. This plugin uses Facebook's client-side Javascript flow. However, I would like to upload photos to FB from the server-side, instead of client-side, as this is much (much) more efficient.
Is it possible to use Facebook's access token generated from client-side on the server-side? (BTW, I have Facebook's PHP SDK working perfectly on stand alone web pages.)
I am getting a "(#200) Permissions Error" trying to pull this off.
If the client-side token cannot be used on the server, any other suggestions? I do not know of a way to generate a server-side Facebook token from a Phonegap app.
Edit:
It appears that you actually can use a client side Facebook token on the server. It's simple matter of upgrading to the latest Facebook PHP SDK, and using the $facebook->setAccessToken() function with a token sent from Javascript.
An access token is valid via client or server. On facebook end it is the same thing: a request through HTTP.
If you are not able to upload photos, check that you have the appropriate user permissions for your application.
If you are trying to figure out what graph api calls you need, a good starting place is the javascript console:
https://developers.facebook.com/tools/console/
Here you can see in the javascript what calls are being made in order to read fromt he api, or make a feed story or upload. You will use the same graph URLS and variables using server side code.
Yes you can use client side token in server side.Actually when user is logged into facebook ,fb create an access token in cookies ,which you can access on both client as well as server and when user is logged out ,cookie is destroyed and access token becomes invalid.
And to upload the photos to facebook i used following code :
// $friends is an array of friend id to be tagged in photo
// $x and $y are the position of tags.
for($i=0;$i<sizeof($friends);$i++)
{
$tags[] = array(
'tag_uid'=>$friends[$i],
'x' => $x[$i],
'y' => $y[$i],
);
}
$photo_details = array(
'access_token'=>$access_token ,
'message'=> 'Any message',
'tags' => $tags
);
$file=$photo_name; //Example image file
$photo_details['image'] = '#' . realpath($file);
$upload_photo = $facebook->api('/me/photos', 'post', $photo_details);
I've got some trouble with Facebook authentication. Even when I'm logged, the function getUser() returns 0
Here's my code :
$fb_params = array(
'appId' => APP_ID,
'secret' => SECRET_ID
);
$fb = new Facebook($fb_params);
echo $fb->getUser(); // UID
Someone's got an idea?
PS : 'I can no long access to $fb->api('/me'), it says it requires an access_token, I think it's linked to the authentication issue...'
Thanks
You are currently not authenticating as a user, only as an application. As a result, the Facebook API can't show you the /me page or respond to a getUser() call since it doesn't know what user you are trying to access the API on behalf of (ie. "Who is /me?"). You will also only be able to access publically-accessible information.
You need to get a user to authenticate your application through Oauth2, store the access_token you are returned, and then include it in any future calls (eg. WIRQjCey1.3600.1309525200.0-509450630|eD6SAR">https://graph.facebook.com/me?access_token=2227470867|2.AQB-_WIRQjCey1.3600.1309525200.0-509450630|eD6SAR...).
To do this using the PHP SDK you can do
$loginUrl = $fb->getLoginUrl();
echo "<a href='$loginUrl'>Login with Facebook</a>";
Clicking that link and having the user authenticate will store the access_token to the $_SESSION, and when you hit refresh the "new Facebook( $fb_params );" constructor will pick out the access token from the $_SESSION and use it for all future calls, so then calls like $fb->getUser(); will return correctly.
There's a functioning example in the examples folder of the SDK, here:
https://github.com/facebook/php-sdk.
You can use it to try calls while authenticated as an application (public data access only), and then as a user.
Problem is, when a user clicks on FB Login button on my site, Facebook API throws him with a window which ask for access permissions(which is the usual flow). But when the user chooses the proxy mail address (anonymous), then I want to force the user to login only using his real email id.
How am I supposed to handle this ?
I can detect that user used proxy email and prevent him from registering, but then I can't remove my app from his list of authorized apps - meaning I can't get him to that initial dialog for choosing which email he will provide.
You can't force the user to go through the authorization dialog again, because as far as Facebook is concerned, the user has installed your application and nothing else needs to happen. The best thing you can do here is write your own form which informs the user that the Facebook proxy e-mail address is unacceptable and you need a real e-mail address. Unfortunately, this does not force the user to give you their Facebook account e-mail address, or even a real e-mail address. This is the best we have via Facebook though, and it's just something we have to deal with.
UPDATE 5/10/11
I was browsing around the Facebook documentation, and found a method that exists in the old Legacy REST API which actually allows you to remove extended permissions for your app from a user. I think you could use this exact API call to manage getting non-proxy addresses from your Facebook user, while still using the native install dialog.
I tested this using the FB JS SDK and it worked! The method you need to use is the auth.revokeExtendedPermission method. Here are 2 examples of calling that method via the JS SDK and the PHP SDK.
Javascript:
<script>
FB.api({
method: 'auth.revokeExtendedPermission',
perm: 'read_stream'
}, function(response)
{
console.log(response)
});
</script>
PHP:
<?php
$facebook->api(array(
'method' => 'auth.revokeExtendedPermission',
'perm' => 'email',
'uid' => $uid
));
Because these use the Legacy REST API they're not as "supported" as the new Graph API. I've not seen anything regarding migrating this feature to the Graph API. Hopefully they will.
Invoke revocation of the app through graph api (as below) with the php sdk then redirect user back through your dialog with this non-proxy requirement explained.
private function revokeAccess() {
$access_token = $this->facebook->getAccessToken();
$result = $this->facebook->api(array(
'method' => 'auth.revokeAuthorization',
'uid' => $this->{facebook id here},
'access_token' => $access_token
));
return $result;
}
This code is php.
This completely removes the app.
Returns 1 on success; 0 on failure.
$this->facebook == facebook object from the facebook php sdk.
I know Omniauth is just for authentication and it doesn't really have FB or Twitter tools included.
However, let's say my Rails 3 app uses Omniauth and I now have some users registered in my system.
How can I then post to their wall? Or do I need some other type of authorization system?
Thanks for any pointers.
I found this link which allowed me to post to both Facebook and Twitter. Very good tutorial:
http://blog.assimov.net/post/2358661274/twitter-integration-with-omniauth-and-devise-on-rails-3
I used this guide while setting up my application to connect to twitter:
http://philsturgeon.co.uk/news/2010/11/using-omniauth-to-make-twitteroauth-api-requests
Helped me a ton, hope it does for you the same.
Original post
Posted: Nov 16, 2010
Using the brilliant user system gem Devise and a gem called OmniAuth you can make a Rails application that logs in or registers users via Twitter, Facebook, Gowalla, etc with amazing ease. But once the user is logged in, how do you go about actually interacting with the API on behalf of the account that has just been authorized?
This article starts where RailsCasts leaves off, so if you are not already up and running with Devise and OmniAuth then you might want to watch:
RailsCast #209: Introducing Devise
RailsCast #235: OmniAuth Part 1
RailsCast #236: OmniAuth Part 2
So, assuming we are all about at the point that the third video ends on, we are all ready to go. I'll be using the example of Twitter but really any of the providers using oAuth will use the same approach. Like in the "ye-olden days" when we used the Twitter username and password to authenticate an API request, we now use a Access Token and Token Secret. You can think of these as being basically the same thing as for the purpose of authenticating API requests, to us, they are.
To get the token and secret you need to add some fields to your authentications table:
rails g migration AddTokenToAuthentications token:string secret:string
rake db:migrate
Now the database is ready to save the credentials we can change the authentication code to populate the fields. Assuming you placed the method in user.rb like RailsCast #236 suggested then open user.rb and modify the following line:
authentications.build(:provider => omniauth['provider'], :uid => omniauth['uid'])
and replace it with:
authentications.build(
:provider => omniauth['provider'],
:uid => omniauth['uid'],
:token => omniauth['credentials']['token'],
:secret => omniauth['credentials']['secret']
)
Now whenever anybody authenticates their account we can save their credentials which are passed back from the internal hidden magic that is OmniAuth.
The next step is to actually make some requests using these saved credentials, which is described almost perfectly in the Twitter Developer Documentation. You'll want to install the oauth gem (put it in your Gemfile and run bundle install) then you can use the following code to test-dump a list of tweets from the user:
class TwitterController < ApplicationController
def recent_tweets
# Exchange your oauth_token and oauth_token_secret for an AccessToken instance.
def prepare_access_token(oauth_token, oauth_token_secret)
consumer = OAuth::Consumer.new("APIKey", "APISecret"
{ :site => "http://api.twitter.com"
})
# now create the access token object from passed values
token_hash = { :oauth_token => oauth_token,
:oauth_token_secret => oauth_token_secret
}
access_token = OAuth::AccessToken.from_hash(consumer, token_hash )
return access_token
end
auth = current_user.authentications.find(:first, :conditions => { :provider => 'twitter' })
# Exchange our oauth_token and oauth_token secret for the AccessToken instance.
access_token = prepare_access_token(auth['token'], auth['secret'])
# use the access token as an agent to get the home timeline
response = access_token.request(:get, "http://api.twitter.com/1/statuses/user_timeline.json")
render :json => response.body
end
end
By pulling the content from current_user.authentications (im finding the first as in my application they should only have one) I can grab the credentials and have full permissions to get their recent tweets, post new ones, see friends tweets, etc.
Now I can tweak this, get stuff saved, faff with the JSON and take what I need. Working with Facebook or any other oAuth provider will work in an almost identical way, or you can install specific gems to interact with their API's if the direct approach is not as smooth as you'd like.
end of original post