When I'm upgrading from 3.4.0 to 7.0.0 an error saying it can't require "crypto" pops up in a React Native app. It used to work fine before.
+1
v7 isn't compatible with AWS Lambda as well. I get uuid: This browser does not seem to support crypto.getRandomValues(). error, which sounds exactly like @dylancompanjen error
Same goes for Azure functions. Getting the message:
uuid: This browser does not seem to support crypto.getRandomValues(). If you need to support this browser, please provide a custom random number generator through options.
Is this just affecting React Native? or also desktop browsers? I have like MANY libs for Node and browser that depends on uuid...
Thanks for the reports, we‘ll have to investigate.
Could anyone of you provide a minimal reproduction case? That would be extremely helpful!
I just confirm that the issue does NOT happen in browsers (using uuid 7.0.0 with browserify). So it may be something just related to react-native, but I cannot check that.
Check this
https://github.com/ai/nanoid#react-native
@TrySound thanks for the hint, maybe we can go a similar route. Will check it out in detail.
Could anyone of you provide a minimal reproduction case? That would be extremely helpful!
I think the issue is that node LTS (12.16.1 as of this message) does not offer the crypto.getRandomValues API. Hence anything that uses node (react-native, AWS Lambda, Azure Functions, etc.) will fail. Here's the minimally reproducible test case in a node REPL:
;{
const crypto = require('crypto');
console.log('TLS 1.3: ', crypto.constants.TLS1_3_VERSION); // prove the import works
crypto.getRandomValues();
}
Output:
TLS 1.3: 772
Uncaught TypeError: crypto.getRandomValues is not a function
@ZebraFlesh You are right getRandomValues is not part of node crypto. Also it's not used in node version.
https://github.com/uuidjs/uuid/blob/master/src/rng.js#L4
getRandomValues is used in browser version and it's correct.
https://github.com/uuidjs/uuid/blob/master/src/rng-browser.js
The problem are environments. They provides own sandboxes.
Here's what I know: performing the following code in a serverless handler which is bundled with webpack fails with the crypto.getRandomValues is not a function message:
import { v1 as uuidv1 } from 'uuid';
export const serve = async (event) => {
const value = uuidv1();
};
Looking at @TrySound's links it's very strange to me that this would go into the browser code instead of the node variant, but it is. (And my webpack config explicitly has a target: 'node' line.)
Very good catch!
@ctavan main does not always mean node environment and module does not always mean browser environment. There is special browser field for this. You should provide commonjs and esm for both node and browser environment.
Look how this is solved here in react-map-gl project
https://github.com/uber/react-map-gl/blob/master/package.json#L17-L25
main does not always mean node environment and module does not always mean browser environment. There is special browser field for this.
Jeez. It took a bit of digging to unpack all that, but you totally nailed it and made me smarter -- so thank you! Here's a link to npm's docs on browser; I couldn't find a concise explainer on module though (looks like it's mostly a proposal that most things are respecting, but not yet formalized in a spec).
Here's spec
https://github.com/defunctzombie/package-browser-field-spec
Also you can do the same to provide react-native specific solution
https://github.com/ai/nanoid/blob/master/package.json#L30-L32
"module" field is just a convention. Modern bundlers prefer it over "main". Though node will provide soon conditional exports as official solution.
https://nodejs.org/api/esm.html#esm_conditional_exports
I believe we have two distinct issues in this one:
This module currently doesn't seem to be compatible with react-native since react-native seems to use the browser code of this library but doesn't expose the WebCrypto global crytpo object. We'll likely be able to provide a fix for this case similar as nanoid does by providing an override in the package.json's react-native field: https://github.com/ai/nanoid/blob/master/package.json#L30-L32
This module apparently doesn't work in Azure functions for similar reasons: Apparently Azure functions try to invoke browser code while it should run the node code. I have created a separate issue to track the Azure functions problem: #378
With regards to react-native I'm happy to take a deeper look at the solution that nanoid chose but I'm equally happy to accept pull-requests that add such a feature.
The good news is that all of the implementations that are now broken very likely were using flawed random number generators with a much higher likelihood of UUID collisions (see https://github.com/tc39/proposal-uuid#how-unique-are-v4-uuids for examples) so we're finally discovering those implementations and now have a chance to fix them.
Same goes for Azure functions. Getting the message:
uuid: This browser does not seem to support crypto.getRandomValues(). If you need to support this browser, please provide a custom random number generator through options.
Same as for AWS, Azure, is also true for google cloud functions such as firebase cloud functions:
error from setNonceMiddleware: Error: uuid: This browser does not seem to support crypto.getRandomValues(). If you need to support this browser, please provide a custom random number generator through options.rng.
@ctavan 3. Also main/module is confused with node/browser
Looking at nanoid it looks like a solution for react-native is unfortunately not 100% straightforward: The random number generators used in node (crypto.randomBytes()) and the browser (crypto.getRandomValues()) are both synchronous. Hence all methods that this library exposes so far are synchronous as well.
The approach that nanoid takes is to offer async variants of the methods and then makes use of expo-random to generate cryptographically secure random numbers.
Nanoid also offers non-secure variants which use Math.random() under the hood. We have explicitly removed insecure random number generation in https://github.com/uuidjs/uuid/pull/354 with the latest major version upgrade and we will not add them for react-native.
That said there seem to be plenty of attempts on npm to provide Polyfills for either node's crypto.randomBytes() or WebCrypto crypto.getRandomValues() for react-native: https://www.npmjs.com/search?q=react%20native%20random
However I found it really hard to judge if any of these implementations satisfy the two criteria
@LinusU I saw that you authored one of these libraries (https://github.com/LinusU/react-native-get-random-values) and since you have been contributing to this library as well, maybe you can shed a bit more light on the situation w.r.t. sync CSPRNGs in react-native (or anyone else who is familiar with the matter)?
I think it's fixed in version 7.0.1
I still have the same error in 7.0.1
import { v4 as uuid4 } from 'uuid'
uuid4()
Error: uuid: This browser does not seem to support crypto.getRandomValues().
If you need to support this browser, please provide a custom random number generator through options.rng.
The same error in 7.0.1
The react-native problem has not yet been fixed and as explained above a fix is not straightforward.
7.0.1 fixed problem with Node.js cloud environments.
@ctavan I think it's very unfortunate that React Native doesn't provide a proper crypto.getRandomValues out of the box 😞
Personally, I think that the best approach is to have instructions to install the react-native-get-random-values polyfill and load that in at the setup for React Native users. That way they get a proper RNG, and the usage would be transparent since you only have to add the global polypill once.
Something like:
React Native: If you are using this library together in React Native, you need to add a polyfill for
crypto.getRandomValues. We recommend usingreact-native-get-random-values.
I'd be happy to update documentation in that package as well if there is anything that is unclear 👍
oh, and for anyone else in this thread, if you want a fix to this problem you should be able to install react-native-get-random-values and add import 'react-native-get-random-values' to the top of your index.js. Than the uuid package should work magically!
Thank you so much for your contributions!
I think we could go even one step further and make this work out of the box using a react-native field in package.json. However documenting how to install a Polyfill might also be a viable solution, in particular since we would define the polyfill as a peerDependency only anyways, so there would be an extra npm install involved anyways…
Just one thing I'm really curious about: Does your polyfill use a random number generator on both platforms (iOS and Android) that qualifies as "cryptographically secure"? And if so, why on earth do all other polyfill libraries for react-native that I found out there only provide async methods and no sync methods (most that I found seem to be using https://www.npmjs.com/package/sjcl as a fallback for their sync implementations)?
So I'm happy to merge your changes I just want to ensure that we are getting the same quality of randomness that we get with node and webcrypto.
@ctavan
I think we could go even one step further and make this work out of the box using a
react-nativefield inpackage.json.
I'm not exactly sure what we would put in that file? Do you mean that it would require the polyfill there? 🤔
However documenting how to install a Polyfill might also be a viable solution, in particular since we would define the polyfill as a
peerDependencyonly anyways, so there would be an extranpm installinvolved anyways…
I'm not sure that declaring react-native-get-random-values as a peerDependency is the best way forward, since that will give an "unmet peer dependency" warning for anyone using uuid on Node.js/browsers.
Just one thing I'm really curious about: Does your polyfill use a random number generator on both platforms (iOS and Android) that qualifies as "cryptographically secure"?
It does. It uses SecRandomCopyBytes on iOS and SecureRandom on Android.
And if so, why on earth do all other polyfill libraries for react-native that I found out there only provide async methods and no sync methods (most that I found seem to be using https://www.npmjs.com/package/sjcl as a fallback for their sync implementations)?
The fact that you can have synchronous methods in a React Native module isn't all that well documented. In fact, I only found out when I tried to add crypto.getRandomValues directly to React Native.
So I'm happy to merge your changes I just want to ensure that we are getting the same quality of randomness that we get with node and webcrypto.
Sounds good 👍
This is still a problem for people using Expo, since they cannot install native modules. Please follow progress (and add your 👍 if it's affecting you) here: https://github.com/expo/expo/issues/7209
Unless I am misunderstanding the docs, this is a workaround for expo:
import { v4 as uuidv4 } from 'uuid'
import * as Random from 'expo-random'
const getuuid = async f => uuidv4( { random: await Random.getRandomBytesAsync( 16 ) } )
Which would give uuidv4 a random number based on the Expo wrapper for native crypto.
@actuallymentor I believe that this code _technically_ works however there's a crucial difference w.r.t. to how [email protected] was working (and [email protected] is still working in all supported environments): Your example getuuid method is now async with potentially totally different performance characteristics than the sync (and usually pretty consistently fast) uuid standard implementation.
That said, if you are generating UUIDs only at a very low rate, it will probably not make a huge difference (but I would be curious about comparisons between [email protected] and the above async code).
Your example
getuuidmethod is now async
That was not a decision on my part but a result of the Random API being async. I would prefer to have it sync, but I need to get my expo/react-native project's uuids somehow :)
I would prefer to have it sync, but I need to get my
expo/react-nativeproject's uuids somehow :)
Yep, I guess the best thing you can do is to express your interest in https://github.com/expo/expo/issues/7209 (already saw you 👍'ed) and follow the issue over there.
Hi there! I use this package and just finished upgrading to v7 for a react-native/reactxp app. After I added the react-native-get-random-values polyfill in my bootstrap everything is working, so that's nice.
I just wanted to mention as the maintainer of react-native-device-info that if you recommend a native module in react-native that uses the barely-documented trick of doing blocking-synchronous calls, it breaks the common development scenario of people using the Chrome Dev Tools to do debugging (since the JS bundle runs in the browser instead of on device, and that can't be blocking/synchronous). This caused me much unexpected developer + maintainer pain.
I personally don't use the Dev tools so I personally don't care, but you may see issues related to the react-native debugger not working, and if you do, that's the root cause. Cheers :-)
it breaks the common development scenario of people using the Chrome Dev Tools to do debugging (since the JS bundle runs in the browser instead of on device, and that can't be blocking/synchronous).
Did you try that? Because as far as I know it already works out of the box with Chrome Dev Tools since that already has crypto.getRandomValues. The polyfill shouldn't be needed then...
Oh my! You are absolutely right, it's in the browser so it's fine - didn't think that through - as stated I don't use them myself, just noted the use of the barely documented sync native stuff and it reminded me of the troubles I had when I used it. I think you're all set then. Thanks again for the library
No worries! ...and I would also like to say that I would love it if we didn't need the polyfill. I've tried upstreaming this into React Native before, but they didn't see the need for it 😞
Yeah I saw that :-), I think you proposed it right as the lean core initiative was taking place and my read on it was that they wanted to make sure it was possible (as a pop quiz on their infrastructure capabilities) but that it wasn't a permanent no. I chimed in for whatever it's worth
I thought I should mention some other related issues i've encountered while trying to upgrade to v7 on RN. Using react-native-get-random-values breaks shortid which is used to generate route keys in React Navigation, hence breaking navigation for your app as well. This seems to only be the case on Android.
@eugenemlee
Using react-native-get-random-values breaks shortid [...]
Hmm, this should be fixed, is there an open issue somewhere? or an explanation of the problem?
@LinusU Ok, I noticed that on Android for all values of n = 3n - 1:
self.getRandomValues(new Uint8Array(n)))
Would always return a n-array where the last item was 0.
So I changed the following line in RNGetRandomValuesModule.java from:
byte[] data = new byte[byteLength];
to
byte[] data = new byte[byteLength + 1];
and it works okay now. I have no idea why that would be the case, maybe you can shed some light.
@eugenemlee crap, that is very bad, I'll push a fix now, thanks for giving me the details 👍
@eugenemlee thanks again for bringing that to my attention, I published a fix as 1.3.1 and have deprecated all other versions with an appropriate message.
To anyone else in this thread, I should say that the bug did not affect the UUID package, as it did not trigger the bug in the package. That being said, you should still update to the latest version asap so that no other part of your app is affected.
@actuallymentor thanks for your workaround, works perfect for the time being. 👍
const getuuid = async f => uuidv4( { random: await Random.getRandomBytesAsync( 16 ) } )
What would this look like outside of Expo?
I installed react-native-get-random-values (1.3.1) to my react-native (0.62.1) project and I'm still getting this error msg:
Failed to run .then() [Error: crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported]
What's the status on this issue?
@ebm93 did you add an import statement to import react-native-get-random-values? Did you run pod install in the ios/ directory?
Yes and Yes,
import 'react-native-get-random-values';
import {v4 as uuidv4} from 'uuid';
console.log("random number", uuidv4())
throws:
Error: crypto.getRandomValues() not supported. See https://github.com/uuidjs/uuid#getrandomvalues-not-supported]
@ebm93 Are you running in the debugger? In that case see #416
@LinusU Yes, but it still happens in release builds on my iPhone X device.
However, only time uuidv4 works fine is when I'm running it in debug mode with React Native Debugger.
Very strange..
@LinusU same problem here
Update: After nuking node_modules and reinstalling with npm ci, it's working now.
@LinusU Yes, but it still happens in release builds on my iPhone X device.
However, only time uuidv4 works fine is when I'm running it in debug mode with React Native Debugger.Very strange..
For what it is worth, I have got it working in Expo with UUID v7.x.x by using v5 and implementing a NAMESPACE constant and creating an id using the value of the thing to be encoding alongside your namespace constant. Basically following this:
https://github.com/uuidjs/uuid#version-5-namespace
My code looks like this:
import { v5 as uuid } from 'uuid';
import NAMESPACE from '../../data/constants/uuid-namespace';
const displayFinalTerms = Object.entries(
copyOnlyValues(final),
).map(([key, value]) => (
<SummaryList
key={uuid(value, NAMESPACE)}
title={key}
summaryTerms={value}
navToTerm="Structure"
/>
));
It's now working fine in my project with no errors....
@PaulHaze this is totally expected since v3/v5 UUIDs are entirely deterministic, i.e. constant input produces constant output and there are no random numbers involved. Hence: No problem for react-native/expo where the missing piece vor v1/v4 UUIDs is a cryptographically secure random number generator.
As a side note: @PaulHaze please make sure that the deterministic nature of v5 UUIDs is really what you need in your use case. If you are just using it to generate a react list key element, then you could equally just run whatever hash function over your value variable and would not really need the added complexity of v5 UUIDs (i.e. the namespace).
@ctavan thanks for replying, I am still very new to this and the main thing I was looking for was some sort of key generating ability and so for thats doing the trick. I have to admit though I am not ENTIRELY sure exactly what is going on under the hood - just that this seems to work where v1 and v4 didnt!
Regarding what you said here
if you are just using it to generate a react list key element, then you could equally just run whatever hash function over your value variable and would not really need the added complexity of v5 UUIDs (i.e. the namespace).
could you explain a bit more what you mean by this?
@PaulHaze if you would have used an UUID v4, the React component would have to re-render the items every time since the key would be different each time.
Since UUID v5 is deterministic, i.e. it will return the same value for the same input, it is more suitable for this.
The downside of using UUID v5 is that it's using a cryptographically secure hash. This is good for some things, but when computing React keys you don't need a cryptographically secure hash.
There are other hashes that are much faster, because they don't have to be cryptographically secure, they just have to have a "good enough" distribution, that are better suited for React keys.
I personally usually use murmur-128 for these kind of things. e.g.
const arrayBufferToHex = require('array-buffer-to-hex')
const murmur128 = require('murmur-128')
const key = (...parts) => arrayBufferToHex(murmur128(parts.join('~')))
// ...
{foo.map((bar) => (
<Qux key={key(bar.title, bar.description)} title={bar.title} description={bar.description} />
))}
@PaulHaze for what it's worth, the keys in React are only used internally and only needs to be unique within the list. You can simply use key={value} if value is guaranteed to be unique string/number. Otherwise using uuid to generate a hash is no better in terms of performance than just using the array index. Like this:
const displayFinalTerms = Object.entries( copyOnlyValues(final), ).map(([key, value], index) =>
(<SummaryList key={index} title={key} summaryTerms={value} navToTerm="Structure" /> ));
alternatively since you're using Object.entries and object keys are guaranteed to be unique you can use key={key}
import React from 'react';
import 'react-native-get-random-values';
import { v4 as uuidv4 } from 'uuid';
...
console.log(uuidv4());
throws this error
null is not an object (evaluating 'RNGetRandomValues.getRandomBase64')
Using
"expo": "~36.0.0",
"react-native": "https://github.com/expo/react-native/archive/sdk-36.0.0.tar.gz",
"uuid": "^7.0.3",
Has anyone figured this out yet?
@SeanRenet unfortunately Expo doesn't support getRandomValues at the moment, please thumbs 👍 this issue to show that this is something that we want: https://github.com/expo/expo/issues/7209
Check this
https://github.com/ai/nanoid#react-native
I'm working with Expo, seems that Nano ID is having issues too. ☹️
For developers working with Expo (+ Firebase <7.13.1), here you have a temporary solution: https://github.com/leahciMic/random-uuid-v4
@andreescajeda please keep in mind that the package that you are linking to exposes the same problem (using flawed Math.random() as the source of randomness) that this uuid module was exposing in versions <7.x. You can equally just continue to use [email protected] and will get the same result.
You could also pass in a custom (weak) random number generator, e.g. the one that was used in [email protected] and you will effectively get the [email protected] behavior back:
const { v4: uuidv4 } = require('uuid');
uuidv4({
rng: function mathRNG() {
const rnds = new Array(16);
for (var i = 0, r; i < 16; i++) {
if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
rnds[i] = (r >>> ((i & 0x03) << 3)) & 0xff;
}
return rnds;
},
});
But with all these workarounds please keep in mind that you have a higher risk of getting UUID collisions than when using a proper cryptographically secure random number generator (which unfortunately currently still does not exist in expo).
So in any case please make sure to upvote the issue in expo: expo/expo#7209
Not sure if related here, but wanted to share. Please delete if inappropriate.
For RN (without Expo), uuid works in debugging mode (chrome debugger ) but in release mode (I.,e. without debugger), it throws an exception.
It took me a while to find out where the issue was.
After removing all calls to uuid, my app works as per normal.
The fact that it did NOT throw an error in debugging mode made it harder to find the root cause of my issue.
@samipshah100 it's because the debugger works by running all code in Chrome, which provides crypto.getRandomValues natively ☺️
If anyone's still having the issue, here's a workaround...
Import React, { useState } from ‘react’;
import { v4 as uuidv4 } from 'uuid';
const App = () => {
const [items, setItems] = useState([
{ id: uuidv4(), text: 'Milk' },
{ id: uuidv4(), text: 'Eggs' },
]);
It worked for react-native-cli
If anyone's still having the issue, here's a workaround...
That invalid javascript and therefore errors.
If anyone's still having the issue, here's a workaround...
That invalid javascript and therefore errors.
Yeah, sorry about that. I used it inside a useState() JSON array. It worked there.
UUID can be assigned to an id there.
—-
I have corrected the previous comment now.
Using "uuid": "^3.0.0" fixed this for me... But I guess this isn't the desired solution.
Using
"uuid": "^3.0.0"fixed this for me... But I guess this isn't the desired solution.
Let’s call this a workaround at best 😉
Please read https://github.com/uuidjs/uuid/issues/375#issuecomment-618845223 and give your 👍 at https://github.com/expo/expo/issues/7209
Excuse me. I've also met this problem in my RN project, is there another way to fix it instead of installing "react-native-get-random-values" because it needs iOS/android developer to help and publish, thx!
@jiawei686 please read https://github.com/uuidjs/uuid/issues/375#issuecomment-643664712
With expo@39 and [email protected] this is finally working as intended.
See:
This issue also refers to the problems with Azure Functions/AWS Lambda/Firebase Functions etc too. Are we saying the same resolution is to use the react-native-get-random-values package?
@jameswoodley I don't think so. For node.js environments this is working now without extra packages. For react-native you must also install a random number generator, react-native-get-random-values works
These are my current dependencies, all tested + working + in production; firebase is cloud functions, public-app is react-native
mike@isabela:~/work/Kullki/ksocialscore/packages (navigation-overhaul) % cat firebase/package.json |egrep 'uuid|random'
"@types/uuid": "^8.3.0",
"uuid": "^8.3.2"
mike@isabela:~/work/Kullki/ksocialscore/packages (navigation-overhaul) % cat public-app/package.json |egrep 'uuid|random'
"@types/uuid": "^8.3.0",
"react-native-get-random-values": "^1.6.0",
"uuid": "^8.3.2",
Hi @mikehardy thanks for the update. I found that rollup was defaulting to browser and not node. Have resolved by adding
exportConditions: ['node'] to the resolve plugin
Thanks!
@jameswoodley the issue you are describing is being tracked in #544
Most helpful comment
@SeanRenet unfortunately Expo doesn't support
getRandomValuesat the moment, please thumbs 👍 this issue to show that this is something that we want: https://github.com/expo/expo/issues/7209