Showing posts with label Firebase. Show all posts
Showing posts with label Firebase. Show all posts

Thursday, 17 February 2022

 

How to implement Phone Verification (OTP Verification) using Firebase Authentication in your Android App (in brief)

1. Create a project in Android Studio.

2. Go Firebase Console, open a firebase account.

3. Sign-in & Add your Android Project to Firebase using package name.

4. Go to Preject Settings>General tab page from the left pane of the firebase console. From here, download the google-services.json. Put this file under the app root folder of your Android Studio project.

5. Now, in your android project add dependency for Firebase Authentication in module level (app level) build gradle file.

   dependencies {

            implementation platform(‘com.google.firebase:firebase-bom:29.1.0’)

            //declare dependencies for Firebase authentication library

            //no need to specify version number if firebase bom is used

            implementation ‘com.google.firebase:firebase-auth’

}

No need to put firebase-auth library version as firebase-bom version is mentioned.

6. Set app’s SHA-1, SHA-256 hash in firebase console. To do so, go to authentication tool from left pane of the project’s firebase console.

To get the SHA-1 & SHA-256 of the Android app, go to signing report from Gradle (right pane of the Android Studio) ->Android->task

7. To enable phone number signing in (OTP verification) for your android app go to Firebase Console à Authentication section.

From the sign-in method page enable Phone number sign-in method.

8. Enable app verification.

To enable app verification, do so through safetyNet , i) enable Android DeviceCheck API from Google Cloud Console. The default firebase API key will be used.

Ii) If SHA-256 is not added, do so from setting page of the firebase console.

Now, we can implement phone verification in our android app.

A. To implements phone verification first create an UI to accept users phone number in our Android Studio platform.

B. Pass this phone number along with other options to request to firebase to verify the phone number through the following method-

PhoneAuthOptions options =
  PhoneAuthOptions.newBuilder(mAuth)
      .setPhoneNumber(phoneNumber)       // Phone number to verify
      .setTimeout(60L, TimeUnit.SECONDS) // Timeout and unit
      .setActivity(this)                 // Activity (for callback binding)
      .setCallbacks(mCallbacks)          // OnVerificationStateChangedCallbacks
      .build();
  PhoneAuthProvider.verifyPhoneNumber(options);    

 

mAuth is an instance of FirebaseAuth class. Get mAuth in the way following,

FirebaseAuth mAuth = FirebaseAuth.getInstance();

FirebaseAuth instance is created depending upon the google-services.json settings file we downloaded in step 4 and saved it in the app root folder of the project.

C. The callback used in firebase phone verification is an instance of OnVerificationStateChangedCallbacks. So, implement it in your Activity.

mCallbacks = new PhoneAuthProvider.OnVerificationStateChangedCallbacks() {

    @Override
    public void onVerificationCompleted(PhoneAuthCredential credential) {
        // This callback will be invoked in two situations:
        // 1 - Instant verification. In some cases the phone number can be instantly
        //     verified without needing to send or enter a verification code.
        // 2 - Auto-retrieval. On some devices Google Play services can automatically
        //     detect the incoming verification SMS and perform verification without
        //     user action.
        Log.d(TAG, "onVerificationCompleted:" + credential);

        signInWithPhoneAuthCredential(credential);
    }

    @Override
    public void onVerificationFailed(FirebaseException e) {
        // This callback is invoked in an invalid request for verification is made,
        // for instance if the the phone number format is not valid.
        Log.w(TAG, "onVerificationFailed", e);

        if (e instanceof FirebaseAuthInvalidCredentialsException) {
            // Invalid request
        } else if (e instanceof FirebaseTooManyRequestsException) {
            // The SMS quota for the project has been exceeded
        }

        // Show a message and update the UI
    }

    @Override
    public void onCodeSent(@NonNull String verificationId,
                           @NonNull PhoneAuthProvider.ForceResendingToken token) {
        // The SMS verification code has been sent to the provided phone number, we
        // now need to ask the user to enter the code and then construct a credential
        // by combining the code with a verification ID.
        Log.d(TAG, "onCodeSent:" + verificationId);

        // Save verification ID and resending token so we can use them later
        mVerificationId = verificationId;
        mResendToken = token;
    }
};

 

 

D. After the user entering the phone number and click to pass the data to firebase through the verification method, the firebase will send a verification code to the user’s phone.

E. Create UI to accept the verification code (OTP).

F. The app will get a verification ID (not OTP) once when the data is sent to firebase through its verification method (look at the onCodeSent() method of the callback). We need to create a PhoneAuthCredential Object using this verificationID and the verification code (OTP) that user will receive in his/her phone.

PhoneAuthCredential credential = PhoneAuthProvider.getCredential(verificationId, code);

 

G. Now the user can be signed in with the credential through the following FirebaseAuth.signInWithCredential method.

private void signInWithPhoneAuthCredential(PhoneAuthCredential credential) {
    mAuth.signInWithCredential(credential)
            .addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
                @Override
                public void onComplete(@NonNull Task<AuthResult> task) {
                    if (task.isSuccessful()) {
                        // Sign in success, update UI with the signed-in user's information
                        Log.d(TAG, "signInWithCredential:success");

                        FirebaseUser user = task.getResult().getUser();
                        // Update UI
                    } else {
                        // Sign in failed, display a message and update the UI
                        Log.w(TAG, "signInWithCredential:failure", task.getException());
                        if (task.getException() instanceof FirebaseAuthInvalidCredentialsException) {
                            // The verification code entered was invalid
                        }
                    }
                }
            });
}

 

The FirebaseUser object  that we get from the above signInWithPhoneCredential() method holds all related information of the FirebaseUser client.

Monday, 25 October 2021

 

SEND FIREBASE CLOUD MESSAGES (FCM) TO YOUR DESIRED ANDROID USERS USING NODE JS SERVER

Architecture:



As the figure says, we will send notification messages from our android set to our own server (say Node js web app). In the Node js web app we implemented the admin sdk of Firebase Cloud messaging (FCM) server. The Nodejs web app communicates with the firebase cloud server using its app credentials. The FCM (Firebase Cloud Messaging) server now serve the users of defined categories.

[This article should be read as the appended section of SEND YOUR FIRST ANDROID NOTIFICATION USING FCM (Firebase Cloud Messaging)]

 

1. In your Firebase console go to Project Settings>Service Accounts

2. In the Service Accounts window click Generate New Private Key. Then in the confirmation window click Generate Key.

3. The Google Credential json file will be downloaded to the downloads folder of the browser.

Keep this file safe.

4. We need to put this file in the project root of our Node js web app.

5. SETUP THE NODEJS WEB SERVER

Now install firebase admin sdk in our server.

In my case my server is an apache shared server. I have node js setup tool in it.



(i) first create a folder in my shared server root directory. Under it I will create my base directory for my nodejs web app.

(ii) it is better to create a demo app in our local WAMP or LAMP server with npm init.

(iii) the main app.js file along with package.json will be created there.

(iv) now upload this app.js along with package.json to our shared server base directory.

(v) Now go to nodejs setup.



(vi) specify Node js app root directory in Application root.

And put the application url.

(vii) Application startup file(app,js) & log file

(viii) click on Edit under Run NPM Install.

(ix) Here enter in the dependency section the firbase-admin as following:

“firebase-admin”: “”

(x) Now install packages by clicking Run NPM Install.

(xi) After successful installation open app.js and add the following code in app.js file:

var express = require('express')

var admin = require("firebase-admin");

const port = 2388;

app.get('/', (req,res)=>{

    res.status(200).send("Hello World")

})

app.listen(port, () =>{

console.log("listening to port"+port);

})

(xii) Save app.js and restart the web app from Node js setup.

 

(xiii) Setup the subdomain for your web app. Make https redirect to it. (xiv) Now in the browser test your web app by typing your (sub)domain name.

This will show now Hello World on the browser.

It proves that our web app setup is correct and working.

6. Now we have Google Credential json file containing firebase keys stored in the base directory of our web app.

7. Now inside the app.js add the following code to initialize the Firebase Admin SDK for messaging.

var admin = require("firebase-admin");

var serviceAccount = require("./myTopic-firebase-msg.json");

admin.initializeApp({

  credential: admin.credential.cert(serviceAccount),

  databaseURL: "htts://<your firebase database>.firebaseio.com"

})

Here myTopic-firebase-msg.json is the json file I downloaded in the step 3 and renamed it. I put this file in web app’s base directory.

Also the initialization requires a database url to the realtime database (rtdc) of your Firebase Account (You will get it from Real Time Database section on your Firebase console).

8. Further put a module like below to accept request from the messaging app that controls the notification to all users.

 app.post('/firebase/notification', (req, res)=>{

    var topic;

    var title;

    var message;

    var body = [];

    req.on('error', (error)=>{

        console.log(error);

    }).on('data', (chunk)=>{

        body.push(chunk);

   }).on('end', ()=>{

        body = Buffer.concat(body).toString();

        res.on('error', (err)=>{                                   

                console.error('Error Occured :'+err);

        });

        var json = JSON.parse(body);

        topic = json.topic

        title = json.title

        message = json.body

   

        const messaging = admin.messaging();

        var payload = {

                notification: {

                        title: title,

                        body: message

                }

        };

        messaging.sendToTopic(topic, payload)

        .then(result=>{

                Console.log('The response: '+result)

        });

        res.send('Its OK..');

        res.end();

   })

})

Here be careful to construct the right payload with notification and topic. This payload will reach to the end users.

9. Now save the app.js file. Restart the Node js web app from nodejs setup.

10. Now make the following changes in our message sending android app:

In your MainActivity do the following changes:

   

public class MainActivity extends AppCompatActivity{

        protected void onCreate(Bundle savedInstanceState) {

                super.onCreate(savedInstanceState);

                setContentView(R.layout.your_layout);

               

                --------   --------- ------

                Button btn = findViewById(R.id.sendMsg);

                Btn.setOnClickListener(new View.OnClickListener() {

                @Override

                public void onClick(View v) {

                        AsyncTaskClass asyncClass = new AsyncTaskClass();

                        asyncClass.execute();

                 }

                });

        }

 

        class AsyncTaskClass extends AsyncTask<Void,Void,String> {

        @Override

        protected void onPreExecute() {

            super.onPreExecute();

        }

 

        @Override

        protected void onPostExecute(String string1) {

            super.onPostExecute(string1);

        }

 

        @Override

        protected String doInBackground(Void... params) {

            ServerUploadPath = "https://your.domain.com/firebase/notification";

            String topic = "urgent";

                        String title = "Test Notification";

                        String body = "Meeting called at 8:30 pm";

            QString = makeMessageString(new MsgToSend(topic , title, body));

 

            String finalData = sendData(ServerUploadPath,QString);

            runOnUiThread(new Runnable() {

                @Override

                public void run() {

                    //process the response finalData

                }

            });

            return finalData;

        }

    }

       

        private String sendData(String requestURL, String QString){

                final StringBuilder stringBuilder = new StringBuilder();

 

        try {

 

            URL url;

            HttpURLConnection httpURLConnectionObject ;

            OutputStream outPutStream;

            BufferedWriter bufferedWriterObject ;

            BufferedReader bufferedReaderObject ;

            int respCode ;

 

            url = new URL(requestURL);

 

            httpURLConnectionObject = (HttpURLConnection) url.openConnection();

            httpURLConnectionObject.setReadTimeout(19000);

            httpURLConnectionObject.setConnectTimeout(19000);

            httpURLConnectionObject.setRequestMethod("POST");

            httpURLConnectionObject.setDoInput(true);

            httpURLConnectionObject.setDoOutput(true);

            outPutStream = httpURLConnectionObject.getOutputStream();

            bufferedWriterObject = new BufferedWriter(new OutputStreamWriter(outPutStream, "UTF-8"));

            bufferedWriterObject.write(QString);

            bufferedWriterObject.flush();

            bufferedWriterObject.close();

            outPutStream.close();

            respCode = httpURLConnectionObject.getResponseCode();

            if (respCode == HttpsURLConnection.HTTP_OK) {

                bufferedReaderObject = new BufferedReader(new InputStreamReader(httpURLConnectionObject.getInputStream()));

                //StringBuilder stringBuilder1 = new StringBuilder();

                String received;

                while ((received = bufferedReaderObject.readLine()) != null){

                    stringBuilder.append(received);

                }

                                Handler handler = new Handler(Looper.getMainLooper());

                handler.post(new Runnable() {

                    @Override

                    public void run() {

                        Toast.makeText(MainActivity.this, stringBuilder.toString(), Toast.LENGTH_LONG).show();

                    }

                });

            }

 

        } catch (Exception e) {

            e.printStackTrace();

        }

        return stringBuilder.toString();

       

        }

       

        private class MsgToSend{

        String topic;

        String title;

        String body;

        public MsgToSend(String topic,String title, String body){

            this.topic = topic;

            this.title = title;

            this.body = body;

        }

    }

        private String makeMessageString(MsgToSend msg){

        Gson gson = new Gson();

        Type type = new TypeToken<MsgToSend>(){}.getType();

        String rjson = gson.toJson(msg,type);

        return rjson;

    }

}      

 

11. In the user’s client Android app keep the FrebaseMessageReceiver class unchanged. (This class we created earlier in the previous section -SEND YOUR FIRST ANDROID NOTIFICATION USING FCM).

Also, In the main launcher activity onCreate method must add the following code to subscribe the user for definite topic.

FirebaseMessaging.getInstance().subscribeToTopic(topicname);

To unsubscribe the topic topicname user app must call unsubscribeFromTopic(topicname);

FirebaseMessaging.getInstance().unsubscribeFromTopic(topicname);

Note:

12. This change in user’s client android app will enable it to receive FCM messages based on its subscribed topic.

Now Install client android app in user’s device and install the sender app to your device. On clicking the button on sender app push notification will be served to those user’s app who have subscribed to topic “urgent” (as I have mentioned “urgent” as the topic in query string (QString) in my sender’s app. This query string is collected by our nodejs web app and extracts the topic from this query.).

 

FURTHER READING: HANDLING RECEIVING OF FCM MESSAGES IN ANDROID IN EFFICIENT MANNER.