Thursday 3 February 2022

Testing For The Brave Browser

How Can We Test For Brave?


By Strictly-Software

The problem with detecting the Brave browser which I use most of the time is that it hides as Chrome and doesn't have its own user agent. It used to, and the hope is that in the future it will again but at the moment it just shows a Chrome user-agent. 

For example, my latest Brave user-agent is:

Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/97.0.4692.99 Safari/537.36

It also apparently used to have a property on the windows object window.Brave which is no longer there anymore. I have read many articles on StackOverflow about detecting Brave and all the various methods that apparently used to work don't anymore due to objects and properties such as window.google and window.googletag no longer exists on the windows object.

As these posts were not written that long ago it seems that there have been a lot of changes on the window object by Chrome / Google / Brave etc. So when writing your own function as I did it is worth outputting all the keys on the window object first to see what is there and what isn't e.g.

const keys = Object.keys(window);
console.log(keys);

This just outputs all the keys such as events, other objects like document and navigator, and properties as well into the developer tools console area that all modern browsers have.

As Brave, Edge, Opera and Chrome are all based on the same Chromium browser they are basically the same standard-compliant browsers.

Remember we should always use feature detection rather than user-agent parsing to choose whether or not to do something in JavaScript but it is amazing when using a user-agent switcher how many modern-day sites break when I change a standards-compliant browsers agent string to IE 6 for example.

If the coder wrote their code properly it wouldn't matter if I changed the latest version of Chromes user-agent to IE 6 or even IE 4 or Netscape Navigator 4, all sites should work perfectly as instead of doing old school tests like

isIe = document.all;
isNet = document.layers;

And then branching off those two as we did in the old days of scripting which led to browsers like Opera which no one catered for, having to support both IE and Netscape event handlers and pretending to not exist. However, they did eventually add the Opera property to the window object so if you really wanted to find it you could just test for:

if(window.opera){
  alert("This is Opera");
}

However, this no longer exists and Opera uses the same WebKit Chromium base as all the other modern browsers apart from Firefox.

Also, remember that the issue with JavaScript is that every object can be overwritten or modified, that is why we have user-agent switchers in the first place because they can, and do overwrite and change the window.navigator object.

Therefore if you are really pedantic there is NO real way to test if anything is real in JavaScript as a plugin or injected script could have added properties to the Window object such as changing the user-agent or adding a property another browser uses to the window so that tests for window.chrome fail in FireFox because you have created a window.chrome property FireFox detects.

There are loads of ifs and buts and we can discuss the best approach all day due to injected code and extensions overwriting objects or accidental setting of objects by forgetting to do == and accidentally setting a variable or object with a single = by mistake, but let's answer the question,

I have read a lot about people trying to detect Brave and a lot of old objects on the Chrome browser like window.google and window.googletag that no longer exist or even window.Brave which apparently existed on the window object at one stage. 

Why might you want to even detect Brave if it is a standards-compliant Browser and you are doing feature detection and not user-agent sniffing anyway you might ask? 

Well good question, if you are writing your code properly you shouldn't ever need to know what Browser the code is running in as it will work whatever the user is running your web page in due to proper use of feature detection. 

However, you may have a site that wants to direct people to the right extension depository or download the correct add-on. Therefore to make the user comfortable in their decision you might want to show that you know what browser the user has so that they are happy downloading the add-on. 

Or there might be a myriad of other reasons such as asking users to update their browser due to an exploit that has been discovered or to ask old people still using IE6 to upgrade to a modern browser. Also if you want them to use one that removes cookies, trackers, and adverts, and is security conscious, you may want to direct non-Brave users to the Brave download page.

Therefore the function I have come up with tries to future proof by hoping Brave does put its name into the user-agent at some point, and it checks for the existence of properties in objects such as the window or navigator object.

You can put all your complaints and ideas for improving this function for detecting Brave in the comments section and maybe we can come up with a better solution.

// pass or don't pass in a user-agent if none is passed in it will get the current navigator agent
function IsBrave(ua){
	
	var isBrave = false;
	ua = (ua === null || ua === undefined) ? ua = window.navigator.userAgent : ua;

	ua = ua.toLowerCase();

	// make sure it's not Mozilla
	if("mozInnerScreenX" in window){		
		return isBrave;
	}else{
		// everything in JavaScript is over writable we could do this to pass the next test 
		// but then where would we stop as every object even window/navigator can be overwritten or changed...?
		// window.chrome="mozilla";
		// window.webkitStorageInfo = "mozilla";

		// make sure its chrome and webkit
		if("chrome" in window && "webkitStorageInfo" in window){
			// it looks like Chrome and has webkit
			if("brave" in navigator && "isBrave" in navigator.brave){
				isBrave = true;
			// test for Brave another way the way the framwwork Prototype checks for it
			}else if(Object.prototype.toString.call(navigator.brave) == '[object Brave]'){
				isBrave = true;			
			// have they put brave back in window object like they used to
			}else if("Brave" in window){
				isBrave = true;			
			// hope one day they put brave into the user-agent
			}else if(ua.indexOf("brave") > -1){
				isBrave = true;
			// make sure there is no mention of Opera or Edge in UA
			}else if(/chrome|crios/.test(ua) && /edge?|opera|opr\//.test(ua) && !navigator.brave){
				isBrave = false;			
			}
		}

		return isBrave;
	}
}

Of course if you really didn't want all these fallback tests and hope that Brave will sort their own user-agent out in the future or re-add a property to the window object as they apparently used to then you could just do something like this:
var w=window,n=w.navigator; // shorten key objects
let isBrave = !("mozInnerScreenX" in w) && ("chrome" in w && "webkitStorageInfo" in w && "brave" in n && "isBrave" in n.brave) ? true : false;

Just to show you that it works here is the "Browser" test page I keep on all my Browser's bookmark bars, written using just JavaScript with some AJAX calls and my own functions which lets me see the following info:
  • My Current IPv4 address by calling a URL that only returns IPv4.
  • My Current IPv6 address by calling a URL that will return one if I am using one, otherwise it converts the IPv4 into an IPv6 address format. Obviously, this is not my real IPv6 address as if I had one it would be returned it is just the IPv4 address converted into IPv6 format.
  • The UserAgent that the browser is showing me, this may be spoofed if a user-agent switcher is being used.
  • The real User-Agent if a spoofed user-agent is being used by a user-agent switcher extension/add-on.
  • The spoofed Browser Name.
  • The real Browser name.
  • Whether a user-agent switcher was detected. I have my own which does both the Request Header as well as overwriting the window.navigator object with different agents details. It creates a copy of the original navigator object so I can output it as well as the spoofed version.
  • An output of the current window.navigator object, if a user-agent switcher was used it will show the overwritten navigator object.
  • An output of the original window.navigator object if my own user-agent switcher was used then I create a copy of the same navigator string displayed for the current navigator object so both the spoofed and real navigator objects are outputted.
  • A script that loads in info about my location based on my IP address e.g: Town, County, Country, ISP, Hostname, Longitude, and Latitude.

Example Browser Test Output

If you look at this image, double click to open bigger if you need to, although it won't show you all the ISP & Location info at the bottom, it will show you the user-agents, both spoofed and real, as well as the spoofed browser and the real browser, which my custom function detects from either just a user-agent string or with the IsBrave() function I outputted above.



You can see here that although the user-agent strings are exactly the same my code that detects the browser has correctly recognised the real browser as Brave by using the function this article is about and for the spoofed Browser name it shows Chrome, which is the browser Brave tries to mask itself as.

This handy little script is on all my browsers bookmark bars for easy access and I find it very helpful for quickly seeing if a user-agent switcher is enabled and what if any, my browser it is pretending to be, as well as my current GEO-IP location in case I am using a global VPN, TOR, Opera or a proxy.

Let me know what you think of this function tested in Firefox, Chrome, Opera, Edge and of course Brave.

By Strictly-Software



23 comments:

Daniel said...

Nice bit of code especially the one liner, I've been looking for a way to tell Brave from Chrome for ages now. Its only for my own page which tells me the Real v Spoofed browser in case I left s user-agent switcher on a wrong browser user-agent. good job.

Anonymous said...

Love the code man

Steve Collins said...

Nice bit of code, I used to be able to use the DuckDuckgo method where you call it and ask "What is my browser" and it would give you back some json telling you that it was Bravem if you were using Bravem however that no longe works,, I do like the functioj, but I like the 2 kiner check at the bottom even more, rules out FireFox or Mozills browsers by a check for a Moz only propety and ensures its Chrome/webkit with a test for a webkit only property and then checks for whether the navigator object holds Brave only properties. Nice.

Tony said...

I Thank you, for looking into this and I know how hard ir that you cannot easily check for Brave due to it wanting to not be found, so it is a case of what isn't there that should be in a real chrome browser.
However as you rightky point out, we should all be using feature detection now not useragent parsing and browser sniffing, It is sad ehen you see modern sites break due to some sort of brower sniffing when if built correctly they wouldn't. It shouldn't matter what the user-agent was as if the objects existed you used them if they didn't you wouldn't, so changing your user-agent to IE 4 or Netscape 4 should make no differene to what you see on your screen as long as its a site built for a standards compliant browser.....

Anonymous said...

Love this code, I do hope Brave puts their name back into the user agent as every browser should have a different agent. I know why they are doing it, to hide, the whole point of Brave is to hide as just another Standards compliant WebKit-based Chromium browser but it isn't. It has many features like the way ut shows ads to the user not from sites and on top of other apps if turned on, Advertising, the stripping of cookies, trackers, and code changes all mean that mean it displays vastly different HTML than Chrome, Edge or Opera.

Jim said...

I do like the idea of a function that has fallback cases and tries to future proof the moves of Brave such as adding a Brave property to the Window object as Opera did, and Brave used to, or hopefully adding Brave into the user-agent string at sine point, but the first line of the IF statement is all that you really need to test if the browser you are on is Brave e.g

if("brave" in navigator && navigator.brave.isBrave()){
isBrave = true;
}

As the other else branches are either just checking the same object in another way, hoping that Brave puts its name back in the UA string and that the strings Edge and Opera names are not found in the User-Agent.

Steve Williams said...

I do like that one-liner test at the bottom, calling to DuckDuckgo used to work but now it doesn't, don't know why. Also the window.google and window.googletag properties used to exist but now have gone it's like window object is being constantly updated so maybe a function with various tests including looking for the word Brave in the user-agent in case they do add it back in is the way to go as you don't know what objects will exist this time next year on the window or navigator objects.

Anonymous said...

i've been looking for some code to test for Brave, I tried this out and it seems to do the trick so thank you.

Anonymous said...

Why can't Brave have a unique user-agent string like every other browser. I know it's trying to hide due to all it's non-tracking, despite the fact it keeps trying to access my web cam for some reason, which I have blocked. Sticky tape is the best measure for web cams I think, just incase they manage to get round any block on accessing your camera.

Anonymous said...

I like the 2 liner version at rhe bottom, could mske it one line if not mimising the nain objects but then iur system minifies our JS code anyway ao it will get down if I remove the ninmised window and navigatoe objext and just put:


let isBrave = !("mozInnerScreenX" in window) && ("chrome" in window && "webkitStorageInfo" in window && "brave" in navigator && navigator.brave.isBrave()) ? true : false;

Will do nicely for coding and I will let our minificstion code do the work of replacing window, document and navigator etc..

Danny said...

Code works for me either the function with future-proofing or the one-liner at the bottom of the page. Been trying to find a way to get Brave returned from a "What is my User-Agent" page for ages now. So thanks!

Rob Reid said...

I have updated the function as I was testing it by changing navigator.brave.isBrave() to isBraver() to see if it would fall to the next line but it caused an error so I used the same is "isBrave" in navigator.brave test instead and also changed the line below which was just a repeat of the first in a different manner to be a different test, taken from Prototypes framework for testing. I also edited the last line to ensure any UA in the future with Edge instead of Edg gets matched and an extra test to ensure !navigator.brave isn't true, due to shortcutting if it was true then it would have got matched earlier on. Also changed the two liner or one if you don't compress window to w and navigator to n to use this different test >> "brave" in n && "isBrave" in n.brave in case the method isBrave() gets dropped or renamed so it won't error. Hopefully this is ok. Tested in FF, Edge and Brave so far.

Jake said...

I was just going to post I got an error but then I've seen the change will go and test it now.

Anonymous said...

I actually prefer this 2nd version, the last one was basically doing the same test for Brave in the first 2 branches but inbn different ways. Trying to test it by changing thrr nsme of the mrthiod being called as I was doing to see how it faired going down the if sratements was causing errors. The "method" in object test id better sd you can test if by putting nonsense in between thr quotesm letting that 1st branch faik and without erroring it drops to the 2nd branch where ir passes or not depening on whether you are using Brave, so thanks for the edit.

Rob Reid said...

Yes I think the new version is a lot better, either the full fallback function to future proof if Brave add their name into the user-agent or a window.brave object again, or the one liner (or two if you don't minimise the window and navigator objects). I hope it continues to work as from what I have seen on stackoverflow a lot of old tests for Brave that used to work like using DuckDuckgo.com or testing for window.google or window.googletag no longer do and I do actually remember when I first got Brave that I did see the word in the user-agent string so maybe it will come back, who knows!

Anonymous said...

Nice job Brave is pretty hard to detect. I wish they would put the word Brave in the user-agent like other browsers but then it is sold as a "privacy" browser so I guess they don't want you to know. It defeats the whole point, however if you are only using the Request.Headers then you only have a user-agent string to parse and there is no way to check for Brave at the moment just from that string, so I guess I will have to use code at the top of the page to log to my logger DB the user-agent from the navigator object with this function otherwise I will be logging incorrect browser and I use the Browser name in my fingerprint for the visitor.

Steve Collins said...

Very useful bit of code. I might adapt it for my own use. Let you know of any changes

Dave said...

Do you actually have a browser detection function that will tell you what browser you have from the Navigator object e.g not just whether it is Brave or not but all browsers?

Robert Reid said...

I do but I only use it on a local page, I don't think browser detection is a good thing, object detection is the way to go.

Danny said...

Nice code, I have been looking for a way to test for Brave, the DuckDuckGo method did use to work for me up until about 8 months ago so did searching for window.googletag. I don't know why or how they can keep adding objects to the window then removing them. I know every variable is actually a window.myvariable object but for browsers to keep changing how they identify themselves is really anoyiing like the way Opera supports both the old IE event and Standards Compliant event listeners so the only way to detect it was Opera once window.opera went was to check if both addEvent and addEventListener existed on the DOM, but then we are object testing to detect a browser when as you say we should just be object testing to detect whether we can use a certain object or property.

Anonymous said...

Does this still work?

Rob Reid said...

Yes it still works, in fact, to prove it I have updated the article to show you a "Browser Settings" test page I use on all my browsers which shows me the real user-agent/Browser and any spoofed agent/Browser if it can detect either my own or another user-agent switcher extension. I find it very helpful to let me know if I have left a VPN logged on, or to show me the location I am in when using TOR or Opera.

If you notice in the picture I included I have exactly the same user-agent string in the agent switcher as my real Brave browser. You can see that even though the agents are the same text my IsBrave() function determines that my current browser is Brave even though the spoofed user-agent/real user-agent shows Chrome.

I find it helpful anyway!

Jake Croft said...

Can I have a copy of your browswer detection code, the whole thing, not just the test for Brave?