Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Info
titleVersion status: 0.5.0 beta for T5.4, 0.4.3 beta for T5.3.x

Check out the federatedaccounts demo

0.4.x introduced a generic Oauth module integration with pac4j

0.3.0 refactored core module for more additional library support

0.2.0 introduced the rolling tokens module

0.1.0 introduced a new, modularized architecture with facebook and twitter modules

Versions before 0.4.x are not maintained anymore

...

Code Block
        // dependencies in your pom.xml - use what you need, don't need all of these
        <dependency>
            <groupId>org.tynamo.security</groupId>
            <artifactId>tynamo-federatedaccounts-facebook</artifactId>
            <version>0.45.3<0</version>
        </dependency>
        <dependency>
            <groupId>org.tynamo.security</groupId>
            <artifactId>tynamo-federatedaccounts-twitter</artifactId>
            <version>0.45.3<0</version>
        </dependency>
        <dependency>
            <groupId>org.tynamo.security</groupId>
            <artifactId>tynamo-federatedaccounts-pac4jbasedoauth</artifactId>
            <version>0.45.3<0</version>
        </dependency>
		// Rolling tokens is a (semi-)secure remember me authentication based on one-time authentication tokens
        <dependency>
            <groupId>org.tynamo.security</groupId>
            <artifactId>tynamo-federatedaccounts-rollingtokens</artifactId>
            <version>0.45.3<0</version>
        </dependency>		

 // in your AppModule.java
	public static void bind(ServiceBinder binder) {
		binder.bind(FederatedAccountService.class, DefaultHibernateFederatedAccountServiceImpl.class);
		// Or for JPA implementation
		// binder.bind(FederatedAccountService.class, DefaultJpaFederatedAccountServiceImpl.class);
	}

	public static void contributeFederatedAccountService(MappedConfiguration<String, Object> configuration) {
		// you can either map each realm to the same entity...
		configuration.add("*", User.class);
		// or, you can use different entities
        // configuration.add(Constants.TWITTER_REALM, TwitterAccount.class);
        // configuration.add(FederatedAccountType.pac4j_.name() + SupportedClient.google2.name(), GoogleAccount.class);

		// Now, you also have to map the desired id (the subject principal) to an attribute of the entity
		configuration.add("facebook.id", "facebookId");
		configuration.add("twitter.id", "twitterId");
	}

    public static void contributeApplicationDefaults(MappedConfiguration<String, String> configuration) {
		// these are the defaults, change as needed
		// configuration.add(FederatedAccountSymbols.COMMITAFTER_OAUTH, "true");
		// configuration.add(FederatedAccountSymbols.HTTPCLIENT_ON_GAE, "false");
		// configuration.add(FederatedAccountSymbols.SUCCESSURL, ""); // empty string implies host name only

		// set your oauth app credentials
		configuration.add(FacebookRealm.FACEBOOK_CLIENTID, "<your oauth client id>");
		configuration.add(FacebookRealm.FACEBOOK_CLIENTSECRET, "<your oauth client secret>");
		configuration.add(TwitterRealm.TWITTER_CLIENTID, "<your oauth client id>");
		configuration.add(TwitterRealm.TWITTER_CLIENTSECRET, "<your oauth client secret>");
		// Use the constants in Pac4jFederatedRealm for pac4j app credentials 
        // configuration.add(Pac4jFederatedRealm.GOOGLE_CLIENTID, "<your_google_api_app_key_here>");
    }

...

Depending on the FederatedAccountService used, you don't necessarily need to provide any meaningful implementation for federate(...) operation, but it's provided in case you want to merge/update some account properties. See the example implementation for ideas (the DefaultHibernateFederatedAccountServiceImpl the DefaultJpaFederatedAccountServiceImpl requires you to implement storing the identifying remote property).

...

The operation is designed to be invoked after a remote authentication has succeeded. "remotePrincipal" parameter is the username/userid in the remote system and the last parameter is an optional object describing the remote account. The current Facebook realm is using RestFB and returns RestFB User object as the remoteAccount. The Twitter realm uses Twitter4j and returns a Twitter4j User as the remoteAccount. DefaultHibernateFederatedAccountServiceImpl DefaultJpaFederatedAccountServiceImpl tries to obtain the configured entity for this realm (see the configuration above) and saves or updates the entity after calling its federate(...) operation.

FederatedAccounts module requires that FederatedAccountService interface is bound to an existing service, but doesn't bind to any by default. This is so you can easily provide a custom implementation for FederatedAccountService, using your own persistence model. Two implementations are provided by default for FederatedAccountService: DefaultHibernateFederatedAccountServiceImpl and DefaultJpaFederatedAccountServiceImpl.

Note
titleFind it confusing to use this module?

Improvements to the documentation are more than welcome. However, we've purposefully tried to keep the documentation light since this family of library was purposefully designed to cover a wide range of use cases and documenting all the different options and possibilities would make the documentation too heavy and complex. The underlying libraries are well battle tested and all of the federatedaccount libraries are in use in production applications today. If you are new to the world of Oauth and federated account systems in general, it's best to dive in by integrating with a single provider first, compare your implementation with existing tests and samples and build up from there.

...

What should you customize and why?

  • If you are not using jpa/hibernate as your persistence framework...
    -> provide a custom FederatedAccountService implementation
  • If you are handling your own transactions...
    -> set autoCommit to false i.e. contribute application symbol configuration.add(HostSymbols.COMMITAFTER_OAUTH,"false")
  • If you are using Hibernate, but need to merge local accounts with remote accounts matching a specific property...
    -> override : DefaultHibernateFederatedAccountServiceImpl.findLocalAccount(Class<?> entityType, String realmName, Object remotePrincipal, Object remoteAccount)
  • If you are adding/updating (i.e. cache) property values from remote account to local account...
    -> implement the required logic in FederatedAccount.federate(String realmName, Object remotePrincipal, Object remoteAccount)
  • If you are not using DefaultHibernateFederatedAccountServiceImpl DefaultXXXFederatedAccountServiceImpl but need to keep the Oauth access token for later use:
    ->You need to do something like:

    Code Block
    		SimplePrincipalCollection principalCollection = new SimplePrincipalCollection(remotePrincipal, realmName);
    		principalCollection.add(authenticationToken, realmName);
    		return new SimpleAuthenticationInfo(principalCollection, authenticationToken.getCredentials());
    

    or, store the access token in your database. Note the (potential) expiration of an access token and cast the authenticationToken argument of federate() call in your FederatedAccountService to OauthAccessToken for easier handling.

  • If you are implementing your own remote account provider...
    -> See the current FacebookRealm implementation, FacebookOauth page and FacebookOauthSignIn component and related classes for examples (and contribute back if it's generally useful!)

Check out more examples from our full-featured functional tests or a simple, live demonstration with the default Facebook authentication in action, running on GAE.

...

If you just need a quick & dirty way for adding a few Oauth providers to your application, check out how Pac4j is being used in the tynamo-federatedaccounts-test module. At this time, Pac4j supports facebook, dropbox, github, google2, linkedIn2, twitter, windowslive, wordpress & yahoo Oauth providers (the number at the end of the provider key refers to the authentication protocol in case the provider itself supports both). The module even sports a fully functional UI component (see the federatedSignInOptionsPanel on the login page of the test app), so you enable specific providers, provider the credentials and entity configuration and you are done! Of course, you don't have to use the options panel but you can also just add specific Oauth logins using the <t:federated.Pac4jOauthSignIn provider="yahoo" / > component. Note that since Pac4j handles multiple specific providers, you need to use the form "pacj_<supported client name>" as a key when setting up entity mapping (e.g.  // configuration.add(FederatedAccountType.pac4j_.name() + SupportedClient.google2.name(), GoogleAccount.class) ).

...

Info
titleWant to do more with FB than just authenticate?

The module uses a fabulous little library, RestFB, for communicating with Facebook's Graph API. You are free to use the full power of RestFB and Facebook's Graph API in the rest of your application! Just remember that you need to store the access token for later use (see the Extension points below). The example application at http://tynamo-federatedaccounts.tynamo.org demonstrates application's use of the access token, feel free to browse the source code for the sample app

First, create a Facebook application/register your website (same thing really). If you need to request specific application permissions, contribute an additional permission symbol (in addition to your application credentials), for example:

...

Tip
titleTapestry makes it easy!

Oauth is based on callback URIs back to your application. This module automatically adds an Facebook Oauth page as the callback URI, and handles the mechanics of obtaining and verifying the Oauth access key. Even if you've secured access to the rest of your site (with tapestry-security), distributed configuration contributed by this module allows unauthenticated requests to access the callback pages (but only if you do it all in Java!).

Workin Working together with the FacebookRealm is FacebookOauthSignIn component. This component displays the Facebook login icon and initiates the Oauth callflow. The component requires that you've contributed values for "facebook.clientid" and "facebook.clientsecret" to work. Additionally, you may require Facebook specific permissions and decide to manage transactions yourself (set FederatedAccountSymbols.COMMITAFTER_OAUTH to false, true by default). FacebookSignIn also support three different window modes [blank|inline|self] (e.g. windowmode="blank"). The live example well demonstrates these configuration choices.

...

Code Block
    // dependencies in your pom.xml 
    <dependency>
     <groupId>org.tynamo.security</groupId>
     <artifactId>tynamo-federatedaccounts-rollingtokens</artifactId>
     <version>0.4.3</version>
    </dependency>

    @Contribute(FederatedAccountService.class)
	public static void contributeFederatedAccountService(MappedConfiguration<String, Object> configuration) {
        configuration.add("rollingtokens", UserAccount.class);
        configuration.add("rollingtokens" + FederatedAccountService.IDPROPERTY, "id");
	}

	// Need to tell principal type to rolling tokens so it can be persisted properly with the ExpiringRollingToken
    public static void contributeApplicationDefaults(MappedConfiguration<String, String> configuration) {
        configuration.add(RollingTokenSymbols.CONFIGURED_PRINCIPALTYPE, Long.class.getName());
    }

	// rollingtokens is currently JPA only
    @Contribute(JpaEntityPackageManager.class)
    public static void addPackagesToScan(Configuration<String> configuration) {
        configuration.add(ExpiringRollingToken.class.getPackage().getName());
    } 

Rollingtoken plays especially well with Shiro's built-in rememberMe and Subject.authenticated feature. In Shiro's default rememberMe a Subject "is remembered, they are NOT considered authenticated". Together with rollingtokens, two cookies are issued to the user. If the matching principal is found but rollingtoken authentication fails, Subject.isAuthenticated() returns false and true if matching server-side token was found and hadn't expired, just as if user had signed in with a username/password pair. Note however, that rollingtokens does weaken the security compared to secure form-based authentication (but is in some ways more secure than BASIC or form-based authentication over plain HTTP).

 // configuration.add(FederatedAccountType.pac4j_.name() + SupportedClient.google2.name(), GoogleAccount.class);