Problem:
Beginning April 30th, 2022 new web applications must use the Google Identity Services library, existing web apps may continue using the Platform Library until the March 31, 2023 deprecation date.
- Create a helper in react let's name it GoogleOauth
with 3 function
- loginUser (uses popup)
- loginUser2 (uses one tap)
- SignUp
# Note
we have 2 login cuz Google One tap UI login has features#exponential_cool_down if user clicks on 'X',the One Tap will be disabled for a while. if user declines one tap login he'll see popup(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;
};
In your React Login page
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);
}
})
);
}
}
In sign up page
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);
}
})
);
}
}
Backend nodejs(function i used)
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
});
}
})
};
Previously I was verifying the token I was recieving, I guess we don't have to use them
//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,
}
Top comments (0)