Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

pkce-challenge

Package Overview
Dependencies
Maintainers
1
Versions
12
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

pkce-challenge - npm Package Compare versions

Comparing version 2.0.0 to 2.1.0

18

CHANGELOG.md
# Changelog
## [2.1.0] - 2019-12-20
### Added
- `verifyChallenge` exported from index
### Changed
- code/comment formatting
- refactored `random` function
## [2.0.0] - 2019-10-18
### Added
- CHANGELOG

@@ -11,2 +24,3 @@ - typescript definition

### Removed
- `generateVerifier` export from index

@@ -18,6 +32,6 @@ - `generateChallenge` export from index

### Contributors
- [lordnox]
[lordnox]: https://github.com/lordnox
[2.0.0]: https://github.com/crouchcd/pkce-challenge/releases/tag/2.0.0
[2.0.0]: https://github.com/crouchcd/pkce-challenge/releases/tag/2.0.0

@@ -13,2 +13,6 @@ // Type definitions for pkce-challenge 2.0

};
export function verifyChallenge(
code_verifier: string,
expectedChallenge: string
): boolean;
}

132

index.js

@@ -1,79 +0,87 @@

const { createHash, randomBytes } = require('crypto');
const { createHash, randomBytes } = require("crypto");
/**
* generate cryptographically secure random string
* @param {number} size how long the result should be
* @param {string} mask string to select chars from
/** Generate cryptographically secure random string
* @param {number} size The desired length of the string
* @param {string} mask A mask of characters (no more than 256) to choose from
* @returns {string} The random string
*/
function random(size, mask) {
let result = '';
const byteLength = Math.pow(2, 8);
if (mask.length > byteLength) {
const howManySkipped = mask.length - byteLength;
console.warn(
`Mask is longer than 2^8. Last ${howManySkipped} items will be skipped.`
);
}
const subscrValues = randomBytes(size);
const maxMaskLength = Math.min(mask.length, byteLength);
// subscript values range from 0 to 0xFF = 256
// mask subscripts range from 0 to (maxMaskLength-1) = maxMaskLength
const scalingValue = byteLength / maxMaskLength;
let result = "";
const randomIndices = randomBytes(size);
const byteLength = Math.pow(2, 8); // 256
const maskLength = Math.min(mask.length, byteLength);
// the scaling factor breaks down the possible values of bytes (0x00-0xFF)
// into the range of mask indices
const scalingFactor = byteLength / maskLength;
for (let i = 0; i < size; i++) {
const randomIndex = Math.floor(randomIndices[i] / scalingFactor);
result += mask[randomIndex];
}
return result;
}
for (let i = 0; i < size; i++) {
const randIdx = Math.floor(subscrValues[i] / scalingValue);
result += mask[randIdx];
}
return result;
}
/**
* @param {string} base64 base64 encoded string to url encode
* @returns {string} base64 url encoded string
/** Base64 url encode a string
* @param {string} base64 The base64 string to url encode
* @returns {string} The base64 url encoded string
*/
function base64UrlEncode(base64) {
return base64
.replace(/=/g, '')
.replace(/\+/g, '-')
.replace(/\//g, '_');
return base64
.replace(/=/g, "")
.replace(/\+/g, "-")
.replace(/\//g, "_");
}
/**
* generate a pkce challenge verifier
* @param {number} length length of the verifier
* @returns {string} random string `length` characters long
/** Generate a PKCE challenge verifier
* @param {number} length Length of the verifier
* @returns {string} A random verifier `length` characters long
*/
function generateVerifier(length) {
const mask = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~';
return random(length, mask);
const mask =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._~";
return random(length, mask);
}
/**
* generate a pkce challenge code from a verifier
* @param {string} code_verifier base64 url encoded string
/** Generate a PKCE challenge code from a verifier
* @param {string} code_verifier
* @returns {string} The base64 url encoded code challenge
*/
function generateChallenge(code_verifier) {
const hash = createHash('sha256')
.update(code_verifier)
.digest('base64');
return base64UrlEncode(hash);
const hash = createHash("sha256")
.update(code_verifier)
.digest("base64");
return base64UrlEncode(hash);
}
/**
* generate a pkce challenge pair
* @param {number} [length=43] specify the length of
* the verifier between 43 and 123 characters long
* @returns {{code_challenge:string,
* code_verifier:string}} pkce challenge pair
/** Generate a PKCE challenge pair
* @param {number} [length=43] Length of the verifer (between 43-128)
* @returns {{code_challenge:string,code_verifier:string}} PKCE challenge pair
*/
function pkceChallenge(length = 43) {
if (length < 43 || length > 128) {
throw `Expected a length between 43 and 128. Received ${length}.`;
}
function pkceChallenge(length) {
if (!length) length = 43;
const verifier = generateVerifier(length);
const challenge = generateChallenge(verifier);
if (length < 43 || length > 128) {
throw `Expected a length between 43 and 128. Received ${length}.`;
}
return {
code_challenge: challenge,
code_verifier: verifier
};
const verifier = generateVerifier(length);
const challenge = generateChallenge(verifier);
return {
code_challenge: challenge,
code_verifier: verifier
};
}
module.exports = pkceChallenge;
module.exports = pkceChallenge;
/** Verify that a code_verifier produces the expected code challenge
* @param {string} code_verifier
* @param {string} expectedChallenge The code challenge to verify
* @returns {boolean} True if challenges are equal. False otherwise.
*/
function verifyChallenge(code_verifier, expectedChallenge) {
const actualChallenge = generateChallenge(code_verifier);
return actualChallenge === expectedChallenge;
}
module.exports.verifyChallenge = verifyChallenge;
{
"name": "pkce-challenge",
"version": "2.0.0",
"description": "Generate a Proof Key for Code Exchange (PKCE) challenge pair",
"version": "2.1.0",
"description": "Generate or verify a Proof Key for Code Exchange (PKCE) challenge pair",
"main": "index.js",

@@ -6,0 +6,0 @@ "types": "./index.d.ts",

# pkce-challenge
Generate a Proof Key for Code Exchange (PKCE) challenge pair.
Generate or verify a Proof Key for Code Exchange (PKCE) challenge pair.
Read more about [PKCE](https://www.oauth.com/oauth2-servers/pkce/authorization-request/).
## Installation
```

@@ -11,4 +13,6 @@ npm install pkce-challenge

## Usage
## Usage
Default length for the verifier is 43
```

@@ -18,7 +22,9 @@ const pkceChallenge = require('pkce-challenge');

```
gives something like:
```
{
{
code_verifier: 'u1ta-MQ0e7TcpHjgz33M2DcBnOQu~aMGxuiZt0QMD1C',
code_challenge: 'CUZX5qE8Wvye6kS_SasIsa8MMxacJftmWdsIA_iKp3I'
code_challenge: 'CUZX5qE8Wvye6kS_SasIsa8MMxacJftmWdsIA_iKp3I'
}

@@ -28,5 +34,18 @@ ```

### Specify a verifier length
```
const challenge = pkceChallenge(128);
expect(challenge.code_verifier.length).equals(128);
```
```
### Challenge verification
```
const {verifyChallenge} = require('pkce-challenge');
expect(
verifyChallenge(
challenge.code_verifier,
challenge.code_challenge
)
).toBe(true);
```

@@ -1,34 +0,63 @@

const { test } = require('tap');
const pkceChallenge = require('../index');
const { test } = require("tap");
const pkceChallenge = require("../index");
const { verifyChallenge } = pkceChallenge;
test('default verifier length is 43', t => {
t.is(pkceChallenge().code_verifier.length, 43);
t.end();
test("default verifier length is 43", t => {
t.is(pkceChallenge().code_verifier.length, 43);
t.end();
});
test('code_verifier pattern matches', t => {
const pattern = /^[A-Za-z\d\-._~]{43,128}$/
const challengePair = pkceChallenge(128);
t.match(challengePair.code_verifier, pattern);
t.end();
test("code_verifier pattern matches", t => {
const pattern = /^[A-Za-z\d\-._~]{43,128}$/;
const challengePair = pkceChallenge(128);
t.match(challengePair.code_verifier, pattern);
t.end();
});
test("code_challenge pattern doesn't have [=+/]", t => {
const challengePair = pkceChallenge(128);
const challengePair = pkceChallenge(128);
t.doesNotHave(challengePair.code_challenge, '=');
t.doesNotHave(challengePair.code_challenge, '+');
t.doesNotHave(challengePair.code_challenge, '/');
t.end();
t.doesNotHave(challengePair.code_challenge, "=");
t.doesNotHave(challengePair.code_challenge, "+");
t.doesNotHave(challengePair.code_challenge, "/");
t.end();
});
test('verifier length < 43 throws error', t => {
t.throws(() => {
pkceChallenge(42);
}, 'Expected a length between 43 and 128. Received 42.');
t.end();
test("verifier length < 43 throws error", t => {
t.throws(() => {
pkceChallenge(42);
}, "Expected a length between 43 and 128. Received 42.");
t.end();
});
test('verifier length > 128 throws error', t => {
t.throws(() => {
pkceChallenge(129);
}, 'Expected a length between 43 and 128. Received 129.');
t.end();
});
test("verifier length > 128 throws error", t => {
t.throws(() => {
pkceChallenge(129);
}, "Expected a length between 43 and 128. Received 129.");
t.end();
});
test("verifyChallenge should return true", t => {
const challengePair = pkceChallenge();
t.is(
verifyChallenge(
challengePair.code_verifier,
challengePair.code_challenge
),
true
);
t.end();
});
test("verifyChallenge should return false", t => {
const challengePair = pkceChallenge();
t.is(
verifyChallenge(
challengePair.code_verifier,
challengePair.code_challenge + "a"
),
false
);
t.end();
});
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc