How can I provide my react-native app with google sign in? - reactjs

I`ve tried to register my app as Web application, generate the user id and implement it in my code but get an error when I press my button for log in with google:
[Unhandled promise rejection: Error: Please provide the appropriate client ID.
enter image description here

If you're using expo, you have to configure the google sign-in like this. This is my configuration. You have to create androidClientId and iosClientId from your account and use it here.
Disclaimer: This is a standalone function only for signingin/signingup a Google user and fetching details. To configure it with firebase you have to add other functions too.
Also, make sure that you're importing this package. I faced a similar problem when I used another package.
import * as Google from 'expo-google-app-auth'
Additionally, are you using the latest version of expo SDK?
async signInWithGoogleAsync() {
try {
const result = await Google.logInAsync({
androidClientId:
'your-id',
iosClientId:
'your-id',
scopes: ['profile', 'email'],
permissions: ['public_profile', 'email', 'gender', 'location']
})
if (result.type === 'success') {
/*put your logic here, I set some states and navigate to home screen
after successful signin.*/
const googleUser = result.user
this.setState({
email: googleUser.email,
name: googleUser.name,
})
this.navigateToLoadingScreen()
return result.accessToken
} else {
return { cancelled: true }
}
} catch (e) {
return { error: true }
}
}

Related

Shopify - App must set security headers to protect against clickjacking

I'm new to Shopify and I'm trying to help a friend with their website. I'm getting the following errors at the moment.
1. App must set security headers to protect against clickjacking.
Your app does not request installation on the shop immediately after clicking "add app". Apps must ask a shop for access when being installed on a shop for the first time, as well as when they are being reinstalled after having been removed. During install or reinstall we expected OAuth to be initiated at https://cambridgetestshop.myshopify.com/admin/oauth/request_grant but was redirected to https://app-staging.hashgifted.com/. Learn more about authentication in our developer documentation
2. App must verify the authenticity of the request from Shopify.
Your app does not request installation on the shop immediately after clicking "add app". Apps must ask a shop for access when being installed on a shop for the first time, as well as when they are being reinstalled after having been removed. During install or reinstall we expected OAuth to be initiated at https://cambridgetestshop.myshopify.com/admin/oauth/request_grant but was redirected to https://app-staging.hashgifted.com/. Learn more about authentication in our developer documentation
We're using React built in Yarn. I'm not sure about next steps, thanks!
it seems that you're not following the documentation in regarding of authentication and app installation process.
As you're using node I suggest you to take a look at this project https://github.com/Shopify/shopify-app-node
and in particular to the authentication middleware, this is one part
import { Shopify } from "#shopify/shopify-api";
import topLevelAuthRedirect from "../helpers/top-level-auth-redirect.js";
export default function applyAuthMiddleware(app) {
app.get("/auth", async (req, res) => {
if (!req.signedCookies[app.get("top-level-oauth-cookie")]) {
return res.redirect(
`/auth/toplevel?${new URLSearchParams(req.query).toString()}`
);
}
const redirectUrl = await Shopify.Auth.beginAuth(
req,
res,
req.query.shop,
"/auth/callback",
app.get("use-online-tokens")
);
res.redirect(redirectUrl);
});
app.get("/auth/toplevel", (req, res) => {
res.cookie(app.get("top-level-oauth-cookie"), "1", {
signed: true,
httpOnly: true,
sameSite: "strict",
});
res.set("Content-Type", "text/html");
res.send(
topLevelAuthRedirect({
apiKey: Shopify.Context.API_KEY,
hostName: Shopify.Context.HOST_NAME,
host: req.query.host,
query: req.query,
})
);
});
app.get("/auth/callback", async (req, res) => {
try {
const session = await Shopify.Auth.validateAuthCallback(
req,
res,
req.query
);
const host = req.query.host;
app.set(
"active-shopify-shops",
Object.assign(app.get("active-shopify-shops"), {
[session.shop]: session.scope,
})
);
const response = await Shopify.Webhooks.Registry.register({
shop: session.shop,
accessToken: session.accessToken,
topic: "APP_UNINSTALLED",
path: "/webhooks",
});
if (!response["APP_UNINSTALLED"].success) {
console.log(
`Failed to register APP_UNINSTALLED webhook: ${response.result}`
);
}
// Redirect to app with shop parameter upon auth
res.redirect(`/?shop=${session.shop}&host=${host}`);
} catch (e) {
switch (true) {
case e instanceof Shopify.Errors.InvalidOAuthError:
res.status(400);
res.send(e.message);
break;
case e instanceof Shopify.Errors.CookieNotFound:
case e instanceof Shopify.Errors.SessionNotFound:
// This is likely because the OAuth session cookie expired before the merchant approved the request
res.redirect(`/auth?shop=${req.query.shop}`);
break;
default:
res.status(500);
res.send(e.message);
break;
}
}
});
}

Firebase functions not being invoked

I am trying to integrate Stripe payments on my webapp using Firebase. I have cloned the code from the repository here: https://github.com/firebase/functions-samples/tree/master/stripe and have followed the documentation here: https://firebase.google.com/docs/use-cases/payments
From reading the documentation, I assumed that when a customer signed in through firebase authentication, their details would be added to a stripe_customer collection in the firestore. I realised this wasn't the case, and manually added a user to test the save card functions. Then I received the following error : "Invalid value for stripe.confirmCardSetup intent secret: value should be a client_secret string. You specified: undefined"
I have a blaze plan for firebase and have configured. From following the steps in the documentation, I assumed this would be working. I'm sorry this question is so vague, but it seems at every corner I'm getting another issue. Is there something very obvious I am missing that is stopping this code from working? I am trying to implement this for a friends business as a favor, and am getting really confused with Firebase. I am coding in Angularjs. Would greatly appreciate any help on this!
This is the code for the function to create a customer
exports.createStripeCustomer = functions.auth.user().onCreate(async (user) => {
const customer = await stripe.customers.create({ email: user.email });
const intent = await stripe.setupIntents.create({
customer: customer.id,
});
await admin.firestore().collection('stripe_customers').doc(user.uid).set({
customer_id: customer.id,
setup_secret: intent.client_secret,
});
return;
});
And this is the code being called in the controller:
const firebaseUI = new firebaseui.auth.AuthUI(firebase.auth());
const firebaseUiConfig = {
callbacks: {
signInSuccessWithAuthResult: function (authResult, redirectUrl) {
// User successfully signed in.
// Return type determines whether we continue the redirect automatically
// or whether we leave that to developer to handle.
return true;
},
uiShown: () => {
document.getElementById('loader').style.display = 'none';
},
},
signInFlow: 'popup',
signInSuccessUrl: '/checkout.html',
signInOptions: [
firebase.auth.GoogleAuthProvider.PROVIDER_ID,
firebase.auth.EmailAuthProvider.PROVIDER_ID,
],
credentialHelper: firebaseui.auth.CredentialHelper.NONE,
// Your terms of service url.
tosUrl: 'https://example.com/terms',
// Your privacy policy url.
privacyPolicyUrl: 'https://example.com/privacy',
};
firebase.auth().onAuthStateChanged((firebaseUser) => {
if (firebaseUser) {
currentUser = firebaseUser;
firebase
.firestore()
.collection('stripe_customers')
.doc(currentUser.uid)
.onSnapshot((snapshot) => {
if (snapshot.data()) {
customerData = snapshot.data();
startDataListeners();
document.getElementById('loader').style.display = 'none';
document.getElementById('content').style.display = 'block';
} else {
console.warn(
`No Stripe customer found in Firestore for user: ${currentUser.uid}`
);
}
});
} else {
document.getElementById('content').style.display = 'none';
firebaseUI.start('#firebaseui-auth-container', firebaseUiConfig);
}
});
The error you've supplied (below) implies that the key in your config isn't been pulled into your code. If you're running this locally you need to run the below any time you change your functions:config values.
firebase functions:config:get > .runtimeconfig.json
Check the doc's out about how to run your function locally:
Error
"Invalid value for stripe.confirmCardSetup intent secret: value should
be a client_secret string. You specified: undefined"

Cannot get Cypress test to work in React with Okta

I am trying to test my React (v16.10.2) application with Cypress (v4.5.0). Our application is using Okta for authentication (with the client #okta/okta-react 1.3.1).
I can use my app from the browser without any issues. The first time I login, I get the Okta login screen. I enter my user ID and password, and the react client calls the authn endpoint with my creds, and then calls the authorization endpoint to get the token. I am then taken to the first screen of our application.
When I try to login in my Cypress test, my user ID and password are entered into the login screen, the authn endpoint is called successfully, but the authorization endpoint returns a 403 error. Unfortunately, there is no other info about why I am getting the 403.
I have compared the authorization requests between the one that works in the browser, and the one that doesn't work from Cypress. The only real difference I see is that the working browser request has an origin header, whereas the failing one does not.
Question #1: Could the missing origin header be the cause of my problem?
In order to avoid a bunch of CORS and cross-site issues, I had to install a couple Chrome extensions (ignore-x-frame-headers and Access-Control-Allow-Origin-master). I am implementing them in the following code in cypress/plugins/index.js:
module.exports = (on, config) => {
on('before:browser:launch', (browser = {}, launchOptions) => {
// The following code comes from https://medium.com/#you54f/configuring-cypress-to-work-with-iframes-cross-origin-sites-afff5efcf61f
// We were getting cross-origin errors when trying to run the tests.
if (browser.name === 'chrome') {
const ignoreXFrameHeadersExtension = path.join(__dirname, '../extensions/ignore-x-frame-headers');
launchOptions.args.push(`--load-extension=${ignoreXFrameHeadersExtension}`);
const accessControlAllowOriginMasterExtension = path.join(__dirname, '../extensions/Access-Control-Allow-Origin-master');
launchOptions.args.push(`--load-extension=${accessControlAllowOriginMasterExtension}`);
launchOptions.args.push("--disable-features=CrossSiteDocumentBlockingIfIsolating,CrossSiteDocumentBlockingAlways,IsolateOrigins,site-per-process");
launchOptions.args.push('--disable-site-isolation-trials');
launchOptions.args.push('--reduce-security-for-testing');
launchOptions.args.push('--out-of-blink-cors');
}
if (browser.name === 'electron') {
launchOptions.preferences.webPreferences.webSecurity = false;
}
return launchOptions;
});
I also added the following to cypress.json:
{
"chromeWebSecurity": false
}
Here is my cypress test:
describe('Order Lookup Test', () => {
const UI_URL: string = 'http://localhost:3000/';
const ORDER_NUMBER: string = '10307906234';
beforeEach(() => {
Cypress.config('requestTimeout', 50000);
cy.visit(UI_URL);
cy.get('#okta-signin-username', {timeout: 10000}).type('xxxxxxxx');
cy.get('#okta-signin-password', {timeout: 10000}).type('xxxxxxxx');
cy.get('#okta-signin-submit', {timeout: 10000}).click();
})
it('should return an order', () => {
cy.get('.number-input', {timeout: 10000}).type(ORDER_NUMBER);
cy.get('.order-lookup-buttons-search-valid').should('be.visible').click();
})
})
Does anyone have any idea what might be going on? What other information should I be including in order to help narrow this down?

Can't get the Generic Sensor API to work in a React app

I'm trying to implement the Generic Sensor API in a React app.
https://www.w3.org/TR/generic-sensor/#the-sensor-interface
I keep getting an error when I try to implement any of the sensors in my code.
For example:
var sensor1 = new AmbientLightSensor();
I get the error: Cannot find name: 'AmbientLightSensor'.
I assume that I need an import statement in my code. All of the examples I've found only include LitElement. I've even tried that but still get the unknown error.
What import statements do I need in my typescript code?
What npm packages do I need?
Below is the typescript code I'm using.
I'm getting a typescript error:
/Users/scoleman/dev/current/bigbrother/src/utility/testAccel.ts(14,24):
Cannot find name 'AmbientLightSensor'. TS2304
export const testAccel = async (
databaseName: string,
) => {
const {state} = await navigator.permissions.query({
name: "ambient-light-sensor"
});
if (state !== "granted") {
console.warn("You haven't granted permission to use the light sensor");
return;
}
const sensor = new AmbientLightSensor();
sensor.addEventListener("reading", () => {
console.log(sensor.illuminance);
});
sensor.addEventListener("error", (err: any) => {
console.error(err);
});
sensor.start();
};
I was able to get these api's running using the polyfill at:
https://github.com/kenchris/sensor-polyfills
This would depend entirely on the browser you are using. I don't think FireFox supports it at the moment so I will focus on Chrome.
Firstly, you might need to be serving your site over HTTPS. It seems like this almost varies from permission to permission and also some are available on a localhost URL no matter what.
Secondly, for Chrome, you have to enable the "Generic Sensor Extra Classes" flag in Chrome at the chrome://flags/#enable-generic-sensor-extra-classes page.
Next, you need to make sure that have permission from the user to use the sensor, then you could actually use it. A snippet that would check that is as follows:
(async function(){
const {state} = await navigator.permissions.query({
name: "ambient-light-sensor"
});
if (state !== "granted") {
console.warn("You haven't granted permission to use the light sensor");
return;
}
const sensor = new AmbientLightSensor();
sensor.addEventListener("reading", () => {
console.log(sensor.illuminance);
});
sensor.addEventListener("error", err => {
console.error(err);
});
sensor.start();
}());

Error: User credentials required in Google Cloud Print API

I'm trying to use Google Cloud Print(GCP) API, but I can't make it works.
Maybe I've understood bad the workflow because is the first time I'm using the google api, please help me to understand how to make it works.
Initial considerations:
I'm trying to implement it in reactJS, but It is indifferent because the logic to make GCP works is independent of the technology. Then you also can help me understand the workflow.
What exactly I want:
To make my first test, I am looking to get all information about my printer.
What I did:
I created a project in: https://console.developers.google.com
Inside the project created, I created a credential:
create credentials -> OAuth client ID
And I chose Application type: Web, and also configure the restrictions to source and redirection to my localhost.
Manually in https://www.google.com/cloudprint, I added my printer, I made a test printing a PDF and was OK.
I created a project in reactJS to get the information of my printer I've added.
Component:
Explanation:
I'm using a component react-google-login to obtain easily the user accessToken: https://github.com/anthonyjgrove/react-google-login
This component only obtains the access token and save it in localStorage, in a variable called googleToken and it draws a button to call a function to obtain the information about the printer.
code:
import React, { Component } from 'react'
import GoogleLogin from 'react-google-login';
import { connect } from 'react-redux'
import { getPrinters } from '../actions/settings'
class Setting extends Component {
responseGoogle(response) {
const accessToken = response.accessToken
localStorage.setItem('googleToken', accessToken)
}
render() {
return (
<div>
<GoogleLogin
clientId="CLIENT_ID_REMOVED_INTENTIONALLY.apps.googleusercontent.com"
buttonText="Login"
onSuccess={this.responseGoogle}
onFailure={this.responseGoogle}
/>
<button
onClick = {() => {
this.props.getPrinters()
}}
>test printer</button>
</div>
)
}
}
const mapStateToProps = state => {
return {
state: state
}
}
const mapDispatchToProps = dispatch => {
return {
getPrinters() {
dispatch(getPrinters())
}
}
}
export default connect(
mapStateToProps,
mapDispatchToProps
)(Setting)
Action or Function to get information printer:
Explanation:
I'm passing the parameter printerid to get information about that printer.
In authorization, I'm using OAuth ... because in the documentation says that(second paragraph).: https://developers.google.com/cloud-print/docs/appInterfaces
The next two headers I wrote it because I tried solutions as:
Google Cloud Print API: User credentials required
Google Cloud Print User credentials required
code:
import axios from 'axios'
axios.defaults.headers.common['Authorization'] = 'OAuth ' + localStorage.getItem('googleToken')
axios.defaults.headers.common['scope'] = 'https://www.googleapis.com/auth/cloudprint'
axios.defaults.headers.common['X-CloudPrint-Proxy'] = 'printingTest'
const getPrinters = () => {
return () => {
return axios.get('https://www.google.com/cloudprint/printer'
, {
params: {
printeid: 'PRINTER_ID_REMOVED_INTENTIONALLY'
}
}
)
.then(response => {
console.log('response of google cloud print')
console.log(response)
})
}
}
export { getPrinters }
Error:
After all explained before, I got the next error:
User credentials required
Error 403
Note:
I'm using CORS plugin by recommendation of:
Chrome extensions for silent print?
because initially, I had cors error.
Any suggestion or recommendation would be very useful, thanks.
I've resolved my problem, my main problem about User Credential required were because I was using the incorrect access token and It was because I was getting the access token incorrectly.
I'm going to explain my whole solution because there are few examples of codes with this API.
Solutions:
The steps described were Ok until the fourth step where I used the external component react-google-login to trying to get the access token, instead I used googleapis module: Link Github googleapis
Also to avoid CORS problem(and not use CORS chrome plugin) I wrote the requests to Google API in server side.(NODEJS)
I had also a problem in the frontend when I tried to generate a popup to give permission for printer(problems about CORS), my solution was to use this very simple module for authentication: Link Github oauth-open
General scheme:
Explanation:
Knowing I have all data described in my question post(until the third step).
Authentication:
The next step in getting a URL and use it to the user can authenticate.
As I said before I used the module oauth-open in the frontend to generate the popup and only this module need the URL. To get the URL in the backend I used the endpoint /googleurl, where here I used the method generateAuthUrl of the module googleapis to generate the URL.
After that In the frontend, I got the authentication_code(that returned the module oauth-open), I send It to my endpoint /googletoken and here I process the authentication_code to generate access token, refresh token and expiration date with the method getToken of the module googleapis. Finally, these data are stored in the database.
Print:
For print, since the frontend, I send what data I need send to the printer. I used my endpoint /print
In the backend endpoint, my logic was the next:
Recover tokens and expiration date from database, with the expiration date check if the token has expired, and if It has already expired then gets another token and replace the old access token with the new one, replacing also with the new expiration date, to obtain this new data only is necessary call to method refreshAccessToken of module googleapis.Note: the refresh token never expires.
After having the access token updated, use it to send data to the printer with Google route(.../submit)
Code:
All the next codes are in only 1 file
Some data as validation, static variables, error handler, etc, has been removed to better understanding.
Route get URL authentication.
const express = require('express');
const google = require('googleapis');
const router = express.Router();
var OAuth2 = google.auth.OAuth2;
const redirect_url = 'http://localhost:3001/setting'; //Your redirect URL
var oauth2Client = new OAuth2(
'CLIENT ID', //Replace it with your client id
'CLIEND SECRET', //Replace it with your client secret
redirect_url
);
var url = oauth2Client.generateAuthUrl({
access_type: 'offline',
scope: 'https://www.googleapis.com/auth/cloudprint'
});
router.get('/googleurl', (req, res) => {
return res.status(200).send({
result: { googleURLToken: url }
});
});
To get tokens using the authentication code and save these in the database.
const Setting = require('../models/setting'); // My model(Mongoose)
router.post('/googletoken', (req, res) => {
oauth2Client.getToken(req.body.code, function (err, tokens) {
oauth2Client.credentials = tokens;
// If refresh token exits save it
// because the refresh token it returned only 1 time! IMPORTANT
if (tokens.hasOwnProperty('refresh_token')) {
let setting = new Setting();
setting.refreshTokenGoogle = tokens.refresh_token;
setting.expirationTokenGoogle = tokens.expiry_date;
setting.tokenGoogle = tokens.access_token;
setting.save()
.then((settingCreated) => {
return res.status(200).send({
message: 'OK'
});
})
}
});
});
To print
const axios = require('axios');
const moment = require('moment');
router.post('/print',async (req, res) => {
const tickeProperties = {
'version': '1.0',
'print': {
'vendor_ticket_item': [],
'color': { 'type': 'STANDARD_MONOCHROME' },
'copies': { 'copies': 1 }
}
};
const accessToken = await getTokenGoogleUpdated();
axios.get(
'https://www.google.com/cloudprint/submit',
{
params: {
printerid : printerID, // Replace by your printer ID
title: 'title printer',
ticket: tickeProperties,
content : 'print this text of example!!!',
contentType: 'text/plain'
},
headers: {
'Authorization': 'Bearer ' + accessToken
}
}
)
.then(response => {
return res.status(200).send({
result: response.data
});
})
}
);
async function getTokenGoogleUpdated() {
return await Setting.find({})
.then(async setting => {
const refreshTokenGoogle = setting[0].refreshTokenGoogle;
const expirationTokenGoogle = setting[0].expirationTokenGoogle;
const tokenGoogle = setting[0].tokenGoogle;
const dateToday = new Date();
// 1 minute forward to avoid exact time
const dateTodayPlus1Minute = moment(dateToday).add(1, 'm').toDate();
const dateExpiration = new Date(expirationTokenGoogle);
// Case date expiration, get new token
if (dateExpiration < dateTodayPlus1Minute) {
console.log('Updating access token');
oauth2Client.credentials['refresh_token'] = refreshTokenGoogle;
return await oauth2Client.refreshAccessToken( async function(err, tokens) {
// Save new token and new expiration
setting[0].expirationTokenGoogle = tokens.expiry_date;
setting[0].tokenGoogle = tokens.access_token;
await setting[0].save();
return tokens.access_token;
});
} else {
console.log('Using old access token');
return tokenGoogle;
}
})
.catch(err => {
console.log(err);
});
}
I hope It helps you if you want to use Google Cloud Print to not waste a lot of time as I did.
The important part there is a scope https://www.googleapis.com/auth/cloudprint which is not obvious and took one day for me to figure out.

Resources