Apache Shiro Authentication with Facebook OAuth - facebook

I am stucked in authenticating my application running on Shiro with Facebook OAuth. I really don't know what am I doing wrong. Bascially, my problem is when I get a "code" from Facebook. I want shiro to authenticate it using that code.
This is my authentication code.
FacebookToken token = null;
try{
org.apache.shiro.subject.Subject currentUser = SecurityUtils.getSubject();
//currentUser.logout();
//This is done to avoid temporary multiple url hit.., when the user is not logged out
token = new FacebookToken(code);
currentUser.login(token); //returns true if valid
result = true;
}catch (UnknownAccountException uae) {
log.info("There is no user with username of " + token.getPrincipal());
} catch (IncorrectCredentialsException ice) {
log.info("Password for account " + token.getPrincipal() + " was incorrect!");
} catch (LockedAccountException lae) {
log.info("The account for username " + token.getPrincipal() + " is locked. " +
"Please contact your administrator to unlock it.");
}
// ... catch more exceptions here (maybe custom ones specific to your application?
catch (AuthenticationException ae) {
log.info("Authentication exception Here.");
}
Here is my facebook token class:
public class FacebookToken implements AuthenticationToken {
private static final long serialVersionUID = 1L;
private String code;
public FacebookToken(){
}
public FacebookToken(String code){
this.code = code;
}
public Object getCredentials() {
return null; //Credentials are handled by facebook
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
public Object getPrincipal() {
return null; //Not known facebook does the login
}
I have the realm for facebook that is extending authorization realms.
public class FacebookRealm extends AuthorizingRealm {
}
and finally here is my shiro.ini file:
[main]
#authc.loginUrl = /login
#authc.successUrl = /hello
#logout.redirectUrl = /hello
# ------------------------
# Database
# Own Realm
jdbcRealm = com.shiro.common.controller.MyCustomRealm
facebookRealm = com.facebook.login.FacebookRealm
# Sha256
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
# base64 encoding, not hex in this example:
sha256Matcher.storedCredentialsHexEncoded = false
sha256Matcher.hashIterations = 1024
#Facebook Credential matcher
fbCredentialsMatcher = com.facebook.login.FacebookCredentialsMatcher
jdbcRealm.credentialsMatcher = $sha256Matcher
facebookRealm.credentialsMatcher = $fbCredentialsMatcher
# User Query
# default is "select password from users where username = ?"
jdbcRealm.authenticationQuery = SELECT password, salt FROM User WHERE email = ?
# permissions
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.userRolesQuery = select roleName from UserRole where email = ?
jdbcRealm.permissionsQuery = select permission from RolesPermission where roleName = ?
# Connection
ds = com.mysql.jdbc.jdbc2.optional.MysqlDataSource
ds.serverName = localhost
ds.user = root
ds.password = root123
ds.databaseName = testdb
jdbcRealm.dataSource=$ds
#authc.usernameParam = email
#authc.passwordParam = password
#authc.failureKeyAttribute = shiroLoginFailure
# Use Built-in Chache Manager
builtInCacheManager = org.apache.shiro.cache.MemoryConstrainedCacheManager
securityManager.cacheManager = $builtInCacheManager
#securityManager.realms = $facebookRealm,$jdbcRealm
securityManager.realms = $facebookRealm
# -----------------------------------------------------------------------------
[urls]
#/hello = authc
#/login = authc
#/admin.jsp = authc, perms["admin:access"]
Now when do i debug and reach at currentuser.login methods and go inside, it throws an exception saying
Realm [FacebookRealm#52039826] does not support authentication token [FacebookToken#132d9844]. Please ensure that the appropriate Realm implementation is configured correctly or that the realm accepts AuthenticationTokens of this type.
Please suggest me whether am I doing correct, or not !! Am i missing any configuration or any thing else. Thank you !!

You should extend your FacebookRealm with the following method:
#Override
public boolean supports(AuthenticationToken token) {
return token instanceof FacebookToken;
}
or add the following line to your ini:
facebookRealm.authenticationTokenClass=<realpackage>.FacebookToken

Related

How to toggle disable/enable of shiro authentication?

Is there a way to toggle on-off shiro authentication in my REST service?
I am currently using dropwizard with shiro for my REST services?
Also how can I confirm shiro is disabled?
I also have a shiro.ini file but is there any setting that I can include to toggle shiro authentication?
#Override
protected ShiroConfiguration narrow(T configuration) {
return configuration.getShiroConfiguration();
}
#Override
protected Filter createFilter(T configuration) {
IniWebEnvironment shiroEnv = new IniWebEnvironment();
shiroEnv.setConfigLocations(this.narrow(configuration).iniConfigs());
shiroEnv.init();
return new AbstractShiroFilter() {
public void init() throws Exception {
Collection<Realm> realms = MyShiroBundle.this.createRealms(configuration);
DefaultWebSecurityManager webSecurityManager =
(DefaultWebSecurityManager)shiroEnv.getWebSecurityManager();
if (webSecurityManager.getRealms() != null) {
webSecurityManager.setRealms(mergeRealms(webSecurityManager.getRealms(), realms));
} else {
webSecurityManager.setRealms(realms);
}
this.setSecurityManager(webSecurityManager);
this.setFilterChainResolver(shiroEnv.getFilterChainResolver());
}
};
}
#Override
protected Collection<Realm> createRealms(T configuration) {
return mergeRealms(super.createRealms(configuration), Collections.singleton(createJdbcRealm(configuration)));
}
private static Collection<Realm> mergeRealms(Collection<Realm> realms, Collection<Realm> realms2) {
return ImmutableList.<Realm>builder().addAll(realms).addAll(realms2).build();
}
private static Realm createJdbcRealm(ReferenceDataConfiguration configuration) {
DataSource ds = configuration.getMyDatabaseDataSourceFactory().build(new MetricRegistry(), "my-shiro");
JdbcRealm realm = new JdbcRealm();
realm.setDataSource(ds);
realm.setPermissionsLookupEnabled(false);
realm.setCredentialsMatcher(new AllowAllCredentialsMatcher());
realm.setAuthenticationQuery(
"select UserId from AppUser where IsActive = 1 and UserId = ?");
realm.setUserRolesQuery("select Role from UserRoles where UserId = ? and ValidToTime = " + VALID_TIME);
realm.setPermissionsQuery(
"select Permission from RolesPermissions where Role = ? and ValidToTime = " + VALID_TIME);
return realm;
}

How to get user email, public_profile from facebook access_token

I'm building a windows phone 8.1 app that allow user to login facebook account.
There is my code:
string productId = "myproductid";
string facebookAppId = "myfacebookappid";
string redirectUri = "msft-"+productId+"://authorize";
string scope = "public_profile,email"; // What you want to fetch from the facebook user
string responseType = "token"; // Other response types possible
UriBuilder authUri = new UriBuilder("https://www.facebook.com/dialog/oauth");
authUri.Query = "client_id=" + facebookAppId + "&redirect_uri=" + redirectUri + "&scope=" + scope + "&response_type=" + responseType;
var success = await Windows.System.Launcher.LaunchUriAsync(uriToLaunch);
And code in App.xaml.cs:
protected override void OnActivated(IActivatedEventArgs args)
{
base.OnActivated(args);
if (args.Kind == ActivationKind.WebAuthenticationBrokerContinuation)
{
App.MobileService.LoginComplete(args as WebAuthenticationBrokerContinuationEventArgs);
}
if (args.Kind == ActivationKind.Protocol)
{
ProtocolActivatedEventArgs eventArgs = args as ProtocolActivatedEventArgs;
WwwFormUrlDecoder decoder = new WwwFormUrlDecoder(eventArgs.Uri.Fragment);
string fbAccessToken = decoder.GetFirstValueByName("#access_token");
/* Now you can use the access token to interact with the Facebook API */
}
}
But how to get user's email and profile from "fbAccessToken"?
Thanks.
You can use the graph APIs of facebook
graph.facebook.com/me?access_token={0}
Please refer this link - https://developers.facebook.com/docs/graph-api/using-graph-api/v2.3
Add scope/Extended Permission to get user profile etc.,
string scope ="user_about_me,read_stream,publish_actions,user_birthday,offline_access,email"

Apache shiro remember me not working

Im trying to use the rememberme feature from apache shiro, but its not working.
I have this shiro.ini
[main]
ds = org.apache.shiro.jndi.JndiObjectFactory
ds.requiredType = javax.sql.DataSource
ds.resourceName = java:/comp/env/jdbc/myDS
# JDBC realm config
jdbcRealm = br.com.myproject.web.service.security.JdbcRealmImpl
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.authenticationQuery = SELECT password FROM user WHERE username = ? AND status = 1
jdbcRealm.dataSource = $ds
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
jdbcRealm.credentialsMatcher = $sha256Matcher
securityManager.realms = $jdbcRealm
[urls]
/** = authcBasic
This is my JdbcRealmImpl:
public class JdbcRealmImpl extends JdbcRealm {
public JdbcRealmImpl() {
super();
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(
final AuthenticationToken token) throws AuthenticationException {
final AuthenticationInfo info = super.doGetAuthenticationInfo(token);
final UserDB userDB = new UserDB();
final User user = userDB.getUserByUsername((String) token.getPrincipal());
return new SimpleAuthenticationInfo(user, info.getCredentials(), getName());
}
}
Since this is a web service project i have a login service:
#POST
#Path("/login")
public Response login(#FormParam("username") final String username, #FormParam("password") final String password, #FormParam("remember") final boolean remember) {
final Subject currentUser = SecurityUtils.getSubject();
if (!currentUser.isAuthenticated()) {
final UsernamePasswordToken token = new UsernamePasswordToken(username, password);
try {
token.setRememberMe(remember);
currentUser.login(token);
} catch (final AuthenticationException e) {
return Response.status(Status.BAD_REQUEST).entity("Invalid user").build();
}
}
return Response.ok().build();
}
The problem is that SecurityUtils.getSubject().isRemembered() always return false even when i set token.setRememberMe(true);
Is there any configuration that im missing?
Subject.isRemembered() is a little tricky in Shiro. It only returns true if the Subject has a valid Remember Me setting (cookie, etc) AND the Subject is not Authenticated. Details here: http://shiro.apache.org/static/1.2.2/apidocs/org/apache/shiro/subject/Subject.html#isRemembered()
So, I suspect that your Remember Me is working fine, but your expectations for Subject.isRemembered() doesn't match what the method actually does.
Actually if you logout from your application throught shiro logout remember me will be erased. To try make session time out just one minute let session expire itself and reload main page now you will find that user is actually being remembered. LOGOUT CLEARS REMEMBER ME.
If you want to use remember me still after logout you can try to extend securitymanager and use this securitymanger for your application.
public class CustomSecurityManager extends DefaultWebSecurityManager {
#Override
protected void beforeLogout(Subject subject)
{
super.removeRequestIdentity(subject);
}
}

Add information to the subject on apache shiro

Im using apache shiro. When i want to know if the user have permissions and roles i use SecutiryUtils.getSubject(). I like to know how to add more information to the subject like email, primary key and any other business information that i need so i can retrieve that information when necessary.
This is my shiro.ini:
[main]
ds = org.apache.shiro.jndi.JndiObjectFactory
ds.requiredType = javax.sql.DataSource
ds.resourceName = java:/comp/env/jdbc/myDS
# JDBC realm config
jdbcRealm = com.mycompany.JdbcRealmImpl
jdbcRealm.permissionsLookupEnabled = true
jdbcRealm.authenticationQuery = SELECT password FROM user WHERE username = ? AND status = 1
jdbcRealm.dataSource = $ds
sha256Matcher = org.apache.shiro.authc.credential.Sha256CredentialsMatcher
jdbcRealm.credentialsMatcher = $sha256Matcher
[urls]
/logout = logout
/** = authcBasic
This is my JdbcRealm
public class JdbcRealmImpl extends JdbcRealm {
public JdbcRealmImpl() {
super();
}
#Override
protected AuthenticationInfo doGetAuthenticationInfo(
final AuthenticationToken token) throws AuthenticationException {
final AuthenticationInfo info = super.doGetAuthenticationInfo(token);
// create a user to test
final User user = new User();
user.setId(11111);
return new SimpleAuthenticationInfo(user, info.getCredentials(),
getName());
}
}
And here is the code where i try to retrieve the user info.
final Subject currentUser = SecurityUtils.getSubject();
final User user = (User) currentUser.getPrincipal();
// null
System.out.println(user);
You should just put that in a database and retrieve it using the Subjects username (for example an emailaddress).

Facebook Integration Session Issue

Here is my UserLogin.aspx Page code, which contains authentication of Facebook user, the problem is i am maintaing token in session which i laterly use it on Default.aspx. if you see the line Session["facebook_token"] = authToken;
On my Default.aspx page load i have placed this code, but it gives me null value, when i go to home or default page.
if (Session["facebook_token"] != null)
lblUserName.Text = Session["facebook_token"].ToString();
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;
using SocialMediaModel;
using Facebook.Components;
public partial class UserLogin : System.Web.UI.Page
{
FacebookService _fbService = new FacebookService();
private const string FACEBOOK_API_KEY = "551810942508804";
private const string FACEBOOK_SECRET = "b63b8cca428b42935e6eab59976367b1";
protected void Page_Load(object sender, EventArgs e)
{
// ApplicationKey and Secret are acquired when you sign up for
_fbService.ApplicationKey = FACEBOOK_API_KEY;
_fbService.Secret = FACEBOOK_SECRET;
_fbService.IsDesktopApplication = false;
string sessionKey = Session["facebook_session_key"] as String;
string userId = Session["facebook_userId"] as String;
string authToken = Request.QueryString["auth_token"];
Session["facebook_token"] = authToken;
// We have already established a session on behalf of this user
if (!String.IsNullOrEmpty(sessionKey))
{
_fbService.SessionKey = sessionKey;
_fbService.UserId = userId;
}
// This will be executed when facebook login redirects to our page
else if (!String.IsNullOrEmpty(authToken))
{
_fbService.CreateSession(authToken);
Session["facebook_session_key"] = _fbService.SessionKey;
Session["facebook_userId"] = _fbService.UserId;
Session["facebook_session_expires"] = _fbService.SessionExpires;
}
// Need to login
else
{
Response.Redirect(#"http://www.facebook.com/login.php?api_key=" +
_fbService.ApplicationKey + #"&v=1.0");
}
}
}
string authToken = Request.QueryString["auth_token"];
Session["facebook_token"] = authToken;
Looks like you are executing these lines on every page load(?) – without checking if there even is a GET parameter by this name.