主要的身份驗證部分在前兩天已經寫完了,這篇要分享的是我在過程中遇到的問題以及解決方式。
這是驗證信預設的內容:

如果想要修改的話,可以到 Amazon Cognito -> User pools -> Messaging 最底下有個 Message templates 找到 Verification message template 編輯即可



當使用 Auth.signIn 登入後關閉 App 重啟,Auth.currentAuthenticatedUser() 會回傳 The user is not authenticated,它不會記住關閉 App 前的登入狀態。
尋找解法的時候我看到了這個 issue,發文的人和我的疑問是一樣的,下面有回覆說是因為 Amplify 所使用的 AsyncStorage 過舊,新版本已經不再支持,所以重新覆寫一下 Storage 的部分即可:
// services/storage.ts
import AsyncStorage from '@react-native-async-storage/async-storage'
/*
* Copyright 2017-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
* the License. A copy of the License is located at
*
* http://aws.amazon.com/apache2.0/
*
* or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
* CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
* and limitations under the License.
*/
const MEMORY_KEY_PREFIX = '@StorageService:'
let dataMemory: any = {}
/** @class */
export class MemoryStorageNew {
static syncPromise: Promise<any> | null = null
/**
* This is used to set a specific item in storage
* @param {string} key - the key for the item
* @param {object} value - the value
* @returns {string} value that was set
*/
static setItem(key: any, value: any) {
AsyncStorage.setItem(MEMORY_KEY_PREFIX + key, value)
dataMemory[key] = value
return dataMemory[key]
}
/**
* This is used to get a specific key from storage
* @param {string} key - the key for the item
* This is used to clear the storage
* @returns {string} the data item
*/
static getItem(key: string) {
return Object.prototype.hasOwnProperty.call(dataMemory, key)
? dataMemory[key]
: undefined
}
/**
* This is used to remove an item from storage
* @param {string} key - the key being set
* @returns {string} value - value that was deleted
*/
static removeItem(key: string) {
AsyncStorage.removeItem(MEMORY_KEY_PREFIX + key)
return delete dataMemory[key]
}
/**
* This is used to clear the storage
* @returns {string} nothing
*/
static clear() {
dataMemory = {}
return dataMemory
}
/**
* Will sync the MemoryStorage data from AsyncStorage to storageWindow MemoryStorage
* @returns {void}
*/
static sync() {
if (!MemoryStorageNew.syncPromise) {
MemoryStorageNew.syncPromise = new Promise((res, rej) => {
AsyncStorage.getAllKeys((errKeys, keys) => {
if (errKeys) rej(errKeys)
const memoryKeys = keys!.filter(key =>
key.startsWith(MEMORY_KEY_PREFIX)
)
AsyncStorage.multiGet(memoryKeys, (err, stores) => {
if (err) rej(err)
stores!.map((result, index, store) => {
const key = store[index][0]
const value = store[index][1]
const memoryKey = key.replace(MEMORY_KEY_PREFIX, '')
dataMemory[memoryKey] = value
})
res(true)
})
})
})
}
return MemoryStorageNew.syncPromise
}
}
/** @class */
export default class StorageHelper {
private storageWindow: any
/**
* This is used to get a storage object
* @returns {object} the storage
*/
constructor() {
this.storageWindow = MemoryStorageNew
}
/**
* This is used to return the storage
* @returns {object} the storage
*/
getStorage() {
return this.storageWindow
}
}
在 Amplify.configure 設置 Auth.storage 為 MemoryStorageNew
import { MemoryStorageNew } from '@/services/storage'
import awsconfig from './aws-exports'
Amplify.configure({
...awsconfig,
ssr: true,
Auth: {
storage: MemoryStorageNew,
}
})
接續上面的繼續說,如果今天登入表單可以自行勾選是否要記住登入狀態,那就需要稍作調整,因為上面的例子是無論如何都記憶住登入狀態。
在網上查了很多資料,得到的都是 React 的解法,就是根據是否要記住登入狀態來修改 storage 為 localStorage 或者 sessionStorage,但是 RN 上無法這樣做。
所以我在調用 getUser 時傳入 isAutomatic 來判斷是不是應用剛啟用正在檢驗身份,如果是並且 rememberMe 為 true 或者調用 Auth.signIn 的話才要自動登入。
import AsyncStorage from '@react-native-async-storage/async-storage'
// ...
const [user, setUser] = useState<any>(null)
const [rememberMe, setRememberMe] = useState(false)
useEffect(() => {
const unsubscribe = Hub.listen('auth', ({ payload: { event, data } }) => {
switch (event) {
case 'signIn':
getUser(false)
break
case 'signOut':
setUser(null)
break
case 'signIn_failure':
console.log('Sign in failure', data)
break
}
})
getUser(true)
return unsubscribe
}, [])
const getUser = async (isAutomatic: boolean) => {
try {
const rememberMe = await AsyncStorage.getItem('rememberMe')
setRememberMe(rememberMe === 'true')
// 勾選"記住我" 或使用 login function 登入
const shouldKeepLoggedIn = isAutomatic && rememberMe === 'true'
if (shouldKeepLoggedIn || !isAutomatic) {
const currentUser = await Auth.currentAuthenticatedUser()
setUser(currentUser)
}
} catch (error) {
console.error(error)
setUser(null)
}
}
const login = async (data) => {
try {
const { email, password } = data
await Auth.signIn(email, password)
// 保存"記住我"的勾選結果
if (rememberMe) {
await AsyncStorage.setItem('rememberMe', 'true')
} else {
await AsyncStorage.setItem('rememberMe', 'false')
}
} catch (error: any) {
console.log(error)
}
}