Мой опыт работы с Google apis и oauth2

В предыдущем посте мы сделали приложение, которое аутентифицирует пользователей с помощью firebase auth, и оно было простым и понятным.

Я даже включил gmail scope, потому что документация обещала возможность работы с другими google apis после входа в систему, потому что я планировал рассылать уведомления по электронной почте от имени человека, который внес изменения, всем пользователям приложения, которых это может касаться.

import { GoogleAuthProvider, signInWithRedirect } from "firebase/auth";
import { auth} from "../../firebase/firebaseConfig";

const provider = new GoogleAuthProvider();
provider.addScope('https://mail.google.com/');

export const loginUser= () => {

signInWithRedirect(auth, provider)
.then((result:any) => {
console.log("auth result === === ",result)
}).catch((error) => {
// Handle Errors here.
console.log("auth error  === ",error)

});

}


Вход в полноэкранный режим Выйдите из полноэкранного режима

затем используйте getredirectresult, когда ваша страница снова загрузится, в моем случае она перенаправляется на домашний компонент после аутентификации

import { User} from 'firebase/auth';
import React,{useEffect}  from 'react'
import { getRedirectResult, GoogleAuthProvider } from "firebase/auth";
import { auth } from '../../firebase/firebaseConfig';


interface HomeProps {
user?:User|null
}

export const Home: React.FC<HomeProps> = () => {

useEffect(() => {

getRedirectResult(auth)
.then((result:any) => {
  // This gives you a Google Access Token. You can use it to access Google APIs.
  const credential = GoogleAuthProvider.credentialFromResult(result);
  const token = credential?.accessToken;
  console.log("creds ==== ", credential)
  console.log("access token ==== ", token)
}).catch((error) => {
console.log("error getting access token  === ",error)
  const credential = GoogleAuthProvider.credentialFromError(error);
  console.log("error getting access token  === ",credential)
  // ...
});


}, [])



return (
 <div className='w-full min-h-full bg-slate-400 flex-center flex-col'>
<button
className='bg-slate-700 p-5 text-xl font-bold'
onClick={()=>{}}
>click</button>

 </div>
);


}

Вход в полноэкранный режим Выйти из полноэкранного режима

Примечание: это не обязательно при использовании signInWithPopup, т.к.
результат доступен в блоке then
официальная документация рекомендует использовать signinWithRedirect, так как он лучше
лучше на мобильных устройствах

import { getAuth, signInWithPopup, GoogleAuthProvider } from "firebase/auth";

const auth = getAuth();
signInWithPopup(auth, provider)
  .then((result) => {
    // This gives you a Google Access Token. You can use it to access the Google API.
    const credential = GoogleAuthProvider.credentialFromResult(result);
    const token = credential.accessToken;
    // The signed-in user info.
    const user = result.user;
    // ...
  }).catch((error) => {
    // Handle Errors here.
    const errorCode = error.code;
    const errorMessage = error.message;
    // The email of the user's account used.
    const email = error.customData.email;
    // The AuthCredential type that was used.
    const credential = GoogleAuthProvider.credentialFromError(error);
    // ...
  });
Вход в полноэкранный режим Выйти из полноэкранного режима

вам также нужно будет включить gmail api в облаке google
консоли под именем вашего проекта firebase

это работает нормально и выдает токен доступа, который возвращается сработавшим

https://www.googleapis.com/oauth2/v1/tokeninfo?access_token=accessToken
Войти в полноэкранный режим Выйти из полноэкранного режима

вставьте приведенный выше текст в адресную строку и замените accessToken на токен доступа из ответа

accessToken доступен только при первом входе в систему, когда внутри функции
функция signin возвращает объект

если он действителен, вы получите такой ответ

{
 "issued_to": "75010101072-jq0gaom2tpgk01t78ffjisvgsgggggg.apps.googleusercontent.com",
  "audience": "75069777777-jq0gaom2fsfsrv78ffjisvgshfafess.apps.googleusercontent.com",
  "user_id": "112901390458597sfstv",
  "scope": "openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email https://mail.google.com/",
  "expires_in": 2244,
  "email": "email@gmail.com",
  "verified_email": true,
  "access_type": "online"
}
Вход в полноэкранный режим Выйти из полноэкранного режима

что достаточно хорошо и может быть использовано для отправки пингов по электронной почте,
но есть еще кое-что.
знание разницы между токенами firebase пригодится.
Если вкратце, то существует 2 типа токенов для аутентификации

  • accessToken : фактический токен, который аутентифицирует вас на api, очень недолговечен (до 30 минут) в целях безопасности.
  • refreshToken :долгоживущий (до 200 дней), используется для генерации другого accessToken, когда срок действия одного истекает, без необходимости повторной аутентификации на стороне клиента.

Обычно, чтобы получить токен обновления при аутентификации, вы включаете

"access_type": "offline"
Вход в полноэкранный режим Выйти из полноэкранного режима

в запрос аутентификации, но это недоступно для Firebase Auth Client SDK, что является неудачей, потому что это было бы идеально, чтобы просто получить все сразу и не заставлять пользователя повторно аутентифицироваться.

Существующие альтернативы — это такие вещи, как google-signin, который скоро будет устаревшим в пользу входа с помощью google, предоставляемого их новой системой идентификации google, инструменты вроде gapi также обернуты вокруг первой упомянутой технологии и не рекомендуются, поскольку они скоро будут устаревшими.

Я нашел решение этой проблемы, подключив сервер nodejs, который имеет свою собственную библиотеку googleapi, которая оборачивается вокруг их клиента node js, чтобы получить обновление и маркер доступа для зарегистрированного пользователя, но процесс настройки очень утомителен.
сначала вам нужно настроить вашу облачную консоль.

  • Нет необходимости создавать новый проект google cloud, если у вас уже есть проект firebase.
  • Установите url перенаправления на http://localhost:4000/creds, вы будете обрабатывать req.query.code, которые будут отправлены на этот маршрут после аутентификации, и сохраните его в firestore для дальнейшего использования.

видео на youtube, объясняющее, как настроить консоль google (до 2:04-6:35)

npm install googleapis
Войдите в полноэкранный режим Выйдите из полноэкранного режима

затем в маршруте авторизации

const express = require('express')
const {google} = require('googleapis');
const path = require('path');
const nodemailer = require('nodemailer');


const router=express.Router()

//replace below with your creds, also note that i hard coded the  
//refresh and access token that i got from the response   
//ideally you'd save it somewhere and load it in as a variable  
 //and refetch if it's invalid   

const creds={
  client_email:"email1@gmail.com",
  client_id:"client_id",
  client_secret:"your client secret",
  serveruri: "http://localhost:4000",
  uirui: "http://localhost:3000",
  redirectURL: "http://localhost:4000/auth/creds",
  access_token: 'your access token',
 refresh_token: 'your refresh token',

}

const oauth2Client = new google.auth.OAuth2(
  creds.client_id,
  creds.client_secret,
   creds.redirectURL
);
const scopes = [
  'https://mail.google.com/'
];


const sendMail=async()=>{
  try{
    // Create the email envelope (transport)
    const transport = nodemailer.createTransport({
     service: 'gmail',
     auth: {
       type: 'OAuth2',
       user:creds.client_email,
       clientId: creds.client_id,
       clientSecret: creds.client_secret,
       accessToken: creds.access_tokenfb,

     },
   });

   // Create the email options and body 
   // ('email': user's email and 'name': is the e-book the user wants to receive)
   const mailOptions = {
     from: `FRONT <${creds.client_email}>`,
     to: "email2@gmail.com",
     subject: `[FRONT]- Here is your e-Book!`,
     html: `Enjoy learning!`,

   };

   // Set up the email options and delivering it
   const result = await transport.sendMail(mailOptions);
   console.log("success    === ",result) 
   return result;

   } catch (error) {
  console.log("error sendng mail    === ",error)   
   return error;
   }
}

//default auth route
router.get('/',async(req,res)=>{
 console.log("hit auth route")
res.send("auth route")

})



//route to handle api client authentication
router.get('/google',async(req,res)=>{

const url = oauth2Client.generateAuthUrl({
  // 'online' (default) or 'offline' (gets refresh_token)
  access_type: 'offline',
 // If you only need one scope you can pass it as a string
  scope: scopes
})
console.log("url returned ======= ",url)
//url returned by google to redirect us to the login consent page // page
if(url){
// render an ejs view with a button that redirects to the url
res.render('authorize',{url:url})
}
})


//redirect route that receives the authentication creds and swaps them for access and refresh token 

router.get('/creds',async(req,res)=>{

const code = req.query.code
console.log("query ==== ",code)
//returns access and refresh tokens
const {tokens} = await oauth2Client.getToken(code)
console.log("query token response==== ",tokens)
//perform save to firestore or your db of choice here

//authenticate oauthclient
oauth2Client.setCredentials(tokens);

//render a view to indicate completion
res.render('done')

})




router.get('/mail',async(req,res)=>{
let email=""
await sendMail().then((result)=>email=result).catch((err)=>email=err)
console.log("email sent or error    === ",email)

await res.json(email)  


})


module.exports=router
Войдите в полноэкранный режим Выйти из полноэкранного режима

Проверьте репозиторий для полного кода

Надеюсь, это сэкономит ваше время на выяснение того, какой подход следует использовать, а также, поскольку у меня уже есть firebase, я мог бы разместить эту логику в облачной функции, которая будет срабатывать для аутентификации и сохранения токена обновления для нового пользователя, а другая — для отправки электронной почты.
Есть более простые варианты, такие как использование расширения firebase или просто использование nodemailer с почтовым клиентом smtp, но у google есть множество apis с щедрыми лимитами, которые могут улучшить любое приложение, над которым вы работаете.
Если есть кто-то с большим опытом в этой теме, я бы очень хотел услышать от вас.

ссылка на репо

firebase client sdk google sign in
консоль GCP
npm googleapis

Оцените статью
Procodings.ru
Добавить комментарий