รู้จัก Firebase Authentication ตั้งแต่ Zero จนเป็น Hero

Jirawatee
Firebase Thailand

--

การพัฒนาระบบ Log-in ขึ้นมาโดยให้มันสามารถใช้งานข้าม platform ได้ทั้ง Android, iOS และ Web คุณคิดว่าเราจะพัฒนามันอย่างไร และนานแค่ไหน โดยเริ่มต้นทุกคนก็คงคิดถึงภาพด้านล่างนี้ก่อน

นั่นหมายความว่า เราจะต้องหา server หรือ cloud ที่ performance ดีๆ และ scale ได้สักตัว, ต้องออกแบบ database เพื่อรองรับ third party ต่างๆ, ต้องทำความเข้าใจเรื่อง OAuth 2.0 และติดตั้ง SSL ลงไปเพื่อความปลอดภัย, ต้องพัฒนา backend และสุดท้ายก็พัฒนา frontend ทั้ง Android, iOS และ Web

แค่คิดก็อยากจะไปพักแล้ว จะทำอย่างไรให้ทั้งหมดที่พูดมาเสร็จได้เร็วที่สุด…แน่นอน คุณโชคดีแล้วที่กำลังอ่านบทความนี้อยู่ เพราะเรากำลังจะทำความรู้จักกับ Firebase Authentication ซึ่งเป็นบริการที่เตรียมทุกอย่างที่ว่ามาให้คุณหมดแล้ว แถมฟรีอีก

Firebase Authentication จะเป็นบริการที่เข้ามาจัดการ backend ให้คุณทั้งหมด ทั้ง การ register, การ sign-in, การ reset password, โดยจะมี SDK ให้ทั้ง Android, iOS และ Web นำไปติดตั้งและใช้งาน ซึ่งรองรับการ sign-in หลากหลายรูปแบบทั้งจาก social network ยอดนิยม, จาก Email และ Password ของผู้ใช้งาน หรือแบบไม่ระบุตัวตน(Anonymous) ก็ได้

วิดีโอแนะนำการทำงานของ Firebase Authentication

ในการพัฒนาตัว Firebase Authentication ผมจะขอแยกออกเป็น 3 parts ดังนี้

  1. การ Set up Firebase และ Auth SDK
  2. การสร้างบัญชีผู้ใช้ และ การเข้าสู่ระบบ
  3. การ sign-in ด้วยบัญชี Provider ต่างๆ
  4. การบริหารจัดการบัญชีผู้ใช้

เมื่อพร้อมแล้ว…ก็เปิด Android Studio ขึ้นมา โดยจะสร้างโปรเจคใหม่ หรือจะใช้โปรเจคเดิมก็ได้

Part 1 การ Set up Firebase และ Auth SDK

ถ้าสร้างโปรเจคใหม่ ให้ไปดูการ Set up Firebase ที่บทความนี้ก่อน

เมื่อ Set up Firebase เรียบร้อยแล้ว ก็ให้เพิ่ม Authentication SDK ใน build.gradle ของ app-level แล้วกด Sync ก็เป็นอันจบส่วนที่ 1 ละ

dependencies {
compile 'com.google.firebase:firebase-auth:11.8.0'
}

Part 2 การสร้างบัญชีผู้ใช้ และ การเข้าสู่ระบบ

การสร้างบัญชีผู้ใช้

ใน Firebase Auth นั้นคุณสามารถจะสร้างจากการ sign-in ด้วยด้วย social network หรือสร้างบัญชีด้วยุ email และ password ของผู้ใช้เอง สำหรับในบทความนี้จะลงรายละเอียดในการสร้างบัญชีผู้ใช้ด้วย email และ password ว่าแล้วก็มาเริ่มกัน โดยเข้าไปที่ Firebase Console เลือกเมนู Auth ทางด้านซ้าย แล้วเลือก tab ชื่อ SIGN-IN METHOD

จากนั้นให้กดเลือกที่แถวของ Email/Password เพื่อที่จะไปเปิดการใช้งาน

ต่อไปคือการสร้างบัญชีด้วย email และ password จะมีด้วยกัน 2 วิธี

วิธีที่ 1 สร้างจาก Firebase Console

โดยคลิกที่ tab แรก ชื่อ USERS

จากนั้นกดปุ่ม ADD USER แล้วกรอก email และ password(ขั้นต่ำ 6 characters)

เสร็จละการสร้างบัญชีด้วย email และ password แบบแรก ง่ายโคตรๆ

วิธีที่ 2 สร้างจากการพัฒนา Android App

2.1 ไปสร้าง layout ที่มี EditText สำหรับ email และ password พร้อมปุ่ม Create Account และ ปุ่ม Sign in ตามที่สบายใจ

2.2 เริ่มด้วยการประกาศตัวแปร FirebaseAuth และ รับค่า Instance ก่อน

private FirebaseAuth mAuth;
// ...
mAuth = FirebaseAuth.getInstance();

2.3 ต่อไปก็สร้าง AuthStateListener เพื่อรอรับข้อมูล user ที่ได้จากการ sign-up และ sign-in โดยสามารถรับข้อมูล user ได้จาก getCurrentUser() หากค่าของ getCurrentUser เป็น null ก็แปลว่า user ยังไม่ได้ sign-in นั่นเอง

private FirebaseAuth.AuthStateListener mAuthListener;

// ...

@Override
protected void onCreate(Bundle savedInstanceState) {
// ...
mAuthListener = new FirebaseAuth.AuthStateListener() {
@Override
public void onAuthStateChanged(FirebaseAuth firebaseAuth) {
FirebaseUser user = firebaseAuth.getCurrentUser();
if (user != null) {
// User is signed in
} else {
// User is signed out
}
// ...
}
};
// ...
}

@Override
public void onStart() {
super.onStart();
mAuth.addAuthStateListener(mAuthListener);
}

@Override
public void onStop() {
super.onStop();
if (mAuthListener != null) {
mAuth.removeAuthStateListener(mAuthListener);
}
}

2.4 จากนั้นก็ validate ตัว email และ password กันตามสมควร เมื่อ validate เรียบร้อยเราก็จะไปสร้างบัญชีผู้ใช้ด้วย email และ password กันละ

mAuth.createUserWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>(){
@Override
public void onComplete(Task<AuthResult> task) {
if (!task.isSuccessful()) {
Toast.makeText(EmailPasswordActivity.this, "Authentication failed.", Toast.LENGTH_SHORT).show();
}
// ...
}
});

จากโค้ดด้านบน หากสร้างบัญชีไม่สำเร็จก็ Toast แจ้งผู้ใช้ไป แต่หากสร้างสำเร็จ AuthStateListener (2.3) ที่เราสร้างไว้ก็จะทำงาน และเราก็สามารถรับข้อมูลผู้ใช้ได้ทันที

เมื่อเข้ามาเช็คใน Firebase Console ก็พบว่าข้อมูลเข้ามาละ และเรายังสามารถสั่ง Reset password(เดี๋ยวลงรายละเอียดใน part ที่ 4) สั่ง ban และ ลบผู้ใช้ได้

การเข้าสู่ระบบ

เราสามารถไป implement ที่ไหนก็ได้ตามต้องการ โดยขั้นตอนก็คล้ายกับการ sign-up คือ จะต้องทำตามข้อ 2.2 และ 2.3 ก่อน จากนั้นก็ทำตาม code ด้านล่างด้วยการส่ง email และ password เข้าไป

mAuth.signInWithEmailAndPassword(email, password)
.addOnCompleteListener(this, new OnCompleteListener<AuthResult>() {
@Override
public void onComplete( Task<AuthResult> task) {
if (!task.isSuccessful()) {
Log.w(TAG, "signInWithEmail", task.getException().getMessage());
Toast.makeText(EmailPasswordActivity.this, "Authentication failed.", Toast.LENGTH_SHORT).show();
}
// ...
}
});

ซึ่งการทำงานหลังจากได้ callback มาก็เหมือนกับตอนสร้างบัญชีผู้ใช้เลย ตัวอย่างจะ sign-in ระบบด้วย email และ password ที่สร้างจาก Firebase Console

Part 3 การ sign-in ด้วยบัญชี Provider ต่างๆ

Firebase Auth รองรับการ sign-in ด้วยบัญชี provider 4 บริการ หลักการทำงานคือ เราจะต้องไปเพิ่ม SDK ใน dependency ของบริการที่เราต้องการ จากนั้นก็ไปทำการ authentication กับบริการนั้นๆ โดยเราจะใช้ข้อมูล token ของแต่ละบริการไปทำการ sign-in กับ Firebase Auth ซึ่ง Firebase Auth จะทำการสร้างบัญชีผู้ใช้ขึ้นมา ผลลัพธ์เทียบเท่ากับการ sign-in ด้วย email และ password โดยในบทความนี้จะไม่ได้ลงรายละเอียด แต่ให้ผู้อ่านลิงค์ไปดูการพัฒนาตามแต่ที่ผู้อ่านสนใจ (ช่วงท้ายจะแนบลิงค์ GitHub ที่รวมการพัฒนาครบทุกอย่างไว้ในนั้นให้ครับ)

คำเตือน! ก่อนการพัฒนาอย่าลืมไปเปิดใช้งานบริการต่างๆที่หน้านี้นะ

  1. วิธี sign-in ด้วยบัญชี Google
  2. วิธี sign-in ด้วยบัญชี Facebook
  3. วิธี sign-in ด้วยบัญชี Twitter
  4. วิธี sign-in ด้วยบัญชี GitHub

Part 4 การบริหารจัดการบัญชีผู้ใช้

การดึงข้อมูลผู้ใช้จาก Firebase

เมื่อ sign-in เรียบร้อย user != null เราจะสามารถดึงข้อมูลผู้ใช้ออกมาได้ เช่น ชื่อ, อีเมล, URL รูปภาพ, Firebase ID เป็นต้น ตามตัวอย่างด้านล่าง

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
String name = user.getDisplayName();
String email = user.getEmail();
Uri photoUrl = user.getPhotoUrl();
String uid = user.getUid();
}

การดึงข้อมูล provider ของผู้ใช้

การ sign-in ของผู้ใช้นั้นสามารถเป็นได้ทั้ง email และ password หรือผู้ให้บริการรายอื่นๆเช่น Google และ Facebook เป็นต้น ซึ่งการดึงข้อมูลแบบด้านบน ก็จะได้เฉพาะข้อมูลผู้ใช้จาก Firebase แต่หากต้องการดึง raw data ที่ได้จากผู้ให้บริการต่างๆจะต้องทำแบบด้านล่างนี้

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
if (user != null) {
for (UserInfo profile : user.getProviderData()) {
// Id of the provider (ex: google.com)
String providerId = profile.getProviderId();
String uid = profile.getUid();
String name = profile.getDisplayName();
String email = profile.getEmail();
Uri photoUrl = profile.getPhotoUrl();
};
}

ตัวอย่างผู้เขียนจะ sign-in ด้วย Facebook จะสังเกตว่าในส่วน provider จะวน loop ออกมาหมดไส้หมดพุง

การอัพเดทข้อมูลผู้ใช้

เราสามารถอัพเดทข้อมูล DisplayName และ Photo URL ผู้ใช้ได้ด้วย updateProfile() เหมาะมากกับการสร้างบัญชีผู้ใช้ด้วย email และ password

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

UserProfileChangeRequest profileUpdates = new UserProfileChangeRequest.Builder()
.setDisplayName("Jirawatee")
.setPhotoUri(Uri.parse("http://placehold.it/96x96"))
.build();

user.updateProfile(profileUpdates)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "User profile updated.");
}
}
});

การเปลี่ยน Email ของผู้ใช้

เนื่องด้วยตัว Firebase Auth ไม่ได้มอง email เป็น primary key เราจึงสามารถ update อีเมลได้ด้วย updateEmail() เพียงแต่อีเมลจะต้องไม่ซ้ำกับผู้ใช้คนอื่น

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

user.updateEmail("jirawatee@yahoo.com")
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "User email address updated.");
}
}
});

ตัวอย่างผมจะเปลี่ยน email จากการ sign-in ด้วย Google โดยจะเปลี่ยนอีเมลจาก jirawatee@gmail.com ไปเป็น jirawatee@yahoo.com

พอเปลี่ยนแล้ว ก็มีเมลวิ่งมาอัตโนมัติด้วย เนื้อหาภายในอีเมลก็จะบอกว่า ถ้าคุณไม่ได้ขอเปลี่ยนอีเมลเป็น jirawatee@yahoo.com ให้คุณกดลิงค์ที่แนบมา เพื่อ rollback กลับไปเป็น jirawatee@gmail.com ได้อีก อะไรจะดีงามพระราม 8.9 ขนาดนี้

*** หมายเหตุ: การเปลี่ยนอีเมลนั้นผู้ใช้จะต้อง sign-in ทิ้งไว้ไม่นานเกินไปจึงจะทำได้ (ดูรายละเอียดที่หัวข้อ Re-authenticate ของ part นี้) ***

และเราก็ยังสามารถไปปรับรูปแบบอีเมลที่ Firebase Console ได้อีกด้วย ที่ tab ชื่อ EMAIL TEMPLATES แล้วเลือกประเภทอีเมลเป็น Email address change

การเปลี่ยน Password

เมื่อ sign-in แล้ว เราสามารถอัพเดท password กับข้อมูลผู้ใช้ที่ sign-in ด้วยบัญชีของผู้ให้บริการอื่นๆ เพราะการ sign-in ด้วยบริการอื่นๆเราจะไม่ได้ password มาด้วย หรือ กรณีที่อยากจะเปลี่ยน password ก็สามาถใช้ updatePassword() เพื่อเปลี่ยนได้

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();
String newPassword = "SOME-SECURE-PASSWORD";

user.updatePassword(newPassword)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "User password updated.");
}
}
});

จากรูปข้างบนจะดึงข้อมูล provider มาแสดง สังเกตว่า provider จะมีเพิ่มมาอีก 1 ละ ซึ่งเป็นประเภท password นั่นเอง

*** หมายเหตุ: การเปลี่ยน password นั้นผู้ใช้จะต้อง sign-in ทิ้งไว้ไม่นานเกินไปจึงจะทำได้ (ดูรายละเอียดที่หัวข้อ Re-authenticate ของ part นี้) ***

ส่งลิงค์ Reset Password ด้วยอีเมล

Firebase Auth ได้เตรียมขั้นตอนการส่งลิงค์ reset password ผ่านทางอีเมลให้แล้ว โดยเราสามารถไปประยุกต์ใช้กับขั้นตอน forgot password ภายในแอพของเราได้ โดยจะมีวิธีการทำได้ 2 แบบด้วยกัน

แบบที่ 1 คือการส่งลิงค์ด้วย Firebase Console
โดยให้คุณคลิกที่ tab ชื่อ EMAIL TEMPLATES แล้วเลือกประเภทอีเมลเป็นแบ Password reset คุณสามารถจะมาปรับแต่งรูปแบบอีเมลที่จะส่งลิงค์ reset password ได้ที่นี่

จากนั้นให้กลับมาที่ tab ชื่อ USERS แล้วเลือกอีเมลที่คุณต้องการจะส่งลิงค์ reset password ไปให้ จากนั้นกด Reset password

จะขึ้นหน้าต่างมาให้คุณยืนยันการส่ง จากตัวอย่างจะส่งไปที่ jirawatee@gmail.com

เมื่อกดส่งแล้วจะมีอีเมลที่แนบลิงค์ในการ reset password มา ก็ซะเลย

แล้วก็ตั้ง password ใหม่ได้เลย เสร็จแล้วกด save ก็พร้อมใช้งานทันที

แบบที่ 2 คือการ request ให้ส่งอีเมล reset password จากแอพ
วิธีนี้เราก็สร้างฟอร์มที่ให้ระบุอีเมลมา จากนั้นก็ส่งอีเมลเข้าไปที่ฟังก์ชัน sendPasswordResetEmail() ซึ่งผลลัพธ์ก็คือจะมีอีเมลวิ่งมาหาเมลนั้นๆที่เรากรอกเพื่อไป reset password เหมือนวิธีในแบบที่ 1

FirebaseAuth auth = FirebaseAuth.getInstance();
String emailAddress = "user@example.com";

auth.sendPasswordResetEmail(emailAddress)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "Email sent.");
}
}
});

การลบบัญชีผู้ใช้

ในการลบข้อมูลผู้ใช้ เราสามารถทำได้ 2 วิธีเช่นกัน

วิธีที่ 1 คือไปลบข้อมูลผู้ใช้ที่ Firebase Console
เราก็คลิกที่ tab ชื่อ USERS แล้วเลือกผู้ใช้ที่เราต้องการจะลบออกจากระบบ

เรียบร้อยหายไปละ

ทดสอบ sign-in ก็ไม่สามารถเข้าได้ละ โดยผู้เขียนได้แสดงข้อความจาก Exception เป็นดังรูป

วิธีที่ 2 คือการเรียกใช้งานฟังก์ชัน delete() จาก Firebase Auth

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

user.delete()
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
if (task.isSuccessful()) {
Log.d(TAG, "User account deleted.");
}
}
});

*** หมายเหตุ: การลบบัญชีผู้ใช้นั้นผู้ใช้จะต้อง sign-in ทิ้งไว้ไม่นานเกินไปจึงจะทำได้ (ดูรายละเอียดที่หัวข้อ Re-authenticate ด้านล่างนี้เลย) ***

Re-authenticate

การกระทำบางอย่าง(ขา write) ใน Firebase Auth นั้นมีความสำคัญต่อความปลอดภัย ได้แก่ การลบบัญชี, การเปลี่ยนอีเมล และ การเปลี่ยน password กระบวนการเหล่านี้จำเป็นที่ผู้ใช้จะต้อง sign-in ทิ้งไว้ไม่นาน และหากผู้ใช้ได้ทำกระบวนการเหล่านั้นและ sign-in ไว้เป็นเวลานาน ก็จะไม่สามารถทำได้ โดยจะมี Exception มาดังภาพ

โดยเมื่อเกิดเหตุการดังกล่าวขึ้น เราจะต้อง handle ด้วยการพาผู้ใช้ไป sign-in ใหม่โดยหากเป็นการ sign-in ด้วย email และ password อาจ popup ขึ้นมาให้ผู้ใช้กรอก email และ password อีกครั้ง หรือถ้าเป็นกรณี sign-in ด้วย provider ก็ให้ทำการ reauthenticate() แบบอัตโนมัติไป ตัวอย่างด้านล่างเป็นการสร้าง credential จาก email และ password ด้วย EmailAuthProvider นะครับ แต่ถ้าหากเป็น provider ก็สามารถสร้าง credential ได้จาก GoogleAuthProvider หรือ FacebookAuthProvider เป็นต้น

FirebaseUser user = FirebaseAuth.getInstance().getCurrentUser();

AuthCredential credential = EmailAuthProvider
.getCredential("user@example.com", "password1234");

user.reauthenticate(credential)
.addOnCompleteListener(new OnCompleteListener<Void>() {
@Override
public void onComplete(Task<Void> task) {
Log.d(TAG, "User re-authenticated.");
}
});

สิ่งที่ยังไม่ได้ลงรายละเอียด

จากทั้งหมดที่เล่าไปยังมีอีก 3 เรื่องที่ไม่ได้ลงรายละเอียด แต่จะเล่าให้ฟังคร่าวๆนะ

  • เรื่องแรก Custom Authentication System เป็นการ sign-in อีกแบบหนึ่งที่เราจะส่งค่าจากแอพเช่น email และ password ไป authenticate กับ server ของเรา จากนั้น server ของเราจะ return token กลับมา token นี้เราเรียกว่า custom token เมื่อได้ custom token มาแล้วจึงนำไป sign-in กับ Firebase Auth ด้วยฟังก์ชัน signInWithCustomToken()
  • เรื่องถัดมาคือ Anonymous Authentication เป็นการ sign-in แบบขั่วคราวโดยไม่ต้องกรอกอะไรเลย ตัวอย่าง แอพจองตั๋วเครื่องบินจะสังเกตว่าเราไม่ต้อง sign-in ก็สามารถจองได้ เริ่มที่เมื่อเลือกสายการบินได้แล้วเราก็ทำการสร้าง Firebase ID ให้ผู้ใช้อัตโนมัติด้วยฟังก์ชัน signInAnonymously() เพื่อให้ผู้ใช้ได้เข้าไปสู่หน้าถัดไปได้ ก่อนจองก็ต้องกรอกข้อมูลส่วนตัวเยอะแยะ แต่หากผู้ใช้ไม่ต้องการกรอกข้อมูล เขาสามารถกด sign-in ด้วย provider แทน หลังจากนั้นเราก็จับ credential ที่ได้จากการ sign-in มา mapping กับ Firebase ID ด้วยฟังก็ชัน linkWithCredential() ผู้ใช้ก็จะได้บัญชีที่สมบูรณ์ในการใช้งานครั้งถัดไป ส่วนเราก็ดูประวัติของเขาได้ละ
  • เรื่องสุดท้ายคือ Link Multiple Auth Providers เป็นการเชื่อมบัญชีต่างๆเข้าด้วยกัน เหมือนที่ยกตัวอย่างใน Anonymous Authentication โดยเราจะใช้ credential ในการนำมาเชื่อมต่อด้วย linkWithCredential() และจะเชื่อมกี่บัญชีก็ได้ และสามารถยกเลิกการเชื่อมต่อแต่ละบัญชีได้ด้วยเช่นกัน

สิ่งที่คุณควรรู้

  • ใน Firebase Auth จะมี fields ที่ถูกกำหนดมาทั้งตอน set และ get อยู่ 4 ตัวคือ unique ID, email, Name, photo URL แต่หากต้องการเพิ่ม field คุณสามารถไปใช้บริการที่ Firebase Realtime Database ได้ (น่าจะเป็นบทความต่อไป)
  • Auth tokens ที่สามารถพบได้ใน Firebase Auth จะมีด้วยกัน 3 แบบ
    - Firebase ID tokens: ตัวนี้จะถูกสร้างด้วย Firebase หลังจาก sign in สำเร็จ โดยค่าที่ได้จะไม่ซ้ำกัน และสามารถใช้ยืนยันตัวตนของผู้ใช้ได้
    - Identity provider tokens: เป็นค่าที่ได้จาก providers เช่น Google และ Facebook เป็นต้น ใช้เพื่อตรวจสอบกับ providers เมื่อผ่านการตรวจสอบจึงจะนำ token ดังกล่าวไป convert เป็น credentials แล้วนำไปใช้กับ Firebase ต่อไป
    - Firebase custom tokens: เป็นค่าที่ได้จากระบบ auth ที่คุณพัฒนาขึ้นมาเอง การใช้งานเหมือนกับ Identity provider tokens
  • สำหรับการ sign-in ด้วยบัญชี Google คุณจะต้องเพิ่ม SHA-1 เข้าไปในโปรเจค Firebase ด้วย
  • หลังจาก sign-in ด้วย Twitter คุณจะไม่ได้ email ซึ่งเราจะต้องให้ผู้ใช้เพิ่มเข้าไปเองภายหลัง

ท้ายรายการ (สักที)

บทความนี้ใช้เวลาเขียนทั้ง Sample app และ บทความ นานสุด นานกว่า FCM ซะอีก และหวังว่า Sample app และ บทความ จะทำให้ทุกคนที่อ่านสามารถประหยัดเวลา และสร้างระบบ authentication ที่มีประสิทธิภาพ ได้นะครับ Source Code ของ Sample app ผมได้เอาขึ้น GitHub ไว้เรียบร้อยแล้ว

ส่วนบทความต่อไป คงจะเป็นเรื่อง Firebase Realtime Database ยังไงฝากติดตามอ่านกันด้วยนะครับ สำหรับวันนี้…ราตรีสวัสดิ์ พี่น้องชาวไทย

--

--

Jirawatee
Firebase Thailand

Technology Evangelist at LINE Thailand / Google Developer Expert in Firebase