react-player and authorization required problem - reactjs

I am developing an application where server streams some video files and ReactJS-based client can play that stream using react-player.
Everything works great when I'm playing the stream using ordinary url variable from ReactPlayer component. But now I need to pass some data (authorization token) in header along with request to get access to the stream.
Ideally would be to somehow make request to the stream using Fetch API and then bypass only video data to ReactPlayer component, but I'm not sure if it is possible.
Does react-player supports such situation? If not, how can I achieve this or is there any other universal video player I can use that supports custom request crafting?
If that matters backend server is a Flask based REST api application.

The solution I used is to modify the XHR request send by HLS when loading the stream.
This is made by giving to ReactPlayer component some options for hls :
<ReactPlayer
config={{
file: {
hlsOptions: {
forceHLS: true,
debug: false,
xhrSetup: function(xhr, url) {
if (needsAuth(url)) {
xhr.setRequestHeader('Authorization', getToken())
}
},
},
},
}}
/>
The list of options for hls is available here.
You may use this since react-player version 1.1.2 according to this github issue

I spent several hours on this until finally found this amazing answer. It worked for me since I needed to pass down the token on the authorization prop.
If your file server supported CORS, you could use fetch and URL.createObjectURL
const token = getUserToken();
const CustomVideo = ({ videoUrl }) => {
const options = {
headers: {
Authorization: `Bearer ${token}`
}
}
const [url, setUrl] = useState()
useEffect(() => {
fetch(videoUrl, options)
.then(response => response.blob())
.then(blob => {
setUrl(URL.createObjectURL(blob))
});
}, [videoUrl])
return (
<ReactPlayer url={url} width="100%" controls />
)
}
Check the details here : https://github.com/CookPete/react-player/issues/282

Related

React App - Issue with CORS - access to fetch has been blocked by cors policy

I'm making app by using react which is gonna catch data from twitter API
that's my code
import {useEffect, useState, useContext} from 'react'
import {Link, useParams, useNavigate, useLocation} from 'react-router-dom'
export default function Home(){
const [content, setContent] = useState();
const token = 'here is the required token but i cant show you that'
useEffect(() => {
const url = 'https://api.twitter.com/1.1/search/tweets.json?q=something';
fetch(url, {
method: 'GET',
headers: {
'Content-Type': 'application/json',
Authorization: `Bearer ${token}`,
}
})
.then((response) => {
if(response.status === 404){
console.log('404');
}
else{
console.log(response.status)
console.log('3')
}
})
.then((data) => {
console.log("abba")
console.log(data)
setContent(data)
})
.catch((e) => {
console.log(e.message);
})
}, [])
return(<div>{content}</div>)
}
and i'm getting this type of error
Access to fetch at 'https://api.twitter.com/1.1/search/tweets.json?q=ewron' from origin 'http://localhost:3000' has been blocked by CORS policy:
When I'm catching data by using the same token by using postman everything works fine
I tried to find solution but everyone is refering to backend set on the same machine. How can I solve it in my case ?
A CORS policy is an important feature that prevents other, external websites from accessing an API.
Some APIs have CORS enabled, others have it disabled. And the whitelist will be tailored to the needs/preferences of the developers of that API.
Twitter will, perhaps, allow twitter.com to use Twitter's public api's. Along with other domains they use. Or maybe none at all, idk how their CORS policy is setup. They'll surely have a bit about it in the API docs.
The reason it works for you on postman is that you're not calling it from a website running in your browser, you're calling it from your computer - which is acting as a sort of 'back-end server'. Which is not a CORS policy violation, so it works fine for you.
It's important to note that CORS policies are being enforced by your browser - theoretically, the API has no idea if the request is from a website or a server. Your browser is telling on you, more or less. So it's not a guarantee of security or anything, but it's a pretty decent guard against it.
All of this is to say -- if you want to use their API, you'll want to set up your own routes and have your front-end call on your own backend server/API, and in turn have that call the twitter API. Which is what I'm guessing other people were trying to tell you.

React Display PDF from authenticated backend

I have a React Frontend and Django Backend. In my frontend I want to include a view for the PDF obtained by the backend. I tried using iframe and object HTML Tags, but they failed due to the missing authentication. My suggested approach would be requesting the PDF with axios.get, since this automatically handles the authentication. However, I could not find out how to handle the obtained PDF in case of temporarily storing and displaying it with react.
Currently my function is able to obtain the PDF and display it in a new window but I want to include it as an element within the current page.
const getPDF = () => {
axios
.get(
`${process.env.REACT_APP_API}/Link/to/the/PDF/`,
{
responseType: "blob",
}
)
.then((r) => {
window.open(URL.createObjectURL(r.data));
});
};
#react-pdf/renderer is used to render pdf from your page/application and is not made to render already made pdfs
You can use react-pdf to do what you want. It works great and lets you style your component the way you want.
In the content of the page I put the following:
<iframe src="" width={600} height={600} />
And I adapted the function to fill the iframe:
const getPDF = () => {
console.log("getPDF");
axios
.get(`${process.env.REACT_APP_API}/Link/to/the/PDF/`, {
responseType: "blob",
})
.then((r) => {
console.log(r.data);
const file = window.URL.createObjectURL(r.data
);
const iframe = document.querySelector("iframe");
if (iframe?.src) iframe.src = file;
})
.catch((err: AxiosError) => {
console.log(err);
});
};
So you have half the work done! in the other half, maybe an option is to look at this component:
#react-pdf/renderer
I used this package without any complaints.
** Sorry for redirecting to the wrong library. I use this instead:
pdf-viewer-reactjs

React provide default authentication for all request

I'm using React with axios mainly. There I have an interceptor for API calls to refresh my JWT token when it expires.
<img src="/media/cache/img.jpg" alt={row.id} width={45} height={45}>
These are also loaded from the server and authentication is needed. But when the token expires and no API query is needed, these images won't load because the token is invalid and authentication is required for these images.
Can I somehow achieve that even in these scenarios the tokens are refreshed correctly before loading the image?
You can use axios to fetch images as well. It looks something like this:
const url = "/media/cache/img.jpg";
const [objectURL, setObjectURL] = useState("");
useEffect(() => {
axios
.get(url, {
responseType: "blob",
})
.then((res) => {
const new_blob = new Blob([res.data], { type: "image/jpg" });
setObjectURL(URL.createObjectURL(new_blob));
});
}, []);
<img src={objectURL} alt={row.id} width={45} height={45}>
Now you can modify this to use your "interceptor for API calls" to refresh your token.

How to implement slack Oauth-2.0 in react js?

I need to implement slack authorization in my project for sending message in channel,Direct message and add reminder .So can anyone suggest me how to implement slack authorization in react project or is there any npm package that i can utilize to implement slack authorization like google authorization.
I had the this problem. Took me ages to solve but anyway here is my solution:
useEffect(() => {
const getdata = async (con) =>{
try{
await axios.get("https://slack.com/api/users.identity",con).then((res) => console.log(res)).catch((err) => console.log(err))
}
catch{
}
}
const gettoken = async () => {
try{
await axios.get("https://slack.com/api/oauth.v2.access",{params: {client_id: sclientId, client_secret: sclientsecret, code: name1[1]}}).then((res) => setData(res.data.authed_user.access_token)).catch((err) => console.log(err));
}catch
{
}
}
gettoken();
const config = {
headers: {
"Access-Control-Allow-Headers": "authorization",
"Access-Control-Allow-Origin": "*",
"Authorization": `Bearer ${ data}`,
"token": data
},
};
getdata(config);
})
so sclientid,sclientsecret, and name1(which stores code) were variables I defined in my code. I had a link(or button) that redirects the user to slack for authorization . code below:
<a href ={`https://slack.com/oauth/v2/authorize?user_scope=identity.basic,identity.email,identity.avatar&client_id=${sclientId}`} ><img src="https://api.slack.com/img/sign_in_with_slack.png" /></a>
after the user has given permission then he is directed back to my site with a code. i used window.location.search to get the code out of the url and store it in name1[1] and then using that my gettoken function sends the client_id,client_secret and code to slack with a get request. this brings back json which includes the token. I then send this token back to slack as a header in a request using my getdata function which returns the user info. To get the client_id ,client_secret and set the redirect url you have to go to apps.slack.com and create an app. Ask me any questions if this is not clear enough

Next.js - Error: only absolute urls are supported

I'm using express as my custom server for next.js. Everything is fine, when I click the products to the list of products
Step 1: I click the product Link
Step 2: It will show the products in the database.
However if I refresh the /products page, I will get this Error
Server code (Look at /products endpoint)
app.prepare()
.then(() => {
const server = express()
// This is the endpoints for products
server.get('/api/products', (req, res, next) => {
// Im using Mongoose to return the data from the database
Product.find({}, (err, products) => {
res.send(products)
})
})
server.get('*', (req, res) => {
return handle(req, res)
})
server.listen(3000, (err) => {
if (err) throw err
console.log('> Ready on http://localhost:3000')
})
})
.catch((ex) => {
console.error(ex.stack)
process.exit(1)
})
Pages - products.js (Simple layout that will loop the products json data)
import Layout from '../components/MyLayout.js'
import Link from 'next/link'
import fetch from 'isomorphic-unfetch'
const Products = (props) => (
<Layout>
<h1>List of Products</h1>
<ul>
{ props.products.map((product) => (
<li key={product._id}>{ product.title }</li>
))}
</ul>
</Layout>
)
Products.getInitialProps = async function() {
const res = await fetch('/api/products')
const data = await res.json()
console.log(data)
console.log(`Showed data fetched. Count ${data.length}`)
return {
products: data
}
}
export default Products
As the error states, you will have to use an absolute URL for the fetch you're making. I'm assuming it has something to do with the different environments (client & server) on which your code can be executed. Relative URLs are just not explicit & reliable enough in this case.
One way to solve this would be to just hardcode the server address into your fetch request, another to set up a config module that reacts to your environment:
/config/index.js
const dev = process.env.NODE_ENV !== 'production';
export const server = dev ? 'http://localhost:3000' : 'https://your_deployment.server.com';
products.js
import { server } from '../config';
// ...
Products.getInitialProps = async function() {
const res = await fetch(`${server}/api/products`)
const data = await res.json()
console.log(data)
console.log(`Showed data fetched. Count ${data.length}`)
return {
products: data
}
}
Similar to the #Shanker's answer, but if you prefer not to install the additional package for this, here is how to do it.
async getInitialProps({ req }) {
const protocol = req.headers['x-forwarded-proto'] || 'http'
const baseUrl = req ? `${protocol}://${req.headers.host}` : ''
const res = await fetch(baseUrl + '/api/products')
}
It sounds silly but worth mentioning. If you're using SSR in your webapp the fetch call will work with a relative link on the client but will fail on the server. Only the server needs an absolute link!
If you want to prevent the server from making the request just wrap it in logic
if(global.window){
const req = fetch('/api/test');
...
}
You could utilize environment variables if your project is hosted on a provider that supports it.
env.local
// Local
URL="http://localhost:3000"
// Production
URL="https://prod.com"
Then you can use the following.
const { URL } = process.env;
const data = await fetcher(URL + '/api');
This simple solution worked for me without having to add an additional config file,
Install
npm install --save next-absolute-url
Usage
import absoluteUrl from "next-absolute-url";
async getInitialProps({ req }){
const { origin } = absoluteUrl(req, req.headers.host);
console.log('Requested URL ->',origin);
// (or) other way
const host = absoluteUrl(req, req.headers.host);
console.log('Requested URL ->',host.origin);
}
Case 1. It's not an error. The isomorphic-unfetch is running by SSR mode, so Node.js needs to know the absolute url to fetch from it, because the back-end doesn't know your browser settings.
Case 2. Another scenario is to prevent the http host poisoning headers attack.
append secret keys and tokens to links containing it:
<a href="http://_SERVER['HOST']?token=topsecret"> (Django, Gallery, others)
....and even directly import scripts from it:
<script src="http://_SERVER['HOST']/misc/jquery.js?v=1.4.4">
Case 3. The isomorphic-unfetch it's the library we are going to use to fetch data. It's a simple implementation of the browser fetch API, but works both in client and server environments.
Read more about it:
Isomorphic unfetch - Switches between unfetch & node-fetch for client & server
Prevent http host headers attack
Fetching Data for Pages
In the NextJS 9.5, we can also use process.cwd().
process.cwd() will give you the directory where Next.js is being executed.
import path from 'path'
import fs from "fs";
export const getStaticProps: GetStaticProps = async () => {
const dataFilePath = path.join(process.cwd(), "jsonFiles", "data.json");
console.log(dataFilePath); // will be YourProject/jsonFiles/data.json
const fileContents = fs.readFileSync(dataFilePath, "utf8");
const data: TypeOfData[] = JSON.parse(fileContents);
return { props: { data } };
};
Ref: https://nextjs.org/docs/basic-features/data-fetching#reading-files-use-processcwd
Putting this out there because this showed up in google results for my problem, even though the question itself isn't really related (outside of the fact that the same dependency is throwing the same error message, albeit in a different context for a different reason).
I got this issue from using hardhat while attempting to verify (verify:verify) my contract on etherscan. The problem was that in the hardhat config, I didn't have a full url under rinkeby (since I was verifying on rinkeby, would be mainnet, etc.). Copy/pasting some config stuff quickly into a project I cloned from someone else, they had a full URL in their .env, while I had the url in the config and stored only my api key in my .env.
To figure this out, though, was straightforward--go into node_modules, then find the node-fetch folder, then lib, (this is from memory--just find the line that is vomitting in your stack trace) then the line number, and put a console log there to see what the "bad" url you're seeing is. Usually that's enough of a clue; in my case, it was an API key, obviously not a URL, and that made it straightforward to solve.
If you have an absolute path issues. Try to use swr to access data.
Notice: This is a React hooks so you must call inside the component.
import useSWR from 'swr';
// optionally you can use unfetch package from npm or built yours to handle promise.
const fetcher = (...args) => fetch(...args).then(res => res.json())
export const AllProducts = () => {
const { data, error } = useSWR('/api/products', fetcher)
if (error) return <div>failed to load</div>
if (!data) return <div>loading...</div>
return (
<div>{data}</div>
);
};
Export or deploying in production
Whenever you are trying to deploy on Vercel you might encounter an error. For instance `
warn - Statically exporting a Next.js application via `next export` disables API routes`.
It means you are trying to export data and NextJS does not support fetching data from pages/api/* directory. To avoid errors, its better to separate build and export command.
// package.json
{
"scripts": {
"dev": "next",
"build": "next build", // No next export command
"start": "next start"
},
}
Thanks folks for great contribution and I hope the answer shared will help somebody too.
Make sure what the value of your API url is
In my case, I was using POST but my url was somewhat undefined.
Use console.log to see where is your request going.
this is a way to get the base hostname to fetch data from external endpoint
without getting that error
function return_url(context) {
if (process.env.NODE_ENV === "production") {
return `https://${context.req.rawHeaders[1]}`;
} else if (process.env.NODE_ENV !== "production") {
return "http://localhost:3000";
}
}
and on the getServerSideProps or getStaticProps functions you use
export async function getServerSideProps(context) {
let url = return_url(context);
const data = await fetch(`${url}/yourEndPoint`).then((res) => res.json());
return {
props: {
data: data,
},
};
}
If you are using next environment config prefix your variables with NEXT_PUBLIC_ as mentioned here Exposing Environment Variables to the Browser.
USE: NEXT_PUBLIC_STRAPI_URL="http://localhost:1337" instead of
NEXT_PUBLIC_STRAPI_URL=http://localhost:1337
use .log(console.log) after nock , so you will get exact unmatched and expected url .
Example:
nock("http://localhost")
.log(console.log)
.persist()
.get("/api/config")
.reply(200, { data: 1234 })

Resources