让我们构建一个使用 Google OAuth 2.0 访问 Google API 的简单 Web 应用程序。用户可以使用他们的 Google 帐户登录并授权应用程序访问他们的 Google Drive 或任何其他 Google 服务。
当用户登录时,Google 会将用户重定向到 Google OAuth 2.0 授权页面。要求用户授予对应用程序的访问权限。然后,应用程序将授权代码交换为访问令牌和刷新令牌。访问令牌将在一小时后过期,但刷新令牌将无限期有效(除非用户手动撤销)。
因此,我们会将刷新令牌存储在 Cloud Firestore 中,并在应用程序需要代表用户访问 Google API 时使用它来生成新的访问令牌。
我们没有使用带有 Firebase 身份验证的 Google 登录,因为它不提供在无人值守的情况下运行后台 API 任务所需的刷新令牌。
第 1 步:创建 Google OAuth 2.0 客户端
如本分步指南中所述,在您的 Google Cloud 项目中创建一个新的 OAuth 2.0 客户端。
在您的 Google Cloud Console 中,转到APIs & Services
部分,单击Credentials
,然后单击Create credentials
> OAuth Client Id
以创建新的客户端 ID。
在开发过程中,您可以将https://localhost:5001/oauthCallback
作为重定向 URI,因为默认情况下,Firebase 模拟器将在本地端口 5001 上运行 Web 应用程序。
记下 Google 提供的客户端 ID 和客户端密码。
第 2 步:初始化 Firebase 函数
打开你的终端,创建一个新的项目目录并初始化 Firebase 项目。
$ mkdir oauth2-application $ cd oauth2-application $ npx firebase init functions $ npm install googleapis
您可以选择Use an existing Firebase project
选项,然后选择具有该功能的 Google Cloud 项目。切换到functions
目录。
步骤 3.初始化 Firebase 环境变量
创建一个新的.env
文件并添加以下环境变量:
CLIENT_ID = < your client ID > CLIENT_SECRET = < your client secret > REDIRECT_URI = < your redirect URI >
步骤 4. 生成授权 URL
我们将创建一个函数来生成授权 URL,以便用户使用其 Google 帐户登录。除了drive
范围之外,我们的应用程序还请求userinfo.email
范围以获取用户的电子邮件地址。
const functions = require ( 'firebase-functions' ) ; const { google } = require ( 'googleapis' ) ; exports . googleLogin = functions . https . onRequest ( ( request , response ) => { const SCOPES = [ 'https://www.googleapis.com/auth/userinfo.email' , 'https://www.googleapis.com/auth/drive.metadata.readonly' , ] ; const oAuth2Client = new google . auth . OAuth2 ( process . env . CLIENT_ID , process . env . CLIENT_SECRET , process . env . REDIRECT_URI ) ; const authUrl = oAuth2Client . generateAuthUrl ( { access_type : 'offline' , scope : SCOPES , prompt : 'consent' , login_hint : request . query . email_address || '' , } ) ; response . set ( 'Cache-Control' , 'private, max-age=0, s-maxage=0' ) ; response . redirect ( authUrl ) ; } ) ;
我们将access_type
设置为offline
以获取刷新令牌。 consent
设置为prompt
强制用户同意该应用程序。如果用户登录到多个 Google 帐户,我们还将login_hint
设置为用户的电子邮件地址。
步骤 5. 存储刷新令牌
用户登录后,Google 会将用户重定向到重定向 URI。重定向 URI 包含我们需要交换访问令牌和刷新令牌以存储在数据库中的授权代码。
const functions = require ( 'firebase-functions' ) ; const admin = require ( 'firebase-admin' ) ; const { google } = require ( 'googleapis' ) ; admin . initializeApp ( ) ; exports . oAuthCallback = functions . https . onRequest ( async ( request , response ) => { const { query : { error , code } = { } } = request ; // User may deny access to the application. if ( error ) { response . status ( 500 ) . send ( error ) ; return ; } const oAuth2Client = new google . auth . OAuth2 ( process . env . CLIENT_ID , process . env . CLIENT_SECRET , process . env . REDIRECT_URI ) ; // Exchange the authorization code for an access token. const { tokens } = await oAuth2Client . getToken ( code ) ; oAuth2Client . setCredentials ( tokens ) ; const oauth2 = google . oauth2 ( { auth : oAuth2Client , version : 'v2' , } ) ; // Get the user's email address and Google user ID const { data } = await oauth2 . userinfo . get ( ) ; const { id , email } = data ; const { refresh_token } = tokens ; // Store the refresh token in the Firestore database. // Set merge: true to not overwrite any other data in the same document await admin . firestore ( ) . collection ( 'users' ) . doc ( id ) . set ( { id , email , refresh_token } , { merge : true } ) ; response . set ( 'Cache-Control' , 'private, max-age=0, s-maxage=0' ) ; response . send ( ` User ${ email } is authorized! ${ id } ` ) ; } ) ;
以下是文档在 Firestore NoSQL 数据库中的存储方式:
第 6 步:访问 Google API
现在我们有了刷新令牌,我们可以使用它来生成新的访问令牌并访问 Google API。在我们的示例中,drive 函数将返回授权用户的 Google Drive 中最近的 5 个文件。
const functions = require ( 'firebase-functions' ) ; const admin = require ( 'firebase-admin' ) ; const { google } = require ( 'googleapis' ) ; admin . initializeApp ( ) ; exports . drive = functions . https . onRequest ( async ( request , response ) => { const { user_id = '' } = request . query ; const user = await admin . firestore ( ) . collection ( 'users' ) . doc ( user_id ) . get ( ) ; if ( ! user . exists ) { response . status ( 404 ) . send ( ` User ${ user_id } not found ` ) ; return ; } const { refresh_token } = user . data ( ) ; const oAuth2Client = new google . auth . OAuth2 ( process . env . CLIENT_ID , process . env . CLIENT_SECRET , process . env . REDIRECT_URI ) ; oAuth2Client . setCredentials ( { refresh_token } ) ; const googleDrive = google . drive ( { version : 'v3' , auth : oAuth2Client } ) ; const { data : { files = [ ] } = { } } = await googleDrive . files . list ( { pageSize : 5 , fields : 'files(id, name)' , } ) ; response . status ( 200 ) . send ( { files } ) ; } ) ;
第 7 步:创建 Firebase 云函数
您可以运行以下命令在本地测试功能:
firebase emulators:start --only functions
当您准备好将函数部署到 Firebase 项目时,您可以运行以下命令:
firebase deploy --only functions
原文: https://www.labnol.org/google-oauth-refresh-token-220423