我通常一直在關注此代碼:https ://github.com/aaronksaunders/ionic-v6-firebase-tabs-auth
我遇到的問題是,當我在使用 ionic serve 并在 Web 瀏覽器中加載應用程式時重繪 頁面時,我的身份驗證狀態不會持續存在。
pinia商店代碼:
import { defineStore } from "pinia";
import { User } from "firebase/auth";
import {
Profile,
getProfile,
setProfile,
} from "@/firebase/helpers/firestore/profileManager";
import { onSnapshot, Unsubscribe, doc } from "@firebase/firestore";
import { db } from "@/firebase/connectEmulators";
import { getAuth } from "@firebase/auth";
import {
onAuthStateChanged,
signInWithEmailAndPassword,
signOut,
createUserWithEmailAndPassword,
updateProfile as updateAuthProfile,
} from "firebase/auth";
import errorHandler from "@/helpers/errorHandler";
/**@see {@link Profile} */
export enum UserType {
DNE,
uploader,
checker,
host,
}
interface State {
user: User | null;
profile: Profile | null;
error: null | any;
unsub: Unsubscribe | null;
}
export const useUserStore = defineStore("user", {
state: (): State => {
return {
user: null,
profile: null,
error: null,
unsub: null,
};
},
getters: {
isLoggedIn: (state) => state.user !== null,
//DEV: do we need this to be a getter?
userError: (state) => {
if(state.error){
switch (state.error.code) {
case "auth/user-not-found":
return "Email or Password incorrect!";
case "auth/wrong-password":
return "Email or Password incorrect!";
default:
return state.error;
}
}
return null;
},
/**
* @see Profile
*/
getType: (state): UserType => {
if (state.user === null) return UserType.DNE;
if (!state.profile) return UserType.DNE;
if (state.profile.locations.length > 0) return UserType.host;
if (state.profile.queues.length > 0) return UserType.checker;
return UserType.uploader;
},
},
actions: {
initializeAuthListener() {
return new Promise((resolve) => {
const auth = getAuth();
onAuthStateChanged(auth, (user) => {
console.log("AuthListener Initialized");
if (user) {
console.log("AuthListener: User exists!");
this.user = user;
getProfile(user.uid).then((profile) => {
if (profile) {
this.profile = profile;
this.initializeProfileListener();
} else {
this.profile = null;
if (this.unsub) this.unsub();
}
});
} else {
console.log("AuthListener: User does not exist!");
this.user = null;
}
resolve(true);
});
});
},
/**
*
* @param email email for login
* @param password password for login
*/
async signInEmailPassword(email: string, password: string) {
try {
const auth = getAuth();
const userCredential = await signInWithEmailAndPassword(
auth,
email,
password
);
this.user = userCredential.user ? userCredential.user : null;
this.error = null;
return true;
} catch (error: any) {
console.log(typeof error.code);
console.log(error.code);
this.user = null;
this.error = error;
return false;
}
},
async logoutUser() {
try {
const auth = getAuth();
await signOut(auth);
this.user = null;
this.profile = null;
this.error = null;
if (this.unsub) this.unsub();
return true;
} catch (error: any) {
this.error = error;
return false;
}
},
async createEmailPasswordAccount(
email: string,
password: string,
userName: string,
refSource: string
) {
try {
const auth = getAuth();
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
//Add username to fireauth profile
//DEV: test for xss vulnerabilities
await updateAuthProfile(userCredential.user, { displayName: userName });
//create user profile data in firestore
let profile: Profile | undefined = new Profile(
userCredential.user.uid,
refSource
);
await setProfile(profile);
profile = await getProfile(userCredential.user.uid);
//set local store
this.user = userCredential.user ? userCredential.user : null;
this.profile = profile ? profile : null;
this.error = null;
//TODO: send email verification
return true;
} catch (error: any) {
this.user = null;
this.error = error;
return false;
}
},
initializeProfileListener() {
try {
if (!this.profile) errorHandler(Error("Profile not set in state!"));
else {
const uid = this.profile.uid;
const unsub: Unsubscribe = onSnapshot(
doc(db, "profiles", uid),
(snapshot) => {
const fbData = snapshot.data();
if (!fbData)
errorHandler(Error("Profile Listener snapshot.data() Null!"));
else {
const profile = new Profile(
snapshot.id,
fbData.data.referralSource
);
profile.data = fbData.data;
profile.settings = fbData.settings;
profile.locations = fbData.locations;
profile.queues = fbData.queues;
profile.checkers = fbData.checkers;
profile.uploadHistory = fbData.uploadHistory;
profile.hostLevel = fbData.hostLevel;
this.profile = profile;
}
},
(error) => {
errorHandler(error);
}
);
this.unsub = unsub;
}
} catch (error) {
errorHandler(error as Error);
}
},
},
});
main.ts 我在其中初始化身份驗證偵聽器:
import { createApp } from "vue";
import App from "./App.vue";
import router from "./router";
import { IonicVue } from "@ionic/vue";
/* Core CSS required for Ionic components to work properly */
import "@ionic/vue/css/core.css";
/* Basic CSS for apps built with Ionic */
import "@ionic/vue/css/normalize.css";
import "@ionic/vue/css/structure.css";
import "@ionic/vue/css/typography.css";
/* Optional CSS utils that can be commented out */
import "@ionic/vue/css/padding.css";
import "@ionic/vue/css/float-elements.css";
import "@ionic/vue/css/text-alignment.css";
import "@ionic/vue/css/text-transformation.css";
import "@ionic/vue/css/flex-utils.css";
import "@ionic/vue/css/display.css";
/* Theme variables */
import "./theme/variables.css";
/* PWA elements for using Capacitor plugins */
import { defineCustomElements } from "@ionic/pwa-elements/loader";
/* Pinia used for state management */
import { createPinia } from "pinia";
import { useUserStore } from "./store/userStore";
const pinia = createPinia();
const app = createApp(App)
.use(IonicVue, {
// TODO: remove for production
mode: process.env.VUE_APP_IONIC_MODE,
})
.use(pinia);
defineCustomElements(window);
//get the store
const store = useUserStore();
store.initializeAuthListener().then(() => {
app.use(router);
});
router.isReady().then(() => {
app.mount("#app");
});
我嘗試重構 main.ts 以將應用程式安裝在回呼中以初始化身份驗證偵聽器,并且我嘗試使我的代碼與上述鏈接的 main.ts 中的代碼完全相同。都沒有解決問題。
我還在這里查看了這個問題:https ://stackoverflow.com/a/67774186/9230780 答案中的大多數要點都不應該相關,因為我目前正在使用 firebase 模擬器來測驗應用程式。
盡管如此,我已經驗證了我的 api 密鑰是正確的。我可以看到當我啟動應用程式時在瀏覽器中創建了 cookie,所以我認為它們被擦除不是問題。
理想情況下,我想避免在這里實作@capacitor/storage,因為它不應該是必要的。
我確實計劃實作這個庫來處理 ios 和 android 的身份驗證:https ://github.com/baumblatt/capacitor-firebase-auth 但這不應該與應用程式的 Web 版本相關。
編輯:意識到我錯過了與該問題相關的一段代碼。不知道我怎么沒有復制它。添加的代碼是初始化組態檔偵聽器函式。
uj5u.com熱心網友回復:
我最終對 pinia 商店進行了重構,從而解決了這個問題。我相信這個問題可能是由身份驗證偵聽器如何呼叫 initializeProfileListener 引起的。我在身份驗證偵聽器中沒有代碼來檢查組態檔偵聽器是否已經初始化,因此每次 authstate 更改或它會初始化一個新的組態檔偵聽器而不取消舊的偵聽器。我不確定這是導致問題的原因。
下面是正常運行的新代碼。松樹專賣店:
import { defineStore } from "pinia";
import { User } from "firebase/auth";
import {
Profile,
getProfile,
profileListener,
} from "@/firebase/helpers/firestore/profileManager";
import {
fbCreateAccount,
fbSignIn,
fbAuthStateListener,
fbSignOut,
} from "@/firebase/helpers/firestore/authHelper";
import {Unsubscribe} from "@firebase/firestore";
import errorHandler from "@/helpers/errorHandler";
/**@see {@link Profile} */
export enum UserType {
DNE,
uploader,
checker,
host,
}
interface State {
user: User | null;
profile: Profile | null;
error: null | any;
unsub: Unsubscribe | null;
}
export const useUserStore = defineStore("user", {
state: (): State => {
return {
user: null,
profile: null,
error: null,
unsub: null,
};
},
getters: {
isLoggedIn: (state) => state.user !== null,
//DEV: do we need this to be a getter?
userError: (state) => {
if (state.error) {
switch (state.error.code) {
case "auth/user-not-found":
return "Email or Password incorrect!";
case "auth/wrong-password":
return "Email or Password incorrect!";
default:
return state.error;
}
}
return null;
},
/**
* @see Profile
*/
getType: (state): UserType => {
if (state.user === null) return UserType.DNE;
if (!state.profile) return UserType.DNE;
if (state.profile.locations.length > 0) return UserType.host;
if (state.profile.queues.length > 0) return UserType.checker;
return UserType.uploader;
},
},
actions: {
initializeAuthListener() {
return new Promise((resolve) => {
fbAuthStateListener(async (user: any) => {
if (user) {
this.user = user;
const profile = await getProfile(user.uid);
if (profile) {
this.profile = profile;
//TODO: initialize profile listener
if(this.unsub === null) {
this.initializeProfileListener();
}
}
}
resolve(true);
});
});
},
/**
*
* @param email email for login
* @param password password for login
*/
async signInEmailPassword(email: string, password: string) {
try {
const userCredential = await fbSignIn(email, password);
this.user = userCredential.user ? userCredential.user : null;
this.error = null;
return true;
} catch (error: any) {
console.log(typeof error.code);
console.log(error.code);
this.user = null;
this.error = error;
return false;
}
},
async logoutUser() {
try {
await fbSignOut();
this.user = null;
this.profile = null;
this.error = null;
if (this.unsub) this.unsub();
return true;
} catch (error: any) {
this.error = error;
return false;
}
},
async createEmailPasswordAccount(
email: string,
password: string,
userName: string,
refSource: string
) {
try {
const { user, profile } = await fbCreateAccount(
email,
password,
userName,
refSource
);
//set local store
this.user = user ? user : null;
this.profile = profile ? profile : null;
this.error = null;
//TODO: send email verification
return true;
} catch (error: any) {
this.user = null;
this.error = error;
return false;
}
},
initializeProfileListener() {
try {
if (this.user) {
const unsub = profileListener(
this.user?.uid,
async (profile: any) => {
if (profile) {
this.profile = profile;
}
}
);
this.unsub = unsub;
}
} catch (error) {
errorHandler(error as Error);
}
},
},
});
authHelper.ts
import { auth } from "@/firebase/firebase";
import {
createUserWithEmailAndPassword,
signInWithEmailAndPassword,
signOut,
onAuthStateChanged,
updateProfile as updateAuthProfile,
} from "@firebase/auth";
import { Profile, setProfile, getProfile } from "./profileManager";
/**
* @param email
* @param password
* @param userName
* @param refSource @see profileManager
* @returns
*/
export const fbCreateAccount = async (
email: string,
password: string,
userName: string,
refSource: string
) => {
//DEBUG: creating a user works but throws an error.
const userCredential = await createUserWithEmailAndPassword(
auth,
email,
password
);
if (userCredential) {
//add username to fireauth profile
await updateAuthProfile(userCredential.user, { displayName: userName });
//create user profile data in firestore
let profile: Profile | undefined = new Profile(
userCredential.user.uid,
refSource
);
await setProfile(profile);
profile = await getProfile(userCredential.user.uid);
//TODO: errorHandling for setProfile and getProfile
return {
user: userCredential.user,
profile: profile,
};
} else {
return {
user: null,
profile: null,
};
}
};
/**
*
* @param email
* @param password
* @returns UserCredential {@link https://firebase.google.com/docs/reference/js/auth.usercredential.md?authuser=0#usercredential_interface}
*/
export const fbSignIn = async (email: string, password: string) => {
const userCredential = signInWithEmailAndPassword(auth, email, password);
//TODO: add call to add to profile signins array
return userCredential;
};
export const fbSignOut = async () => {
await signOut(auth);
return true;
};
/**
* @see {@link https://firebase.google.com/docs/reference/js/auth.md?authuser=0&hl=en#onauthstatechanged}
* @param callback contains either user or null
*/
export const fbAuthStateListener = (callback: any) => {
onAuthStateChanged(auth, (user) => {
if (user) {
//user is signed in
callback(user);
} else {
//user is signed out
callback(null);
}
});
};
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/466896.html
