Many mobile applications today connect to external web services to access some type of data. These web services may be a third-party data provider, such as Twitter, or it may be an in house service for connecting to a corporate calendar, for example. In many of these cases, to access that data through the web service, you must authenticate and authorize an application on your mobile device. The goal of the spring-android-auth module is to address the need of an Android application to gain authorization to a web service.
There are many types of authorization methods and protocols, some custom and proprietary, while others are open standards. One protocol that is rapidly growing in popularity is OAuth. OAuth is an open protocol that allows users to give permission to a third-party application or web site to access restricted resources on another web site or service. The third-party application receives an access token with which it can make requests to the protected service. By using this access token strategy, a user's login credentials are never stored within an application, and are only required when authenticating to the service.
The initial release of the spring-android-auth module provides OAuth 1.x and 2.0 support in an Android application by utilizing Spring Social. It includes a SQLite repository, and Android compatible Spring Security encryption. The Spring Social project enables your applications to establish Connections with Software-as-a-Service (SaaS) Providers such as Facebook and Twitter to invoke Service APIs on behalf of Users. In order to make use of Spring Social on Android the following classes are available.
The SQLiteConnectionRepository
class implements the ConnectionRepository
interface from Spring Social. It is used to persist the connection information to a SQLite database on the Android device. This connection repository is designed for a single user who accesses multiple service providers and may even have multiple accounts on each service provider.
If your device and application are used by multiple people, then a SQLiteUsersConnectionRepository
class is available for storing multiple user accounts, where each user account may have multiple connections per provider. This scenario is probably not as typical, however, as many people do not share their phones or devices.
The Spring Security Crypto library is not currently supported on Android. To take advantage of the encryption tools in Spring Security, the Android specific class, AndroidEncryptors
has been provided in Spring for Android. This class uses an Android compatible SecureRandom
provider for generating byte array based keys using the SHA1PRNG algorithm.
There are a few methods for including external jars in your Android app. One is to manually download them and include them in your app's libs/
folder. Another option is to use Maven for dependency management.
In order to use RestTemplate in your Android application, you must include the following Spring jars in the libs/
folder. These are available from the SpringSource Community Downloads page.
spring-android-auth-{version}.jar
spring-android-rest-template-{version}.jar
spring-android-core-{version}.jar
spring-security-crypto-{version}.jar
spring-social-core-{version}.jar
Each Spring Social provider may have additional dependencies. For example, to use Spring Social Twitter, the following jars are required.
spring-social-twitter-{version}.jar
spring-social-core-{version}.jar
spring-security-crypto-{version}.jar
jackson-mapper-asl-{version}.jar
jackson-core-asl-{version}.jar
If you are building your project with Ant, Ant will automatically include any jars located in the libs/
folder located in the root of your project. However, in Eclipse you must manually add the jars to the Build Path. Follow these steps to add the jars to your existing Android project in Eclipse.
libs/
folder and jars display in the Package Explorer.BuildPath
submenu.Add to Build Path
from the context menu.
Add the spring-android-auth artifact to your classpath. See the Spring for Android and Maven section for more information.
<dependency> <groupId>org.springframework.android</groupId> <artifactId>spring-android-auth</artifactId> <version>${spring-android-version}</version> </dependency>
The transitive dependencies are automatically imported by Maven, but they are listed here for clarity.
<dependency> <groupId>org.springframework.android</groupId> <artifactId>spring-android-rest-template</artifactId> <version>${spring-android-version}</version> </dependency> <dependency> <groupId>org.springframework.android</groupId> <artifactId>spring-android-core</artifactId> <version>${spring-android-version}</version> </dependency> <dependency> <groupId>org.springframework.security</groupId> <artifactId>spring-security-crypto</artifactId> <version>${spring-security-crypto-version}</version> <exclusions> <!-- Exclude in favor of Spring for Android Core --> <exclusion> <artifactId>spring-core</artifactId> <groupId>org.springframework</groupId> </exclusion> </exclusions> </dependency> <dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-core</artifactId> <version>${spring-social-version}</version> <exclusions> <!-- Exclude in favor of Spring for Android RestTemplate --> <exclusion> <artifactId>spring-web</artifactId> <groupId>org.springframework</groupId> </exclusion> </exclusions> </dependency>
To use the Spring Social Twitter provider, you can add it to your classpath. Note the exclusions in this dependency. Commons Logging is built into Android, and many of the Spring Social provider libraries are built with support for Spring Web, which is not needed on Android.
<dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-twitter</artifactId> <version>${spring-social-version}</version> <exclusions> <exclusion> <!-- Provided by Android --> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency>
Similarly, you can use the Spring Social Facebook provider by adding it to your classpath. Again note the exclusions.
<dependency> <groupId>org.springframework.social</groupId> <artifactId>spring-social-facebook</artifactId> <version>${spring-social-version}</version> <exclusions> <!-- Provided by Android --> <exclusion> <artifactId>commons-logging</artifactId> <groupId>commons-logging</groupId> </exclusion> </exclusions> </dependency>
Both the Spring Social Twitter and Facebook libraries transitively depend on the Jackson JSON processor. Again, if you are not using Maven, you will need to include these in your libs/
folder.
<dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-mapper-asl</artifactId> <version>${jackson-version}</version> </dependency> <dependency> <groupId>org.codehaus.jackson</groupId> <artifactId>jackson-core-asl</artifactId> <version>${jackson-version}</version> </dependency>
Below are several usage examples that illustrate how to use Spring for Android with Spring Social.
The following examples are based on a sample Android application, which has Facebook and Twitter examples using Spring Social. You can retrieve the source code for the sample app with Git:
$ git clone git://github.com/SpringSource/spring-android-samples.git
SQLiteConnectionRepositoryHelper
extends SQLiteOpenHelper
. Create a new instance by passing a context
reference. Depending on your implementation, and to avoid memory leaks, you will probably want to use the Application Context when creating a new instance of SQLiteConnectionRepositoryHelper
. The name of the database file created is spring_social_connection_repository.sqlite
, and is created the first time the application attempts to open it.
Context context = getApplicationContext();
SQLiteOpenHelper repositoryHelper = new SQLiteConnectionRepositoryHelper(context);
This example show how to set up the ConnectionRepository
for use with multiple connection factories.
To establish a ConnectionRepository
, you will need the following objects.
ConnectionFactoryRegistry connectionFactoryRegistry; SQLiteOpenHelper repositoryHelper; ConnectionRepository connectionRepository;
The ConnectionFactoryRegistry
stores the different Spring Social connections to be used in the application.
connectionFactoryRegistry = new ConnectionFactoryRegistry();
You can create a FacebookConnectionFactory
, if your application requires Facebook connectivity.
// the App ID and App Secret are provided when you register a new Facebook application at facebook.com String appId = "8ae8f060d81d51e90fadabaab1414a97"; String appSecret = "473e66d79ddc0e360851dc512fe0fb1e"; // Prepare a Facebook connection factory with the App ID and App Secret FacebookConnectionFactory facebookConnectionFactory; facebookConnectionFactory = new FacebookConnectionFactory(appId, appSecret);
Similarly, you can also create a TwitterConnectionFactory
. Spring Social offers several different connection factories to popular services. Additionally, you can create your own connection factory based on the Spring Social framework.
// The consumer token and secret are provided when you register a new Twitter application at twitter.com String consumerToken = "YR571S2JiVBOFyJS5MEg"; String consumerTokenSecret = "Kb8hS0luftwCJX3qVoyiLUMfZDtK1EozFoUkjNLUMx4"; // Prepare a Twitter connection factory with the consumer token and secret TwitterConnectionFactory twitterConnectionFactory; twitterConnectionFactory = new TwitterConnectionFactory(consumerToken, consumerTokenSecret)
After you create a connection factory, you can add it to the registry. Connection factories may be later retrieved from the registry in order to create new connections to the provider.
connectionFactoryRegistry.addConnectionFactory(facebookConnectionFactory); connectionFactoryRegistry.addConnectionFactory(twitterConnectionFactory);
The final step is to prepare the connection repository for storing connections to the different providers.
// Create the SQLiteOpenHelper for creating the local database Context context = getApplicationContext(); SQLiteOpenHelper repositoryHelper = new SQLiteConnectionRepositoryHelper(context); // The connection repository takes a TextEncryptor as a parameter for encrypting the OAuth information TextEncryptor textEncryptor = AndroidEncryptors.noOpText(); // Create the connection repository ConnectionRepository connectionRepository = new SQLiteConnectionRepository(repositoryHelper, connectionFactoryRegistry, textEncryptor);
Spring Social supports encrypting the user's OAuth connection information within the ConnectionRepository
through the use of a Spring Security TextEncryptor
. The password and salt values are used to generate the encryptor's secret key. The salt value should be hex-encoded, random, and application-global. While this will encrypt the OAuth credentials stored in the database, it is not an absolute solution. When designing your application, keep in mind that there are already tools available for translating a DEX to a JAR file, and decompiling to source code. Because your application is distributed to a user's device, it is more vulnerable than if it were running on a web server, for example.
String password = "password"; String salt = "5c0744940b5c369b"; TextEncryptor textEncryptor = AndroidEncryptors.text(password, salt); connectionRepository = new SQLiteConnectionRepository(repositoryHelper, connectionFactoryRegistry, textEncryptor);
During development you may wish to avoid encryption so you can more easily debug your application by viewing the OAuth data being saved to the database. This TextEncryptor
performs no encryption.
TextEncryptor textEncryptor = AndroidEncryptors.noOpText();
connectionRepository = new SQLiteConnectionRepository(repositoryHelper,
connectionFactoryRegistry, textEncryptor);
The following steps illustrate how to establish a connection to Twitter. A working example is provided in the sample application described earlier.
The first step is to retrieve the connection factory from the registry that we created earlier.
TwitterConnectionFactory connectionFactory;
connectionFactory = (TwitterConnectionFactory) connectionFactoryRegistry.getConnectionFactory(Twitter.class);
Fetch a one time use request token. You must save this request token, because it will be needed in a later step.
OAuth1Operations oauth = connectionFactory.getOAuthOperations(); // The callback url is used to respond to your application with an OAuth verifier String callbackUrl = "x-org-springsource-android-showcase://twitter-oauth-response"; // Fetch a one time use Request Token from Twitter OAuthToken requestToken = oauth.fetchRequestToken(callbackUrl, null);
Generate the url for authorizing against Twitter. Once you have the url, you use it in a WebView so the user can login and authorize your application. One method of doing this is provided in the sample application.
String authorizeUrl = oauth.buildAuthorizeUrl(requestToken.getValue(), OAuth1Parameters.NONE);
Once the user has successfully authenticated and authorized the application, Twitter will call back to your application with the oauth verifier. The following settings from an AndroidManifest illustrate how to associate a callback url with a specific Activity. In this case, when the request is made from Twitter to the callback url, the TwitterActivity will respond.
<activity android:name="org.springframework.android.showcase.social.twitter.TwitterActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="x-org-springsource-android-showcase" android:host="twitter-oauth-response" /> </intent-filter> </activity>
The Activity that responds to the callback url should retrieve the oauth_verifier
querystring parameter from the request.
Uri uri = getIntent().getData();
String oauthVerifier = uri.getQueryParameter("oauth_verifier");
Once you have the oauth_verifier, you can authorize the request token that was saved earlier.
AuthorizedRequestToken authorizedRequestToken = new AuthorizedRequestToken(requestToken, verifier);
Now exchange the authorized request token for an access token. Once you have the access token, the request token is no longer required, and can be safely discarded.
OAuth1Operations oauth = connectionFactory.getOAuthOperations(); OAuthToken accessToken = oauth.exchangeForAccessToken(authorizedRequestToken, null);
Finally, we can create a Twitter connection and store it in the repository.
Connection<TwitterApi> connection = connectionFactory.createConnection(accessToken); connectionRepository.addConnection(connection);
The following steps illustrate how to establish a connection to Facebook. A working example is provided in the sample application described earlier. Keep in mind that each provider's implementation may be different. You may have to adjust these steps when connecting to a different OAuth 2.0 provider.
The first step is to retrieve the connection factory from the registry that we created earlier.
FacebookConnectionFactory connectionFactory;
connectionFactory = (FacebookConnectionFactory) connectionFactoryRegistry.getConnectionFactory(Facebook.class);
Specify the redirect url. In the case of Facebook, we are using the client-side authorization flow. In order to retrieve the access token, Facebook will redirect to a success page that contains the access token in a URI fragment.
String redirectUri = "https://www.facebook.com/connect/login_success.html";
Define the scope of permissions your app requires.
String scope = "publish_stream,offline_access,read_stream,user_about_me";
In order to display a mobile formatted web page for Facebook authorization, you must pass an additional parameter in the request. This parameter is not part of the OAuth specification, but the following illustrates how Spring Social supports additional parameters.
MultiValueMap<String, String> additionalParameters = new LinkedMultiValueMap<String, String>(); additionalParameters.add("display", "touch");
Now we can generate the Facebook authorization url to be used in the browser or web view
OAuth2Parameters parameters = new OAuth2Parameters(redirectUri, scope, null, additionalParameters);
OAuth2Operations oauth = connectionFactory.getOAuthOperations();
String authorizeUrl = oauth.buildAuthorizeUrl(GrantType.IMPLICIT_GRANT, parameters);
The next step is to load the generated authorization url into a webview within your application. After the user logs in and authorizes your application, the browser will redirect to the url specified earlier. If authentication was successful, the url of the redirected page will now include a URI fragment which contains an access_token parameter. Retrieve the access token from the URI fragment and use it to create the Facebook connection. One method of doing this is provided in the sample application.
AccessGrant accessGrant = new AccessGrant(accessToken);
Connection<FacebookApi> connection = connectionFactory.createConnection(accessGrant);
connectionRepository.addConnection(connection);