Android: User Signup & Login


#1

I am new to couchbase and I ahve been trying to find a tutorial about how to allow users to signup and login from my Android application. Any ideas, tutorials, examples?


#2

There are different options for setting up authentication, see https://developer.couchbase.com/documentation/mobile/1.4/guides/authentication/index.html?language=ios. I suggest starting with basic auth and credentials hard coded in the config file :slight_smile:


#3

thank you for your response.
I was hoping for something more complete, is there anything else?


#4

Sorry, we don’t have any other materials that walk through the steps to setup sign up and login on Android from end to end. But happy to help with any questions you may have.


#5

this might help you, FYI


#6

thank you very much!
there is an error, since the class NetworkHelper is missing from the project.
Have you tried to run it?


#7

you can find the class detail in the README
Create a new java class called NetworkHelper with the following:

public static final MediaType JSON = MediaType.parse("application/json; charset=utf-8");

OkHttpClient client = new OkHttpClient();

Call post(String url, String json, Callback callback) {
    RequestBody body = RequestBody.create(JSON, json);
    Request request = new Request.Builder()
            .url(url)
            .post(body)
            .build();
    Call call = client.newCall(request);
    call.enqueue(callback);
    return call;
}


#8

I ended up using this example as a guide but it is still not working. Below is the SignupActivity.java that I’m using:

public class SignupActivity extends AppCompatActivity implements View.OnTouchListener {

public static final String DB_NAME = "myapp";

private Database mDatabase = null;

@Bind(R.id.input_name) EditText _nameText;
@Bind(R.id.input_firstname) EditText _firstnameText;
@Bind(R.id.input_lastname) EditText _lastnameText;
@Bind(R.id.input_confirmpassword) EditText _confirmpasswordText;
@Bind(R.id.input_email) EditText _emailText;
@Bind(R.id.input_password) EditText _passwordText;
@Bind(R.id.btn_signup) Button _signupButton;
@Bind(R.id.link_login) TextView _loginLink;
@Bind(R.id.ScrollViewSignup)    ScrollView _scrollLayout;

@Override
public void onCreate(Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_signup);
    ButterKnife.bind(this);

    _signupButton.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            MyUtilities.hideSoftKey(SignupActivity.this,v);
            signup();
        }
    });

    _loginLink.setOnClickListener(new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            // Finish the registration screen and return to the Login activity
            finish();
        }
    });
    _scrollLayout.setOnTouchListener(this);

    // Create a manager
    Manager manager = null;
    try {
        manager = new Manager(new AndroidContext(getApplicationContext()), Manager.DEFAULT_OPTIONS);
    } catch (IOException e) {
        e.printStackTrace();
    }

    // Create or open the database named app
    Database database = null;
    try {
        database = manager.getDatabase(DB_NAME);
    } catch (CouchbaseLiteException e) {
        e.printStackTrace();
    }

    // The properties that will be saved on the document
    Map<String, Object> properties = new HashMap<String, Object>();
    properties.put("name", _nameText);
    properties.put("password", _passwordText);
    properties.put("firstname", _firstnameText);
    properties.put("surname", _lastnameText);
    properties.put("email", _emailText);


    // Create a new document
    Document document = database.createDocument();

    // Save the document to the database
    try {
        document.putProperties(properties);
    } catch (CouchbaseLiteException e) {
        e.printStackTrace();
    }

    // Log the document ID (generated by the database)
    // and properties
    Log.d(DB_NAME, String.format("Document ID :: %s", document.getId()));
    Log.d(DB_NAME, String.format("Learning %s with %s %s %s %s", (String) document.getProperty("name"), (String) document.getProperty("password"),(String) document.getProperty("firstname"),
            (String) document.getProperty("surname"), (String) document.getProperty("email") ));

    // Create replicators to push & pull changes to & from Sync Gateway.
    URL url = null;
    try {
        url = new URL("http://serverurl:8080/REST-Model-context-root/resources/register");
    } catch (MalformedURLException e) {
        e.printStackTrace();
    }
    Replication push = database.createPushReplication(url);
    push.setContinuous(true);

    // Start replicators
    push.start();
}

public void signup() {

    if (!validate()) {
        onSignupFailed();
        return;
    }

    _signupButton.setEnabled(false);

    final ProgressDialog progressDialog = new ProgressDialog(SignupActivity.this,
            R.style.AppTheme_Dark_Dialog);
    progressDialog.setIndeterminate(true);
    progressDialog.setMessage("Creating Account...");
    progressDialog.show();

    String name = _nameText.getText().toString();
    String email = _emailText.getText().toString();
    String password = _passwordText.getText().toString();


    new android.os.Handler().postDelayed(
            new Runnable() {
                public void run() {
                    // On complete call either onSignupSuccess or onSignupFailed
                    // depending on success
                    onSignupSuccess();
                    progressDialog.dismiss();
                }
            }, 3000);
}


public void onSignupSuccess() {
    _signupButton.setEnabled(true);
    setResult(RESULT_OK, null);
    finish();
}

public void onSignupFailed() {
    Toast.makeText(getBaseContext(), "Login failed", Toast.LENGTH_LONG).show();

    _signupButton.setEnabled(true);
}

public boolean validate() {
    boolean valid = true;

    String name = _nameText.getText().toString();
    String email = _emailText.getText().toString();
    String password = _passwordText.getText().toString();

    if (name.isEmpty() || name.length() < 3) {
        _nameText.setError("at least 3 characters");
        valid = false;
    } else {
        _nameText.setError(null);
    }

    if (email.isEmpty() || !android.util.Patterns.EMAIL_ADDRESS.matcher(email).matches()) {
        _emailText.setError("enter a valid email address");
        valid = false;
    } else {
        _emailText.setError(null);
    }

    if (password.isEmpty() || password.length() < 4 || password.length() > 10) {
        _passwordText.setError("between 4 and 10 alphanumeric characters");
        valid = false;
    } else {
        _passwordText.setError(null);
    }

    return valid;
}

@Override
public boolean onTouch(View v, MotionEvent event) {

    MyUtilities.hideSoftKey(SignupActivity.this,v);
    return true;
}
}

Any ideas will be appreciated!!


#9

Hi @stoumpos,
What does a part of your codes not work as you expect?


#10

@hideki There are no errors but the users are not registered in the database


#11

I believe registering user into the DB points to following codes.

I am not super familiar with ButterKnife SDK. In your codes variables start with _ are Android View. _nameText does not automatically returns String value in the TextEdit. I assume you should do EditText.getText().toString() for example _name.getText().toString()

I hope this helps.


#12

I changed the code but unfortunately the result is the same…


#13

I suggest starting with basic auth and credentials hard coded in the config file

I’m researching how to authenticate our users with Couchbase Mobile. Hard coding user and passwords in the config file doesn’t ring well in my ears; that’s potentially a huge security risk.

Is this the recommended way? I want to use a basic authentication with username and password.


#14

True security on a mobile device is a myth (no matter how well you hide the keys to the door, if they are in the same place then someone will be able to find them eventually). That being said, there are things you can do to be reasonably secure.

For example, on Android you can make use of the built in encryption hardware, or at least the OS, to generate a per device encryption that you can use to store your password and username into the user preferences (assuming it is chosen at runtime).

If it is a static password, things are less easy because it will eventually have to either be stored somewhere in memory or on disk in unencrypted form in order to encrypt it for storage. I am not quite sure what to do in that situation.


#15

I’m not familiar with Android, but it must have an API for securely storing passwords and other credentials. (The iOS equivalent is the Keychain.) Any Android tutorial/demo/sample app that shows how to authenticate with an online service will show how to use this, since the basic task of storing a username and password is the same.


#16

The typical pattern on Android is to store the credentials in shared user preferences (as Jim suggested) and to encrypt it. The encryption key can be stored in the keystore. Before the keystore days, it was quite typical to use proguard obfuscation to hide the key.


#17

Thank you, all, for your answers.

What about the sync gateway config? It’s all still a bit fuzzy.

I’ve looked through some the examples. My understanding is that you can users to the sync gateway config file to authenticate (and sync) a certain user and her data. I don’t really understand what users to add…

My application have hundreds of users, divided in to a few (say, 25) user groups. Can these groups be inserted as user entities in the sync gateway config, or is the general recommendation to treat each application user uniquely and add all to the sync gateway config file? Or should I create new replication users (with little or no connection to the application users at all)?


#18

I wouldn’t add the users to the config file. Instead, use the admin REST API to add users.

And you probably want a separate user account for each application user.


#19

Alright, I think I got most of it now. I have one follow-up question, though.

I’ve been looking into the SG REST API to authenticate my users. My understanding is that you can use OpenID to authenticate users with the /{db}/_oidc api. We don’t use any OpenID solution in our application at the moment. Is it possible to use a basic authentication with the /{db}/_user/ api instead? (We’ve fooled around with this API, and one issue we have faced is that the password-- hashed or not --is not returned).

Thank you very much.


#20

OpenID isn’t required. You can specify a password when creating a user, and the client can log in via basic auth, or with cookies through the _session API.

one issue we have faced is that the password-- hashed or not --is not returned

Exposing the user’s password (even hashed) would be a security hole.