Reputation: 168
I'm using next-auth with firebase adapter. Everything is working fine in terms of saving users in database, but something is not working in terms of authentication.
import NextAuth from "next-auth"
import GoogleProvider from "next-auth/providers/google"
import { FirebaseAdapter } from "@next-auth/firebase-adapter"
import { db } from "../../../utils/firebase/firebase"
import * as firestoreFunctions from 'firebase/firestore'
import { adminAuth } from "../../../utils/firebase/firebaseAdmin"
import { getAuth, signInWithCustomToken } from "firebase/auth"
const auth = getAuth()
export default NextAuth({
providers: [
GoogleProvider({
clientId: process.env.GOOGLE_ID,
clientSecret: process.env.GOOGLE_SECRET,
state: false,
}),
],
adapter: FirebaseAdapter({
db: db,
...firestoreFunctions,
}),
callbacks: {
async signIn({ user, account, profile, email, credentials }) {
console.log(user, 'user')
const customToken = await adminAuth.createCustomToken(user.id)
const customSignIn = await signInWithCustomToken(auth, customToken)
console.log(customSignIn, 'customSignIn')
console.log(customSignIn.user, 'customSignIn.user')
user = customSignIn.user
console.log(user, 'user 2')
return true
},
async redirect({ url, baseUrl }) {
return baseUrl
},
async session({ session, user, token }) {
if (session?.user) {
session.user.id = token.sub
}
return session
},
async jwt({ token, user, account, profile, isNewUser }) {
if (isNewUser) {
const additionalClaims = {
isStudent: true,
isTeacher: false,
isStaff: false,
isAdmin: false
}
const customToken = await adminAuth.createCustomToken(token.sub, additionalClaims)
const customSignIn = await signInWithCustomToken(auth, customToken)
user = customSignIn.user
}
return token
}
},
session: {
strategy: 'jwt',
},
})
My users can log in, but are not authenticated.
const auth = getAuth()
onAuthStateChanged(auth, (user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
console.log('user')
console.log(user)
// ...
} else {
console.log('no user')
// User is signed out
// ...
}
})
The Observer returns 'no user', but I'm logged in.
Upvotes: 6
Views: 5945
Reputation: 168
Ok, so what I finally did is: as @esi're said, pass the customToken created with firebase auth, to the session:
async jwt({
token,
user,
account,
profile,
isNewUser
}) {
if (isNewUser || user) {
const additionalClaims = {
isStudent: true,
isTeacher: false,
isStaff: false,
isAdmin: false
}
const customToken = await adminAuth.createCustomToken(token.sub, additionalClaims)
console.log(customToken, '***customToken')
token.customToken = customToken
}
return token
}
Then, in the session callback:
async session({
session,
token,
user
}) {
if (session ? .user) {
session.user.id = token.sub
session.customToken = token.customToken
}
return session
},
And finally, in the page where users are first redirected:
const [user, setUser] = useState()
useEffect(() => {
if (status === "authenticated") {
const loggedInUser = {
userName: session.user.name,
userEmail: session.user.email,
userPhoto: session.user.image,
userUID: session.user.id,
}
signInWithCustomToken(auth, session.customToken)
dispatch(setActiveUser(loggedInUser))
}
}, [status])
const handleLogOut = () => {
dispatch(setUserLogOutState())
logout()
signOut({
callbackUrl: '/'
}) //TODO: goodbye page
// Rest of jsx code when session is true
And that's it. Hope it works for someone else too.
Thanks !
Upvotes: 3
Reputation: 66
You're not seeing a logged in user because the firebase auth is being done on the server side, and not the client side.
You should try passing the customToken to the session, then use it on the client side to sign in the user to firebase auth.
You would also want to wrap the useSession hook in a custom hook like below and use that instead of useSession.
const useFirebaseSession = () => {
const session = useSession();
const [status, setStatus] = useState(session.status);
useEffect(() => {
if (session && session.status === 'authenticated') {
signInWithCustomToken(auth, session.customToken).then(() => {
setStatus('authenticated');
});
}
}, [session]);
useEffect(() => {
if(session.status !== 'authenticated') {
setStatus(session.status)
}
}, [session.status]);
return { data: session.data, status };
}
Upvotes: 5