0

I'm creating an Android application that makes use of a MySQL database to store user credentials, and PHP which is ran on an XAMPP server on my local machine. I have created an authentication flow that makes use of the database and the Android app does it via HTTP requests using the PHP.

Yesterday I managed to get a working authentication that allowed me to register a user, and then login using that users credentials. Today, I start up my server and go to register a user and I receive the error that I have written in Android when the createUser function is called - Unknown error occurred. Please try again!. I have checked the logs for my server and there are no error messages or anything that looks out of place.

Does anyone know what might be the issue here? It still allows me to login using the user that I created yesterday when it was running fine, but won't allow any further registrations.

register.php:

<?php

require_once 'include/db_functions.php';
$db = new DB_FUNCTIONS();

$response = array("error" => FALSE);

if (isset($_POST['name']) && isset($_POST['email']) && isset($_POST['password']) && isset($_POST['phone_number'])) {
    $name = $_POST['name'];
    $email = $_POST['email'];
    $password = $_POST['password'];
    $phone_number = $_POST['phone_number'];

    if ($db -> isExistingUser($email)) {
        $response["error"] = TRUE;
        $response["error_msg"] = "Email address is already registered. Please try signing in: " . $email;
        echo json_encode($response);
    } else {
        $user = $db -> createUser($name, $email, $password, $phone_number);
        if ($user) {
            $response["error"] = FALSE;
            $response["uid"] = $user["uid"];
            $response["user"]["name"] = $user["name"];
            $response["user"]["email"] = $user["email"];
            $response["user"]["phone_number"] = $user["phone_number"];
            $response["user"]["created_at"] = $user["created_at"];
            $response["user"]["updated_at"] = $user["updated_at"];
            echo json_encode($response);
        } else {
            $response["error"] = TRUE;
            $response["error_msg"] = "Unknown error occurred. Please try again!";
            echo json_encode($response);
        }
    }
} else {
    $response["error"] = TRUE;
    $response["error_msg"] = "Some of the required parameters are missing. Please ensure all required fields are valid.";
    echo json_encode($response);
}

db_functions.php:

<?php

class DB_FUNCTIONS {

    private $conn;

    function __construct()
    {
        require_once 'db_connect.php';

        // Connecting to database
        $db = new DB_CONNECT();
        $this -> conn = $db -> connect();
    }

    function __destruct()
    {
        // TODO: Implement __destruct() method.
    }

    public function createUser($name, $email, $password, $phone_number) {
        $uuid = uniqid('', true);
        $hash = $this -> hashSSHA($password);
        $encrypted_password = $hash["encrypted"];
        $salt = $hash["salt"];


        $sql = "INSERT INTO users(uid, name, email, password, salt, phone_number, created_at) VALUES (?, ?, ?, ?, ?, ?, NOW())";
        if ($stmt = $this -> conn -> prepare($sql)) {
            $stmt -> bind_param("ssssss", $uuid, $name, $email, $encrypted_password, $salt, $phone_number);

            $result = $stmt -> execute();
            $stmt -> close();
        } else {
            $error = $this -> conn -> errno . '' . $this -> conn -> error;
            echo $error;
        }

        if ($result) {
            $stmt = $this -> conn -> prepare("SELECT * FROM users WHERE email = ?");
            $stmt -> bind_param("s", $email);
            $stmt -> execute();
            $user = $stmt -> get_result() -> fetch_assoc();
            $stmt -> close();

            return $user;
        } else {
            return false;
        }
    }

    public function getUserByEmailAndPassword($email, $password) {
        $stmt = $this -> conn -> prepare("SELECT * FROM users WHERE email = ?");
        $stmt -> bind_param("s", $email);

        if ($stmt -> execute()) {
            $user = $stmt -> get_result() -> fetch_assoc();
            $stmt -> close();

            $salt = $user['salt'];
            $encrypted_password = $user['password'];
            $hash = $this -> checkSSHA($salt, $password);

            if ($encrypted_password == $hash) {
                return $user;
            }
        } else {
            return NULL;
        }
    }

    public function isExistingUser($email) {
        $sql = "SELECT email from users WHERE email = ?";
        if ($stmt = $this -> conn -> prepare($sql)) {
            $stmt -> bind_param("s", $email);
            $stmt -> execute();
            $stmt -> store_result();
        } else {
            $error = $this -> conn ->errno . '' . $this -> conn -> error;
            echo $error;
        }

        if ($stmt->num_rows > 0) {
            $stmt->close();
            return true;
        } else {
            $stmt->close();
            return false;
        }
    }

    public function hashSSHA($password) {
        $salt = sha1(rand());
        $salt = substr($salt, 0, 30);
        $encrypted = base64_encode(sha1($password . $salt, true) . $salt);
        $hash = array("salt" => $salt, "encrypted" => $encrypted);
        return $hash;
    }

    public function checkSSHA($salt, $password) {
        $hash = base64_encode(sha1($password . $salt, true) . $salt);
        return $hash;
    }
}

RegisterActivity.java:

package com.example.dentdevils.ui;

import androidx.appcompat.app.AppCompatActivity;

import android.app.ProgressDialog;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;

import com.android.volley.Request;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.StringRequest;
import com.example.dentdevils.MainActivity;
import com.example.dentdevils.R;
import com.example.dentdevils.helper.AppConfig;
import com.example.dentdevils.helper.AppController;
import com.example.dentdevils.helper.SQLiteHandler;
import com.example.dentdevils.helper.SessionManager;

import org.json.JSONException;
import org.json.JSONObject;

import java.util.HashMap;
import java.util.Map;

public class RegisterActivity extends AppCompatActivity {

    private static final String TAG = RegisterActivity.class.getSimpleName();

    private Button btnRegister;
    private Button backToLogin;
    private EditText inputName;
    private EditText inputEmail;
    private EditText inputPassword;
    private EditText inputNumber;
    private ProgressDialog progressDialog;
    private SessionManager sessionManager;
    private SQLiteHandler db;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_register);

        inputName = findViewById(R.id.txtEditName);
        inputEmail = findViewById(R.id.txtEditEmailAddress);
        inputPassword = findViewById(R.id.txtEditPassword);
        inputNumber = findViewById(R.id.txtEditPhoneNumber);
        btnRegister = findViewById(R.id.btnRegister);
        backToLogin = findViewById(R.id.btnBackToLogin);

        progressDialog = new ProgressDialog(this);
        progressDialog.setCancelable(false);

        sessionManager = new SessionManager(getApplicationContext());

        db = new SQLiteHandler(getApplicationContext());

        if (sessionManager.isLoggedIn()) {
            Intent intent = new Intent(RegisterActivity.this, HomeActivity.class);
            startActivity(intent);
            finish();
        }

        // Register Button Click Event
        btnRegister.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                String name = inputName.getText().toString().trim();
                String email = inputEmail.getText().toString().trim();
                String password = inputPassword.getText().toString().trim();
                String phone_number = inputNumber.getText().toString().trim();

                if (!name.isEmpty() && !email.isEmpty() && !password.isEmpty() && !phone_number.isEmpty()) {
                    registerUser(name, email, password, phone_number);
                } else {
                    Toast.makeText(getApplicationContext(), "All fields must be filled in!", Toast.LENGTH_LONG).show();
                }
            }
        });

        backToLogin.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                Intent backToHome = new Intent(getApplicationContext(), MainActivity.class);
                startActivity(backToHome);
                finish();
            }
        });
    }


    private void registerUser(final String name, final String email, final String password, final String phone_number) {
        String tag_string_req = "req_register";

        progressDialog.setMessage("Registering user...");
        showDialog();

        StringRequest stringRequest = new StringRequest(Request.Method.POST, AppConfig.URL_REGISTER, new Response.Listener<String>() {
            @Override
            public void onResponse(String response) {
                Log.d(TAG, "Registration log: " + response.toString());
                hideDialog();

                try {
                    JSONObject jsonObject = new JSONObject(response);
                    boolean error = jsonObject.getBoolean("error");
                    if (!error) {
                        String uid = jsonObject.getString("uid");

                        JSONObject user = jsonObject.getJSONObject("user");
                        String name = user.getString("name");
                        String email = user.getString("email");
                        String phone_number = user.getString("phone_number");
                        String created_at = user.getString("created_at");

                        db.addUser(uid, name, email, phone_number, created_at);
                        Toast.makeText(getApplicationContext(), "User account created successfully! You may now login!", Toast.LENGTH_LONG).show();

                        Intent toLogin = new Intent(RegisterActivity.this, MainActivity.class);
                        startActivity(toLogin);
                        finish();
                    } else {
                        String errorMsg = jsonObject.getString("error_msg");
                        Toast.makeText(getApplicationContext(), errorMsg, Toast.LENGTH_LONG).show();
                    }
                } catch (JSONException e) {
                    e.printStackTrace();
                }
            }
        }, new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(TAG, "Registration failed: " + error.getMessage());
                Toast.makeText(getApplicationContext(), error.getMessage(), Toast.LENGTH_LONG).show();
                hideDialog();
            }
        }) {
            @Override
            protected Map<String, String> getParams() {
                Map<String, String> params = new HashMap<String, String>();
                params.put("name", name);
                params.put("email", email);
                params.put("password", password);
                params.put("phone_number", phone_number);

                return params;
            }
        };

        AppController.getInstance().addToRequestQueue(stringRequest, tag_string_req);
    }

    private void showDialog() {
        if (!progressDialog.isShowing()) {
            progressDialog.show();
        }
    }

    private void hideDialog() {
        if (progressDialog.isShowing()) {
            progressDialog.dismiss();
        }
    }
}

mysql_error.log:

2020-07-14 13:28:28 0 [Note] InnoDB: Mutexes and rw_locks use Windows interlocked functions
2020-07-14 13:28:28 0 [Note] InnoDB: Uses event mutexes
2020-07-14 13:28:28 0 [Note] InnoDB: Compressed tables use zlib 1.2.11
2020-07-14 13:28:28 0 [Note] InnoDB: Number of pools: 1
2020-07-14 13:28:28 0 [Note] InnoDB: Using SSE2 crc32 instructions
2020-07-14 13:28:28 0 [Note] InnoDB: Initializing buffer pool, total size = 16M, instances = 1, chunk size = 16M
2020-07-14 13:28:28 0 [Note] InnoDB: Completed initialization of buffer pool
2020-07-14 13:28:28 0 [Note] InnoDB: Starting crash recovery from checkpoint LSN=628186
2020-07-14 13:28:28 0 [Note] InnoDB: 128 out of 128 rollback segments are active.
2020-07-14 13:28:28 0 [Note] InnoDB: Removed temporary tablespace data file: "ibtmp1"
2020-07-14 13:28:28 0 [Note] InnoDB: Creating shared tablespace for temporary tables
2020-07-14 13:28:28 0 [Note] InnoDB: Setting file 'C:\xampp\xampp\mysql\data\ibtmp1' size to 12 MB. Physically writing the file full; Please wait ...
2020-07-14 13:28:28 0 [Note] InnoDB: File 'C:\xampp\xampp\mysql\data\ibtmp1' size is now 12 MB.
2020-07-14 13:28:28 0 [Note] InnoDB: Waiting for purge to start
2020-07-14 13:28:28 0 [Note] InnoDB: 10.4.13 started; log sequence number 628195; transaction id 724
2020-07-14 13:28:28 0 [Note] InnoDB: Loading buffer pool(s) from C:\xampp\xampp\mysql\data\ib_buffer_pool
2020-07-14 13:28:28 0 [Note] Plugin 'FEEDBACK' is disabled.
2020-07-14 13:28:28 0 [Note] Server socket created on IP: '127.0.0.1'.
2020-07-14 13:28:28 0 [Note] InnoDB: Buffer pool(s) load completed at 200714 13:28:28

Apache error.log:

[Tue Jul 14 13:28:27.937378 2020] [core:warn] [pid 9200:tid 592] AH00098: pid file C:/xampp/xampp/apache/logs/httpd.pid overwritten -- Unclean shutdown of previous Apache run?
[Tue Jul 14 13:28:27.961314 2020] [mpm_winnt:notice] [pid 9200:tid 592] AH00455: Apache/2.4.43 (Win64) OpenSSL/1.1.1g PHP/7.4.7 configured -- resuming normal operations
[Tue Jul 14 13:28:27.961314 2020] [mpm_winnt:notice] [pid 9200:tid 592] AH00456: Apache Lounge VC15 Server built: Apr 22 2020 11:11:00
[Tue Jul 14 13:28:27.961314 2020] [core:notice] [pid 9200:tid 592] AH00094: Command line: 'c:\\xampp\\xampp\\apache\\bin\\httpd.exe -d C:/xampp/xampp/apache'
[Tue Jul 14 13:28:27.963309 2020] [mpm_winnt:notice] [pid 9200:tid 592] AH00418: Parent: Created child process 6588
[Tue Jul 14 13:28:28.387533 2020] [mpm_winnt:notice] [pid 6588:tid 740] AH00354: Child: Starting 150 worker threads.

If there is anything else required please ask.

AF_web
  • 89
  • 11
  • could you check the php error too ? https://stackoverflow.com/questions/3719549/where-does-phps-error-log-reside-in-xampp for where you can find the log – benjiii Jul 14 '20 at 12:51
  • @benjiii I believe the PHP errors are logged to the Apache error log which I have already shown – AF_web Jul 14 '20 at 13:12
  • well that is weird, are you sure your errors are logged? cf https://stackoverflow.com/questions/7667160/not-displaying-php-errors – benjiii Jul 14 '20 at 14:12
  • @benjiii yes I ensured all my errors were being logged. I'm confused as to what would change overnight, I've spent the entire day going back through my git commits and seeing if changing something fixes it but it does not unfortunately – AF_web Jul 14 '20 at 14:20
  • Your $result could be undefined if there's a database error, depending on your error displaying it might be silently failing. As a side note, you should looking into using PHP's password_hash/verify functions instead of writing your own – lukesrw Jul 14 '20 at 14:23
  • @lukesrw you're right, it seems that $result is showing up as being undefined so I'll look into that now! Thanks for the reccommendation into password_hash, I'll take a look! – AF_web Jul 14 '20 at 14:28

0 Answers0