#node.js #firebase #express #firebase-authentication #firebase-admin
#node.js #firebase #экспресс #firebase-аутентификация #firebase-администратор
Вопрос:
Я тестирую маршрут в приложении firebase, которое я создаю. Рассматриваемый маршрут получает «крики», которые похожи на обновления статуса. В любом случае, я только что интегрировал аутентификацию для защиты этого маршрута с помощью FBuath, но я продолжаю получать следующую ошибку:
Срок действия токена Firebase ID истек. Получите новый токен ID из своего клиентского приложения и повторите попытку
Я пробовал повторно регистрироваться с использованием действительных учетных данных, а затем мгновенно пытался опубликовать что-то по маршруту, но продолжал получать эту ошибку. Есть мысли о том, почему? Код следующий, и рассматриваемый маршрут имеет конечную точку ‘/ shout’. Приветствия
const functions = require('firebase-functions');
const admin = require('firebase-admin')
admin.initializeApp()
const config = {
apiKey: "AIzaSyBZjz9BNwj4UDwWLoQ1SOD5hB5QcNw3qqs",
authDomain: "social-ape-21874.firebaseapp.com",
databaseURL: "https://social-ape-21874.firebaseio.com",
projectId: "social-ape-21874",
storageBucket: "social-ape-21874.appspot.com",
messagingSenderId: "323044904203",
appId: "1:323044904203:web:edcbc619169a2087f8e60e",
measurementId: "G-T34PXDM1X7"
}
admin.initializeApp
const express = require('express')
const app = express()
const firebase = require('firebase')
firebase.initializeApp(config)
const db = admin.firestore()
app.get('/shouts', (req,res) => {
db
.collection('shouts')
.orderBy('createdAt', 'desc') //returns shouts in order in which they were made
.get()
.then((data) => {
let shouts = []
data.forEach((doc) => {
shouts.push({
shoutId: doc.id,
body: doc.data().body,
userHandle: doc.data().userHandle,
createdAt: doc.data().createdAt
})
})
return res.json(shouts)
})
.catch((err) => console.error(err))
})
const FBauth = (req,res,next) => {
let idToken
if(req.headers.authorization amp;amp; req.headers.authorization.startsWith('Bearer ')){
idToken = req.headers.authorization.split('Bearer ')[1]
}else{
console.error('No token found')
return res.status(403).json({error: 'Unauthorized'})
}
//verify that this token was issued by our application
admin.auth().verifyIdToken(idToken)
.then(decodedToken => {
req.user = decodedToken
return db.collection('users')
.where('userId', '==', req.user.uid)
.limit(1) //limits results to one document
.get()
})
.then(data => {
req.user.handle = data.docs[0].data().handle //data() is a function that extracts data from document
return next() //next() is a function that allows request to proceed to shout post route
})
.catch(err => {
console.error('Error while verifying token', err)
return res.status(403).json(err)
})
}
app.post('/shout', FBauth, (req,res) => {
const newShout = {
body: req.body.body,
userHandle: req.body.userHandle, //userhandle identifies who is owner of shout
createdAt: new Date().toISOString()
}
db
.collection('shouts')
.add(newShout)
.then((doc) => {
res.json({message: `document ${doc.id} created successfully`})
})
.catch((err) =>{
res.status(500).json({error: 'something went wrong'})
console.error(err)
})
})
//helper function to determine if string is empty or not
//note: .trim() removes whitespace from email field
const isEmpty = (string) => {
if (string.trim()=== '') {
return true
} else {
return false
}
}
//helper function to determine if valid email
const isEmail = (email) => {
const regEx = /^(([^<>()[]\.,;:s@"] (.[^<>()[]\.,;:s@"] )*)|(". "))@(([[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}.[0-9]{1,3}])|(([a-zA-Z-0-9] .) [a-zA-Z]{2,}))$/;
if (email.match(regEx)) {
return true
} else {
return false
}
}
//Sign up route
app.post('/signup', (req,res) => {
//here we need to extract form data from request body
const newUser = {
email: req.body.email,
password: req.body.password,
confirmPassword: req.body.confirmPassword,
handle: req.body.handle,
}
let errors = {}
if(isEmpty(newUser.email)){
errors.email = 'Email must not be empty'
} else if(!isEmail(newUser.email)) {
errors.email = 'Must be a valid email address'
} //if not empty, need to check if valid email
if(isEmpty(newUser.password)){
errors.password = 'Must not be empty'
}
if(newUser.password !== newUser.confirmPassword) {
errors.confirmPassword = 'Passwords must match'
}
if(isEmpty(newUser.handle)){
errors.handle = 'Must not be empty'
}
if(Object.keys(errors).length>0) {
return res.status(400).json(errors)
}
//sign up user
let token
db.doc(`/users/${newUser.handle}`).get()
.then((doc)=> {
if(doc.exists){
return res.status(400).json({handle: 'this handle is already taken'})
} else {
return firebase
.auth()
.createUserWithEmailAndPassword(newUser.email, newUser.password)
}
})
.then(data => {
userId = data.user.uid
return data.user.getIdToken()
})
.then(token => {
token=token
const userCredentials = {
handle: newUser.handle,
email: newUser.email,
createdAt: new Date().toISOString(),
userId:userId
}
db.doc(`/users/${newUser.handle}`).set(userCredentials)
return res.status(201).json({token})
})
.then(() => {
return res.status(201).json({token})
})
.catch(err => {
console.error(err)
return res.status(500).json({error:err.code})
})
})
//token is used to access route that is protected
//login route
app.post('/login', (req,res) => {
const user = {
email: req.body.email,
password: req.body.password
}
let errors = {}
if(isEmpty(user.email)){
errors.email = 'Must not be empty'
}
if(isEmpty(user.password)){
errors.password = 'Must not be empty'
}
if(Object.keys(errors).length >0) {
return res.status(400).json(errors)
}
firebase.auth().signInWithEmailAndPassword(user.email, user.password)
.then(data => {
return data.user.getIdToken()
})
.then(token => {
return res.json({token})
})
.catch(err => {
console.error(err)
if(err.code ==="auth/wrong-password" ){
return res.status(403).json({general: 'Wrong credentials, please try again'})
} else
return res.status(500).json({error: err.code})
})
})
exports.api = functions.https.onRequest(app)
Комментарии:
1. Синхронизированы ли ваши часы? Время выдачи / истечения срока действия токена ID сверяется с локальными часами.
2. @HiranyaJayathilaka когда я установил время работы своего ноутбука на то же время, что и местоположение в облачном хранилище firestore, проблема была устранена. Но я хочу, чтобы люди в любом часовом поясе использовали мое приложение, не имея токенов с истекшим сроком действия — какие-либо решения о том, как это исправить?
3. Обычно ваши локальные часы должны быть корректными (синхронизированными). Фактический часовой пояс не имеет значения, поскольку временные метки JWT выражаются в количестве секунд в эпоху UTC.