Hi Kalle, The link you provided me doesn't seem to be working. I was able to get it to work using your FederatedAccountServiceExample.class, however I have some additional questions.
I'd like to start by explaining my goals. I'd like to use a native login with the option to use a third party login such as facebook. I'm using hibernate and would need to be able to sync the database up with the third party. 1.) I'm assuming UserRealm is for native logins and not used by the third party? 2.) Not sure why, but I was unable to get things working with DefaultHibernateFederatedAccountService.class, I was getting the previously posted error. 3.) If I was able to get the DefaultHibernateFederatedAccountService.class working, would I be able to sync and update the database? 4.) When clicking the facebook login link, for a fraction of a second the redirect screen appears. Is there a way to prevent that from happening? I notice it happens in your demo as well. 5.) I don't know how to use Federated in the BasicUser.class hibernate persistence class. My persistance class, "BasicUser" models your example with the exception of roles, http://svn.codehaus.org/tynamo/trunk/tynamo-federatedaccounts/tynamo-federatedaccounts-core/src/test/java/org/tynamo/security/federatedaccounts/testapp/entities/User.java Below is my implementation, "I'm not really sure how to best merge the database with the remote user", but hopefully you'll be able to point me in the right direction. Any help would be appreciated. Thanks Kalle @SubModule(value = {SecurityModule.class, FederatedAccountsModule.class}) public class AppModule { public static void bind(ServiceBinder binder) { binder.bind(UserInfo.class, UserInfoImpl.class); binder.bind(AuthorizingRealm.class, FederatedAccountsAuthorizingRealm.class).withId( FederatedAccountsAuthorizingRealm.class.getSimpleName()); binder.bind(FederatedAccountService.class, FederatedAccountServiceExample.class); } @Startup public static void initApplication(Scheduler scheduler) { scheduler.init(); } public static void contributeFederatedAccountService(MappedConfiguration<String, Object> configuration) { configuration.add("facebook", BasicUser.class); configuration.add("facebook.id", "facebookEmail"); // configuration.add("twitter.id", "twitterUserId"); } public static void contributeApplicationDefaults( MappedConfiguration<String, String> configuration) { // Contributions to ApplicationDefaults will override any contributions to // FactoryDefaults (with the same key). Here we're restricting the supported // locales to just "en" (English). As you add localised message catalogs and other assets, // you can extend this list of locales (it's a comma separated series of locale names; // the first locale name is the default when there's no reasonable match). configuration.add(SymbolConstants.SUPPORTED_LOCALES, "en"); // The factory default is true but during the early stages of an application // overriding to false is a good idea. In addition, this is often overridden // on the command line as -Dtapestry.production-mode=false configuration.add(SymbolConstants.PRODUCTION_MODE, "false"); // The application version number is incorprated into URLs for some // assets. Web browsers will cache assets because of the far future expires // header. If existing assets are changed, the version number should also // change, to force the browser to download new versions. configuration.add(SymbolConstants.APPLICATION_VERSION, "1.0-SNAPSHOT"); configuration.add(JQuerySymbolConstants.SUPPRESS_PROTOTYPE, "true"); configuration.add(FacebookRealm.FACEBOOK_CLIENTID, "Util.clientID"); configuration.add(FacebookRealm.FACEBOOK_CLIENTSECRET, "Util.clientSecret"); configuration.add(FacebookRealm.FACEBOOK_PERMISSIONS, "email,user_birthday,user_location"); configuration.add(FacebookRealm.FACEBOOK_PRINCIPAL, FacebookRealm.PrincipalProperty.email.name()); configuration.add(SecuritySymbols.LOGIN_URL, "/login"); configuration.add(SymbolConstants.HOSTNAME, "localhost"); configuration.add(SymbolConstants.HOSTPORT, "8080"); configuration.add(FederatedAccountSymbols.SUCCESSURL, "/community/index"); } public static void contributeWebSecurityManager(Configuration<Realm> configuration, @InjectService("UserRealm") AuthorizingRealm userRealm) { // FacebookRealm is automatically contributed as long as federatedsecurity is on the classpath configuration.add(userRealm); } public static void contributeHibernateSessionSource(OrderedConfiguration<HibernateConfigurer> configuration) { configuration.addInstance("cardaddy", CarDaddyHibernateConfigurer.class); } @Match("*DAO") public static void adviseTransactions(HibernateTransactionAdvisor advisor, MethodAdviceReceiver receiver) { advisor.addTransactionCommitAdvice(receiver); } public class FederatedAccountServiceExample implements FederatedAccountService { @Inject private Session session; @Inject private ApplicationStateManager applicationStateManager; @Override public AuthenticationInfo federate(String realmName, Object remotePrincipal, AuthenticationToken authenticationToken, Object remoteAccount) { BasicUser user = this.merge(remoteAccount); SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(remotePrincipal, realmName); principalCollection.add(authenticationToken, realmName); user.federate(realmName, remotePrincipal, remoteAccount); return new SimpleAuthenticationInfo(user.getId(), authenticationToken.getCredentials(), realmName); } public BasicUser merge(com.restfb.types.User user) { BasicUser basicUser = (BasicUser) session.createCriteria(BasicUser.class).add(Restrictions.eq("facebookUserId", user.getId())).uniqueResult(); if (basicUser == null) { basicUser = new BasicUser(); } basicUser.setFirstName(user.getFirstName()); basicUser.setLastName(user.getLastName()); basicUser.setEmailAddress(user.getEmail()); basicUser.setFacebookUserId(user.getId()); session.saveOrUpdate(basicUser); return basicUser; } public BasicUser merge(Object account) { if (account instanceof com.restfb.types.User) { return merge((com.restfb.types.User) account); } else if (account instanceof twitter4j.User) { return merge((twitter4j.User) account); } return null; } } public class FederatedAccountsAuthorizingRealm extends AuthorizingRealm { private Logger logger; public FederatedAccountsAuthorizingRealm(Logger logger) { super(new MemoryConstrainedCacheManager()); this.logger = logger; setName("oauthauthorizer"); setAuthenticationTokenClass(OauthAccessToken.class); setPermissionResolver(new WildcardPermissionResolver()); } @Override protected AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // We are overcomplicating things for the purposes of this example // If you really only wanted to know if user was authenticated against Facebook or Twitter, // you do the above check in any page or service by obtaining Subject from SecurityService // However, we wanted to demonstrate local authorization with remote authentication SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); if (!principals.fromRealm(FederatedAccount.FederatedAccountType.facebook.name()).isEmpty()) { authorizationInfo.addStringPermission(FederatedAccount.FederatedAccountType.facebook.name()); } // Might be odd if these were both true at the same time but we don't care if (!principals.fromRealm(FederatedAccount.FederatedAccountType.twitter.name()).isEmpty()) { authorizationInfo.addStringPermission(FederatedAccount.FederatedAccountType.twitter.name()); } if (!principals.fromRealm("local").isEmpty()) { authorizationInfo.addStringPermission("local"); } return authorizationInfo; } @Override protected AuthenticationInfo doGetAuthenticationInfo(AuthenticationToken token) throws AuthenticationException { // Never participate in authentication process return null; } } @Scope(ScopeConstants.PERTHREAD) public class UserInfoImpl implements UserInfo { private final Session session; public UserInfoImpl(Session session) { this.session = session; } public Long getUserId() { return (Long) SecurityUtils.getSubject().getPrincipal(); } public BasicUser getUser() { Long userId = getUserId(); if (userId != null) { return (BasicUser) session.get(BasicUser.class, userId); } return null; } } @Entity public class BasicUser implements FederatedAccount, Serializable { private Long id; private String facebookUserId; private String username; private String firstName; private String lastName; private String emailAddress; private String encodedPassword; private Date created = new Date(); private boolean accountLocked; private boolean credentialsExpired; private byte[] passwordSalt; private List<Vehicle> vehicles; @Id @GeneratedValue(strategy = GenerationType.AUTO) @NonVisual public Long getId() { return id; } public void setId(Long id) { this.id = id; } @Override public boolean equals(Object obj) { try { return (obj instanceof BasicUser && ((BasicUser) obj).getUsername().equals(username)); } catch (NullPointerException e) { return false; } } @Override public int hashCode() { return username == null ? 0 : username.hashCode(); } @OneToMany(mappedBy = "basicUser", cascade = CascadeType.ALL) public List<Vehicle> getVehicles() { return vehicles; } public void setVehicles(List<Vehicle> vehicles) { this.vehicles = vehicles; } @NaturalId @Column(unique = true) @Index(name = "User_username") public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getFirstName() { return firstName; } public void setFirstName(String firstName) { this.firstName = firstName; } public String getLastName() { return lastName; } public void setLastName(String lastName) { this.lastName = lastName; } @Validate("required,regexp=^[0-9a-zA-Z._%+-]+@[0-9a-zA-Z]+[\\.]{1}[0-9a-zA-Z]+[\\.]?[0-9a-zA-Z]+$") public String getEmailAddress() { return emailAddress; } public void setEmailAddress(String emailAddress) { this.emailAddress = emailAddress; } @NonVisual public String getEncodedPassword() { return encodedPassword; } public void setEncodedPassword(String encodedPassword) { this.encodedPassword = encodedPassword; } @NonVisual @Temporal(javax.persistence.TemporalType.DATE) public Date getCreated() { return created; } public void setCreated(Date created) { this.created = created; } public boolean isAccountLocked() { return accountLocked; } public void setAccountLocked(boolean accountLocked) { this.accountLocked = accountLocked; } public boolean isCredentialsExpired() { return credentialsExpired; } public void setCredentialsExpired(boolean credentialsExpired) { this.credentialsExpired = credentialsExpired; } @Transient public String getPassword() { return ""; } public void setPassword(String password) { if (password != null && !password.equals(encodedPassword) && !"".equals(password)) { ByteSource saltSource = new SecureRandomNumberGenerator().nextBytes(); this.passwordSalt = saltSource.getBytes(); this.encodedPassword = new Sha1Hash(password, saltSource).toString(); } } @Override public String toString() { return "User " + username; } public void setFacebookUserId(String facebookUserId) { this.facebookUserId = facebookUserId; } @NonVisual @Column(unique = true, nullable = true) public String getFacebookUserId() { return facebookUserId; } public void setPasswordSalt(byte[] passwordSalt) { this.passwordSalt = passwordSalt; } @NonVisual @Column(length = 128) public byte[] getPasswordSalt() { return passwordSalt; } @Override public boolean federate(String realmName, Object remotePrincipal, Object remoteAccount) { if (remoteAccount instanceof com.restfb.types.User) { // remotePrincipal is null, this is a federated account update if (remotePrincipal == null) { // update federated/overlapping properties } else { // newly created account but oviously you could also check if (local) id is null // If you don't allow auto-federation and didn't implement a custom FederatedAccountService, // you could throw an exception here or initialize the account in locked state com.restfb.types.User fbUser = (com.restfb.types.User) remoteAccount; facebookUserId = fbUser.getId(); // initialize other federated/overlapping properties } } return false; } } -- View this message in context: http://tapestry.1045711.n5.nabble.com/Tapestry-Security-Federate-Facebook-Exception-tp5714861p5714863.html Sent from the Tapestry - User mailing list archive at Nabble.com. --------------------------------------------------------------------- To unsubscribe, e-mail: users-unsubscr...@tapestry.apache.org For additional commands, e-mail: users-h...@tapestry.apache.org