Angular & Firebase Authentication part 1
Authentication เป็นการแสดงตัวตน ยืนยันตัวตนว่ามีอยู่จริง คำตอบของมันมีแค่ใช่หรือไม่ใช่ แม้คำตอบจะมีหนึ่งเดียวแต่วิธีการยืนยันมีหลากหลายขึ้นอยู่กับความเหมาะสมและการอำนวยความสะดวกครับ
Intro มา
Firebase Authentication
Firebase เตรียม backend services ให้เราได้พัฒนา application ในหลายเทคโนโลยี ได้แก่
- iOS
- Android
- Web
- C++
- Unity
พร้อมกับจัดเตรียมวิธีที่จะทำให้รู้ว่าผู้ใช้ (user) ที่ต้องการเข้าใช้ระบบของเราคือใคร (identity of a user) เพื่อประโยชน์ 2 อย่าง คือ
- เพื่ออนุญาตให้ application บันทึกข้อมูลผู้ใช้อย่างปลอดภัยไว้บน cloud
- เพื่อมอบประสบการณ์ที่เป็นส่วนตัวให้เหมือนกันในทุกอุปกรณ์ของผู้ใช้
และด้วยความตั้งใจข้างต้น (ณ ปัจจุบัน) Firebase ได้จัดเตรียมวิธีการแสดงตัวตนหรือการยืนยันตัวตนไว้ดังนี้
- sign in ด้วย Basic Authentication คือ username กับ password
- sign in ด้วยเบอร์โทรศัพท์
- sign in กับจ้าวดังด้าน social ทั้งหลายไม่ว่าจะเป็น Google, Facebook, Twitter
- sign in กับจ้าวดังด้าน OS และการบริการ software ไม่ว่าจะเป็น Microsoft, Apple
- sign in กับ Github
- หรือกระทั่งขาจร (guest user)
อนาคตก็คงเพิ่มอีกเรื่อยๆ (เอ๋ สงสัยว่า Line เอาด้วยไหม)
โดยเขาแบ่งกลุ่มการยืนยันตัวตนนี้ออกเป็น 2 กลุ่ม
- FirebaseUI Auth
- Firebase SDK Authentication
FirebaseUI Auth
ขณะนี้รองรับเพียง iOS, Android และ Web
หน้าตามันเป็นอย่างไร ดูภาพด้านล่างนี้เลยเข้าใจง่ายกว่า
อ่านเพิ่มเติม (อันนี้เฉพาะของ web)
Firebase SDK Authentication
แตกต่างกันแบบง่ายๆเลยคือไม่มี UI หรือปุ่มกดมาให้ ไปวาดไปเขียนเอาเองแค่นั้นแหละ
Firebase SDK Authentication คือเป้าหมายของเราที่จะนำมาเขียนร่วมกับ Angular ผมอ่านและอ้างอิงจาก
หิวโซกันหรือยัง เรื่องการ authentication ให้เลือกเอาตามความเหมาะสมหรือที่อำนวยความสะดวกแก่ฐานลูกค้าของเราเป็นสำคัญนะครับ
Let’s get it started
เบื้องต้นนี้ผมก็จะทำตามตัวอย่างแหละ
อ้อ สำหรับใครที่เพิ่งเข้ามาอ่านบทความนี้ครั้งแรกจะไม่รู้ที่มาที่ไปว่าผมไปเอาโปรเจกต์ไหนมาใช้ หากอยากทำตามผมขอให้อ่าน 2 บทความนี้ก่อน
และ
เล่าก่อน เชื่อว่ามีพวกใจร้อนอยากได้วิชาไปใช้ บอกเลยนะว่าต้องมีพื้นฐาน Angular มาสักหน่อย อ่อ ขอ CSS จาก Bootstrap ด้วยจะดีมาก (เดี๋ยวชวนทำงานเลย 555+)
เราเริ่มที่การแบ่งหน้าครับ
แบ่งด้วย route ซึ่งขณะนี้มีแล้ว 3 routes เรียกไป 3 components ดังนี้
ไฟล์ app-routing.module.ts
จากรูปข้างต้นคือ
realtimedb/trailers เรียกไป RealtimedbTrailersComponent
cloudfirestore/trailers เรียกไป CloudfirestoreTrailersComponent
และ cloudstorage/videos เรียกไป CloudStorageVideosComponent
ตอนนี้ที่เรียกไปได้ก็เพราะผมเปิดเป็นโหมดทดสอบ (test) ทั้งหมด ความตั้งใจแรกคือเมื่อทำระบบ authentication นี้เสร็จแล้ว 3 routes จะต้องถามหาการยืนยันตัวตน
:)
:-)
:=)
Angular มีลุงยามรักษาความปลอดภัยที่เรียกว่า Guard สามารถนำเขามาปกป้องประดา routes ของเราได้
อ่านเพิ่มเติม
โดยทั่วไป routes สามารถแบ่งได้หลายชั้น (parent & children) ในที่นี้คือแบบธรรมดาที่สุด (1 parent) มีแค่ชั้นเดียวอีกด้วย
ด้วยเหตุนี้เราจะเขียน service ในลักษณะของ guard เพียงตัวเดียวเท่านั้น ตั้งชื่อว่า AuthGuard
เรียกลุงมาเลยครับ
ng g guard guards/auth
เลือกเป็น CanActivate
ลุงจะคืนค่าได้ 1 อย่างคือ true หรือ false ถ้า true ก็ได้รับอนุญาติ ถ้า false ก็ตรงข้าม
เราจะถามว่าผู้ใช้ที่ได้คลิกเลือก route ของเรา เขาได้ authenticate แล้วหรือยัง หากทำแล้วลุงจะคืนค่า true หาไม่แล้วลุงจะคืนค่า false
เปิดไฟล์ auth.guard.ts
ฟังก์ชัน canActivate จะ return ค่าเป็น true ให้เปลี่ยนมันเป็น false ทำเพื่อให้เห็นว่าเมื่อค่าเป็น false ลิงก์จะใช้งานไม่ได้
canActivate( next: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean | UrlTree> | Promise<boolean | UrlTree> | boolean | UrlTree { return false;}
:)
จากนั้นเปิดไฟล์ app-routing.module.ts
เพิ่ม canActivate เข้าไป แล้วเรียกมาที่ AuthGuard ดังนี้
import { AuthGuard } from './guards/auth.guard';
และ
{ path: 'realtimedb/trailers', component: RealtimedbTrailersComponent, canActivate: [AuthGuard] },
เสร็จแล้วทดสอบ
ได้ความว่าไม่สามารถคลิกลิงก์ Realtime Database ได้ (แต่ลิงก์อื่นๆยังคงคลิกได้ปกติ) จากตรงนี้เราจะนำกลไกการตรวจสอบ authentication มาวางไว้แทนการ return ค่า false นั่น
AngularFireAuth
อยู่ใน module ชื่อ AngularFireAuthModule
เปิดไฟล์ app.module.ts
import { AngularFireAuthModule } from '@angular/fire/auth';
และ
imports: [... AngularFireAuthModule,...],
จากนั้นเปิดไฟล์ auth.guard.ts
ที่ไฟล์นี้เราจะฟังว่า user ที่พยายามคลิกลิงก์ใดๆที่ถูกทำ canActivate ไว้ได้ authenticated แล้วหรือไม่
ที่ constructor ของคลาส AuthGuard นำมันเข้ามา
import { AngularFireAuth } from '@angular/fire/auth';
และ
constructor( private auth: AngularFireAuth) { }
เมธอด canActivate เพิ่มการรับฟัง ฟังว่า user ได้ authenticated แล้วหรือยัง เขียนประมาณนี้
import { Observable, of } from 'rxjs';import { switchMap } from 'rxjs/operators';
และ
canActivate( ... return this.auth.authState.pipe( switchMap(user => { const authenticated = user != null; return of(authenticated); }) );...
เสร็จแล้วทดสอบ
route ไหนที่แปะ canActivate: [AuthGuard] ก็จะ access ไม่ได้ ส่วน route ไหนที่ยังไม่ได้แปะก็สามารถคลิกได้ปกติ ถูกต้องนะครับ
this.auth.authState นี้ให้ผลเป็น Observable ของ Firebase User รายระเอียดค่อยพูดถึงกัน เมื่อได้ออบเจ็กต์ user มาแล้วเราถามต่อว่าเป็น null หรือไม่ ถ้าเป็น null แสดงว่ายังไม่ได้ authenticated ผลลัพธ์ก็จะได้ Observable ของ false ส่งกลับออกมา
เข้าใจนะ โอเคร~ผ่าน (ถ้าผ่านไม่ได้ให้ไปอ่านพื้นฐาน RxJS)
ทีนี้ถามว่าแล้วจะให้ user เข้าสู่ระบบได้อย่างไร (sign in หรือ log in นั่นแหละ) ก็ให้กลับไปดูที่ผมแปะอ้างอิงเอาไว้
เราจะเรียกใช้
this.auth.signInWithPopup(new auth.GoogleAuthProvider());
โดยตัวแปร authenticated มีค่าเป็น false เราจะเรียกมันขึ้นมา ได้ว่า
import { auth } from 'firebase/app';
และ
...return this.auth.authState.pipe( switchMap(user => { const authenticated = user != null; if (!authenticated) { this.auth.signInWithPopup(new auth.GoogleAuthProvider()); } return of(authenticated); }));...
โค้ดข้างต้น GoogleAuthProvider จะเรียกไปที่ Firebase ถามว่าได้อนุญาตสิทธิ์การเข้าใช้งานผ่าน Google Account หรือไม่ หากเรายังไม่อนุญาตก็จะได้หน้าตา error อย่างนี้ครับ
{code: “auth/operation-not-allowed”, message: “The identity provider configuration is disabled.”, a: null}
อย่างที่พวกเราทราบ พวกเราเป็นเจ้าของโปรเจกต์ Firebase จึงต้องสามารถควบคุมได้ทุกอย่าง เปิด Firebase ขึ้นมา
ตรงไปที่
Enable แล้วใส่ email ตามที่มันร้องขอ
กลับไปลองใหม่
ผลเมื่อคลิกลิงก์ซ้ำอีกที
จากนั้นให้กลับไปดูที่ Firebase เราจะพบว่า รายการของผู้ใช้ที่ได้ logged in แล้วนั้นมีใครบ้าง
ก็ขอให้สังเกตและลองเล่นดูครับ แล้วเอามาเล่าให้กันฟังบ้าง ช่วยกันศึกษานะครับ
:)
ก็ขอจบในส่วนของการแนะนำ Firebase SDK Authentication โดย AngularFireAuth ไว้เท่านี้ก่อน หากเข้าใจ concept ที่ Firebase ต้องการสื่อแล้วอื่นๆก็คือศึกษาวิธีการเขียนโค้ดเพื่อใช้งานตามความเหมาะสมของโปรเจกต์เท่านั้นเอง
ไว้เจอกัน บาย~
อ่านต่อ part 2
อ้างอิง
https://firebase.google.com/docs/reference/js/firebase.auth.Auth
https://firebase.google.com/docs/auth
https://angular.io/cli/generate