In this tutorial, we will build a simple Android app that stores user generated notes remotely, with user authentication managed by Stormpath and our new Android SDK. This tutorial will take about 15 minutes, and will demonstrate how easy and seamless it is to integrate Stormpath into new and existing Android applications, as well as cover some security and performance issues in Android development.

Why Stormpath for Android Applications?

Stormpath is an authentication service that securely stores your user accounts and private user information. Plus, it’s free to use for small projects!

Having an authentication service like Stormpath allows you to build secure mobile applications that can register and login users in a variety of ways, without compromising your device security.

Using Stormpath, users are logged in securely using the OAuth2 protocol with signed JSON Web Tokens.

With Stormpath, you don’t need to be an expert in cryptography to create a trusted front door to your application. In the context of this tutorial, the “User” is on the client side mobile app, and the “Application” is the server hosted on Heroku.

Using the Stormpath Android library makes it simple to implement user authentication correctly, as opposed to spending lots of time and effort building secure authentication methods into your next mobile app!

The Stormpath Android library can also be used to help build more complex authentication scenarios, such as multi-tenancy and single sign on.

In network-driven mobile apps, implementing authentication incorrectly can be an enormous risk to your users, and cause lots of wasted time and development effort fixing bugs that should never have been introduced.

Getting a proper authentication library implemented into your mobile app codebase early on is a great way to speed up development and reduce frustration later on.

OK! Let’s Build Something!

Let’s build a simple note-taking application in Android. By the end of this tutorial you will have an app that allows you to register and login as a user, save a note, and retrieve that note from your server. Literally, synchronize your note data with a cloud backend!

This tutorial is for Android apps written in Java and the Android SDK. If you aren’t hip enough to write code that works on 2,300 different devices, there is an iOS tutorial written in Swift as well >:)

Setting up Our Android Project

In this tutorial, we’ve done all of the backend work for you, and the API server is hosted at https://stormpathnotes.herokuapp.com

A typical Stormpath integration primarily involves your API server architecture communicating with Stormpath’s backend service. This allows you to keep your Stormpath API keys on your server instead of hard-coded in your mobile app.

To integrate with your existing backend (instead of our example one), take some time to look at our language specific server side tutorials. Or — just shoot that link to your backend team =)

The backend API service we’re using exposes two protected endpoints for your Android application to use:

GET /notes – Returns the notes for the authenticated user in the form of a JSON object.

– Returns the notes for the authenticated user in the form of a JSON object. POST /notes – Takes a JSON object with the notes and saves it for the authenticated user.

The JSON object is always in the form of:

{"notes": "The notes the user saved"} 1 2 { "notes" : "The notes the user saved" }

In case you’re curious, we used the following tools to build the backend for Stormpath Notes:

Express – A Node.js framework that makes it easy to build API services.

Express-Stormpath – Exposes a configurable REST API for our mobile clients within Express.

Stormpath – Allows us to store and authenticate users without having to create our own backend for it.

Heroku – Hosts the code for Stormpath Notes online.

Installing Stormpath

git clone https://github.com/stormpath/stormpath-android-notes-example.git 1 2 git clone https : //github.com/stormpath/stormpath-android-notes-example.git

If you’d like to see the finished version of the project, check out the finished branch.

Create The “Notes” Android Application

Open the project using Android Studio and it should be configured to compile.

The Gradle dependency for including the Stormpath SDK is:

compile 'com.stormpath.sdk:stormpath-sdk-android:1.1.3' 1 2 compile 'com.stormpath.sdk:stormpath-sdk-android:1.1.3'

In the Application class ( NotesApp.java ), add the following in the onCreate method:

// Initialize Stormpath StormpathConfiguration stormpathConfiguration = new StormpathConfiguration.Builder() .baseUrl(baseUrl) .build(); Stormpath.init(this, stormpathConfiguration); 1 2 3 4 5 6 // Initialize Stormpath StormpathConfiguration stormpathConfiguration = new StormpathConfiguration . Builder ( ) . baseUrl ( baseUrl ) . build ( ) ; Stormpath . init ( this , stormpathConfiguration ) ;

Optionally, for debug information, add this method before StormpathConfiguration ‘s method calls.

// We only want to show the logs in debug builds, for easier debugging. if (BuildConfig.DEBUG) { Stormpath.setLogLevel(StormpathLogger.VERBOSE); } 1 2 3 4 5 // We only want to show the logs in debug builds, for easier debugging. if ( BuildConfig . DEBUG ) { Stormpath . setLogLevel ( StormpathLogger . VERBOSE ) ; }

User Login and Registration Flow

This project includes the Java classes for pre-built Stormpath login. These files are also included in the Stormpath SDK-UI library as configurable login UI, which is distinct from the SDK classes. If you want your own custom user experience, the Stormpath.login network method can be used without the view controls.

These SDK-UI classes authenticate using the Stormpath SDK to a server running Stormpath dependences.

StormpathLoginActivity can be started with:

startActivity(new Intent(this, StormpathLoginActivity.class)); 1 2 startActivity ( new Intent ( this , StormpathLoginActivity . class ) ) ;

In NotesActivity.java there are a few instances where the StormpathLoginActivity will need to be revealed.

Within the onResume method, we can check if there is a user logged by grabbing the user profile. If there isn’t one, show the Login, otherwise, retrieve the note.

Stormpath.getUserProfile(new StormpathCallback<UserProfile>() { @Override public void onSuccess(UserProfile userProfile) { getNotes(); } @Override public void onFailure(StormpathError error) { // Show login view startActivity(new Intent(context, StormpathLoginActivity.class)); } });``` **NOTE**: Stormpath access tokens are automatically created for users who sign into your application, and are stored securely on your Android device using [SharedPreferences](http://developer.android.com/reference/android/content/SharedPreferences.html). For our custom endpoints `/notes`, we are using the [OkHttp3](https://square.github.io/okhttp/3.x/okhttp/okhttp3/package-summary.html) library. So let's initialize the OkHttp3 objects in the `NotesActivity.java`'s `onCreate()` method. ```java // Initialize OkHttp library. HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor(new HttpLoggingInterceptor.Logger() { @Override public void log(String message) { Stormpath.logger().d(message); } }); httpLoggingInterceptor.setLevel(HttpLoggingInterceptor.Level.BODY); this.okHttpClient = new OkHttpClient.Builder() .addNetworkInterceptor(httpLoggingInterceptor) .build(); 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 Stormpath . getUserProfile ( new StormpathCallback < UserProfile > ( ) { @ Override public void onSuccess ( UserProfile userProfile ) { getNotes ( ) ; } @ Override public void onFailure ( StormpathError error ) { // Show login view startActivity ( new Intent ( context , StormpathLoginActivity . class ) ) ; } } ) ; ` ` ` ** NOTE* * : Stormpath access tokens are automatically created for users who sign into your application , and are stored securely on your Android device using [ SharedPreferences ] ( http : //developer.android.com/reference/android/content/SharedPreferences.html). For our custom endpoints ` / notes ` , we are using the [ OkHttp3 ] ( https : //square.github.io/okhttp/3.x/okhttp/okhttp3/package-summary.html) library. So let 's initialize the OkHttp3 objects in the `NotesActivity.java`' s ` onCreate ( ) ` method . ` ` ` java // Initialize OkHttp library. HttpLoggingInterceptor httpLoggingInterceptor = new HttpLoggingInterceptor ( new HttpLoggingInterceptor . Logger ( ) { @ Override public void log ( String message ) { Stormpath . logger ( ) . d ( message ) ; } } ) ; httpLoggingInterceptor . setLevel ( HttpLoggingInterceptor . Level . BODY ) ; this . okHttpClient = new OkHttpClient . Builder ( ) . addNetworkInterceptor ( httpLoggingInterceptor ) . build ( ) ;

Look at the private methods getNotes() and saveNote() for examples of OkHttp3 method preparation.

The way both of these methods work is that they form the request, add headers, make the network call, and broadcast to another part of the app on a successful response from the network.

private void saveNote() { RequestBody requestBody = new FormBody.Builder() .add("notes", mNote.getText().toString()) .build(); Request request = new Request.Builder() .url(NotesApp.baseUrl + "notes") .headers(buildStandardHeaders((Stormpath.accessToken()))) .post(requestBody) .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { Intent intent = new Intent(ACTION_POST_NOTES); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); } }); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 private void saveNote ( ) { RequestBody requestBody = new FormBody . Builder ( ) . add ( "notes" , mNote . getText ( ) . toString ( ) ) . build ( ) ; Request request = new Request . Builder ( ) . url ( NotesApp . baseUrl + "notes" ) . headers ( buildStandardHeaders ( ( Stormpath . accessToken ( ) ) ) ) . post ( requestBody ) . build ( ) ; okHttpClient . newCall ( request ) . enqueue ( new Callback ( ) { @ Override public void onFailure ( Call call , IOException e ) { } @ Override public void onResponse ( Call call , Response response ) throws IOException { Intent intent = new Intent ( ACTION_POST_NOTES ) ; LocalBroadcastManager . getInstance ( context ) . sendBroadcast ( intent ) ; } } ) ; }

private void getNotes() { Request request = new Request.Builder() .url(NotesApp.baseUrl + "notes") .headers(buildStandardHeaders(Stormpath.accessToken())) .get() .build(); okHttpClient.newCall(request).enqueue(new Callback() { @Override public void onFailure(Call call, IOException e) { } @Override public void onResponse(Call call, Response response) throws IOException { JSONObject mNotes; try { mNotes = new JSONObject(response.body().string()); String noteCloud = mNotes.getString("notes"); // You can also include some extra data. Intent intent = new Intent(ACTION_GET_NOTES); intent.putExtra("notes", noteCloud); LocalBroadcastManager.getInstance(context).sendBroadcast(intent); } catch (JSONException e) { } } }); } 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 private void getNotes ( ) { Request request = new Request . Builder ( ) . url ( NotesApp . baseUrl + "notes" ) . headers ( buildStandardHeaders ( Stormpath . accessToken ( ) ) ) . get ( ) . build ( ) ; okHttpClient . newCall ( request ) . enqueue ( new Callback ( ) { @ Override public void onFailure ( Call call , IOException e ) { } @ Override public void onResponse ( Call call , Response response ) throws IOException { JSONObject mNotes ; try { mNotes = new JSONObject ( response . body ( ) . string ( ) ) ; String noteCloud = mNotes . getString ( "notes" ) ; // You can also include some extra data. Intent intent = new Intent ( ACTION_GET_NOTES ) ; intent . putExtra ( "notes" , noteCloud ) ; LocalBroadcastManager . getInstance ( context ) . sendBroadcast ( intent ) ; } catch ( JSONException e ) { } } } ) ; }

Both of these methods require the Headers object to be prepared properly for authentication. buildStandardHeaders() does this for you:

private Headers buildStandardHeaders(String accessToken) { Headers.Builder builder = new Headers.Builder(); builder.add("Accept", "application/json"); if (StringUtils.isNotBlank(accessToken)) { builder.add("Authorization", "Bearer " + accessToken); } return builder.build(); } 1 2 3 4 5 6 7 8 9 10 11 private Headers buildStandardHeaders ( String accessToken ) { Headers . Builder builder = new Headers . Builder ( ) ; builder . add ( "Accept" , "application/json" ) ; if ( StringUtils . isNotBlank ( accessToken ) ) { builder . add ( "Authorization" , "Bearer " + accessToken ) ; } return builder . build ( ) ; }

Within the Stormpath database, the Notes key/value pair is stored within the user’s “CustomData” field. Although arbitrary key/value pairs can be added to the User’s object, Stormpath is not intended to used as arbitrary object storage.

saveNotes() is called within the FloatingActionButton ‘s onClickListener

Finally, let’s add a logout method in the Toolbar’s menu. In onOptionsItemSelected find the if statement regarding (id == R.id.action_logout) and add the following:

Stormpath.logout(); startActivity(new Intent(context, StormpathLoginActivity.class)); 1 2 3 Stormpath . logout ( ) ; startActivity ( new Intent ( context , StormpathLoginActivity . class ) ) ;

Now the user will be able to logout, and will immediately be presented with the Login flow.

An optional addition would be saving the notes client side.

Now compile and run! If you now run and try out your app again, you’ll find that you can now register users, log in, and save your notes!

Try the iOS SDK – If you (or a friend) is into iOS development, try following through the iOS tutorial for Stormpath Notes. Since the app will make requests against the same API, you’ll notice that you can save your notes on one device, and open them up on the other!

Build a Backend with Stormpath – Try building this API from scratch! Stormpath Notes’ example backend is just 45 lines of code! See code on GitHub. Alternatively, try getting started with express-stormpath or stormpath-laravel (more integrations coming soon!)

Stormpath is free to use, and can help your team write a secure, scalable application without worrying about the nitty-gritty details of authentication, authorization, and user security. Sign up for an account today!

Android is a trademark of Google Inc.

The Android robot is reproduced or modified from work created and shared by Google and used according to terms described in the Creative Commons 3.0 Attribution License.