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
// .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.
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./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.
Share Improve this question edited Mar 28, 2022 at 22:07 jonrsharpe 122k30 gold badges267 silver badges474 bronze badges asked Mar 28, 2022 at 22:04 Diego González CruzDiego González Cruz 1681 silver badge10 bronze badges 6- I don't understand, if you're using next-auth why are you using firebase auth sdk? – mocherfaoui Commented Mar 28, 2022 at 23:35
- Because I need to write firestore rules, as I'm setting custom claims and also, use other firebase services such as Storage. Does it make sense? How would you do it? – Diego González Cruz Commented Mar 29, 2022 at 0:48
-
does
auth
have a value before you pass it on? did you try to putonAuthStateChanged
insideuseEffect
and see if it works? – mocherfaoui Commented Mar 29, 2022 at 1:41 - nop, it does not have a value. I copied both inside useEffect (client) and outside it, (server), and my user is not recognized. – Diego González Cruz Commented Mar 29, 2022 at 16:23
- @DiegoGonzálezCruz Hi. I've faced the same problem. To solve the problem, I made a customToken issue api that can be used after sign in from next-auth, and the frontend kept requesting customToken after 1 hour. I also added a rule that invalidates the issued customToken when sign out of next-auth, creating an example of linking next-auth and firebase authentication. I think this example has secured a similar level of security to the session, and I wonder what you think. I will organize it more neatly and leave an answer. – lowfront Commented May 6, 2022 at 15:31
2 Answers
Reset to default 5You'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 };
}
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 !