A JavaScript implementation of RFC 7564 (The PRECIS Framework).
Available as NPM package precis-js:
npm install --save precis-js
The PRECIS Framework, otherwise known as RFC 7564, is a system for preparing arbitrary Unicode strings for use in strict protocols, such as authentication systems. In a general sense, PRECIS encompasses what needs to be done to correctly handle Unicode in places like usernames and passwords.
The introduction of Unicode usernames and passwords comes with its own set of challenges, especially in terms of security and usability. Spotify faced many of these challenges when they decided to implement Unicode usernames, and the article Creative usernames and Spotify account hijacking does a good job of explaining the scope of the problem.
The PRECIS Framework obsoletes stringprep (RFC 3454) the previous de-facto solution for handling Unicode usernames and passwords. PRECIS takes a more sustainable approach than stringprep, because it is designed to adapt to future versions of Unicode.
precis = require('precis-js');
profile = new precis.profile.UsernameCaseMappedProfile();
try {
result = precis.enforce(profile, string);
} catch (e) {
// handle error
}
This package supports browser usage via Browserify and the brfs transform. However, there are important caveats that may adversely affect the size of the Browserify bundle. See the Modules section for more information.
In a typical server-client scenario, it is recommended to use the prepare only module provided by PRECIS-JS:
precis = require('precis-js/prepare');
profile = new precis.profile.UsernameCaseMappedProfile();
try {
result = precis.prepare(profile, string);
} catch (e) {
// handle error
}
Preparation should be favored when the goal is simply to check whether a string is valid for a given profile. This can be used, for example, to highlight invalid input in a browser or other client:
precis.prepare(profile, string)
- Where
profile
is a profile object, andstring
is the string to prepare. - Throws an exception if
string
is invalid forprofile
. - Available in all PRECIS-JS modules.
- Return type is not guaranteed, and may change in future versions without warning.
Enforcement involves not only checking the string against the rules of a profile, but producing a canonical result string. In a sense, enforcement is a type of normalization, and in fact usually involves Unicode normalization as a part of the process.
The result of enforcement is the string that should be considered the canonical version of the input string:
canonicalString = precis.enforce(profile, string)
- Where
profile
is a profile object, andstring
is the string to prepare. - Returns the canonicalized string for use in comparison etc.
- Throws an exception if
string
is invalid forprofile
. - Not available in the
precis-js/prepare
module.
PRECIS-JS does not currently implement a comparison interface. This may change in future, but all current PRECIS profiles seem to be using simple byte-for-byte string comparison. This can be accomplished via JavaScript's identity operator (===).
PRECIS-JS provides a few standard PRECIS profiles, all of which live in the
precis.profile
module. Each profile is implemented as a class:
precis.profile.NicknameProfile
is an implementation of the Nickname Profile found in the draft specification Preparation, Enforcement, and Comparison of Internationalized Strings Representing Nicknames.precis.profile.OpaqueStringProfile
is an implementation of the OpaqueString Profile found in RFC 7613.precis.profile.UsernameCaseMappedProfile
is an implementation of the UsernameCaseMapped Profile found in RFC 7613.precis.profile.UsernameCasePreservedProfile
is an implementation of the UsernameCasePreserved Profile found in RFC 7613.
Custom profiles are also possible, and are very simple to implement. For now, this is left as an exercise for the reader. Please see the included profiles for sample code.
PRECIS-JS provides several alternate versions of the primary precis-js
module. These are useful when code size is an issue, such as in a browser.
When using PRECIS-JS, the primary contributor to code size is the Unicode data that must be included to implement the various algorithms of PRECIS. The data used directly by PRECIS constitutes approximately 7KB. There is no way to avoid including this data.
In addition, unless the ECMAScript 6 String.prototype.normalize function is available, PRECIS-JS requires the unorm package in order to implement the Enforcement API. This adds approximately 150KB of Unicode normalization data. Fortunately, the Preparation API, which does not require this data, should be sufficient for typical client usage.
The available modules are as follows:
- The
precis-js
module includes both the Preparation, and Enforcement APIs, but requires unorm, and hence produces the biggest size. - The
precis-js/enforce
module is an alias for theprecis-js
module. - The
precis-js/enforce-es6
module is for use when String.prototype.normalize is guaranteed to be available. It includes both the Preparation, and Enforcement APIs, but does not require the unorm package. This is the best of both worlds, if you can use it. - The
precis-js/prepare
module includes only the Preparation API, and hence produces the smallest possible size. Suitable for clients in a typical client-server scenario.
To regenerate the PRECIS-JS data from the latest Unicode data, run the
included scripts/generate-data
script. This will fetch the latest version of
the Unicode Character Database, run PrecisMaker, and convert the data into
an optimized format.