spring security problem using grails - facebook

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.

Related

How to achieve Single Logout using Thinktecture Identity Server3 and OpenId owin middleware for multiple ASP.NET MVC applications

I have 2 ASP.NET MVC applications and I am using the OpenID middleware UseOpenIdConnectAuthentication to do the single sign on to both. Configuration is described as below. SSO works great. I login from application 1 and then I check if I am logged into application 2 too that connects to idserver, I can see myself logged in. Both the client applications now have the same id_token and token.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
SignInAsAuthenticationType = "Cookie",
Authority = "<my-idserver-url>",
ClientId = "client1",
RedirectUri = "https://localhost:44360",
PostLogoutRedirectUri = "https://localhost:44360",
ResponseType = "id_token token",
//scopes from config file
Scope = ConfigurationManager.AppSettings.Get("Scope"),
Notifications = new OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
var claims = n.AuthenticationTicket.Identity.Claims;
var accepted_claims = new[]{"name", "tenant", "email", "sub"};
var new_claims = claims.Where(x => accepted_claims.Contains(x.Type)).ToList();
new_claims.Add(new Claim("id_token", n.ProtocolMessage.IdToken));
new_claims.Add(new Claim("access_token", n.ProtocolMessage.AccessToken));
var ci = new ClaimsIdentity(new_claims, n.AuthenticationTicket.Identity.AuthenticationType, "email", "role");
n.AuthenticationTicket = new AuthenticationTicket(ci, n.AuthenticationTicket.Properties);
return Task.FromResult(0);
}
On clicking logout in the app, when the RedirectToIdentityProvider method is invoked in the clients, I am setting the id_token as the IdTokenHint as below:
RedirectToIdentityProvider = n =>
{
if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnectRequestType.LogoutRequest)
{
var user = n.OwinContext.Authentication.User;
if (user != null && user.Identity.IsAuthenticated)
{
var id_token_claim = user.Claims.FirstOrDefault(x => x.Type == "id_token");
if (id_token_claim != null)
{
n.ProtocolMessage.IdTokenHint = id_token_claim.Value;
}
}
}
return Task.FromResult(0);
}
Problem area > Single Logout:
Success Scenario:
When I logout from the application 1, it redirects to idserver and logs me out and redirects me back to application 1. On debugging I can see that the signout message in the logout method of the DefaultViewService is not null in this case. This works great.
Failure Scenario:
The problem is when I try to logout from application 2 instead of application 1(remember that I logged in from application 1), it does take me to the logout page at idserver, and I can logout. But it does not redirect back to the application 2 and stays on the idserver logged out page. On debugging I can see that the signout message in the logout method of the DefaultViewService is null in this case.
Is it probably because the id_token was originally issued to the application1?
What am I missing?
Do I need to handle this kind of scenario and somehow get hold of the client in identity server, that the user used, to actually logout(application 2)? and then probably issue a new SignoutMessage and put in the postlogout redirect Url for that client?
Or I got it all wrong and this should work automatically?
Update:
A blunder was to try this out with everything on IISExpress localhost on different ports, both clients(locahost, different ports) somehow get the same id_token and access token which shouldn't happen.
Moving out to the IIS hosting different sites solved this and now the applications can get their own individual id_token and access token.
I am still working on the Single Logout, exploring the iFrame approach.
I've been struggling on this myself for a few days. Googling pointed me to your question :)
This is what I've done which worked for me (Not sure if this is the correct approach):
Added both Client Uris in the PostLogoutRedirectUris for each client configuration:
PostLogoutRedirectUris = new List<string>()
{
"http://localhost:28560/",
"http://localhost:57311/"
}

Laravel Socialite with Facebook not logging in

I'm following the documentation exactly.
https://github.com/laravel/socialite and https://laravel.com/docs/5.1/authentication#social-authentication
I've created my app on Facebook and got everything working. When I click my log in with Facebook button, it authorizes the app and takes me back to my site.
However, it doesn't show me as logged in. If I dd() instead of the redirect below, I get all of the data from my Facebook account. But the pages that are only visible to logged in users, aren't visible.
Here is my controller:
public function redirectToProvider()
{
return Socialite::driver('facebook')->redirect();
}
public function handleProviderCallback()
{
$user = Socialite::driver('facebook')->user();
return redirect('my-profile')
->with('message', 'You have signed in with Facebook.');
}
Here are my routes:
Route::get('login/facebook', 'Auth\AuthController#redirectToProvider');
Route::get('login/facebook/callback', 'Auth\AuthController#handleProviderCallback');
Socialite is installed properly in composer.json. The classes are in config/app.php and the IDs for my FB app are in config/services.php.
Any ideas as to why it's not working?
In the handleProviderCallback method you need to create and authenticate the user returned by the driver.
Create the user if doesn't exist:
$userModel = User::firstOrNew(['email' => $user->getEmail()]);
if (!$userModel->id) {
$userModel->fill([.....]);
$userModel->save();
}
Then authenticate the user:
Auth::login($userModel);
Your method will look like this:
public function handleProviderCallback() {
$user = Socialite::driver('facebook')->user();
$userModel = User::firstOrNew(['email' => $user->getEmail()]);
if (!$userModel->id) {
$userModel->fill([.....]);//Fill the user model with your data
$userModel->save();
}
Auth::login($userModel);
return redirect('my-profile')
->with('message', 'You have signed in with Facebook.');
}

Spring Security and social SignUp with Rest

My application exposes a REST API for services and uses SpringSecurity to manage login at the private services.
With custom signup and login I don't have any kind of problem, but now I try to implement login/signup with Facebook or Twitter, and I don't know how to do this.
Has anyone had the same problem and solved it?
I tried to use a custom password "very long" for every Facebook and Twitter account but that didn't work.
UPDATE
I try your solution, but get an error. This is my code
public UserDetails loadUserByUsername(String mail) throws UsernameNotFoundException {
ServletRequestAttributes attr = (ServletRequestAttributes) RequestContextHolder.currentRequestAttributes();
if (ConstantPWCabinet.SOCIAL_LOGIN_FACEBOOK.equalsIgnoreCase(attr.getRequest().getParameter(ConstantPWCabinet.LOGIN_TYPE))) {
User facebookInfo = dao.getFacebookInfo(new FacebookTemplate(attr.getRequest().getParameter(ConstantPWCabinet.FACEBOOK_TOKEN)));
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
authorities.add(new SimpleGrantedAuthority(Role.ROLE_USER_FACEBOOK.toString()));
org.springframework.security.core.userdetails.User user = new org.springframework.security.core.userdetails.User(facebookInfo.getEmail(), null, authorities);
Authentication auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
return user;
}
logger.debug("Mail di accesso: " + mail);
User user = dao.getUserSelectedMail(mail);
List<GrantedAuthority> authorities = new ArrayList<GrantedAuthority>();
String role = user.getRole().toString();
if (StringUtils.isEmpty(role))
authorities.add(new SimpleGrantedAuthority(Role.ROLE_USER.toString()));
else
authorities.add(new SimpleGrantedAuthority(role));
return new org.springframework.security.core.userdetails.User(user.getEmail(), user.getPassword(), authorities);
}
But i get and "Bad credential" and no get login.
You have an AuthenticationFilter that listens to url j_spring_security_check
Filter creates an authentication object and sends to Authentication Provider.
AuthenticationProvider calls UserDetailsService to load user by username and authenticate the user.
Filter then checks the authentication object returned by the provider and sends request to success/failure handler.
When you do it through social medium, your user is authenticated by an external source, so you do not need to authenticate user at your end.
You can simple do
// Authenticate the user
UserDetails user = userDetailsService.loadUserByUsername(username);
Authentication auth = new UsernamePasswordAuthenticationToken(user, null, user.getAuthorities());
SecurityContextHolder.getContext().setAuthentication(auth);
This will authenticate the user without password.

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

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()
}

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
}