React 노드의 Google ID 서비스/Google Auth 2022

문제:



2022년 4월 30일부터 새로운 웹 애플리케이션은 Google ID 서비스 라이브러리를 사용해야 하며, 기존 웹 앱은 지원 중단 날짜인 2023년 3월 31일까지 플랫폼 라이브러리를 계속 사용할 수 있습니다.
  • 반응에서 도우미를 만들고 이름을 GoogleOauth로 지정합니다.
    3가지 기능으로
  • loginUser(팝업 사용)
  • loginUser2(한 번 탭 사용)
  • 가입하기



  • # 메모

    사용자가 'X'를 클릭하면 Google One 탭 UI 로그인에 2개의 로그인이 있습니다features#exponential_cool_down. 잠시 동안 One Tap이 비활성화됩니다. 사용자가 한 번의 탭 로그인을 거부하면 팝업(loginUser)이 표시됩니다.

    // googleOauth.js
    
    const id = "xxxxxx";
    //generate this id(web oauth) from google console
    
    const createScript = () => {
      // load the sdk
      const script = document.createElement("script");
      script.src = "https://accounts.google.com/gsi/client";
      script.async = true;
      script.onload = initGoogleGSI;
      document.body.appendChild(script);
    };
    
    createScript();
    const initGoogleGSI = () => {
      console.log("initGoogleGSI SDK initialized");
    };
    
    export const loginUser = async () => {
      const client = window.google.accounts.oauth2.initTokenClient({
        client_id: id,
        scope: `profile email`,
        callback: "", // defined at request time
      });
      const tokenResponse = await new Promise((resolve, reject) => {
        try {
          // Settle this promise in the response callback for requestAccessToken()
          client.callback = (resp) => {
            if (resp.error !== undefined) {
              reject(resp);
            }
    
            // console.log("client resp",resp);
            resolve(resp);
          };
          // console.log("client",client);
          client.requestAccessToken({ prompt: "consent" });
        } catch (err) {
          console.log(err);
        }
      });
      return tokenResponse;
    };
    export const SignUpUser = async () => {
    const SCOPES = ["https://www.googleapis.com/auth/user.birthday.read",
      "https://www.googleapis.com/auth/profile.agerange.read",
      "https://www.googleapis.com/auth/userinfo.profile",
      "https://www.googleapis.com/auth/userinfo.email",
      "https://www.googleapis.com/auth/user.gender.read",
    ].join(" ");
      const client = window.google.accounts.oauth2.initTokenClient({
        client_id: id,
        scope: SCOPES,
        callback: "", // defined at request time
      });
      const tokenResponse = await new Promise((resolve, reject) => {
        try {
          // Settle this promise in the response callback for requestAccessToken()
          client.callback = (resp) => {
            if (resp.error !== undefined) {
              reject(resp);
            }
    
            // console.log("client resp",resp);
            resolve(resp);
          };
          // console.log("client",client);
          client.requestAccessToken({ prompt: "consent" });
        } catch (err) {
          console.log(err);
        }
      });
      return tokenResponse;
    };
    
    export const loginUser2 = async () => {
      const tokenResponse = await new Promise((resolve, reject) => {
        try {
          const goog = window.google.accounts.id;
          const client = goog.initialize({
            client_id: id,
            scope: `profile email`,
            callback: handleCredentialResponse, // defined at request time
          });
          // Settle this promise in the response callback for requestAccessToken()
          function handleCredentialResponse(resp) {
            if (resp.error !== undefined) {
              reject(resp);
            }
    
            // console.log("client resp",resp);
            resolve(resp);
          }
          // console.log("client",client);
          window.google.accounts.id.prompt((notification) => {
            if (notification.isNotDisplayed() || notification.isSkippedMoment()) {
              window.google.accounts.id.prompt();
              console.log("Prompt cancelled by user");
              resolve(loginUser());
            }
          });
        } catch (err) {
          console.log("loginUser2 err", err);
        }
      });
      return tokenResponse;
    };
    
    


    React 로그인 페이지에서

    import { GoogleLoginButton } from "react-social-login-buttons";
    import * as GoogleInit from "../../../utils/googleOauth";
    class LoginPage extends Component {
      constructor(props) {
        super(props);
    
        this.state = {
          userId: "",
          password: "",
        };
    
        this.loginGoogle = this.loginGoogle.bind(this);
    
        this.GoogleSignInResponse = this.GoogleSignInResponse.bind(this);
      }
    
      render() {
        return (
          <div>
            {" "}
            <GoogleLoginButton
              align={"center"}
              onClick={(e) => this.loginGoogle(e)}
            >
              <span>Sign in with Google</span>
            </GoogleLoginButton>
          </div>
        );
      }
    
      async loginGoogle(e) {
        e.preventDefault();
        // console.log("loginGoogle");
        try {
          //our helper
          let data = await GoogleInit.loginUser2();
    
          // console.log("signInGoogle.signInGoogle",data);
          this.GoogleSignInResponse(data);
        } catch (error) {
          console.error(error);
        }
      }
    
      GoogleSignInResponse(value) {
        console.log("GoogleSignInResponse", value);
    //send to backend (redux used here)
        this.props.dispatch(
          UserActions.googlelogin(value, (response) => {
            console.log("Response from DB", response);
            if (response.data.dob == "1000-12-01") {
              this.props.history.push("/birthday-wall");
              return;
            }
            if (response.status) {
              this.props.history.push("/");
            } else {
              let error = response.data.message
                ? response.data.message
                : "Something went wrong, try again later!";
              alert(error);
            }
          })
        );
      }
    }
    
    


    가입 페이지에서

    import { GoogleLoginButton } from "react-social-login-buttons";
    import * as GoogleInit from "../../../utils/googleOauth";
    class SignupPage extends Component {
      constructor(props) {
        super(props);
    
        this.state = {
          userId: "",
          password: "",
        };
    
        this.signupGoogle = this.signupGoogle.bind(this);
    
        this.GoogleSignUpResponse = this.GoogleSignUpResponse.bind(this);
      }
    
      render() {
        return (
          <div>
            {" "}
            <GoogleLoginButton
              align={"center"}
              onClick={(e) => this.loginGoogle(e)}
            >
              <span>Sign in with Google</span>
            </GoogleLoginButton>
          </div>
        );
      }
    
      async signupGoogle(e) {
        e.preventDefault();
        // console.log("signupGoogle");
        try {
          let data = await GoogleInit.SignUpUser();
    
          // console.log("signInGoogle.signInGoogle",data);
          this.GoogleSignUpResponse(data);
        } catch (error) {
          console.error(error);
        }
      }
      GoogleSignUpResponse(value) {
        //send to backend
        this.props.dispatch(
          UserActions.Googlesignup(value, (response) => {
            console.log("DB_response", response);
            if (response.status) {
              if (response.data.dob == "1000-12-01") {
                this.props.history.push("/birthday-wall");
                return;
              }
              this.props.history.push("/");
            } else {
              let error;
              if (response.data.error.code == 11000) {
                error = "Data Already Exists";
              } else {
                error = response.data.message
                  ? response.data.message
                  : "Something went wrong, try again!";
              }
    
              alert(error);
            }
          })
        );
      }
    }
    
    


    백엔드 nodejs(사용한 함수)




    
    var mongoose = require("mongoose"),
    Customer = mongoose.model("Customer"),
    SocialLogin = Helpers.socialLogin;
    const moment = require("moment");
    function UserDataGoogle(response) {
        if(response.payload) {
            return response.payload;
        }
        return response;
    }
    async function getgoogleDOB(googlePeopleAPiData) {
        let dob;
        let agerange = await googlePeopleAPiData.ageRange;
        const {
            year,
            month,
            day
        } = googlePeopleAPiData.birthdays[0].date;
        dob = `${year}-${month}-${day}`;
        if(!year) {
            if(agerange == 'TWENTY_ONE_OR_OLDER') {
                dob = `1998-${month}-${day}`;
            } else {
                dob = false;
            }
        }
        return dob;
    }
    
    function checkForKeysinAPIdata(neededKeys, apidata) {
        return neededKeys.every(key => Object.keys(apidata).includes(key));
    }
    
    function createCustomerFromSocialMedia(customer) {
        return stripe.createCustomer(customer).then(result => {
            customer.stripeID = result.id
            return Customer.create(customer).then(async function(user) {
                    return user;
                }).then(function(u) {
                    return Customer.findOne({
                        _id: u._id
                    })
                }) // We need a Customer instance for the then statement of tokenize to work. generateVeirficationEmail restricts the returned user and is not an instane of the Customer model.
                .then(Customer.tokenize);
        });
    }
    var signUPGoogleCustomer = endPointHandler(async function(req) {
        var customer = req.body;
        const {
            tokenId,
            googleId,
            access_token
        } = customer;
        let GoogleUserInfo = await SocialLogin.axiosGet(`https://www.googleapis.com/oauth2/v3/userinfo?access_token=${access_token}`);
        const {
            email_verified,
            email,
            given_name,
            family_name
        } = GoogleUserInfo;
        let googlePeopleAPiData = await SocialLogin.axiosGet(`https://people.googleapis.com/v1/people/me?personFields=birthdays,genders,age_range&access_token=${access_token}`);
        const neededKeys = ['birthdays', 'ageRange'];
        let googlecustomer = {};
        if(!checkForKeysinAPIdata(neededKeys, googlePeopleAPiData)) {
            let keys = Object.keys(googlePeopleAPiData);
            let difference = neededKeys.filter(x => !keys.includes(x)).toString();
            // throw {
            //   status: 403,
            //   message: `Unable to read ${difference}`
            // };
            //set default date in db if date,agerange not found
            googlecustomer.dob = `1000-12-01`;
        } else {
            let dob = await getgoogleDOB(googlePeopleAPiData);
            if(!dob) {
                throw {
                    status: 403,
                    message: "User is not over the age of 21 or Invalid Birthdate"
                };
            }
            googlecustomer.dob = dob;
        }
        googlecustomer.email = email;
        googlecustomer.name = {
            first: given_name,
            last: family_name
        };
        googlecustomer.isEmailVerified = email_verified;
        googlecustomer.password = Math.random().toString(36).slice(2, 10);
        return createCustomerFromSocialMedia(googlecustomer);
    });
    var GoogleCustomerLogin = async function(req, res, next) {
        var customer = req.body;
        const {
            credential, access_token
        } = customer;
        let decode;
        if(credential) {
            decode = Helpers.jwt.decodeJwt(credential);
        } else {
            decode = await SocialLogin.axiosGet(`https://www.googleapis.com/oauth2/v3/userinfo?access_token=${access_token}`);
        }
        const {
            email
        } = UserDataGoogle(decode);
        let result = Customer.findOne({
            email: email
        }).then(function(user) {
            try {
                var Guser = user.restrict();
                return res.status(200).json({
                    user: Guser,
                    token: jwt.signJwt(Guser)
                });
            } catch(e) {
                return res.status(400).json({
                    message: "User not found, Please Signup!",
                    error: e,
                    status: 400
                });
            }
        })
    };
    


    이전에 받은 토큰을 확인하고 있었는데 사용할 필요가 없는 것 같습니다.

    //Social login
    const axios = require('axios');
    var config = require("../config").dev.google;//google client id env
    const {OAuth2Client} = require('google-auth-library');
    const GoogleClient= new OAuth2Client(config.client_id);
    async function axiosGet(url) {
      try {
        const {data:response} = await axios.get(url) //use data destructuring to get data from the promise object
        return response;
      }
      catch (error) {
        console.log(error);
      }
    }
    async function googleVerifyId(token) {
      const ticket = await GoogleClient.verifyIdToken({
          idToken: token,
          audience: config.client_id,  // Specify the CLIENT_ID of the app that accesses the backend
      });
      const payload = ticket.getPayload();
      const userid = payload['sub'];
      return payload;
    }
    async function googleTokenInfo(token) {
      // after acquiring an oAuth2Client...
    const tokenInfo = await GoogleClient.getTokenInfo(token);
    // console.log("tokeninfo",tokenInfo);
      return tokenInfo;
    }
    module.exports = {
      axiosGet,
      googleVerifyId,
      googleTokenInfo,
    }
    

    좋은 웹페이지 즐겨찾기