Safari FIDO U2F
FIDO U2F support for Safari
To use the extension:
- Download the latest release
- Run It
- Quit Safari (if it is open)
Open Safari Preferences
- Enable the
Safari FIDO U2F Extension
This extension requires macOS 10.12 and later, or macOS 10.11.5 and Safari 10.
Many sites will notice that
window.u2f is present, and should just work. The following have been tested:
- Yubico Demo
- Google Demo
- AkiSec Demo
- u2f.bin.coffee Demo
- U2F Test Page
- Github Account Two-factor authentication
There are two main reasons for sites not working:
The extension works by injecting code into to the page as it loads. Some sites perform their checks too early, before the injected code is present. Note that version 2.0 of the plugin now injects code quite early, and seems to have fixed most of these issues.
Some sites assume that only Chrome supports U2F on the Mac, and won't even try to use U2F if they detect Safari. As a workaround, changing Safari's User-Agent to Chrome may make these sites function. Unfortunately, other incompatibilities between Chrome and Safari can then often cause problems, since the site attempts to use unrelated Chrome-only features. Really the way to fix these sites is to talk to their developers and ask them to use another way to determine whether to enable their U2F support. Checking that
window.u2fis non-null should be enough.
This extension uses the Safari App Extensions API introduced in Safari 10, which allows a Safari extension to be built using native code, and embedded in another app.
The native part is written in Swift, and uses libu2f-host to actually talk to the hardware.
bridge.js script is injected into the top level page automatically by the extension.
This performs three tasks:
- listen for
beforeload, and use it to inject another script
u2f.jsinto the document
- listen for
u2f_messages posted by
u2f.js, and passes them on to the native code
- listen for messages sent back from the native code and pass them back to
It does this by setting
window.u2f to an object which provides implementations of
u2f.getApiVersion. Both the API 1.1 and API 1.0 variants of
sign are supported. If the page had already set
window.u2f, we attempt to merge our implementation into it, rather than completely replacing it. If we get there first, we try to lock down the
window.u2f property so that other scripts can't replace it with a different object (they can still set custom properties on it though). We also provide a non-standard
u2f.isSafari function which can be used to detect whether this particular implementation is present.
The high-level implementation converts the supplied parameters into a dictionary and sends this through for processing by the native code. The native code effectively implements the low-level API also described in the specification. In theory a site is free to talk directly to the native code by posting messages to it using the low-level API.
The high-level API is asynchronous, and returns its results via callbacks. A
requestId parameter is used to track requests sent to the extension. When replies come back from the extension, the same
requestId is used to associate the reply with the correct callback, which is then called.
As mentioned in problems above, the fact that we have to wait for the
u2f.js script to load can in theory cause timing problems. If a page has code which decides whether to enable U2F support based on whether
window.u2f is present, it's possible that it will run too early. We now inject
u2f.js by listening for the
beforeload event. This seems to get sent early enough (before
DOMContentLoaded) that most pages work.
To Build With Xcode
- Clone this project
- Open Xcode workspace and select the scheme
- Extensions must be code signed. To build locally, you will need to adjust the
Development Teamsetting of the project to a team that you have Mac Developer certificates for
- Xcode should build & launch the small app. You can then enable the extension from within Safari.
This plugin makes use of the following:
- hidapi: https://github.com/signal11/hidapi.git
- json-c: https://github.com/json-c/json-c.git
- u2f-host: https://github.com/Yubico/libu2f-host.git
For the sake of simplicity, compiled binaries for all three are committed to this repo.
The source for all three is available in the github repos above.
If you wish to build the libraries locally instead, you can do so using Homebrew, with
brew install hidapi json-c libu2f-host.
There are two sets of unit tests.
- The Swift tests can be run by choosing
npm install. The tests can then be run with
CI is set up using Travis.
Committing branches or making pull requests in this repository will trigger builds of the tests. If you fork the repo, you will probably need to tweak
.travis.yml if you want to run the tests with your own instance.
Still To Do
There are a few things still to do. See the issues for more details.
If you find sites that don't work, or features that are missing feel free to make an issue to report them.
If you have the urge to contribute, that would be welcome. The code does not all follow a consistent standard yet but we're slowly cleaning it up.
By all means just hack on the code and make a pull request, but it might be best to check your plan first by making an issue and discussing it.
This extension is still experimental, and use of it is entirely at your own risk!
All feedback and other contributions welcomed.
In particular, please tell us about sites that do / don't work! Please also consider contacting the owners of those sites to make them aware of this extension, so that we can work together to fix any problems.