I am trying to deploy my next app to vercel. I am using next-auth for authentication with credentials and an api that returns a JWT. On localhost it works ok, but when I put it on vercel, and try to login, nothing happens. However when I view the logs in the project's dashboard functions, there's an error:
[POST] /api/auth/callback/credentials
03:32:18:58
2021-12-21T00:32:19.071Z 410db3c3-d841-45d1-8df2-0cdbb4aa8793 ERROR TypeError: "ikm"" must be an instance of Uint8Array or a string
at normalizeUint8Array (/var/task/node_modules/#panva/hkdf/dist/node/cjs/index.js:20:15)
at normalizeIkm (/var/task/node_modules/#panva/hkdf/dist/node/cjs/index.js:24:17)
at hkdf (/var/task/node_modules/#panva/hkdf/dist/node/cjs/index.js:47:60)
at getDerivedEncryptionKey (/var/task/node_modules/next-auth/jwt/index.js:99:34)
at Object.encode (/var/task/node_modules/next-auth/jwt/index.js:45:34)
at Object.callback (/var/task/node_modules/next-auth/core/routes/callback.js:457:37)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async NextAuthHandler (/var/task/node_modules/next-auth/core/index.js:197:28)
at async NextAuthNextHandler (/var/task/node_modules/next-auth/next/index.js:40:7)
at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils.js:101:9)
2021-12-21T00:32:19.071Z 410db3c3-d841-45d1-8df2-0cdbb4aa8793 ERROR TypeError: "ikm"" must be an instance of Uint8Array or a string
at normalizeUint8Array (/var/task/node_modules/#panva/hkdf/dist/node/cjs/index.js:20:15)
at normalizeIkm (/var/task/node_modules/#panva/hkdf/dist/node/cjs/index.js:24:17)
at hkdf (/var/task/node_modules/#panva/hkdf/dist/node/cjs/index.js:47:60)
at getDerivedEncryptionKey (/var/task/node_modules/next-auth/jwt/index.js:99:34)
at Object.encode (/var/task/node_modules/next-auth/jwt/index.js:45:34)
at Object.callback (/var/task/node_modules/next-auth/core/routes/callback.js:457:37)
at processTicksAndRejections (internal/process/task_queues.js:95:5)
at async NextAuthHandler (/var/task/node_modules/next-auth/core/index.js:197:28)
at async NextAuthNextHandler (/var/task/node_modules/next-auth/next/index.js:40:7)
at async Object.apiResolver (/var/task/node_modules/next/dist/server/api-utils.js:101:9)
RequestId: 410db3c3-d841-45d1-8df2-0cdbb4aa8793 Error: Runtime exited with error: exit status 1
Runtime.ExitError
Additional info:
The SignIn() function looks like this:
signIn("credentials", {
email: values.name,
password: values.password,
redirect: false,
callbackUrl: `/app`
})
.then((data) => console.log("loginpagethen", data))
.catch((error) => console.log("loginpagecatch", error));
I think the problem occurs somewhere in the jwt callback function because authorize returns a user(with all the data, including jwt from server). Also if I console.log the user in the jwt callback function,I can actually see the user object. However the session isn't present.
async jwt({ token, user, account, profile, isNewUser }) {
if (user) {
console.log("user", user); //user is there
token.accessToken = user.token;
}
return token;
},
async session(session, token) {
console.log("session", session); //session is not there
return session;
},
};
P.S. I have added environment variables.
Any idea on how I can get my app to behave on vercel?
in your .env file creates an environment variable:
API_SECRET_KEY=q3t6w9z$C&F)asdfasdfasfasdfasdfasd1sdfasf
Related
I'm building a Nextjs app with as Supabase backend. I have built a straight forward Signup and Authentication for the app following the examples on the Supabase website.
The main index page of the app has a getUser() function which is used to fetch the current user's information and allow permissions to certain parts of the app. This function works when a user is logged in however I am getting the errors below when there isn't a user logged in.
helpers.js:61 GET https://vvfxhlmmtgtpvbhzfgwn.supabase.co/auth/v1/user 401
AuthApiError: invalid claim: missing sub claim
at eval
Here below is the code I have for the getUser() function
useEffect(() => {
const getUser = async () => {
try {
const { data: { user }, error } = await supabase.auth.getUser();
if (error) throw error;
if (user) {
const userId = user.id;
setIsAuthenticated(true);
setUserId(userId);
}
} catch (error) {
console.log("error: ", error);
}
}
getUser()
}, [])
If anyone has any idea on what this error means or how it can be fixed, it will be greatly appreciated.
Thanks
I have been trying to develop an application in Nuxt 3, and i am very new to this Meta-Framework. While a user logs in, the API sends token in response and it is saved to localStorage using if(process.client), however once the login is completed, i need my homepage to get the token stored in localstorage and now i am getting LocalStorage undefined error on build. Below is my code for setItems and getItems
code for setItems
methods: {
async handleSubmit() {
const response = await axios.post('http://localhost:8000/api/login', { email: this.email, password: this.password })
console.log(response);
notify({ title: response })
if (process.client) {
localStorage.setItem('token', response.data.token)
}
this.$router.push('/')
}
}
getItems code
async created() {
const response = await axios.get('http://localhost:8000/api/user', {
headers: {
Authorization: 'Bearer' + localStorage.getItem('token')
}
})
console.log(response)
},
process.client should probably not be needed here, because handleSubmit will probably be called upon a click or a user interaction.
Meanwhile, you could use process.client in your second snippet because created lifecycle hook is ran on both server and client side. And there is no such thing as localStorage in Node.js.
Or you could use mounted, which is only called client-side.
I know that there are similar posts about this, but I tried all of them and non worked.
I wrote this function for getting the user's name from the UID. I have a collection called users, with each user's uid as the name of the document, and a value called displayName in it.
exports.getDisplayName = functions.https.onCall((data, context) => {
if(context.auth){
const uid = data.uid
return admin.database().ref(`/users/${uid}`).get().then(doc=>{return{name: doc.data().displayName}})
}else{
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
})
When it deploy it everything works fine but when I call it I get these 2 errors and I can't figure out how to solve it.
Failed to load resource: the server responded with a status of 500 ()
Unhandled Promise Rejection: Error: INTERNAL
I have another function that works fine:
exports.getUidFromEmail = functions.https.onCall((data, context) => {
if(context.auth){
const email = data.email
return admin.auth().getUserByEmail(email).then((userRecord)=>{ return {user: userRecord.uid}})
}else{
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
})
edit: the error it says is this:
Unhandled error FirebaseError: Can't determine Firebase Database URL.
at FirebaseDatabaseError.FirebaseError [as constructor] (/workspace/node_modules/firebase-admin/lib/utils/error.js:44:28)
at new FirebaseDatabaseError (/workspace/node_modules/firebase-admin/lib/utils/error.js:205:23)
at DatabaseService.ensureUrl (/workspace/node_modules/firebase-admin/lib/database/database-internal.js:97:15)
at DatabaseService.getDatabase (/workspace/node_modules/firebase-admin/lib/database/database-internal.js:65:26)
at FirebaseApp.database (/workspace/node_modules/firebase-admin/lib/firebase-app.js:228:24)
at FirebaseNamespace.fn (/workspace/node_modules/firebase-admin/lib/firebase-namespace.js:191:45)
at /workspace/index.js:17:22
at func (/workspace/node_modules/firebase-functions/lib/providers/https.js:273:32)
at processTicksAndRejections (internal/process/task_queues.js:97:5) {
errorInfo: {
code: 'database/invalid-argument',
message: "Can't determine Firebase Database URL."
}
}
Solved my issue. It was because I was using Cloud Firestore instead of Realtime database, because a lot of the examples and docs are using it, I thought it just had a different syntax 🤦🏻
here's the fixed code:
exports.getDisplayName = functions.https.onCall((data, context) => {
if(context.auth){
const uid = data.uid
return admin.firestore().collection("users").doc(uid).get().then(doc=>{
console.log(doc)
return{name: doc.data().displayName}
})
}else{
throw new functions.https.HttpsError('failed-precondition', 'The function must be called while authenticated.');
}
})
it's admin.firestore() not admin.database()
Following the Firebase SDK docs on https://firebase.google.com/docs/auth/admin/email-action-links#generate_email_verification_link and getting the following error, which makes little sense as the function is triggered from the server environment using the authenticated admin.auth().
Might anyone know what is causing the issue?
Error from admin.auth().generateEmailVerificationLink : { Error: There is no user record corresponding to the provided identifier.
at FirebaseAuthError.FirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:42:28)
at FirebaseAuthError.PrefixedFirebaseError [as constructor] (/srv/node_modules/firebase-admin/lib/utils/error.js:88:28)
at new FirebaseAuthError (/srv/node_modules/firebase-admin/lib/utils/error.js:147:16)
at Function.FirebaseAuthError.fromServerError (/srv/node_modules/firebase-admin/lib/utils/error.js:186:16)
at /srv/node_modules/firebase-admin/lib/auth/auth-api-request.js:1201:49
at <anonymous>
at process._tickDomainCallback (internal/process/next_tick.js:229:7)
errorInfo:
{ code: 'auth/user-not-found',
message: 'There is no user record corresponding to the provided identifier.' },
codePrefix: 'auth' }
The code is just this:
exports = module.exports = functions.firestore
.document("xxx/{docId}")
.onCreate(async (snap, context) => {
let yyy = snap.data();
let { uid, userData } = yyy;
console.log("from sendEmailVerification, uid, userdata: ", userData);
const actionCodeSettings = {
url: `${emailConfirmationUrl}`,
handleCodeInApp: false
};
return admin.auth().generateEmailVerificationLink(userData.email, actionCodeSettings)
.then(async (link) => {
console.log("uid from generateEmailVerificationLink and email: ", uid, userData.email)
await admin.firestore().collection('users').doc(uid).set({
verificationLink: link,
emailVerified: false
}, { merge: true });
return emailfunc.sendCustomVerificationEmail(userData.email, link);
})
.catch((err) => {
console.error("Error from admin.auth().generateEmailVerificationLink :", err);
return Promise.reject(err);
});
});
You read the user's email address from the database (Firestore). That user account, however, doesn't exist in Firebase Auth. It must also exist in Firebase Auth if you wish to use APIs like getUser() and generateEmailVerificationLink(). Having it only in Firestore is not enough.
I have two firebase functions deployed, one uses functions.https.onRequest (companyRequest) and one uses functions.https.onCall (companyCall). Both do the exact same thing: retrieve a document from firestore (the exact same document from the exact same collection).
Here are the functions:
import * as functions from "firebase-functions";
import * as admin from "firebase-admin";
admin.initializeApp(functions.config().firebase);
export const companyRequest = functions.https.onRequest((request, response) => {
const companyId = "VpOVFo82m9ls3aIK7dra";
admin
.firestore()
.collection("company")
.doc(companyId)
.get()
.then(result => {
response.send(result.data());
})
.catch(error => {
response.send(error);
});
});
export const companyCall = functions.https.onCall((data, context) => {
if (context && context.auth) {
console.log(
"AUTH",
context.auth.uid,
context.auth.token.email
);
}
const companyId = "VpOVFo82m9ls3aIK7dra";
admin
.firestore()
.collection("company")
.doc(companyId)
.get()
.then(result => {
return result.data();
})
.catch(error => {
return error;
});
});
I call companyRequest with curl and it works:
> curl https://us-central1-xxxxx.cloudfunctions.net/company
{"name":"Duny Comp."}
I call companyCall from flutter and it fails (on firebase, server site):
Future click() async {
final HttpsCallable callable = CloudFunctions.instance.getHttpsCallable(
functionName: 'companyCall',
);
HttpsCallableResult resp = await callable.call(<String, dynamic>{
'companyId': 'VpOVFo82m9ls3aIK7dra',
});
print(resp.data);
}
the error I get for companyCall is this:
AUTH 3gvfgYJpk2gIgUpkOLxdkdId uuuu#xxxx.com
ERROR: Error: Could not load the default credentials. Browse to https://cloud.google.com/docs/authentication/getting-started for more information
The error seems quite clear, but why does the unauthenticated call with curl work, but companyCall with firebase authentication via flutter does have a permission problems? In the output you can even see the auth information from the enduser in the flutter app, so he is authenticated.
The question is, why there is a difference between the two? The proposed solutions like Error: Could not load the default credentials (Firebase function to firestore) also feel very strange...
Update:
This is not the same question as in Firebase Cloud Functions: Difference between onRequest and onCall, in this question I ask why there is a difference in the security behaves different between the two different methods. Why do I need to authenticate with an admin account to access the same collection from onCall as I don't need it when access the collection from a onRequest method?