What's the proper way to handle insufficient permissions with the Facebook Authentication for Spring Security plugin? - facebook

I'm using the Spring Security 2.0-RC4 and the Facebook Authentication for Spring Security 0.15.2-CORE2 to allow users to authenticate with a Facebook login. I request the extra FB permission "email" since I use email as the primary key for my User class, so if the email permission is unselected by the user I need to abort login. Currently I check for null email in my FacebookAuthService.create() and return null if email was not set.
In the normal case everything works fine. Login succeeds, I get new User and FacebookUser records created and my User object's email property is updated with the Facebook user's email address. However, if the user elects to remove email permission during login then I run into problems.
In my FacebookAuthService.create() I check if email was returned (similar to in this question), and if not return null to abort the authentication process:
FacebookUser create(FacebookAuthToken token) {
Facebook facebook
FacebookProfile fbProfile
try {
facebook = new FacebookTemplate(token.accessToken.accessToken)
fbProfile = facebook.userOperations().userProfile
} catch (org.springframework.social.ApiException apiex) {
return null
}
String email = fbProfile.email
if (!email) {
return null
}
...
When email is null and I return null my security state seems to be messed up. I have a beforeInterceptor on my controller which gets called after I return null from create():
def beforeInterceptor = {
def user = springSecurityService.currentUser
log.trace("${user?.email} - End action ${controllerName}Controller.${actionName}() : returns $model, view ${modelAndView?.viewName}")
}
getCurrentUser() should be returning null but instead it throws an exception:
org.codehaus.groovy.grails.web.errors.GrailsExceptionResolver - NullPointerException occurred when processing request: [GET] /rar/user/home
Cannot get property 'username' on null object. Stacktrace follows:
java.lang.NullPointerException: Cannot get property 'username' on null object
at org.codehaus.groovy.runtime.NullObject.getProperty(NullObject.java:56)
at org.codehaus.groovy.runtime.InvokerHelper.getProperty(InvokerHelper.java:169)
at org.codehaus.groovy.runtime.callsite.NullCallSite.getProperty(NullCallSite.java:44)
at org.codehaus.groovy.runtime.callsite.AbstractCallSite.callGetProperty(AbstractCallSite.java:227)
at grails.plugin.springsecurity.SpringSecurityService.getCurrentUser(SpringSecurityService.groovy:87)
at grails.plugin.springsecurity.SpringSecurityService$$FastClassBySpringCGLIB$$6e53ab8e.invoke(<generated>)
...
It seems that SpringSecurityService thinks that someone is logged in because getCurrentUser()'s call to isLoggedIn() returns true, which causes an exception later when principal is null and a property is accessed on the null principal object.
Should I be aborting the Facebook login process in a different way instead of returning null from FacebookAuthService.create()?

Solved by throwing an exception from FacebookAuthService.create() rather than returning null when email permission has been removed.
My create() now includes this code segment:
if (!email) {
def grailsWebRequest = WebUtils.retrieveGrailsWebRequest()
def flash = grailsWebRequest.flashScope
flash.message = 'Login to Facebook failed. We must have access to your email address in order to proceed with login.'
throw new InsufficientAuthenticationException()
}

Related

laravel 5.2 redirect after registration by id

I've found here some examples, but they are not answering how to redirect registered user to his own profile by id.
protected $redirectPath = '/profile/view/'.$user->id; Did not help.
I have a project where users are being authorized without email confirmation and they are sent to /home after registration.
Route to user profile is: /profile/view/id (id is unique of course).
I managed to send them there after login (in AuthController :
public function authenticated($request,$user)
{
return redirect('/profile/view/'.$user->id);
}
but redirect to profile after registration I can't handle.
Approach 1.
Let users view their own profile without ID.
route (make ID optional):
Route::get('profile/view/{id?}', ...);
controller:
public function view($id = null) {
if (is_null($id) { //set id of currently signed in user if id == null
$id = Auth::user()->id;
}
//continue as before
}
Approach 2.
Modify routes so redirect happens to correct url.
Note: order of routes is important
Route::get('profile/view', function() {
return redirect()->route('profile.view', ['id' => Auth::user()->id]);
});
Route::get('profile/view/{id}', ...)->name('profile.view');
Note: in both approaches auth middleware is a must, else you going to get error if user is not logged in (PHP error: Trying to get property of non-object on line X)
With both approaches you just redirect user to profile/view:
is shown profile (without ID in URL)
is redirected to proper url profile/view/ID.

Why does the Spring Social plugin occasionally return an empty email on the User class?

I have a Grails project (v2.4.2) that is making use of the spring-security-facebook:0.17 plugin to authenticate via Spring Security. At first sight, all seems well. However, there is a large set of users that for some unknown reason I cannot access their email address. I am using spring social to grab the email. I have permission and it is set in the scope. Here is a code snippet where I authenticate a new user:
log.info("Create domain for facebook user $token.uid")
//Use Spring Social Facebook to load details for current user from Facebook API
log.info("create: FacebookAuthToken: $token")
log.info("created FacebookAuthToken.FacebookAccessToken = ${token.accessToken}")
Facebook facebook = new FacebookTemplate(token.accessToken.accessToken)
org.springframework.social.facebook.api.User fbProfile = facebook.userOperations().getUserProfile()
// Check if email is actual granted because in production some are coming back null
boolean isEmailGranted=false
List<Permission> permissions = facebook?.userOperations()?.getUserPermissions()
String permissionString = "["
for (int i=0;i<permissions.size();i++) {
permissionString += "["+ permissions[i].getName() + ":" + permissions[i].getStatus()+"]"
if (permissions[i].getName()=="email" && permissions[i].isGranted())
isEmailGranted=true
}
permissionString += "]"
log.info("create: Facebook Permissions = " + permissionString)
def grailsWebRequest = WebUtils.retrieveGrailsWebRequest()
def flash = grailsWebRequest.flashScope
if (!isEmailGranted) {
log.warn("create: Unable to subscribe facebook user because email priviledge was not granted.")
flash.message = 'Login to Facebook failed. We must have access to your email address in order to proceed with login.'
throw new InsufficientAuthenticationException("Facebook email not accessible")
}
log.info("created: ")
String email = fbProfile.getEmail()
String firstName = fbProfile.getFirstName()
String lastName = fbProfile.getLastName()
String fullName = fbProfile.getName()
String username = firstName
String password = token.accessToken.accessToken
if (!email) {
log.error("create: Permission was granted to use facebook email but the value is null.")
flash.message = 'Login to Facebook failed. We are temporarily unable to access your email although permission has been granted'
throw new InsufficientAuthenticationException("Facebook email not accessible for unknown reason")
}
Why would I receive an empty email when permission has been granted? Is there a preferred method for handling this behavior (other than failing the authentication and making up a fake email address). Many thanks!
The documentation for the 'email' field of the 'user' object ( https://developers.facebook.com/docs/reference/api/user/ ) clarifies the expected behaviour here, which is:
"this field will not be returned if no valid email address is available"
There is a detailed explanation about different situations where an email won't be sent. Please check it out:
https://developers.facebook.com/bugs/298946933534016/
I hope it helps.

CakePHP 2.3.1 Facebook authentication

I'm using CakePHP 2.3.1 and this plugin: https://github.com/webtechnick/CakePHP-Facebook-Plugin to implement a facebook authentication. I followed this screen cast http://tv.cakephp.org/video/webtechnick/2011/01/12/nick_baker_--_facebook_integration_with_cakephp but it doesn't work, I can't receive the Facebook user information. I mean $this->Connect->user() always return null.
First make sure your user as granted access to your app. Then make a call to retrieve personal details through the api(/me) => $this->Connect->user()
Personally, I use this
public function beforeFilter(){
if($this->Connect->FB->getUser() != 0){
$this->set('facebookUser', $this->Connect->user());
}
Initiating ConnectComponent will populate (or not) the Auth Session. If no id is to be found, redirect user to a login dialog.
Call this method from beforefilter..and save the post data received in some variable like fb_data here.
protected function _valdiateFbRequest() {
if (!isset($this->request->data['signed_request'])) {
// not a valid request from fb
// throw exception or handle however you want
return;
}
$this->signedRequest = $this->request->data['signed_request'];
$this->fb_data=$this->Connect->registrationData(); //save fb data
unset($this->request->data['signed_request']);
if (empty($this->request->data)) {
$this->Security->csrfCheck = false;
}
// validate the request
}

ServiceStack OAuth - registration instead login

In servicestack OAuth implementation I only saw possibility to automatically login with eg. facebook account.
But is there abbility to support registration process with facebook login. What I wanted is to let users login to facebook app, and then load their Name, Surname and email and prefill needed text boxes for real registration on my site (since I also have to have mobile phone verification etc.) I don't want user to be authorized and authenticated when he logs in with facebook. Only credentials login should be valid one for full site access.
Edit: I found a solution.
In FacebookProvider.cs
public override bool IsAuthorized(IAuthSession session, IOAuthTokens tokens, Auth request = null)
{
if (request != null)
{
if (!LoginMatchesSession(session, request.UserName)) return false;
}
return tokens != null && session.UserName!=null && !string.IsNullOrEmpty(tokens.AccessTokenSecret);
}
The catch was the && session.UserName!=null part. So we can check if user is logged in using credentials, this will be !=null and user can use all services. If not, this will be ==null and he can only get facebook info from session.
The SocialBootstrap API project shows an example of handling the callback after a successful Authentication by overriding the OnAuthenticated() hook of its custom user session:
I've pulled out, rewrote some and highlighted some of the important bits:
public class CustomUserSession : AuthUserSession
{
public override void OnAuthenticated(IServiceBase authService,
IAuthSession session,
IOAuthTokens tokens,
Dictionary<string, string> authInfo)
{
base.OnAuthenticated(authService, session, tokens, authInfo);
//Populate matching fields from this session into your own MyUserTable
var user = session.TranslateTo<MyUserTable>();
user.Id = int.Parse(session.UserAuthId);
user.GravatarImageUrl64 = CreateGravatarUrl(session.Email, 64);
foreach (var authToken in session.ProviderOAuthAccess)
{
if (authToken.Provider == FacebookAuthProvider.Name)
{
user.FacebookName = authToken.DisplayName;
user.FacebookFirstName = authToken.FirstName;
user.FacebookLastName = authToken.LastName;
user.FacebookEmail = authToken.Email;
}
else if (authToken.Provider == TwitterAuthProvider.Name)
{
user.TwitterName = authToken.DisplayName;
}
}
//Resolve the DbFactory from the IOC and persist the user info
using (var db = authService.TryResolve<IDbConnectionFactory>().Open())
{
//Update (if exists) or insert populated data into 'MyUserTable'
db.Save(user);
}
}
//Change `IsAuthorized` to only verify users authenticated with Credentials
public override bool IsAuthorized(string provider)
{
if (provider != AuthService.CredentialsProvider) return false;
return base.IsAuthorized(provider);
}
}
Basically this user-defined custom logic (which gets fired after every successful authentication) extracts data from the UserSession and stores it in a custom 'MyUserTable'.
We've also overridden the meaning of IsAuthorized to only accept users that have authenticated with CredentialsAuth.
You can use this data to complete the rest of the registration.
Other possible customizations
ServiceStack's built-in Auth persists the AuthData and populates the Session automatically for you. If you want to add extra validation assertions you can simply use your own custom [Authentication] attribute instead containing additional custom logic. Look at the implementation of the built-in AuthenticateAttribute as a guide.

spring security problem using grails

I am new to grails and spring security plugin core , but i am facing a problem of
ERROR GrailsExceptionResolver - a different object with the same identifier value was already associated with the session: [nayax.SecUserSecRole#nayax.SecUserSecRole : null]
my code is :
NayaxUser populateDataFromJson(def user) {
//todo: HANDLE CASES OF CREATING ROLES FOR USER AND REAUTHENTICATE THEM
println "#### In FUNCTION ####"
Facebook existingProfile = Facebook.findByFid(user.id)
if (existingProfile) {
println "### User already present in the database ####"
existingProfile.setUser(existingProfile.user)
//todo: CREATE ROLE AND REAUTHENTICATE USER
SecRole secRole1 = SecRole.findByAuthority(SecRoleConstants.ROLE_USER)
SecUserSecRole.create(existingProfile.user, secRole1)
//todo: REAUTHENTICATE USER
springSecurityService.reauthenticate(existingProfile.user.username)
existingProfile.user.merge()
return existingProfile.user
}
else {
Facebook facebookObj = new Facebook(fid: user.id, lastLogin: new Date(), creationDate: new Date()).save(flush: true)
NayaxUser nayaxUser = new NayaxUser(facebookUrl: user.link, fullName: user.name, facebook: facebookObj, username: user.email, password: springSecurityService.encodePassword("pleaseChangeMe"), enabled: true)
if (nayaxUser.save(flush: true)) {
println "### WORK DONE , saved user and save the user in session ###"
facebookObj.setUser(nayaxUser)
//todo: CREATE ROLE AND REAUTHENTICATE USER
SecRole secRole = SecRole.findByAuthority(SecRoleConstants.ROLE_USER)
SecUserSecRole.create(nayaxUser, secRole)
//todo: REAUTHENTICATE USER
springSecurityService.reauthenticate(nayaxUser.username)
nayaxUser.merge()
return nayaxUser
}
else {
println "### ERROR IN VALIDATING DATA So NOT SETTING THE USER IN SESSION ####"
nayaxUser.errors.allErrors.each { error ->
println("### ERROR IS ${error} ####")
}
return nayaxUser
}
}
}
Actually , when i am loggin in from the facebook button and then logging out from my implementation and then logging back again quicky then there is an exception but after logging out i refresh the page a few times the problem disappears. I think something is wrong with my facebook logout implementation or is there something in the code ???
Any suggestions are welcome..
Try to use facebook javascript api for logout before call your logout controller to clear session and etc e.g:
if facebook user exists
logout facebook user with fb js api
js redirect to logout controller
else
redirect user to logout controller
This logic should be applied in the frontend of your application assuming that if there isn't facebook user, user will be redirected directly to the logout controller.
I have problems with facebook logout with java and php api from the fb application but in that way I overcome those problems.