simple-hmackey-auth
v1.0.3
Published
A simple, convenient, and safe interface for using the HMAC Key pattern of authentication and authorization
Downloads
12
Maintainers
Readme
simple-hmackey-auth
A simple, convenient, and safe interface for using the HMAC Key pattern of authentication and authorization
Simple:
- exposes simple, declarative functions for each supported use case
- throws self explanatory errors when something goes wrong
- leverages open source standards to securely simplify the auth process
Safe:
- enforces best practices of HMAC Key authentication
- eliminates accidentally using HMAC Keys unsafely, by constraining exposed methods to secure and declarative use cases
In otherwords, it's built to provide a pit of success
Background
HMAC Key authentication is a great way to implement authentication and authorization for SDK applications
HMAC Key authentication provides us the following guarantees on authenticated requests
- request integrity: the data sent by the client to the server has not tampered with
- request origination: the request comes to the server from a trusted client
- request originality: the request was not captured by an intruder and being replayed
Well known examples of this pattern in production:
References:
- HMAC: Keyed-Hashing for Message Authentication
- What is HMAC Authentication and why is it useful?
- API Security: HMAC+Key vs JWT
- HMAC
- How and when do I use HMAC?
Note: if you're looking to implement authentication and authorization for user facing applications, JSON Web Tokens (JWTs) may be a better fit due to their stateless and decentralized nature
Install
npm install --save simple-hmackey-auth
Example
Issue a client key pair
Creates a client key pair that can be used by the client to send authable requests to the issuer.
import { issueClientKeyPair } from 'simple-hmackey-auth';
const {
/**
* the client-public-key identifies the keypair
* - should be sent to client
* - should be saved by issuer
*/
clientPublicKey,
/**
* the client-private-key is the secret used by the client to sign requests
* - should be sent to client
* - should be irrecoverably forgotten by issuer
*/
clientPrivateKey,
/**
* the client-private-key hash is a hash of the private-key that the issuer will use to auth requests
* - is not needed by the client
* - should be saved by the issuer, indexed by the client-public-key
*/
clientPrivateKeyHash
} = await issueClientKeyPair();
Create a secure request signature
Creates a request signature that can be securely authed by the issuer. Useful any time you need to make an authable request (e.g., client side)
Make sure to include any data that affects the outcome of the request in the request input to this function. The signature only ensures the integrity of the request data you told it about.
import { createSecureRequestSignature } from 'simple-hmackey-auth';
const signature = await createSecureRequestSignature({
clientPublicKey,
clientPrivateKey,
request: {
host: 'https://your.domain.com',
endpoint: '/your/endpoint/...',
headers,
payload,
},
});
Assert request signature is authentic
Checks whether the signature was authentic via request origination and request integrity. Useful any time you need to make sure the request was authentic (e.g., server side)
Make sure to include any data that affects the outcome of the request in the request input to this function. The signature only ensures the integrity of the request data you told it about.
import { assertRequestSignatureAuthenticity } from 'simple-hmackey-auth';
await assertRequestSignatureAuthenticity({
signature,
getClientPrivateKeyHash: async ({ clientPublicKey }) => {/** a method you define to lookup the private key hash from your database using the public key */},
setOriginalUsageOfNonce: async ({ nonce }) => {/** a method you define to record the first usage of the nonce and throw an error if it has already been used to stop replay attacks */}
millisUntilExpiration: 5 * 60 * 1000, // the number of milliseconds allowed to elapse from the time the request was sent before we reject it to stop replay attacks
request: {
host: 'https://your.domain.com',
endpoint: '/your/endpoint/...,
headers,
body,
},
});
Get signature from headers
This grabs the signature from the standard authorization header header for you. Useful whenever you need to grab a signature from an HTTP request.
import { getRequestSignatureFromHeaders } from 'simple-hmackey-auth';
const signature = getRequestSignatureFromHeaders({ headers });
Request signatures are typically passed to apis through the Authorization
header, following the OAuth 2.0 Authorization Standard pattern, so this exposes an easy way to grab the token from there.
Docs
fn:assertRequestSignatureAuthenticity({ signature: string, getClientPrivateKeyHash: ({ clientPublicKey }) => Promise<string>, request: SignableRequest })
Use this function when you want to authenticate a request that was made to you.
We check the authenticity of the request in the following ways:
- request integrity
- by verifying the signature against the request data, we prove that the data was not tampered with
- request origination
- by verifying the signature against the shared secret's hash, we prove that the owner of the client-private-key made the request
- by verifying the client-public-key identifies a client-secret-key-hash in your database, we prove that you issued the key used to sign the request and that you were the intended audience for the request
- request originality
- by verifying the nonce has not already been seen for a request, we prove that this is the original request and not a replay attack
- by verifying the millis-since-epoch of the request is recent enough, we add another mechanism of preventing replay attacks
This method will throw errors in the following cases
- an
UnauthableRequestSignatureError
is thrown if the request signature does not have the data required to check for authenticity - an
UnauthenticRequestSignatureError
is thrown if we have successfully checked the request signature and found that the request is unauthentic
Example:
import { assertRequestSignatureAuthenticity } from 'simple-hmackey-auth';a
const claims = assertRequestSignatureAuthenticity({
/**
* the request signature you're checking the request for authenticity against
*/
signature: string;
/**
* a method you define which can lookup the client-private-key-hash using the client-public-key
*/
getClientPrivateKeyHash: ({}: { clientPublicKey: string }) => Promise<string>;
/**
* a method you define which records that a nonce has been used and throws an error if this is not the first time
*
* note
* - if you choose to not define this method, your api will be vulnerable to replay attacks up to the millisUntilExpiration duration
* - if your function does not correctly assert that the nonce has not been used before, your api will be vulnerable to replay attacks up to the millisUntilExpiration duration
*/
setOriginalUsageOfNonce: ({}: { nonce: string }) => Promise<void>;
/**
* the number of milliseconds that could have passed since the timestamp on the request until we decide the request is expired
*
* note
* - the longer this duration is, the more opportunity an attacker has to replay a request
* - the default duration is 5 minutes
*/
millisUntilExpiration: number;
/**
* the request we will be checking against the signature to check it was not tampered with
*/
request: SimpleSignableRequest;
});