Проблема с авторизацией в Firebase — почему срок действия моих токенов истекает мгновенно?

#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.