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
Related
I have this code in my /api/[verificationToken] which is when accessed by the user, the verification token will be updated. For now, I am trying to check if the token exists in the database and corresponds to a registered email.
import prisma from "../../../lib/prisma";
export default async function handler(req, res) {
const token = req.query;
const findEmail = await prisma.user.findFirst({
where: {
token: token.verificationToken,
},
});
if (findEmail) {
console.log("email exists");
} else {
console.log("email doesn't exist");
return res.redirect("/auth/login");
}
}
The problem is, when I go to http://localhost:3000/auth/api/nonexistenttoken, "email doesn't exist" displays in a loop. I have also tried
res.writeHead(302, {
Location: '/auth/login'
});
res.end();
But it still gives me the same loop. What I want to happen is that when the token doesn't exist (which also means the email also doesn't), it should redirect the user to the login page.
I am using Firebase authentication in my iOS app. Is there any way in Firebase when user login my app with Firebase then logout that user all other devices(sessions)? Can I do that with Firebase admin SDK?
When i had this issue i resolved it with cloud functions
Please visit this link for more details https://firebase.google.com/docs/auth/admin/manage-sessions#revoke_refresh_tokens
Do the following;
Set up web server with firebase cloud functions (if none exists)
use the admin sdk(thats the only way this method would work) - [Visit this link] (
(https://firebase.google.com/docs/admin/setup#initialize_the_sdk).
Create an api that receives the uid and revokes current sessions as specified in the first link above
admin.auth().revokeRefreshTokens(uid)
.then(() => {
return admin.auth().getUser(uid);
})
.then((userRecord) => {
return new Date(userRecord.tokensValidAfterTime).getTime() / 1000;
})
.then((timestamp) => {
//return valid response to ios app to continue the user's login process
});
Voila users logged out. I hope this gives insight into resolving the issue
Firebase doesn't provide such feature. You need to manage it yourself.
Here is the Firebase Doc and they haven't mentioned anything related to single user sign in.
Here is what you can do for this-
Take one token in User node (Where you save user's other data) in Firebase database and regenerate it every time you logged in into application, Match this token with already logged in user's token (Which is saved locally) in appDidBecomeActive and appDidFinishLaunching or possibly each time you perform any operation with Firebase or may be in some fixed time interval. If tokens are different logged out the user manually and take user to authenticate screen.
What i have done is:
Created collection in firestore called "activeSessions".User email as an id for object and "activeID" field for holding most recent session id.
in sign in page code:
Generating id for a user session every time user is logging in.
Add this id to localstorage(should be cleaned everytime before adding).
Replace "activeID" by generated id in collection "activeSessions" with current user email.
function addToActiveSession() {
var sesID = gen();
var db = firebase.firestore();
localStorage.setItem('userID', sesID);
db.collection("activeSessions").doc(firebase.auth().currentUser.email).set({
activeID: sesID
}).catch(function (error) {
console.error("Error writing document: ", error);
});
}
function gen() {
var buf = new Uint8Array(1);
window.crypto.getRandomValues(buf);
return buf[0];
}
function signin(){
firebase.auth().signInWithEmailAndPassword(email, password).then(function (user) {
localStorage.clear();
addToActiveSession();
}
}), function (error) {
// Handle Errors here.
var errorCode = error.code;
var errorMessage = error.message;
if (errorCode === 'auth/wrong-password') {
alert('wrong pass');
} else {
alert(errorMessage);
}
console.log(error);
};
}
Then i am checking on each page if the id session in local storage is the same as "activeID" in firestore,if not then log out.
function checkSession(){
var db = firebase.firestore();
var docRef = db.collection("activeSessions").doc(firebase.auth().currentUser.email);
docRef.get().then(function (doc) {
alert(doc.data().activeID);
alert(localStorage.getItem('userID'));
if (doc.data().activeID != localStorage.getItem('userID')) {
alert("bie bie");
firebase.auth().signOut().then(() => {
window.location.href = "signin.html";
}).catch((error) => {
// An error happened.
});
window.location.href = "accountone.html";
} else{alert("vse ok");}
}).catch(function (error) {
console.log("Error getting document:", error);
});
}
PS: window has to be refreshed to log inactive session out.
I have been working on a oauth2 flow for spotify by following this similar tutorial by the Firebase team for Instagram HERE
I am able to submit my credentials and return the user code and state in the url, but when I run the method to submit the code to return an auth token, the auth token that I print to console in the Firebase functions returns: Auth Token Error Not Found. Here's my workflow:
Here's the Spotify docs
FIRST, I have a function to configure my spotifyOAuth:
function spotifyOAuth2Client() {
// Spotify OAuth 2 setup
const credentials = {
client: {
id: functions.config().spotify.clientid,
secret: functions.config().spotify.clientsecret,
},
auth: {
tokenHost: 'https://accounts.spotify.com',
authorizePath: '/authorize'
},
};
return require('simple-oauth2').create(credentials);
}
I use that function in this Firebase function that is called using https://us-central1-<my project string>.cloudfunctions.net/redirect:
exports.redirect = functions.https.onRequest((req, res) => {
const oauth2 = spotifyOAuth2Client();
cookieParser()(req, res, () => {
const state = req.cookies.state || crypto.randomBytes(20).toString('hex');
console.log('Setting verification state:', state);
res.cookie('state', state.toString(), {
maxAge: 3600000,
secure: true,
httpOnly: true,
});
const redirectUri = oauth2.authorizationCode.authorizeURL({
redirect_uri: OAUTH_REDIRECT_URI,
//scope: OAUTH_SCOPES,
state: state,
});
console.log('Redirecting to:', redirectUri);
res.redirect(redirectUri);
});
});
The code above returns a url string with the proper parameters, the following code block is where my code breaks, I have another cloud function that runs after being redirected from the res.redirect(redirectUri) above. And when I try to run the getToken() method, it appears to not return anything because I hit the catch block instead? This is where I observe the Auth Token Error Not Found.
const oauth2 = spotifyOAuth2Client();
try {
return cookieParser()(req, res, async () => {
console.log('Received verification state:', req.cookies.state);
console.log('Received state:', req.query.state);
if (!req.cookies.state) {
throw new Error('State cookie not set or expired. Maybe you took too long to authorize. Please try again.');
} else if (req.cookies.state !== req.query.state) {
throw new Error('State validation failed');
}
console.log('Received auth code:', req.query.code);
console.log(OAUTH_REDIRECT_URI);
// Get the access token object (the authorization code is given from the previous step).
const tokenConfig = {
code: req.query.code,
redirect_uri: 'http://localhost:8100/popup'
};
// Save the access token
try {
const result = await oauth2.authorizationCode.getToken(tokenConfig)
const accessToken = oauth2.accessToken.create(result);
console.log('inside try');
console.log(result);
console.log(accessToken);
} catch (error) {
console.log('Access Token Error', error.message);
}
I've double checked my spotify client/secret credentials in the config, what is going wrong with this OAuth2 flow?
Resolved my issue, I was not using the correct endpoints:
const credentials = {
client: {
id: functions.config().spotify.clientid,
secret: functions.config().spotify.clientsecret,
},
auth: {
tokenHost: 'https://accounts.spotify.com',
authorizePath: '/authorize',
tokenPath: '/api/token'
},
};
I'm using Firebase in a React Native project with the RNFirebase library. I'm trying to get user phone numbers during onboarding. After initial signup with email/password the flow to get phone number is:
Enter and send phone number with firebase.auth().verifyPhoneNumber(phoneNumber)
Receive verification code and confirm
If success, add phone number to the current auth user
So I have two methods in my component: sendCode which sends a code to the provided phone number and verifyCode which compares the code input to the code sent.
sendCode = () => {
const { phoneNumber } = this.state
firebase.auth()
.verifyPhoneNumber(phoneNumber)
.on('state_changed', (phoneAuthSnapshot) => {
switch (phoneAuthSnapshot.state) {
case firebase.auth.PhoneAuthState.CODE_SENT:
// This ends up creating a NEW user instead of adding phone number to the current user
firebase.auth().signInWithPhoneNumber(phoneNumber)
.then(confirmResult => this.setState({ confirmResult }))
.catch(err => {console.log('some other error:', err)})
break;
case firebase.auth.PhoneAuthState.ERROR:
console.log(phoneAuthSnapshot.error);
break;
}
}, (error) => {
console.log(error);
}, (phoneAuthSnapshot) => {
console.log(phoneAuthSnapshot);
})
}
verifyCode = () => {
const { codeInput, confirmResult } = this.state;
if (confirmResult && codeInput.length) {
confirmResult.confirm(codeInput)
.then(user => {
console.log(user);
})
.catch(err => {console.log('error verifying code:', err)})
}
}
Following this example I am able to send the verification code, however the Promise returns and object rather than a function, which I would need to verify the code in verifyCode.
The example suggests to use firebase.auth().signInWithPhoneNumber(phoneNumber) which then returns a function to confirm the code. This did not work well as it created a new auth user rather than adding the phone number to the current user. Another problem is that the user experiences two reCaptcha challenges instead of one...
Any suggestions?
Here's my 2 working methods that should link a given phone number to facebook or email/password auth.
verifyPhone: async phnumber => {
try {
let verify = await FirebaseAuth.auth()
.verifyPhoneNumber(phnumber)
.on("state_changed", phoneAuthSnapshot => {
switch (phoneAuthSnapshot.state) {
case FirebaseAuth.auth.PhoneAuthState.CODE_SENT:
return phoneAuthSnapshot;
break;
case FirebaseAuth.auth.PhoneAuthState.ERROR:
console.log(phoneAuthSnapshot.error);
return null;
break;
}
});
return verify;
} catch (error) {
console.log(error);
}
}
verifyPhone function will accept a phone number and return an object that contains your verificationId. Next is call this function,
processVerificationCode: async (verificationId, code) => {
try {
let credential = FirebaseAuth.auth.PhoneAuthProvider.credential(
verificationId,
code
);
let currentUser = FirebaseAuth.auth().currentUser;
return currentUser.linkWithCredential(credential);
} catch (error) {
console.log(error);
}
}
processVerificationCode function will accept 2 parameters verificationId and inputedCode, then call PhoneAuthProvider to get phone number credential. Next is to get the current logged in user (assuming you have an existing login), then from that user call linkWithCredential and pass your phone number credential.
Thats it. Hope it helps.
I am developing an application, with an feature of Google Login through Firebase. I am trying to login via Google with the help of an library, known as react-native-google-signin. It is well known library in the field of ReactNative for Google Login.
My problem is not with this library, but the problem is that while I am using react-native-google-signin library with firebase to login via google. Firebase User is not staying persistence, I mean to say that when I am opening app after close FirebaseUser is getting null. Below the code I am using to login via firebase,
GoogleSignin.signIn().then(data => {
const credentials = firebase.auth.GoogleAuthProvider.credential(data.idToken, data.accessToken);
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then() => {
return firebase.auth().signInWithCredential(credentials);
}).catch(error => {
console.log('Error', error);
})
}).then(user => {
console.log('user', firebase.auth().currentUser);
}).catch(error => {
console.log('Error', error);
})
I also checked Firebase Docs, tried by using setPersistence() method but still I am getting null user after open app again.
You can try this
when you first time open your app you get user null, but after login one time then reopen your app and you will get previously logged in user in your console
async _setupGoogleSignin() {
try {
await GoogleSignin.hasPlayServices({ autoResolve: true });
await GoogleSignin.configure({
webClientId: 'YOUR WEBCLIENTID',
offlineAccess: false
});
const user = await GoogleSignin.currentUserAsync();
console.log("user",user); // HERE YOU GET LOGGED IN USER IN YOUR CONSOLE FIRST TIME IT WILL BE NULL BUT AFTER YOU GET PREVIOUSLY LOGGED IN USER
this.setState({user});
}
catch(err) {
console.log("Play services error", err.code, err.message);
}}
then
_signIn() {
GoogleSignin.signIn()
.then((user) => {
console.log(user);
this.setState({user: user});
const credential = firebase.auth.GoogleAuthProvider.credential(user.idToken, user.accessToken);
// console.log(credential);
return firebase.auth().signInAndRetrieveDataWithCredential(credential);
})
.catch((err) => {
console.log('WRONG SIGNIN', err);
})
.done();}
it is worked for me...
hope it will help you...