Ionic Tutorial
Lesson 8: Authentication with Firebase
Revised on 2025/12
Ionic v8.0.0
Angular v20.0.1
@angular/fire v7.21.0
firebase v9.21.0
登入認證方式
- Email/Password登入
- 帳號/密碼儲存於雲端資料庫
- 可使用Firebase Authentication功能
- 可使用Firebase Authentication功能
- 帳號/密碼儲存於雲端資料庫
- 社群網站帳號登入
- Google登入
- 必須在Google後台設定「伺服器」
- Facebook登入
- 必須在FB for Developer後台設定「FB應用程式」
- Google登入
Email/Password登入
(1)啟用Firebase Authentication的Email認證服務
前往 https://firebase.google.com/
(2) 呼叫firebase提供的API撰寫「登入/註冊/登出」功能
(3) 在頁面使用
引入服務、叫用「登入/註冊/登出」功能
ionic g service services/auth例:
啟用Email認證服務新增firebase專案(1/4)

啟用Email認證服務啟用登入方式供應商(2/4)
進入專案後...
編輯要啟用的供應商
需符合各供應商的登入連線要求

啟用Email認證服務啟用電子郵件登入(3/4)

啟用Email認證服務建立登入帳號(4/4)

確認已啟用
啟用Email認證服務建立登入帳號(4/4)

啟用Email認證服務建立登入帳號(4/4)
直接輸入電子郵件與密碼

啟用Email認證服務建立登入帳號(4/4)

Email/Password登入
- 練習範例
(1) 呼叫firebase提供的API撰寫「登入/註冊/登出」功能
(2) 在頁面使用
引入服務、叫用「登入/註冊/登出」功能
練習: 登入頁面
Email/password登入
登入後顯示帳號資訊(預設只有email)
紀錄登入狀態?
取決於AuthState
登入認證連線至Firebase
登入頁面範例建立專案
cd loginApp
ionic g page login
ionic g page register1.建立空白專案
ionic start loginApp blank2.新增所需頁面
HomePage
已登入時,則至首頁
LoginPage
未登入時,則顯示登入畫面
RegisterPage
註冊頁面
AuthService
連結Firebase進行登入驗證
ionic g service services/auth.service3.新增登入認證service
登入頁面範例安裝firebase套件(1/4)
6.安裝firebase, @angular/fire套件
npm i -g @angular/cli
npm i firebase
ng add @angular/fire如尚未安裝
登入頁面範例安裝firebase套件(2/4)

ng add @angular/fire

需依序回答下列問題
回傳使用資料: No
選擇欲設定的功能: Authentication(登入認證), Firestore(資料庫)
登入頁面範例安裝firebase套件(3/4)
ng add @angular/fire



選擇登入Firebase的Google帳號(應與前面Firebase主控台步驟相同)
選取先前建立的專案名稱
選取先前建立的[應用程式]名稱
自動更新app.module.ts
注意:自動更新的
app.module.ts有錯誤
登入頁面範例安裝firebase套件(4/4)
//...[省略]...
@NgModule({
declarations: [AppComponent],
imports: [BrowserModule, IonicModule.forRoot(), AppRoutingModule],
providers: [{ provide: RouteReuseStrategy, useClass: IonicRouteStrategy },
provideFirebaseApp(() => initializeApp(
{ projectId: "dbexample-512b4",
...[省略]...
messagingSenderId: "776927995954",
projectNumber: "....",
version: 2
})),
provideAuth(() => getAuth()), provideFirestore(() => getFirestore())],
bootstrap: [AppComponent],
})
// ...[省略]...刪除這兩個屬性,目前版本已不支援
src/app/app.module.ts
登入頁面範例登入認證服務 (1/2)
...
import { Auth, signInWithEmailAndPassword, signOut, User, user } from '@angular/fire/auth';
...
auth.service.ts片段
angular/fire/auth: 提供Firebase認證函式
登入頁面範例登入認證服務 (2/2)
...
export class AuthService {
router: Router = inject(Router);
// 登入狀態(authState)
auth: Auth = inject(Auth);
user$ = user(this.auth); // 建立登入狀態Observable, 狀態改變時會發出通知
currentUser: User | null = this.auth.currentUser; // 登入使用者資訊
constructor() { }
login(mail: string, password: string) { /* 功能1. 登入(email登入)*/
signInWithEmailAndPassword(this.auth, mail, password).then( uc=> {
console.log('current user:', uc.user); // 印出目前登入使用者的資訊
this.router.navigate(['home']); // 登入成功後,前往個人首頁
}).catch( error=> {
/* 帳密驗證失敗 */
console.log('帳密輸入錯誤', error);
});
}
logout(): Promise<any> { /* 功能2. 登出(所有登出)*/
return signOut(this.auth);
}
}auth.service.ts片段
登入頁面範例登入頁面(1/3)
import { Component, OnInit } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { Router } from '@angular/router';
import { User } from '@angular/fire/auth';
...
export class LoginPage implements OnInit {
email= ''; // email欄位
password=''; // 密碼欄位
currentUser: User | null = null; // 目前登入之使用者
userSubscription: Subscription; // 暫存subscription, 可用於取消訂閱
constructor(public as: AuthService, public router: Router) {
// 訂閱 user$ 以便隨時更新本地的 currentUser 變數
// 隨時知道當前是誰登入,或者使用者是否已登出
this.userSubscription = this.as.user$.subscribe((aUser: User | null) => {
this.currentUser = aUser; // 登入之使用者
console.log('current user', this.currentUser);
});
}
ngOnInit(){}
// 登入callback
signIn(email: string, password: string) {
this.as.login(email, password); // 呼叫service
}
}login.page.ts 片段
訂閱user$,authState streaming
登入頁面範例登入頁面(2/3)
<ion-header>
<ion-toolbar>
<ion-title>登入</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-item>
<ion-label position="floating">Email Address</ion-label>
<ion-input type="text" [(ngModel)]="email"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">密碼</ion-label>
<ion-input type="password" [(ngModel)]="password"></ion-input>
</ion-item>
<ion-button expand="block" (click)='signIn(email, password)'>
登入
</ion-button>
</ion-content>
login.page.html
登入按鈕
帳號、密碼欄位
登入頁面範例登入頁面:資料模型(3/3)
export interface User {
uid: string;
email: string;
displayName: string;
photoURL: string;
emailVerified: boolean;
}在src資料夾建立models/user.ts
此五個欄位是Firebase內建的五個欄位
方便與Firebase資料庫認證串連
登入頁面範例首頁 (1/2)
import { Component } from '@angular/core';
import { AuthService } from '../services/auth.service';
import { User } from '@angular/fire/auth';
@Component({
selector: 'app-home',
templateUrl: 'home.page.html',
styleUrls: ['home.page.scss'],
standalone: false,
})
export class HomePage {
currentUser: User | null = null; // 登入之user, 得自訂閱服務
constructor(public as: AuthService, public router: Router) {
this.as.user$.subscribe( (user: User|null) => {
console.log('登入使用者:', user)
this.currentUser = user;
})
}
signOut() { // 登出callback function
this.as.logout().then(()=>{
this.router.navigate(['/login']);
}).catch(e=>{
console.log('登出失敗', e);
});
}
}home.page.ts片段
登入頁面範例首頁 (2/2)
<ion-header [translucent]="true">
<ion-toolbar>
<ion-title>
首頁
</ion-title>
<ion-buttons slot="end"> <!-- 登出按鈕-->
<ion-button (click)="signOut()">
<ion-icon slot="start" name="log-out"></ion-icon>
登出
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content [fullscreen]="true">
歡迎 {{ currentUser?.email }}
</ion-content>
home.page.html
註冊註冊服務 (1/7)
...
export class AuthService {
auth: Auth = inject(Auth);
router: Router = inject(Router);
// 建立一個 Observable,當登入狀態改變時會發出通知
user$ = user(this.auth);
currentUser: User | null = this.auth.currentUser;
constructor() {}
....
// 功能3. 註冊新使用者
registerUser(mail: string, password: string) {
createUserWithEmailAndPassword(this.auth, mail, password).then(uc => {
console.log('註冊使用者:', uc.user); /* 註冊成功, 印出使用者 */
this.router.navigate(['login']); /* 前往登入畫面 */
}).catch(error => {
console.log('帳密註冊失敗', error);
});
}
...
}auth.service.ts片段
註冊註冊頁面 (2/7)
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { ConfirmedValidator } from '../helpers/confirm.validator';
....
export class RegisterPage implements OnInit {
// 使用 ! 告訴 TS 這會在 ngOnInit 初始化
registerForm!: FormGroup; // HTML表單
formErrors: Record<string, string> = { // 錯誤訊息的空白物件
email: '', // 注意:每一個欄位名稱必須與 //
password: '', // registerForm建立時的欄位 //
confirm: '' // 名稱相同 //
};
// errorMessages 的定義,讓它允許字串索引
errorMessages: { [key: string]: { [key: string]: string } } = { // 想要顯示在畫面上的「錯誤訊息」
'email': {
'required': '必填欄位',
'email': '請照電子郵件格式填入'
},
'password': {
'required': '必填欄位',
'minlength': '密碼最少8碼'
},
'confirm': {
'required': '必填欄位',
'confirmedValidator': '重新輸入密碼不符'
}
};
// ....未完,續下一頁...
}
register.page.ts片段(1/3)
import { AbstractControl, ValidationErrors } from '@angular/forms';
export function ConfirmedValidator(controlName: string, matchingControlName: string) {
return (formGroup: AbstractControl): ValidationErrors | null => {
const control = formGroup.get(controlName); // 第一個欄位
const matchingControl = formGroup.get(matchingControlName); // 比對欄位
if (matchingControl?.errors && !matchingControl.errors['confirmedValidator']) {
// 若有其他錯誤,則直接return
return null;
}
// 根據比對結果 設定matchingControl錯誤狀態
if (control?.value !== matchingControl?.value) { // 兩者不相等
matchingControl?.setErrors({ confirmedValidator: true });
return { confirmedValidator: true };
} else {
matchingControl?.setErrors(null);
return null;
}
}
}函式功能:比對兩個欄位的內容是否相同
helpers/confirm.validator.ts
手動建立 helpers/confirm.validator.ts
註冊註冊頁面 (3/7)
import { AuthService } from './../services/auth/auth.service';
import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, Validators } from '@angular/forms';
import { Router } from '@angular/router';
import { ConfirmedValidator } from '../helpers/confirm.validator';
....
export class RegisterPage implements OnInit {
// ...續上一頁(2/3)
constructor(private builder: FormBuilder,
public router: Router, public as: AuthService) { }
ngOnInit() {
this.buildForm();
}
//使用FormBuilder服務建立表單,並訂閱表單資料變動資料流
private buildForm() {
this.registerForm = this.builder.group({
email: ['', [Validators.required, Validators.email]],
password: ['', [Validators.required, Validators.minLength(8)]],
confirm: ['', [Validators.required]]
}, {
validators: [ConfirmedValidator('password', 'confirm')]
});
// 訂閱表單「輸入資料變動」的資料流,以便變動發生時,可於表單畫面即時回應錯誤訊息
this.registerForm.valueChanges.subscribe(data => this.onValueChanged(data));
}
// ...未完-續上一頁...
}register.page.ts片段(2/3)
註冊註冊頁面 (4/7)
export class RegisterPage implements OnInit {
// ...續上一頁(3/3)
private onValueChanged(data?: any) {
if (!this.registerForm) return; // 若無registerForm則直接回傳
this.formErrors = { email: '', password: '', confirm: '' }; // 1. 先清空該欄位的錯誤訊息
for (const field in this.formErrors) { // 走訪formErrors裡的每一個欄位
const control = this.registerForm.get(field); // 透過欄位名稱取得表單真正的欄位
if (control && control.dirty && control.invalid) {
// (存在 且 已編輯過 且 不正確) => 代表出現錯誤
const messages = this.errorMessages[field]; // 取得該欄位所有的錯誤訊息
console.log('message:', messages)
for (const key in control.errors) { // 走訪該欄位所有錯誤的狀況
this.formErrors[field] += messages[key];
}
}
}
}
// 註冊
register() {
const formValue = this.registerForm.value;
const email = formValue.email;
const password = formValue.password;
if (email && password) {
this.as.registerUser(email, password);
}
}
// 回到首頁
home() {
this.router.navigate(['home']);
}
}register.page.ts片段(3/3)
export class RegisterPage implements OnInit {
...
formErrors: Record<string, string> = {
email: '', // 注意:每一個欄位名稱必須與 //
password: '', // registerForm建立時的欄位 //
confirm: '' // 名稱相同 //
};
...
}
註冊註冊頁面 (5/7)
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { IonicModule } from '@ionic/angular';
import { RegisterPageRoutingModule } from './register-routing.module';
import { RegisterPage } from './register.page';
@NgModule({
imports: [
CommonModule,
FormsModule,
ReactiveFormsModule,
IonicModule,
RegisterPageRoutingModule
],
declarations: [RegisterPage]
})
export class RegisterPageModule {}register.module.ts
為了在頁面使用表單,必須引入FormsModule, ReactiveFormsModule
註冊註冊頁面 (6/7)
<ion-content class="ion-padding">
<form [formGroup]="registerForm" (ngSubmit)="register()">
<ion-card>
<ion-card-content>
<ion-item fill="solid"> <!--第一個欄位: 輸入區塊 -->
<ion-input label="Email Address" labelPlacement="floating" formControlName="email"></ion-input>
</ion-item>
@if (formErrors['email']!=='') { <!--第一個欄位: 錯誤訊息顯示區塊 -->
<div class="ion-padding-start">
<ion-text color="danger"><small>{{ formErrors['email'] }}</small></ion-text>
</div>
}
<!-- 第二、三欄位省略 -->
<ion-button type="submit" [disabled]="registerForm.invalid" expand="block" class="ion-margin-top">
註冊
</ion-button>
</ion-card-content>
</ion-card>
</form>
</ion-content>register.page.html片段
註冊註冊頁面 (7/7)
<ion-header>
<ion-toolbar color="primary">
<ion-title>註冊</ion-title>
<ion-buttons slot="end">
<ion-button (click)="home()">
<ion-icon slot="icon-only" name="home-outline"></ion-icon>
</ion-button>
</ion-buttons>
</ion-toolbar>
</ion-header>
<ion-content class="ion-padding">
<form [formGroup]="registerForm" (ngSubmit)="register()">
<ion-card>
<ion-card-content>
<ion-item fill="solid">
<ion-input label="Email Address" labelPlacement="floating" formControlName="email"></ion-input>
</ion-item>
@if (formErrors['email']!=='') {
<div class="ion-padding-start">
<ion-text color="danger"><small>{{ formErrors['email'] }}</small></ion-text>
</div>
}
<ion-item fill="solid" class="ion-margin-top">
<ion-input label="密碼" labelPlacement="floating" type="password" formControlName="password"></ion-input>
</ion-item>
@if (formErrors['password']) {
<div class="ion-padding-start">
<ion-text color="danger"><small>{{ formErrors['password'] }}</small></ion-text>
</div>
}
<ion-item fill="solid" class="ion-margin-top">
<ion-input label="驗證密碼" labelPlacement="floating" type="password" formControlName="confirm"></ion-input>
</ion-item>
@if (formErrors['confirm']) {
<div class="ion-padding-start">
<ion-text color="danger"><small>{{ formErrors['confirm'] }}</small></ion-text>
</div>
}
<ion-button type="submit" [disabled]="registerForm.invalid" expand="block" class="ion-margin-top">
註冊
</ion-button>
</ion-card-content>
</ion-card>
</form>
</ion-content>register.page.html完整
<ion-header>
<ion-toolbar>
<ion-title>登入</ion-title>
</ion-toolbar>
</ion-header>
<ion-content>
<ion-item>
<ion-label position="floating">Email Address</ion-label>
<ion-input type="text" [(ngModel)]="email"></ion-input>
</ion-item>
<ion-item>
<ion-label position="floating">密碼</ion-label>
<ion-input type="password" [(ngModel)]="password"></ion-input>
</ion-item>
<ion-button expand="block" (click)='signIn(email, password)'>
登入
</ion-button>
<ion-item>
<ion-label class="ion-text-center" [routerLink]="['/register']">我要註冊</ion-label>
</ion-item>
</ion-content>
記得將註冊功能的超連結放入頁面,如login頁面
修改login.page.html
練習
- 將DiaryApp加上登入功能
- 歷史上的今天不需登入即可使用
- 資料庫編修功能則需要登入
Angular Route Guard
- 路徑守衛:管理頁面的存取權限
- 在app-routing.module.ts加上設定
ionic g guard guards/authRoute Guard路徑守衛 (1/3)
1. 建立authGuard服務

實作哪些介面? 選擇 CanActivate
import { CanActivateFn } from '@angular/router';
export const testGuard: CanActivateFn = (route, state) => {
return true;
};
guards/auth-guard.ts
預設內容
import { CanActivateFn, Router } from '@angular/router';
import { AuthService } from '../services/auth.service';
import { inject } from '@angular/core';
import { map } from 'rxjs';
export const authGuard: CanActivateFn = (route, state) => {
const as = inject(AuthService); // 注入自訂服務
const router = inject(Router); // 注入Router服務
return as.user$.pipe( // 訂閱登入狀態
map( (user) => {
if (user) { // 登入狀態若為已登入
return true; // 映對至true
} else { // 反之, 映對至false
router.navigate(['login']); // 轉至login頁面
return false;
}
})
);
};
Route Guard路徑守衛 (2/3)
guards/auth-guard.ts
修改內容
pipe(): 串連運算子
map(): 逐一將訂閱資料映對(mapping)至新值
Route Guard路徑守衛 (3/3)
2. 修改app-routing.module.ts
//...省略....
import { authGuard } from './guards/auth-guard';
const routes: Routes = [
{
path: 'home',
canActivate: [authGuard],
loadChildren: () => import('./home/home.module').then( m => m.HomePageModule)
},
{
path: '',
redirectTo: 'home',
pathMatch: 'full'
},
{
path: 'login',
loadChildren: () => import('./login/login.module').then( m => m.LoginPageModule)
},
];
//...省略....
app/app-routing.module.ts
加入canActivate屬性設定
What's the problem?
沒有足夠的會員資料欄位
需另外儲存於資料庫!
Google登入
- Firebase端設定
- iOS版/Android版不同
Google帳號登入


1. 點選登入
2. 彈出OAuth對話框
3. 收取後續存取資訊

4. 送出認證碼到伺服器
5. 認證碼換取後續存取權杖
6. google回傳後續存取權杖
7.伺服器確認登入
伺服器
Google登入頁面範例建立專案(1/2)
cd loginApp
ionic g page login1.建立空白專案,並設定app id!
ionic start loginApp blank --id com.aumis.loginapp2.新增所需頁面
注意如何修改appModule !
4.新增ionic storage所需[npm套件] 與[cordova外掛]
ionic cordova plugin add cordova-sqlite-storage
npm install --save @ionic/storageionic g service services/auth3.新增登入認證provider
<widget id="com.aumis.loginapp"...>或是開啟現有專案的config.xml, 修改 id值
id格式:
與網址順序相反
com.公司名.產品名
config.xml
Google登入頁面範例建立專案(2/2)
import { IonicStorageModule } from '@ionic/storage';
import { HttpClientModule } from '@angular/common/http';
@NgModule({
...
imports: [
...
HttpClientModule,
IonicStorageModule.forRoot()
...
],
...
app.module.ts片段
5.修改app.module.ts
引入IonicStorageModule, HttpClientModule 模組
Google登入頁面範例安裝firebase套件(1/4)
6.安裝firebase, @angular/fire套件
npm install firebase --save
npm install @angular/fire --savefirebase: v. 7.14.3
@angular/fire: v. 6.0.0
2020/5/13
Google登入頁面範例firebase套件(2/4)
複製反白區域
Google登入頁面範例firebase套件(3/4)
7.在app資料夾新增app.firebase.config.ts, 貼入連結參數
export const environment = {
production: false,
firebaseConfig: {
apiKey: 'XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX',
authDomain: '專案id.firebaseapp.com',
databaseURL: 'https://專案id.firebaseio.com',
projectId: '專案id',
storageBucket: '專案id.appspot.com',
messagingSenderId: 'XXXXXXXXXXXX'
}
};將上頁複製內容貼上
Google登入頁面範例firebase套件(4/4)
8.再次修改app.module.ts
import { NgModule } from '@angular/core';
import { AngularFireModule } from '@angular/fire';
import { AngularFireAuthModule } from '@angular/fire/auth';
import { AngularFirestoreModule } from '@angular/fire/firestore';
import { environment } from '../environments/environment';
...略...
@NgModule({
...略...
imports: [
BrowserModule,
AngularFireModule.initializeApp( environment.firebaseConfig),
AngularFireAuthModule,
AngularFirestoreModule
],
...略...
})
Google登入頁面範例Firebase設定(1/3)
9.建立偵錯簽署憑證 SHA-1(使用JDK工具keytool)
keytool -exportcert -list -v -alias androiddebugkey -keystore ./.android/debug.keystore密碼輸入 android
keytool為JAVA SDK所提供
複製到Firebase
cd .android
keytool -genkey -v -keystore debug.keystore -alias androiddebugkey -keyalg RSA -validity 1000010.將id與SHA-1憑證填入Firebase
iOS只須填入id
android須填入id與SHA-1憑證
Google登入頁面範例Firebase設定(2/3)
1. 填入id
2. 填入SHA-1憑證
3.
4. iOS比照辦理,但只須填入id
Google登入頁面範例Firebase設定(3/3)
Google登入頁面範例安裝google外掛(1/3)
11.從Firebase後台找出REVERSED_CLIENT_ID
1.專案設定
2. iOS應用程式
以便安裝google登入外掛
3.下載並開啟GoogleService-info.plist
4.從中找出REVERSED_CLIENT_ID
12. 安裝Ionic Native外掛:Google Plus
ionic cordova plugin add cordova-plugin-googleplus --variable REVERSED_CLIENT_ID=FIREBASE後台提供之ID
npm install --save @ionic-native/google-plusID在GoogleService-info.plist檔案內
註: angularfire2的版本(5.0.0-rc.8.0)有時會導致無法安裝
(可嘗試降版本重新安裝, 如降至5.0.0-rc.6.0)
Google登入頁面範例安裝google外掛(2/3)
13.再次修改app.module.ts
...
import { GooglePlus } from '@ionic-native/google-plus';
@NgModule({
...
providers: [
GooglePlus,
StatusBar,
...
]
})
...
app.module.ts片段
引入GooglePlus模組
Google登入頁面範例安裝google外掛(3/3)
Google登入頁面範例首頁設定
app.component.ts首頁設定
...
import { Storage } from '@ionic/storage';
import { LoginPage } from '../pages/login/login';
...
export class MyApp {
rootPage:any = HomePage;
constructor(platform: Platform, statusBar: StatusBar, splashScreen: SplashScreen,
storage: Storage) {
platform.ready().then(() => {
storage.get('signin').then((result)=>{
if(result){ // 已登入, 'signin' is set to true in storage
this.rootPage = HomePage;
} else { // not logged in
this.rootPage = LoginPage;
}
});
statusBar.styleDefault();
splashScreen.hide();
});
}
}app.component.ts片段
storage 儲存資料"signin"
檢查"signin"值, 決定rootPage為何
Google登入頁面範例登入頁面(1/3)
login.html
<ion-header>
<ion-navbar color="primary">
<ion-title>登入</ion-title>
</ion-navbar>
</ion-header>
<ion-content padding>
<ion-item>
<ion-label floating>Email Address</ion-label>
<ion-input type="text" [(ngModel)]="user.email"></ion-input>
</ion-item>
<ion-item>
<ion-label floating>密碼</ion-label>
<ion-input type="password" [(ngModel)]="user.password"></ion-input>
</ion-item>
<button ion-button block (click)='signIn(user)'>登入</button>
<button ion-button block (click)="signInWithFB()"> FB登入 </button>
<button ion-button block color="danger" (click)="signInWithGoogle()"> Google登入 </button>
</ion-content>
Google登入頁面範例登入頁面(2/3)
login.ts 片段
import { User } from '../../models/user';
...
export class LoginPage {
user= {} as User;
constructor(public navCtrl: NavController, public navParams: NavParams,
private auth: AuthProvider, private storage: Storage) {
}
...
/** Google登入 **/
async signInWithGoogle(){
await this.auth.signInWithGoogle();
this.navCtrl.setRoot(HomePage);// 跳轉至home page
}
}
export interface User {
email: string;
password: string;
}
models/user.ts
Google登入頁面範例登入頁面(3/3)
login.ts
import { Component } from '@angular/core';
import { IonicPage, NavController, NavParams } from 'ionic-angular';
import { Storage } from '@ionic/storage';
import { AuthProvider } from '../../providers/auth/auth';
import { User } from '../../models/user';
import { HomePage } from '../home/home';
@IonicPage()
@Component({
selector: 'page-login',
templateUrl: 'login.html',
})
export class LoginPage {
user= {} as User;
constructor(public navCtrl: NavController, public navParams: NavParams,
private auth: AuthProvider, private storage: Storage) {
}
signIn(user) {
this.auth.signIn(user).then((resp)=>{
console.log(resp);
this.storage.set('signin', true);
this.storage.set('name', resp.user.email);
this.navCtrl.setRoot(HomePage);// 跳轉至home page
}).catch((err)=>{
console.log('Error:', err);
});
}
/**
Facebook登入
**/
signInWithFB() {
this.auth.signInWithFacebook().then((resp)=>{
console.log(resp);
this.storage.set('signin', true);
this.storage.set('name', resp.user.displayName);
this.navCtrl.setRoot(HomePage);// 跳轉至home page
}).catch((err) => {
console.log("SignIn Error: FB", err);
});
}
async signInWithGoogle(){
await this.auth.signInWithGoogle();
this.navCtrl.setRoot(HomePage);// 跳轉至home page
}
}
Google登入頁面範例登入認證(1/4)
...
import { Storage } from '@ionic/storage';
import { GooglePlus } from '@ionic-native/google-plus';
import { Platform } from 'ionic-angular';
@Injectable()
export class AuthProvider {
private user: Observable<firebase.User>; // 屬性:Observer of firebase.User
private userDetails: firebase.User = null; // user帳號資料
constructor(public http: HttpClient,
private google: GooglePlus,
private afAuth: AngularFireAuth,
private storage: Storage,
private platform: Platform) {
this.user = this.afAuth.authState; // 建立Observable
// 訂閱
this.user.subscribe((result) => {
if (result) { // 有結果
this.userDetails = result;
console.log(this.userDetails);
} else {
this.userDetails = null;
}
});
}
...
auth.ts片段
GooglePlus登入:手機版/Web版
手機版:需填寫google(非firebase)提供之id
Web版:angularfire2全權處理
Google登入頁面範例登入認證(2/4)
1. 確認專案名稱與Firebase專案相同
2. 在OAuth 2.0用戶端 ID區塊,找到Web client
3. 複製最後一欄「用戶端id」
Google登入頁面範例登入認證(3/4)
export class AuthProvider {
...
// Google login 瀏覽器版
async webGoogleLogin(): Promise<void> {
try {
const provider = new firebase.auth.GoogleAuthProvider();
const credential = await this.afAuth.auth.signInWithPopup(provider);
console.log('login credential:', credential);
this.storage.set('signin', true);
this.storage.set('name', credential.user.displayName);
} catch (err) {
console.log('Google Web Login Err:', err);
}
}
// Google login 手機版
async nativeGoogleLogin(): Promise<void> {
try {
const googleUser = await this.google.login({
'webClientId': 'Google Web Client用戶端id填在此',
'offline': true,
'scopes': 'profile email'
});
return await this.afAuth.auth.signInWithCredential(firebase.auth.GoogleAuthProvider.credential(googleUser.idToken));
} catch (err) {
console.log('Native Login Error:', err);
}
}
// Google 登入
async signInWithGoogle(){
if (this.platform.is('cordova')) {
await this.nativeGoogleLogin(); // 手機
} else {
await this.webGoogleLogin(); // 瀏覽器
}
}
...
auth.ts片段
Google登入頁面範例登入認證(4/4)
import { HttpClient } from '@angular/common/http';
import { Injectable } from '@angular/core';
import { User } from '../../models/user';
import { AngularFireAuth } from 'angularfire2/auth';
import firebase from 'firebase/app';
import { Observable } from 'rxjs/Observable';
import { Storage } from '@ionic/storage';
import { GooglePlus } from '@ionic-native/google-plus';
import { Platform } from 'ionic-angular';
@Injectable()
export class AuthProvider {
private user: Observable<firebase.User>; // 屬性:Observer of firebase.User
private userDetails: firebase.User = null; // user帳號資料
constructor(public http: HttpClient,
private google: GooglePlus,
private afAuth: AngularFireAuth,
private storage: Storage,
private platform: Platform) {
this.user = this.afAuth.authState; // 建立Observable
// 訂閱
this.user.subscribe((result) => {
if (result) { // 有結果
this.userDetails = result;
console.log(this.userDetails);
} else {
this.userDetails = null;
}
});
}
// FB 登入
signInWithFacebook() {
return this.afAuth.auth.signInWithPopup(
new firebase.auth.FacebookAuthProvider()
);
}
async webGoogleLogin(): Promise<void> {
try {
const provider = new firebase.auth.GoogleAuthProvider();
const credential = await this.afAuth.auth.signInWithPopup(provider);
console.log('login credential:', credential);
this.storage.set('signin', true);
this.storage.set('name', credential.user.displayName);
} catch (err) {
console.log('Google Web Login Err:', err);
}
}
// Google login 手機版
async nativeGoogleLogin(): Promise<void> {
try {
const googleUser = await this.google.login({
'webClientId': 'Google Web Client用戶端id填在此',
'offline': true,
'scopes': 'profile email'
});
return await this.afAuth.auth.signInWithCredential(firebase.auth.GoogleAuthProvider.credential(googleUser.idToken));
} catch (err) {
console.log('Native Login Error:', err);
}
}
// Google 登入
async signInWithGoogle(){
if (this.platform.is('cordova')) {
await this.nativeGoogleLogin(); // 手機
} else {
await this.webGoogleLogin(); // 瀏覽器
}
}
// email/password登入
signIn(user: User) {
return this.afAuth.auth.signInWithEmailAndPassword(user.email, user.password);
}
// 登出
signOut() {
return this.afAuth.auth.signOut();
}
state():boolean{
if(this.afAuth.authState) {
return true
} else {
return false
}
}
}
auth.ts
Google登入頁面範例首頁(1/2)
<ion-header>
<ion-navbar>
<ion-title>
首頁
</ion-title>
<ion-buttons end>
<button ion-button (click)="logout()">登出</button>
</ion-buttons>
</ion-navbar>
</ion-header>
<ion-content padding>
<p>
Welcome {{ name }}
</p>
</ion-content>home.html
Google登入頁面範例首頁(2/2)
import { Component } from '@angular/core';
import { NavController, NavParams } from 'ionic-angular';
import { AuthProvider } from '../../providers/auth/auth';
import { Storage } from '@ionic/storage';
import { LoginPage } from '../login/login';
...
export class HomePage {
name:any;
constructor(public navCtrl: NavController, public navParams: NavParams,
private auth: AuthProvider,
private storage: Storage) {
this.storage.get('name').then(data => {
this.name = data;
});
}
logout(){
this.auth.signOut().then(resp=>{
this.storage.set('signin', false);
this.navCtrl.setRoot(LoginPage);
});
}
}
home.ts片段
從storage中讀取 "name"值
get()回傳類型:Promise<any>
登出時將storage中"signin"設為false
signOut()回傳類型:Promise<any>
Google登入頁面範例執行畫面
Facebook帳號登入



1. 點選登入
2. 彈出OAuth對話框
1. 送出FB登入認證
2. 要求app轉址
3. 進行轉址
4. 要求取得存取權限
5. 回傳存取權杖
6. 要求讀取用戶資料
7. 回傳用戶資料
8. 顯示用戶資料
FB應用程式
FB登入fackbook應用程式設定
FB登入fackbook應用程式設定
FB登入fackbook應用程式設定
複製到Firebase
FB登入fackbook應用程式設定
複製回Facebook
FB登入fackbook應用程式設定
Ionic Tutorial
By Leuo-Hong Wang
Ionic Tutorial
Lesson 7: Authentication with Firebase
- 1,813