I am trying to use react-recaptcha-v3 (https://www.npmjs.com/package/react-recaptcha-v3) and I exactly wrote the example :
import React, { Component } from 'react';
import { ReCaptcha } from 'react-recaptcha-v3'
import { loadReCaptcha } from 'react-recaptcha-v3'
class ExampleComponent extends Component {
verifyCallback = (recaptchaToken) => {
// Here you will get the final recaptchaToken!!!
console.log(recaptchaToken, "<= your recaptcha token")
}
componentDidMount() {
loadReCaptcha('site key (I can't give it here)')
}
render() {
return (
<div>
<ReCaptcha
sitekey="site key (I can't give it here)"
action={console.log('action')}
verifyCallback={this.verifyCallback}
/>
<h2>Google ReCaptcha with React </h2>
<code>
1. Add <strong>your site key</strong> in the ReCaptcha component. <br/>
2. Check <strong>console</strong> to see the token.
</code>
</div>
);
};
};
export default ExampleComponent;
I wrote my domains names like this :
localhost
localhost:3000
And I got a site key and a secret key.
Here is what i get in console :
New answer :)
Firstly, I'm unsure if you said you added localhost & localhost:3000 to allowed domains. If you did you should remove them
We advise to use a separate key for development and production and to not allow localhost on your production site key.
Even if you are using this for a test environment I wouldn't recommend it. Instead use the test site key & secret key. Localhost is enabled for test.
Next, I noticed your comment about using reCaptcha v2. If you are trying to implement reCaptcha the way v3 intends this won't work.
reCaptcha v2
Adds a script to a div that detects a users input checking if they a human.
reCaptcha v3
Adds an invisible div that detects wether it thinks the user is a bot or not. You have to setup a backend for it to work. It creates a score from 0.0 to 1.0. 0.0 meaning it is most likely a bot, 1.0 meaing it is most likely a human. It uses a token and then on your backend gives you a response that looks like this.
{
"success": true|false, // whether this request was a valid reCAPTCHA token for your site
"score": number // the score for this request (0.0 - 1.0)
"action": string // the action name for this request (important to verify)
"challenge_ts": timestamp, // timestamp of the challenge load (ISO format yyyy-MM-dd'T'HH:mm:ssZZ)
"hostname": string, // the hostname of the site where the reCAPTCHA was solved
"error-codes": [...] // optional
}
https://developers.google.com/recaptcha/docs/faq
Next, I checked the documentation of the package.
You can't use loadReCaptcha in the same component, it must be in a Parent. Also action={console.log('action')} needs to be a string action="action" you configure this string in your google account.
https://developers.google.com/recaptcha/docs/v3#frontend_integration
Next, I won't recommend relying on a package as you make an uanessacry dependency.
I wrote an article here about it https://medium.com/#alexjamesdunlop/unnecessary-packages-b3623219d86.
import React, { Component } from 'react';
class ExampleComponent extends Component {
componentDidMount() {
const script = document.createElement("script")
script.src = "https://www.google.com/recaptcha/api.js?render=_reCAPTCHA_site_key"
script.addEventListener("load", () => {
window.grecaptcha.ready(function() {
window.grecaptcha.execute('_reCAPTCHA_site_key_', {action: 'homepage'}).then(function(token) {
// ...
});
});
})
document.body.appendChild(script)
}
render() {
return (
<div
className="g-recaptcha"
data-sitekey="_Your_Key_"
data-size="invisible"
>
</div>
)
}
}
export default ExampleComponent;
If you do want to continue using the package this is what it should look like.
import React, { Component } from 'react';
import { loadReCaptcha, ReCaptcha } from 'react-recaptcha-v3'
class ExampleComponent extends Component {
verifyCallback = (token) => {
// This is the token you send to your backend
console.log("token: ", token)
}
render() {
return (
<div>
<ReCaptcha
sitekey={your_site_key}
// This must be a string and an example google gives is 'homepage' or 'login'
action="action"
verifyCallback={this.verifyCallback}
/>
</div>
)
}
}
class ParentComponent extends Component {
componentDidMount() {
loadReCaptcha(your_site_key)
}
render() {
return <ExampleComponent />
}
}
export default ParentComponent;
Now if you did want to use v2 instead of v3 I show how to do that in the article
https://medium.com/#alexjamesdunlop/unnecessary-packages-b3623219d86
I recommend moving away from react Components and moving towards hooks, it's common practice to move away from it now, Components isn't supported.
https://reactjs.org/docs/hooks-effect.html
Related
My websocket works because i tested it from django side white simple chat app. The route also works which is http://localhost:8000/chat/room. But It doesnt work on react side. It says No route found for path 'chat/myroom'. I've been trying to solve this for 2 hours, as a last hope, I wanted to ask here.
My routing.py file
from django.urls import re_path
from api import consumers
websocket_urlpatterns = [
re_path(r'ws/chat/(?P<room_name>\w+)/$', consumers.ChatConsumer.as_asgi()),
]
My roomPage.js file
`import React from "react";
import useWebSocket, { ReadyState } from "react-use-websocket";
import { useParams } from "react-router-dom";
export default function RoomPage() {
const { readyState } = useWebSocket("ws://127.0.0.1:8000/chat/myroom", {
onOpen: () => {
console.log("Connected!");
},
onClose: () => {
console.log("Disconnected!");
}
});
const connectionStatus = {
[ReadyState.CONNECTING]: "Connecting",
[ReadyState.OPEN]: "Open",
[ReadyState.CLOSING]: "Closing",
[ReadyState.CLOSED]: "Closed",
[ReadyState.UNINSTANTIATED]: "Uninstantiated"
}[readyState];
return (
<div>
<span>The WebSocket is currently {connectionStatus}</span>
</div>
);
}`
I tried on django side and it worked! But react side doesn't working
After +10 hours i finally found the solution and I want to kill myself! The only problem is calling websocket without "end slash"
Wrong!
useWebSocket("ws://127.0.0.1:8000/chat/myroom"
Works
useWebSocket("ws://127.0.0.1:8000/chat/myroom/"
I created a Google Cloud Platform account, and made a simple hello_world type Python "Cloud Function" that just spits out some simple text. I made this function "HTTP" accessible and only able to be called/authenticated by a "Service Account" that I made for the purpose of calling this very function. I generated a key for this "Service Account" and downloaded the json file for the key.
The problem is that I can't find any documentation on how to call this function with my service account in a next.js app. I tried this:
import React from 'react';
import { Button } from 'react-bootstrap';
import { GoogleAuth } from 'google-auth-library';
const projectId = 'gtwitone';
const keyFilename = '/Users/<myusername>/path/to/cloudfunction/credentials.json';
class Middle extends React.Component {
handleClick() {
console.log('this is:', this);
}
// This syntax ensures `this` is bound within handleClick. // Warning: this is *experimental* syntax. handleClick = () => { console.log('this is:', this); }
/* async listFunctions() {
const [functions] = await client.listFunctions();
console.info(functions);
} */
async runGoogleCloudFunctionTest() {
// Define your URL, here with Cloud Run but the security is exactly the same with Cloud Functions (same underlying infrastructure)
const url = "https://us-central1-<projectname>.cloudfunctions.net/<functionname>"
//Example with the key file, not recommended on GCP environment.
const auth = new GoogleAuth({keyFilename: keyFilename})
//Create your client with an Identity token.
const client = await auth.getIdTokenClient(url);
const res = await client.request({url});
console.log(res.data);
}
render() {
return (
<div className="col-md-12 text-center">
<Button variant='primary' onClick={this.runGoogleCloudFunctionTest}>
Click me
</Button>
</div>
);
}
}
export default Middle;
But I got this error in my terminal:
<myusername>#<mycomputername> <thisnextjsappdirectory> % yarn dev
yarn run v1.22.17
$ next dev
ready - started server on 0.0.0.0:3000, url: http://localhost:3000
wait - compiling...
event - compiled client and server successfully in 267 ms (124 modules)
wait - compiling / (client and server)...
wait - compiling...
error - ./node_modules/google-auth-library/build/src/auth/googleauth.js:17:0
Module not found: Can't resolve 'child_process'
Import trace for requested module:
./node_modules/google-auth-library/build/src/index.js
./components/Middle.tsx
./pages/index.tsx
https://nextjs.org/docs/messages/module-not-found
Native Node.js APIs are not supported in the Edge Runtime. Found `child_process` imported.
Could not find files for / in .next/build-manifest.json
Could not find files for / in .next/build-manifest.json
^C
<myusername>#<mycomputername> <thisnextjsappdirectory> %
I know that this is problem with server side rendering in my Next.js app and people recommend using a client side package like this https://github.com/google/google-api-javascript-client. But google-api-javascript-client doesn't have any documentation on authenticating with a .json credentials file instead of an API KEY which I do not have.
In short how do I get my app to work and run the Google Cloud function with a .json credentials file for am authenticated service account?
I fixed it by simply moving the GoogleAuth api call to the pages/api route.
pages/api/google.ts
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
import type { NextApiRequest, NextApiResponse } from "next"
import { GoogleAuth } from "google-auth-library"
export default async function handler(req: NextApiRequest, res: NextApiResponse<any>) {
const url = process.env.FUNCTION_URL as string
//Example with the key file, not recommended on GCP environment.
const auth = new GoogleAuth({ keyFilename: process.env.KEYSTORE_PATH })
//Create your client with an Identity token.
const client = await auth.getIdTokenClient(url)
const result = await client.request({ url })
console.log(result.data)
res.json({ data: result.data })
}
components/Middle.tsx
import React from "react"
import { Button } from "react-bootstrap"
class Middle extends React.Component {
handleClick() {
console.log("this is:", this)
}
// this talks with /pages/api/google
async imCallingAnAPI() {
const result = await fetch("/api/google")
console.log({ result })
}
render() {
return (
<div className="col-md-12 text-center">
<Button variant="primary" onClick={this.imCallingAnAPI}>
Click me
</Button>
</div>
)
}
}
export default Middle
pages/index.tsx
import type { NextPage } from 'next'
import Header from '../components/Header';
import Footer from '../components/Footer';
import Middle from '../components/Middle';
const Home: NextPage = () => {
return (
<><main className='d-flex flex-column min-vh-100'>
<Header />
<br></br>
<br></br>
<Middle />
</main>
<footer>
<Footer />
</footer>
</>
)
}
export default Home
I think that next.js has trouble loading GoogleAuth in a component. I'm not 100% sure why, but I think it has to do with next.js not knowing exactly how to handle GoogleAuth with server-side rendering.
I made one react app. My app works as expected. This app's target is practice AWS-COGNITO. For Cognito validation I am using amazon-cognito-identity-js package. I made one helper function where I validate the Congnito. and reuse it in different component. I split my Nav bar into two components. From Congnito current user I made one callback function and use it in useEffect, and dependencies put the callback function, by default getAuthenticatedUser is null. I add condition where it fetch the data, if getAuthenticatedUser then redirect to signin and signup page. I deployed my app to s3 bucket and this the link. This app runs first time, When I refresh it then got error: 404 Not Found. I really don't know what is the issue and somehow the path react path get disappear. I share my code in code-sandbox.
This is my conditional path
import React from "react";
import SigninLinks from './SigninLinks';
import SignoutLinks from './SignoutLinks';
import useHandlder from '../configHandler/useHandler';
const Nav = () => {
const { getAuthenticatedUser } = useHandlder();
const Links = getAuthenticatedUser() === null ? <SignoutLinks /> : <SigninLinks />
return (
<nav className="nav-wrapper grey darken-3">
<div className="container">
<h2 className="brand-logo">Logo</h2>
{
Links
}
</div>
</nav>
);
};
export default Nav;
This is my handler functions
import React, { useCallback, useEffect } from 'react';
import { CognitoUserPool } from 'amazon-cognito-identity-js';
const Pool_Data = {
UserPoolId: "us-east-1_9gLKIVCjP",
ClientId: "629n5o7ahjrpv6oau9reo669gv"
};
export default function useHandler() {
const userPool = new CognitoUserPool(Pool_Data)
const getAuthenticatedUser = useCallback(() => {
return userPool.getCurrentUser();
},
[],
);
useEffect(() => {
getAuthenticatedUser()
}, [getAuthenticatedUser])
const signOut = () => {
return userPool.getCurrentUser()?.signOut()
}
return {
userPool,
getAuthenticatedUser,
signOut
}
};
It's paths issue. You get 404 on /path not in root /. Check S3 settings for hosting static sites. On S3 make sure static website hosting is enabled:
You react app loads on /index.html JavaScript then redirects and takes over the path. You need S3 to resolve path to index.html, then it will work.
So I'm trying to extend the existing Auth components in AWS Amplify such as SignIn, SignUp, etc. and override the showComponent() function to return a custom form as detailed in this post: https://blog.kylegalbraith.com/2018/11/29/how-to-easily-customize-the-aws-amplify-authentication-ui/
I'm using typescript for my nextjs project and I'm getting the following error: when I try to throw the custom component under the Authenticator component:
[ts]
JSX element type 'CustomSignUp' is not a constructor function for JSX elements.
Type 'CustomSignUp' is missing the following properties from type 'ElementClass': render, context, setState, forceUpdate, and 3 more.
Here's my _app.tsx:
import {SignUp} from 'aws-amplify-react/dist/Auth/SignUp';
class NewApp extends App {
static async getInitialProps({Component, ctx}) {
let pageProps = {};
if (Component.getInitialProps) {
pageProps = await Component.getInitialProps(ctx);
}
return { pageProps }
}
render() {
const {props} = this as any
const {Component, pageProps} = props
return (
<Container>
<Authenticator hide={[SignUp]}>
<CustomSignUp/>
<Component {...pageProps}/>
</Authenticator>
</Container>
)
}
}
export default NewApp;
And the CustomSignUp.tsx is just something stupidly simple for demonstration purposes:
class CustomSignUp extends SignUp {
_validAuthStates;
constructor(props) {
super(props);
this._validAuthStates = ['signUp'];
}
showComponent(theme) {
return(
<div>
Hi!
</div>
)
}
}
export default CustomSignUp;
What's the way to do this correctly?
Per your request above I will provide more detail. As I mentioned in my comment, we found implementing and customizing the AWS Amplify components to be restrictive and difficult. We therefore decided to simply build out our own UI Components as we normally would, manage authentication globally with the Amplify HUB module and a Cognito Auth method helper class. Finally, we pushed Cognito user data down through our components with our own simple HOC.
To start, in a Component mounted when your app first loads, you can import Hub from aws-amplify to add any event listeners relevant to your app in the Component -- perhaps in the constructor -- including listeners to monitor auth state:
Hub.listen("auth", data => {
const { payload } = data;
if (payload.event === "signOut") {
props.navigation.navigate("SigninScreen");
}
}
You can listen/respond to auth changes throughout your app, even if the component in which you established the listeners unmounts.
Next, you can build a simple class with the various methods from the Auth module, again imported from aws-amplify, encapsulating functionality such as Auth.currentAuthenicatedUser, Auth.signUp, etc. With our own UI we simply attached/invoked the Cognito methods at the appropriate places and time.
If you decide to take this route, the last gap to fill is how to pass down the data from Auth's currentAuthenticatedUser method to your components (as Amplify's out of the box HOC would do). You can make your own HOC which fetches user data by Auth.currentAuthenticatedUser(), and pass the received data via props to any Component it wraps - fairly straightforward.
Because we were using graphql/Apollo, in our case we decided to use Apollo Client local resolvers to retrieve/pass Cognito user data. You can read more about Apollo Client local resolvers here here if you're interested.
I am using the Redux Bees library in a project. This library offers a HOC that I would like to use named [query][2].
The example they give is:
import React from 'react';
import api from './api';
import { query } from 'redux-bees';
#query('posts', api.getPosts)
export default class App extends React.Component {
render() {
const { posts, status } = this.props;
return (
<div>
{
!status.posts.hasStarted &&
'Request not started...'
}
{
status.posts.isLoading &&
'Loading...'
}
{
status.posts.hasFailed &&
JSON.stringify(status.posts.error)
}
{
posts &&
JSON.stringify(posts)
}
</div>
);
}
}
As you can see, it uses this HOC as a decorator. As currently my project doesn't allow the use of decorators, I was wondering if it is still possible to use this HOC in the standard, compositional way.
As I'm pretty sure decorators are just some sugar over standard class functionality, I would think this is possible, but I can't quite figure out how it is done.
I have tried:
const withApiData = query('post', api.getPage, (perform, props) =>
perform({ id: props.match.params.id })
)
Then wrapping the export within withApiData:
export default withApiData(connect(mapStateToProps, mapDispatchToProps)(Page))
But this doesn't work. Is what I am after even possible?
Spoke too soon: it actually works as I described, it turned out that the error stemmed from the fact the HOC is hardwired to make use of a certain redux store key, where I figured that was configurable.
Keeping this here for posterity :)