Comparing version 0.3.1 to 2.0.0
# Embassy Change Log | ||
This project adheres to [Semantic Versioning](http://semver.org/). | ||
## [Development] | ||
Nothing yet! | ||
## [v2.0.0] - 2020-01-13 | ||
### Changes | ||
- Embassy has been completely rewritten in TypeScript. While the API is similar to previous versions, there was no attempt to maintain backward-compatibility for this first major release. Notable changes follow. | ||
- Permissions have been changed to scopes, which can be granted and revoked as plain strings | ||
- Claims can now be read and written directly on a `token.claims` object without accessor functions | ||
- HMAC signing and verification is now supported | ||
## v1.0.0 | ||
This version intentionally skipped due to 2015 publishing error | ||
## [v0.3.2] | ||
### Fixed | ||
- Updated all dependencies to resolve security warnings | ||
## [v0.3.1] | ||
### Fixed | ||
- Updated all dependencies to resolve security warnings | ||
## [v0.3.0] | ||
### Added | ||
- refreshPermissions, getPrivKey, and getPubKey are now called with the Token object itself as the final argument. | ||
## [v0.2.2] | ||
### Fixed | ||
- options.getPrivKey is now called when a public key for the same KID already exists | ||
## [v0.2.1] | ||
### Fixed | ||
- sign() can now be called when an `exp` claim is already set. Embassy will delete the existing claim before calculating the new one and signing. | ||
## [v0.2.0] | ||
### Added | ||
- The `getPrivKey` option to dynamically load a private signing key. | ||
### Changed | ||
- The sign() function now only throws if the subject is not found. Key resolution errors are rejections with a KeyNotFoundError. | ||
## v0.1.0 | ||
### Added | ||
- Initial release | ||
[Development]: https://github.com/TomFrost/Embassy/compare/v0.3.0...HEAD | ||
[development]: https://github.com/TomFrost/Embassy/compare/v2.0.0...HEAD | ||
[v2.0.0]: https://github.com/TomFrost/Embassy/compare/v0.3.2...v2.0.0 | ||
[v0.3.2]: https://github.com/TomFrost/Embassy/compare/v0.3.1...v0.3.2 | ||
[v0.3.1]: https://github.com/TomFrost/Embassy/compare/v0.3.0...v0.3.1 | ||
[v0.3.0]: https://github.com/TomFrost/Embassy/compare/v0.2.2...v0.3.0 | ||
@@ -36,0 +73,0 @@ [v0.2.2]: https://github.com/TomFrost/Embassy/compare/v0.2.1...v0.2.2 |
@@ -1,2 +0,2 @@ | ||
Copyright (c) 2017-2018 Tom Shawver | ||
Copyright (c) 2017-2021 Tom Shawver | ||
@@ -3,0 +3,0 @@ Permission to use, copy, modify, and/or distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. |
{ | ||
"name": "embassy", | ||
"version": "0.3.1", | ||
"version": "2.0.0", | ||
"description": "JWT token generator/verifier with built-in permission flags", | ||
"main": "src/Embassy.js", | ||
"main": "dist/index.js", | ||
"engines": { | ||
"node": ">=4.2" | ||
"node": ">=12" | ||
}, | ||
"scripts": { | ||
"prepublish": "npm run build", | ||
"build": "tsc", | ||
"docs": "typedoc --excludePrivate src/index.ts", | ||
"format": "eslint '*/**/*.{js,ts}' --fix; prettier -l --write **/*.{md,json}", | ||
"lint": "eslint '*/**/*.{js,ts}'; prettier -l **/*.{md,json}", | ||
"clean": "rm -rf node_modules coverage", | ||
"test": "npm run lint && npm run test-cov && npm run check-cov", | ||
"mocha": "mocha", | ||
"test-cov": "istanbul cover _mocha", | ||
"check-cov": "istanbul check-coverage --statements 95 --branches 95 --functions 100 --lines 100", | ||
"lint": "standard" | ||
"test": "npm run lint && jest --coverage", | ||
"test:watch": "jest --watch" | ||
}, | ||
@@ -21,2 +23,5 @@ "repository": { | ||
}, | ||
"files": [ | ||
"./dist/*" | ||
], | ||
"keywords": [ | ||
@@ -40,13 +45,67 @@ "JWT", | ||
"dependencies": { | ||
"base64-js": "^1.3.0", | ||
"jsonwebtoken": "^8.3.0" | ||
"base64-js": "^1.5.1", | ||
"jsonwebtoken": "^8.5.1" | ||
}, | ||
"devDependencies": { | ||
"chai": "^4.1.2", | ||
"chai-as-promised": "^7.1.1", | ||
"@types/jest": "^26.0.20", | ||
"@types/jsonwebtoken": "^8.5.0", | ||
"@types/node": "^14.14.20", | ||
"@typescript-eslint/eslint-plugin": "^4.12.0", | ||
"@typescript-eslint/parser": "^4.13.0", | ||
"delay": "^3.1.0", | ||
"istanbul": "^0.4.5", | ||
"mocha": "^5.2.0", | ||
"standard": "^11.0.1" | ||
"eslint": "^7.17.0", | ||
"eslint-config-prettier": "^7.1.0", | ||
"eslint-plugin-prettier": "^3.3.1", | ||
"eslint-plugin-tsdoc": "^0.2.10", | ||
"husky": "^4.3.7", | ||
"jest": "^26.6.3", | ||
"lint-staged": "^10.5.3", | ||
"prettier": "^2.2.1", | ||
"ts-jest": "^26.4.4", | ||
"typedoc": "^0.20.14", | ||
"typedoc-plugin-markdown": "^3.4.0", | ||
"typescript": "^4.1.3" | ||
}, | ||
"eslintIgnore": [ | ||
"node_modules", | ||
"coverage", | ||
".vscode", | ||
"dist" | ||
], | ||
"husky": { | ||
"hooks": { | ||
"pre-commit": "lint-staged" | ||
} | ||
}, | ||
"lint-staged": { | ||
"*.{js,ts,tsx}": [ | ||
"eslint --fix" | ||
], | ||
"*.{md,json}": [ | ||
"prettier -l --write" | ||
] | ||
}, | ||
"jest": { | ||
"preset": "ts-jest", | ||
"testMatch": [ | ||
"**/__tests__/**/*.spec.{js,ts}?(x)" | ||
], | ||
"testEnvironment": "node", | ||
"verbose": true, | ||
"testPathIgnorePatterns": [], | ||
"coverageThreshold": { | ||
"global": { | ||
"branches": 100, | ||
"functions": 100, | ||
"lines": 100, | ||
"statements": 100 | ||
} | ||
}, | ||
"collectCoverageFrom": [ | ||
"src/**/*.ts" | ||
], | ||
"coveragePathIgnorePatterns": [ | ||
"/node_modules/" | ||
] | ||
} | ||
} |
279
README.md
@@ -1,177 +0,206 @@ | ||
# Embassy [![Build Status](https://travis-ci.org/TomFrost/Embassy.svg?branch=master)](https://travis-ci.org/TomFrost/Embassy) | ||
Generate and verify Javascript Web Tokens, containing dynamic microservice permissions and settings. | ||
<!-- LOGO AND SHIELDS --> | ||
<br /> | ||
<p align="center"> | ||
<a href="https://github.com/TomFrost/Embassy"> | ||
<img src="http://i.tomfro.st/uTz1mF.svg" alt="Embassy Logo" width="400" height="250"> | ||
</a> | ||
## Use cases | ||
<h3 align="center">Simple JSON Web Tokens (JWT) with embedded scopes for services</h3> | ||
While Embassy can be used to provide tokens for an OAuth or OpenID Connect flow, it was built to solve the problem of authentication and permission control in a durable microservices environment. Suggested implementation: | ||
<p align="center"> | ||
Create access, ID, and refresh tokens • Embed hundreds of scopes in a small token string • Zero-IO token verification in your services • Auto-download and cache missing keys • As easy as <code>embassy.parseToken(token).verify()</code> | ||
<br /> | ||
<a href="https://github.com/TomFrost/Embassy/blob/master/docs/modules.md"><strong>Explore the docs »</strong></a> | ||
<br /> | ||
<br /> | ||
<a href="https://www.npmjs.com/package/embassy"><img alt="npm" src="https://img.shields.io/npm/v/embassy?style=flat-square"></a> | ||
<a href="https://travis-ci.org/TomFrost/Embassy"><img src="https://img.shields.io/travis/tomfrost/embassy/master?style=flat-square" alt="build status" height="20"></a> | ||
<a href="https://www.npmjs.com/package/embassy"><img alt="download count" src="https://img.shields.io/npm/dm/embassy?style=flat-square"></a> | ||
<a href="https://codeclimate.com/github/TomFrost/Embassy"><img alt="Code Climate coverage" src="https://img.shields.io/codeclimate/coverage/TomFrost/Embassy?style=flat-square"></a> | ||
<a href="https://codeclimate.com/github/TomFrost/Embassy"><img alt="Code Climate maintainability" src="https://img.shields.io/codeclimate/maintainability/TomFrost/Embassy?style=flat-square"></a> | ||
<a href="https://github.com/TomFrost/Embassy/blob/master/LICENSE.txt"><img alt="License" src="https://img.shields.io/github/license/tomfrost/embassy?style=flat-square"></a> | ||
</p> | ||
</p> | ||
- In each of your microservices, define an array of boolean permissions such as canEditUsers in your users service, or canUploadFiles, canDeleteFiles in your hypothetical file management service. | ||
- Implement a user management service that assigns permissions to users in any manner | ||
- Allow users to authenticate with that service via your favorite secure method | ||
- Provide the user with an Embassy token, setting the user's permissions and options for each individual microservice. Options are non-boolean values that get stored in the token. | ||
- Implement Embassy in all your microservices. On each request to a privileged endpoint, validate the token. If it's valid, check it for the permissions or options that endpoint requires. Use identifying information in the token's claims for your log messages. | ||
- Implement Embassy in any frontends that need to know what permissions an authenticated user has in order to streamline their experience. Embassy was designed with frontend builds in mind! | ||
- Bask in the knowledge that your user service can go completely dead without interrupting the service of your logged-in users, or compromising your security. | ||
## Install | ||
## Installation | ||
``` | ||
```shell | ||
npm install --save embassy | ||
``` | ||
## Example | ||
or | ||
```shell | ||
yarn add embassy | ||
``` | ||
## Initialize | ||
```javascript | ||
const Embassy = require('embassy') | ||
import { Embassy } from 'embassy' | ||
const embassy = new Embassy({ | ||
domainPermissions: { | ||
userService: { | ||
writeUsers: 0, | ||
readUsers: 1, | ||
deleteUsers: 2 | ||
domainScopes: { | ||
users: { | ||
readEmail: 0, | ||
readProfile: 1, | ||
writeProfile: 2 | ||
}, | ||
chatService: { | ||
sendMessages: 0, | ||
deleteOwnMessages: 1, | ||
deleteOthersMessages: 2 | ||
store: { | ||
readPurchaseHistory: 0, | ||
addToCart: 1, | ||
submitOrder: 2, | ||
cancelOrder: 3 | ||
} | ||
}, | ||
keys: { | ||
'2016-05-20': { | ||
pub: 'pem-encoded-pubkey', | ||
priv: 'pem-encoded-privkey', | ||
algo: 'ES256' | ||
myKey: { | ||
privateKey: 'shared-secret', | ||
algorithm: 'HS512' | ||
} | ||
}, | ||
issuer: 'userservice.myapp.com', | ||
expiresInSecs: 900 | ||
issuer: 'api.myapp.com/auth', | ||
audience: 'api.myapp.com' | ||
}) | ||
``` | ||
// Create a user token after successful login | ||
const token = embassy.createToken({ sub: 'someuser@someplace.com' }) | ||
token.setOption('chatService', 'adminRank', 2) | ||
token.grantPermission('userService', 'readUsers').then(() => { | ||
return token.sign('2016-05-20') | ||
}).then(token => { | ||
// token is now a JWT/OpenID Connect string, containing the user's | ||
// email, options, and binary-encoded permissions. | ||
}).catch(e => { | ||
// We'd end up here if the permission we set is unknown, or the key | ||
// ID we used didn't have a 'priv' component. | ||
Embassy can be configured to find public and private keys when an unknown key ID is found, and refresh the scopes when an unknown scope is encountered. Always run smoothly without forced restarts or configuration updates. **[See the options »](https://github.com/TomFrost/Embassy/blob/master/docs/interfaces/embassyoptions.md)** | ||
## Integrate | ||
### Create and sign access tokens | ||
```javascript | ||
const token = embassy.createToken({ | ||
sub: 'userid', | ||
email: 'user@email.com' | ||
}) | ||
const tokenString = await token.sign('myKey') | ||
// Token expires in an hour by default | ||
``` | ||
// Validate a user's token and read its data | ||
const token = embassy.parseToken(tokenString) | ||
token.verify().then(() => { | ||
console.log(token.getOption('chatService', 'adminRank')) // 2 | ||
return token.hasPermission('userService', 'readUsers') | ||
}).then(hasPerm => { | ||
console.log(hasPerm) // true | ||
}).catch(e => { | ||
// We'd end up here if the issuer string doesn't match what was set in | ||
// Embassy, if the token is older than 900 seconds, if the signature | ||
// was forged, or if the permission we're checking for is unknown. | ||
**[embassy.createToken docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/embassy.md#createtoken) • [token.sign docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#sign)** | ||
### Verify access tokens | ||
```javascript | ||
const token = embassy.parseToken(bearerToken) | ||
const claims = await token.verify() // Throws if invalid, expired, etc | ||
console.log(`New request from ${claims.email}`) | ||
``` | ||
**[embassy.parseToken docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/embassy.md#parsetoken) • [token.verify docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#verify)** | ||
### Create and sign refresh tokens | ||
```javascript | ||
const token = embassy.createToken({ | ||
sub: 'userid', | ||
email: 'user@email.com' | ||
}) | ||
const tokenString = await token.sign('myKey', { | ||
audience: 'api.myapp.com/auth', // Prevent this from being used as an access token | ||
expiresInSecs: 3600 * 24 * 365 // Make it last for a year | ||
}) | ||
``` | ||
## API | ||
### Embassy | ||
#### new Embassy([options]) | ||
Embassy must be instantiated with any necessary options before it can be used. Available options are: | ||
- **domainPermissions:** A map of domains (service names) to permission maps. Permission maps store permission names as keys, with the values being unique integers; preferably incrementing up from zero. The integer defines which bit in a binary blob represents this permission, so it's imperative that the number not be changed or reused while there are still active tokens. | ||
- **keys:** A mapping of key IDs to key objects used for signing and verifying tokens. Key objects require `priv` (a private key or HMAC secret) and `algo` (the [crypto algorithm](https://github.com/auth0/node-jsonwebtoken#algorithms-supported) to use) for token signing. Additionally, provide `pub` (a public key or HMAC secret) for key verification. | ||
- **refreshPermissions _function({Token} token)_:** An optional function that, when called, will return either a replacement domainPermissions map, or a Promise that resolves to one. Only called when Embassy encounters a permission that doesn't exist in its current domainPermissions map. Using this, permissions can be stored in a database and created in real time in a live system. | ||
- **refreshPermsAfterMs:** _Default 500._ The number of milliseconds after the domainPermissions have been refreshed that it is eligible to be refreshed again. | ||
- **getPrivKey _function({string} kid, {Token} token)_:** In the event that `sign()` is called with a key ID that Embassy doesn't currently know about, this function (if specified) will be called with the key ID string. It must return either an object with a "priv" and "algo" property (referring to the PEM-formatted private key or HMAC secret, and the key algorithm, respectively), or a promise that resolves to the same. Using this, private keys can be stored in an encrypted system (like [Cryptex](https://github.com/TomFrost/Cryptex)) and only decrypted when requested. | ||
- **getPubKey _function({string} kid, {Token} token)_:** An optional function that, when called with a key ID, will return either the public key/HMAC secret for that ID, or a Promise that resolves to one. Using this, public keys can be stored in a highly durable, publicly accessible URL, and private keys can be rotated in the token-generating service without having to inform the other microservices of new keys. | ||
- **expiresInSecs:** The number of seconds after a token has been created at which it will expire. This can be overridden when the token is signed. | ||
- **audience:** An arbitrary string defining who the intended audience for this token is. If specified, any tokens that do not have this audience string will fail validation. | ||
- **issuer:** An arbitrary string defining who the issuer of this token is. If specified, any tokens that do not have this issuer string will fail validation. | ||
**[embassy.createToken docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/embassy.md#createtoken) • [token.sign docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#sign)** | ||
#### embassy.createToken([claims]) | ||
Creates a new Token object, optionally initialized with the given map of claims. Claims are small bits of information about the token or user, such as "sub" ("subject", normally the user ID or email), or nonstandard items like avatar URLs, real name, etc. | ||
### Verify refresh tokens | ||
#### embassy.parseToken(token) | ||
Parses a signed token string and returns a new Token object initialized with its contents. The Token object will be immediately ready for a `.verify()` call to determine its authenticity. | ||
```javascript | ||
const token = embassy.parseToken(bearerToken) | ||
const claims = await token.verify({ | ||
audience: 'api.myapp.com/auth' | ||
}) // Throws if invalid, expired, wrong audience, etc | ||
console.log(`Checking if ${claims.email} is still in good status...`) | ||
``` | ||
#### Embassy properties | ||
Embassy exposes a set of convenience properties: | ||
- **Embassy.Token:** The Token class | ||
- **Embassy.KeyNotFoundError:** Thrown when `Token.sign` or `Token.verify` references an unknown Key ID. | ||
- **Embassy.PermissionNotFoundError:** Thrown when a permission is referenced that does not exist in `domainPermissions`, and `refreshPermissions` was either not specified or did not produce a permissions map containing the permission. | ||
- **Embassy.TokenParseError:** Thrown when `embassy.parseToken` is called with an invalid token string. | ||
**[embassy.parseToken docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/embassy.md#parsetoken) • [token.verify docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#verify)** | ||
### Token | ||
#### new Token([options]) | ||
The Token class is rarely instantiated directly; instead, use `embassy.createToken` and `embassy.parseToken` so that keys and domainPermissions are persisted throughout all an Embassy's tokens. However, if necessary to use this, Token accepts all options that Embassy does, plus these: | ||
- **claims:** A set of claims with which to initialize this Token | ||
- **token:** A token string with which to initialize this Token | ||
### Grant scopes to tokens | ||
#### token.getClaim(claim) | ||
Returns the value for a given claim, or undefined if it doesn't exist. | ||
```javascript | ||
// One at a time | ||
await token.grantScope('user|readEmail') | ||
// Many at a time | ||
await token.grantScopes(['user|readProfile', 'user|writeProfile']) | ||
// You can separate the domain | ||
await token.grantScope('store', 'readPurchaseHistory') | ||
// Or pass an entire domain-to-scopes map | ||
await token.grantScopes({ | ||
user: ['readProfile', 'writeProfile'], | ||
store: ['addToCart', 'submitOrder'] | ||
}) | ||
// Signing the token will encode these scopes in a binary format, so a | ||
// single token can hold hundreds of scopes and still stay small! | ||
``` | ||
#### token.getOption(domain, optName) | ||
Gets the value for a given option name within a certain domain (microservice name), or undefined if it doesn't exist. | ||
**[token.grantScope docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#grantscope) • [token.grantScopes docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#grantscopes)** | ||
#### token.grantPermission(domain, permission) | ||
Grants the given permission inside of the given domain. Returns a Promise that resolves on success, or rejects with `Embassy.PermissionNotFoundError` if the given permission cannot be found. | ||
_Tip: Change "grant" to "revoke" and it does exactly what you'd expect!_ | ||
#### token.grantPermissions(domain, permissions) | ||
Grants an array of permissions inside the given domain. Returns a Promise that resolves on success, or rejects with `Embassy.PermissionNotFoundError` if any given permission cannot be found. | ||
### Query tokens for scopes | ||
#### token.hasPermission(domain, permission) | ||
Returns a Promise that resolves with true if the token has the given permission for the given domain, or false otherwise. Rejects with `Embassy.PermissionNotFoundError` if the given permission cannot be found. | ||
```javascript | ||
// These each resolve with `true` or `false`: | ||
// One at a time | ||
await token.hasScope('user|readEmail') | ||
// Many at a time | ||
await token.hasScopes(['user|readProfile', 'user|writeProfile']) | ||
// You can separate the domain | ||
await token.hasScope('store', 'readPurchaseHistory') | ||
// Or pass an entire domain-to-scopes map | ||
await token.hasScopes({ | ||
user: ['readProfile', 'writeProfile'], | ||
store: ['addToCart', 'submitOrder'] | ||
}) | ||
``` | ||
#### token.hasPermissions(domain, permissions) | ||
Returns a Promise that resolves with true if the token has each given permission for the given domain, or false otherwise. Rejects with `Embassy.PermissionNotFoundError` if any given permission cannot be found. | ||
**[token.hasScope docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#hasscope) • [token.hasScopes docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md#hasscopes)** | ||
#### token.revokePermission(domain, permission) | ||
Revokes the given permission from a token. Returns a Promise that resolves on success, or rejects with `Embassy.PermissionNotFoundError` if the given permission cannot be found. | ||
### Read and write claims | ||
#### token.revokePermissions(domain, permissions) | ||
Revokes an array of permissions from the given domain in the token. Returns a Promise that resolves on success, or rejects with `Embassy.PermissionNotFoundError` if any given permission cannot be found. | ||
```javascript | ||
console.log(`Request initiated by userId ${token.claims.sub}`) | ||
token.claims.nonce = myNonce | ||
// Token can be signed with no further action | ||
``` | ||
#### token.setClaim(claim, val) | ||
Sets a claim within the body of the JSON Web Token (JWT). Note that JWTs have a standard set of claims that should not be overridden! | ||
**[Token docs](https://github.com/TomFrost/Embassy/blob/master/docs/classes/token.md)** | ||
#### token.setOption(domain, key, val) | ||
Sets a domain-specific option on this token. `val` can be anything that will serialize to JSON, but it's recommended to keep token-stored information as small as possible to reduce bandwidth usage and avoid HTTP header size constraints (if it's passed in an HTTP header). | ||
## Generate keys | ||
#### token.sign(keyID, [options]) | ||
Serializes the data set within this Token instance and signs it with a private key. The result is a Promise that resolves with a JWT token string. `keyID` should be the ID of a key set in the constructor for either Token or Embassy. The available options override any constructor-specified options, if provided: | ||
- **expiresInSecs:** _Default 900._ The number of seconds after a token has been created at which it will expire. | ||
- **audience:** An arbitrary string representing the intended audience for this token. | ||
- **subject:** The ID of the intended user of this token. Optional only if a 'sub' claim has already been set. | ||
- **issuer:** A string representing the issuer of this token. | ||
- **noTimestamp:** _Default false._ Set to `true` to avoid generating an 'iat' (issued at) claim. | ||
- **header:** A mapping of keys and values to be set in the token header, additionally to what the signing process creates by default. | ||
### HMAC | ||
#### token.verify([options]) | ||
Verifies a token's validity by checking its signature, expiration time, and contents. This method returns a Promise that resolves on success, or rejects if the token is invalid. Note that this can only be called if the Token was initialized with a token string, or if `token.sign` has already been called. The available options override any constructor-specified options, if provided: | ||
- **algorithms:** List of strings with the names of the allowed algorithms. For instance, `["HS256", "HS384"]`. Allows all algorithms if omitted. See the [list of supported algorithms](https://github.com/auth0/node-jsonwebtoken#algorithms-supported). | ||
- **audience:** Ensures the token's audience string matches this value. | ||
- **issuer:** Ensures the token's issuer string matches this value. | ||
- **ignoreExpiration:** _Default false._ If true, expired tokens will pass verification checks. | ||
- **maxAgeSecs:** If specified, will fail verification if the token is older than the specified number of seconds. | ||
- **key:** The PEM-encoded public key to be used to verify the token's signature. If specified, the key ID will be ignored. May be provided as a String or Buffer. | ||
HMAC is a symmetric signing algorithm, which means the same key is used to sign and verify the token. Embassy supports the following HMAC algorithms: `HS256`, `HS384`, `HS512`. Trying to choose? Higher numbers mean more security, but longer tokens and steeper CPU usage. Use `HS256` for access tokens since they're short-lived, and consider higher for refresh tokens. | ||
## Generating keys | ||
The "shared secret" for HMAC can be any string -- but you should choose a long one! Be sure to keep it private. Never commit it to git, never send it over Slack, never give your CI/CD access to it. | ||
For asymmetric token signing, the openssl tool can handle creating all the different [key types](https://github.com/auth0/node-jsonwebtoken#algorithms-supported) Embassy supports. 256-bit Elliptic Curve keys (ES256) are recommended due to their low overhead and high security. The following commands will generate a PEM-formatted key pair (replace KEY_ID appropriately): | ||
### Asymmetric keys | ||
``` | ||
Embassy supports the following RSA and Elliptic Curve signing algorithms: `RS256`, `RS384`, `RS512`, `PS256`, `PS384`, `PS512`, `ES256`, `ES384`, `ES512`. The algorithms sign tokens with a private key that must be kept secret, but verify their authenticity with a public key that can be shared openly. | ||
For most use cases, 256-bit Elliptic Curve keys (ES256) are recommended for access tokens due to their low overhead and high security. The following commands will generate a PEM-formatted key pair appropriate for use with Embassy (replace `MyKeyPair` appropriately): | ||
```shell | ||
KEY_ID="MyKeyPair" | ||
# Private key | ||
openssl ecparam -genkey -name secp256k1 -noout -out KEY_ID.priv.pem | ||
openssl ecparam -genkey -name secp256k1 -noout -out "${KEY_ID}.priv.pem" | ||
# Public key | ||
openssl ec -in KEY_ID.priv.pem -pubout -out KEY_ID.pub.pem | ||
openssl ec -in "${KEY_ID}.priv.pem" -pubout -out "${KEY_ID}.pub.pem" | ||
``` | ||
## Versions | ||
Embassy supports Node 4 LTE and higher out of the box. For 0.12 or frontend support, consider compiling with Babel. | ||
Embassy is committed to supporting all active LTE versions of Node.js, and strives to stay updated for new non-LTE releases. | ||
## License | ||
Embassy is Copyright (c) 2017-2018 Tom Shawver, released under the ultra-permissive ISC license. See LICENSE.txt for details. | ||
Embassy is Copyright (c) 2017-2021 Tom Shawver, released under the ultra-permissive ISC license. See LICENSE.txt for details. | ||
## Credits | ||
Embassy was originally created at [TechnologyAdvice](http://www.technologyadvice.com) in Nashville, TN. | ||
Created by [Tom Shawver](https://github.com/TomFrost) in 2016 as convenience layer on top of [Auth0](https://github.com/auth0)'s fantastic [jsonwebtoken](https://github.com/auth0/node-jsonwebtoken) Node.js library. Embassy was rewritten in Typescript in 2021. | ||
Originally created for [TechnologyAdvice](http://www.technologyadvice.com) in Nashville, TN. |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
Trivial Package
Supply chain riskPackages less than 10 lines of code are easily copied into your own project and may not warrant the additional supply chain risk of an external dependency.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Debug access
Supply chain riskUses debug, reflection and dynamic code execution features.
Found 1 instance in 1 package
Environment variable access
Supply chain riskPackage accesses environment variables, which may be a sign of credential stuffing or data theft.
Found 1 instance in 1 package
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
Found 1 instance in 1 package
No v1
QualityPackage is not semver >=1. This means it is not stable and does not support ^ ranges.
Found 1 instance in 1 package
0
207
0
14032
18
5
6
2
Updatedbase64-js@^1.5.1
Updatedjsonwebtoken@^8.5.1