Android – Introduction to ASP.NET Identity

Following the post C# – ASP.NET Web API and ASP.NET Identity, I would like to show how we can consume a REST web service authenticated by ASP.NET Identity within Android. In this post, we’ll repeat the steps we’ve done before in .NET client but now with Java and Android. If you follow my posts before in Android section, you must be very familiar with consuming ASP.NET Web API service. We’ll use same technique again, no big deal, however I’ll introduce some new functions in JSONHttpClient class for communication with ASP.NET Identity.

1. Prerequisites

I suggest to read the post above first (ASP.NET Identity and .NET client) if you don’t know what ASP.NET Identity is and how it works. Registering user in Android will be exactly same, we’ll send same model data, same HTTP actions to service. Therefore I will fly through the code without explaining much about registration procedure.
I use AndroidAnnotations and Maven for keeping code small and clean. If you don’t know what they are, please read this post first Android – Introduction to AndroidAnnotations, Maven in Intellij IDEA

2. Server

We will use the sample service http://aspnetwebapiandaspnetidentity.apphb.com/ from previous post. As a REST web service, this sample service is able to be universally consumed by any client on any platform. It doesn’t matter if client is on Windows, Linux, IOS or Android… What important is, the client must support HTTP protocol and that’s all.

3. Client

The Android client is very simple and consists of 2 activities: register/login and resource activity. When the demo get started, it’ll welcome user with register activity. User should register himself and then he can log in with his authentication data. A successful log in will forward him to resource of service.

Register activity

Protected resources

Not like other public service we’ve done before, though the new protected service can be called through browser

http://aspnetwebapiandaspnetidentity.apphb.com/api/values

but if we browse directly to this URL we’ll receive a rejected response of Authorization has been denied for this request because the authentication data is not available. The protected service requires a valid token to let any request through. So we need to authenticate ourselves as a registered users to access it with following 3 steps

1. Register user.
2. Get OAuth Token.
3. Set OAuth Token in HTTP header for every request sent to service.

3.1 Register user and get token

As explained in other posts, to register a user, we have to post an object of RegisterBindingModel to api/Account/Register. The RegisterBindingModel contains username and password as defined in code listing below

public class RegisterBindingModel {
    private String email;
    private String password;
    private String confirmPassword;

    public RegisterBindingModel() {
    }

    public RegisterBindingModel(String email, String password, String confirmPassword) {
        this.email = email;
        this.password = password;
        this.confirmPassword = confirmPassword;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public String getConfirmPassword() {
        return confirmPassword;
    }

    public void setConfirmPassword(String confirmPassword) {
        this.confirmPassword = confirmPassword;
    }
}

As default, a valid password for ASP.NET Identity must contains at least one number, one special character, one lower case, one uppercase and at least 6 characters long. Therefore in Android client we have to validate entered password before sending it to service. I suggest using Regex.

@Click(R.id.buttonRegister)
void Register() {
	String email = editTextEmail.getText().toString();
	String password = editTextPassword.getText().toString();
	if (!StringUtil.isNullOrWhitespace(email) && !StringUtil.isNullOrWhitespace(email)) {
		if (StringUtil.isEmailAddress(email)) {
			if (StringUtil.isMatch("^(?=.*\\d)(?=.*[a-z])(?=.*[A-Z])(?=.*(_|[^\\w])).{6,}$", password)) {
				RegisterBindingModel model = new RegisterBindingModel(email, password, password);
				new RegisterAsyncTask().execute(model);
			} else
				AlertMessageBox.Show(MainActivity.this, "Error", "Passwords must have at least one non letter and digit character. Passwords must have at least one lowercase ('a'-'z'). Passwords must have at least one uppercase ('A'-'Z').", AlertMessageBox.AlertMessageBoxIcon.Error);

		} else
			AlertMessageBox.Show(MainActivity.this, "Error", "A valid email address is required", AlertMessageBox.AlertMessageBoxIcon.Error);
	} else
		AlertMessageBox.Show(MainActivity.this, "Error", "Email and password are required", AlertMessageBox.AlertMessageBoxIcon.Error);
}

public class StringUtil {
    public static boolean isMatch(String regex,String text)
    {
        try {
            Pattern pattern = Pattern.compile(regex);
            Matcher matcher = pattern.matcher(text);
            return matcher.matches();
        } catch (RuntimeException e) {
            return false;
        }
    }
}

After validating username and password, we can send RegisterBindingModel model to service to register user

class RegisterAsyncTask extends AsyncTask<RegisterBindingModel, String, AuthenticationResult> {
	@Override
	protected AuthenticationResult doInBackground(RegisterBindingModel... models) {
		String url = ServiceUrl.REGISTER;
		RegisterBindingModel model = models[0];
		JSONHttpClient httpClient = new JSONHttpClient();
		String result = httpClient.PostObject(ServiceUrl.REGISTER, model);
		if (StringUtil.isNullOrWhitespace(result)) {

			url = ServiceUrl.TOKEN;
			String object = String.format("grant_type=password&username=%s&password=%s", model.getEmail(), model.getPassword());
			TokenModel tokenModel = httpClient.PostStringAsFormUrlEncodedContent(ServiceUrl.TOKEN, object, TokenModel.class);
			if (tokenModel != null && tokenModel.AccessToken != null) {
				return new AuthenticationResult(true, tokenModel.AccessToken, null);
			} else
				return new AuthenticationResult(false, null, "Get Token failed");

		} else
			return new AuthenticationResult(false, null, result);


	}

	@Override
	protected void onPostExecute(final AuthenticationResult result) {


		if (!result.isSuccessful())
			AlertMessageBox.Show(MainActivity.this, "Error", result.getError(), AlertMessageBox.AlertMessageBoxIcon.Error);
		else {
			AlertMessageBox.Show(MainActivity.this, "Successful", "Register successfully. You're going to be forwarded to protected resource. ", AlertMessageBox.AlertMessageBoxIcon.Info, new AlertMessageBoxOkClickCallback() {
				@Override
				public void run() {
					ValueActivity_.intent(MainActivity.this).accessToken(result.getAccessToken()).start();
				}
			});


			super.onPostExecute(result);
		}
	}
}

If the registration process encounters no error, web service will reply with a response of HTTP STATUS 200 OK and blank body. Then we can ask service for an OAuth Token by sending HTTP POST with body of grant_type=password&username=%s&password=%s to Token. Having this OAuth Token we’ll can now access any protected resources.

@EActivity(R.layout.value)
public class ValueActivity extends ListActivity {

    String[] values;

    @Extra("AccessToken")
    String accessToken;

    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
    }

    @AfterViews
    void afterViews() {
        new GetValuesTask().execute();

    }



    class GetValuesTask extends AsyncTask<String, String, String> {

        @Override
        protected String doInBackground(String... params) {

            List<NameValuePair> headers = new ArrayList<NameValuePair>();
            headers.add(new BasicNameValuePair("Authorization", String.format("Bearer %s", accessToken)));

            JSONHttpClient httpClient = new JSONHttpClient();
            values = httpClient.GetWithHeader(ServiceUrl.VALUES,headers,new ArrayList<NameValuePair>(),String[].class );
            return null;
        }

        @Override
        protected void onPostExecute(String s) {
            ListAdapter adapter = new ArrayAdapter(ValueActivity.this,android.R.layout.simple_list_item_1,values);
            setListAdapter(adapter);
            super.onPostExecute(s);
        }
    }
}

3.2 Query protected resources

With OAuth Token in hand we can now access protected resources by adding the Token into HTTP header with format of Authorization: Bearer {Token}. After registration, the Token is issued by calling Token with syntax of grant_type=password&username=%s&password=%s.

class GetValuesTask extends AsyncTask<String, String, String> {

	@Override
	protected String doInBackground(String... params) {

		List<NameValuePair> headers = new ArrayList<NameValuePair>();
		headers.add(new BasicNameValuePair("Authorization", String.format("Bearer %s", accessToken)));

		JSONHttpClient httpClient = new JSONHttpClient();
		values = httpClient.GetWithHeader(ServiceUrl.VALUES,headers,new ArrayList<NameValuePair>(),String[].class );
		return null;
	}

	@Override
	protected void onPostExecute(String s) {
		ListAdapter adapter = new ArrayAdapter(ValueActivity.this,android.R.layout.simple_list_item_1,values);
		setListAdapter(adapter);
		super.onPostExecute(s);
	}
}

The GetWithHeader functions is just a simple GET with Token headers in it

public <T> T GetWithHeader(String url, final List<NameValuePair> headers, final List<NameValuePair> params, final Class<T> objectClass)
{


	DefaultHttpClient defaultHttpClient = new DefaultHttpClient();
	if (params.size() > 0) {
		String paramString = URLEncodedUtils.format(params, "utf-8");
		url += "?" + paramString;
	}
	HttpGet httpGet = new HttpGet(url);
	try {

		httpGet.setHeader("Accept", "application/json");
		httpGet.setHeader("Accept-Encoding", "gzip")  ;
		for(NameValuePair header:headers)
		{
			httpGet.setHeader(header.getName(),header.getValue());
		}

		HttpResponse httpResponse = defaultHttpClient.execute(httpGet);
		HttpEntity httpEntity = httpResponse.getEntity();
		if (httpEntity != null) {
			InputStream inputStream = httpEntity.getContent();
			Header contentEncoding = httpResponse.getFirstHeader("Content-Encoding");
			if (contentEncoding != null && contentEncoding.getValue().equalsIgnoreCase("gzip")) {
				inputStream = new GZIPInputStream(inputStream);
			}

			String resultString = convertStreamToString(inputStream);
			inputStream.close();
			if (resultString.indexOf("{")>=0)
			{
				resultString = resultString.substring(resultString.indexOf("{"), resultString.lastIndexOf("}") + 1);
			}
			return new GsonBuilder().create().fromJson(resultString, objectClass);

		}

	} catch (UnsupportedEncodingException e) {
		e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
	} catch (ClientProtocolException e) {
		e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
	} catch (IOException e) {
		e.printStackTrace();  //To change body of catch statement use File | Settings | File Templates.
	}
	return null;
}

4. Conclusion

ASP.NET Identity allows us to build a REST web service with OAuth authentication. By using ASP.NET Identity we can easily protect our resources from unauthorized access. The integration to available ASP.NET Web API isn’t too complicated. Registration as well as authentication can be made through standard HTTP actions which can be created on any platform.

Source code : https://bitbucket.org/hintdesk/android-hintdesk-core
Source code : https://bitbucket.org/hintdesk/android-introduction-to-asp-net-identity/

4 thoughts on “Android – Introduction to ASP.NET Identity”

  1. I use OkHttp instead JSONHttpClient and works like a charms:

    Request request = new Request.Builder()
    .url(ServerURLServices.VALUES)
    .addHeader(“Authorization”, String.format(“Bearer %s”,Token ))

    Thanks for this examples, Excellent work.

    Greetings from colombia.

  2. Good day, I have been working through your project and I am stumped on something, I have the code all written out, but Android Studio gives an error at this location.

    ValueActivity.intent(MainActivity.this).accessToken(result.getAccessToken()).start();

    it is saying the method intent can’t be resolved, I have chacked and double checked, and can’t seem to find out what is missing

  3. This is an exellent article.
    Can You give me an information what is license type for package com.hintdesk.core.* ?
    Can I use this package in commercial projects?

Leave a Reply

Your email address will not be published. Required fields are marked *