【问题标题】:Google One Tap Integration with Meteor谷歌与 Meteor 的一键式集成
【发布时间】:2020-11-05 09:34:32
【问题描述】:

我正在将 Meteor 应用程序与 Google 的 One Tap 集成。尝试使用 Meteor 的 loginWithGoogle 来让用户保存到 Meteor Accounts(内置在 Meteor.js 中)。其复杂性在于

One-Tap 库并非旨在授权用户(即生产 Access Token),仅用于验证用户身份

因此,我必须做的是使用 Google Api 或 gapi 对用户进行身份验证,以检索必要的 access_tokenid_token。支持this post

到目前为止,我得到的如下:

HTML

<div data-prompt_parent_id="g_id_onload" style={{ position: "absolute", top: "5em", right: "1em" }} id="g_id_onload"></div>

客户端

google.accounts.id.initialize({
  prompt_parent_id: "g_id_onload",
  client_id: "42424242-example42.apps.googleusercontent.com",
  auto_select: false,
  callback: handleCredentialResponse
});

const handleCredentialResponse = async oneTapResponse => {
  // see the SERVER SIDE code, which is where validation of One Tap response happens
  Meteor.call("verifyOneTap", oneTapResponse.credential, oneTapResponse.clientId, (error, result) => {
    if (error) {
      console.log(error);
    }
    if (result) {
      // Initialize the JavaScript client library.
      gapi.load("auth2", function() {
        // Ready. Make a call to gapi.auth2.init or some other API 
        gapi.auth2.authorize(
          {
            client_id: oneTapResponse.clientId,
            scope: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
            response_type: "code token id_token",
            prompt: "none",
            // this is the actual email address of user, example@gmail.com, passed back from the server where we validated the One Tap event...
            login_hint: result.email
          },
          function(result, error) {
            if (error) {
              // An error happened.
              console.log(error);
              return;
            }
            //these are the authentication tokens taht are so difficult to capture...
            let theAccessToken = result.access_token;
            let theIdToken = result.id_token;

            //*********************************
            //this is the part that doesn't work
            //trying to get it to create the account without another Google prompt...
            Meteor.loginWithGoogle({ accessToken: theAccessToken, idToken: theIdToken, prompt: "none" }, function(err, res) {
              if (err) {
                console.log(err)
              }
            });
            //*********************************
          }
        );
      });
    }
  });
};

google.accounts.id.prompt(notification => {
  //this just tells you when things go wrong...
  console.log(notification);
});

服务器端

const { OAuth2Client } = require("google-auth-library");
const clientOA2 = new OAuth2Client("42424242-example42.apps.googleusercontent.com");

// the token and clientId are returned from One Tap in an object, are credential (token) and clientId (clientId)
verifyOneTap: async (token, clientId) => {
  const ticket = await clientOA2.verifyIdToken({
    idToken: token,
    audience: clientId // Specify the CLIENT_ID of the app that accesses the backend
    // Or, if multiple clients access the backend:
    //[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
  });
  const payload = await ticket.getPayload();

  //perform validation here so you don't get hacked...

  return payload;
  // If request specified a G Suite domain:
  // const domain = payload['hd'];
}

尝试在客户端/服务器上以不同的方式编写此内容,以及考虑解决此问题的方法并仅注册 Meteor 的Accounts.createUser,但这并不理想。我传递给loginWithGoogle[options] 有什么问题?我认为accessTokenidToken 就足够了……

发生的情况是,在登录时,它确实让我通过 Google One Tap 的第一阶段登录,但后来我输入Meteor.loginWithGoogle 的选项不知何故无法被识别:

这可行(一步流程的第一步)=>

但随后它再次要求登录:|

loginWithGoogle 上的 documentation 声明格式通常为:

Meteor.loginWith<ExternalService>([options], [callback])

关于loginWithGoogle

选项还可以包括Google’s additional URI parameters


Google 的附加 URI 参数

必填: client_id、nonce、response_type、redirect_uri、范围

可选: access_type、display、hd、include_granted_scopes、login_hint、prompt


不幸的是,它显然无法识别我正在传递的 [options] 中的某些内容,否则它会将用户保存到 MongoDB,而它没有这样做。

【问题讨论】:

    标签: node.js google-signin google-authentication meteor-accounts google-one-tap


    【解决方案1】:

    好的,找到了答案 - 我正在研究更清洁的东西,但这是当前的修复 - 感谢 jimmy knootmethodx 的启发。

    注意:其他一切与上述原始问题相同。

    客户

    // this is the callback from the Google One Tap `google.accounts.id.initialize` (see original Stack Overflow question above)
    const handleCredentialResponse = async oneTapResponse => {
      // see the SERVER SIDE code, which is where validation of One Tap response happens
      Meteor.call("verifyOneTap", oneTapResponse.credential, oneTapResponse.clientId, (error, result) => {
        if (error) {
          console.log(error);
        }
        if (result) {
          // Initialize the JavaScript client library.
          gapi.load("auth2", function() {
            // Ready. Make a call to gapi.auth2.init or some other API 
            gapi.auth2.authorize(
              {
                client_id: oneTapResponse.clientId,
                scope: "https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email",
                response_type: "code token id_token",
                prompt: "none",
                // this is the actual email address of user, example@gmail.com, passed back from the server where we validated the One Tap event...
                login_hint: result.email
              },
              function(tokens, error) {
                if (error) {
                  // An error happened.
                  console.log(error);
                  return;
                }
                //gapi returns tokens including accessToken and idToken...
                Meteor.call("createOneTapUser", result, tokens, (error, stampedLoginToken) => {
                  if (error) {
                    console.log(error);
                  }
                  //this logs in with the token created earlier...should do whatever your normal google login functionality does...
                //*********************************
                // this is where you skip the Google login popup :) 
                  Meteor.loginWithToken(stampedLoginToken);
                });
                //*********************************
              }
            );
          });
        }
      });
    };
    

    服务器

    createOneTapUser: async (userDetails, accessDetails) => {
      //just including details here for what part of a user object would look like from Meteor.loginWithGoogle > note especially resume > loginTokens
      let oneTapUserObj = {
        services: {
          google: {
            accessToken: accessDetails.access_token,
            idToken: accessDetails.id_token,
            scope: ["https://www.googleapis.com/auth/userinfo.email", "https://www.googleapis.com/auth/userinfo.profile", "openid"], // yours may be different...
            expiresAt: accessDetails.expires_at,
            id: userDetails.sub,
            email: userDetails.email,
            verified_email: userDetails.email_verified,
            name: userDetails.name,
            given_name: userDetails.given_name,
            family_name: userDetails.family_name,
            picture: userDetails.picture,
            locale: "en"
          },
          resume: {
            loginTokens: []
          }
        } //...whatever your user object normally looks like.
      };
      //manually inserting the user
      Meteor.users.insert(oneTapUserObj);
    
      let newOneTapUser = await Meteor.users.findOne({ "profile.email": userDetails.email });
      // generates the login token that goes under user > services > resume > loginTokens...
      let stampedLoginToken = Accounts._generateStampedLoginToken();
      Accounts._insertLoginToken(newOneTapUser._id, stampedLoginToken);
    
      //sets the social media image from the google account...you'll need to build your own...
      userDetails.picture ? scrapeSocialMediaImage(newOneTapUser._id, userDetails.picture) : console.log("Google One Tap user " + newOneTapUser._id + " has no profile picture...");
    
      return stampedLoginToken.token;
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多