@bb-labs/pkce 0.0.1 → 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +15 -84
- package/dist/index.d.ts +14 -4
- package/dist/index.js +70 -19
- package/package.json +10 -2
- package/dist/pkce/expo.d.ts +0 -10
- package/dist/pkce/expo.js +0 -55
- package/dist/pkce/nodejs.d.ts +0 -10
- package/dist/pkce/nodejs.js +0 -49
package/README.md
CHANGED
|
@@ -1,13 +1,6 @@
|
|
|
1
1
|
# @bb-labs/pkce
|
|
2
2
|
|
|
3
|
-
A lightweight
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- 🎯 **Zero Dependencies**: No external crypto libraries required
|
|
8
|
-
- 🔄 **Cross-Platform**: Works in React Native (expo-crypto), Node.js (built-in crypto), and browsers
|
|
9
|
-
- 🚀 **Auto-Detection**: Automatically uses the best available crypto implementation
|
|
10
|
-
- 📦 **Tree-Shakable**: Import only what you need
|
|
3
|
+
A lightweight PKCE (Proof Key for Code Exchange) library using `@noble/hashes` for universal compatibility.
|
|
11
4
|
|
|
12
5
|
## Installation
|
|
13
6
|
|
|
@@ -17,8 +10,6 @@ npm install @bb-labs/pkce
|
|
|
17
10
|
|
|
18
11
|
## Usage
|
|
19
12
|
|
|
20
|
-
### Automatic Environment Detection (Recommended)
|
|
21
|
-
|
|
22
13
|
```typescript
|
|
23
14
|
import { generateCodeVerifier, createCodeChallenge } from "@bb-labs/pkce";
|
|
24
15
|
|
|
@@ -26,99 +17,39 @@ import { generateCodeVerifier, createCodeChallenge } from "@bb-labs/pkce";
|
|
|
26
17
|
const verifier = generateCodeVerifier();
|
|
27
18
|
|
|
28
19
|
// Create the corresponding code challenge
|
|
29
|
-
const challenge =
|
|
30
|
-
|
|
31
|
-
console.log("Verifier:", verifier);
|
|
32
|
-
console.log("Challenge:", challenge);
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
### Platform-Specific Imports
|
|
36
|
-
|
|
37
|
-
#### React Native / Expo
|
|
38
|
-
|
|
39
|
-
```typescript
|
|
40
|
-
import { generateCodeVerifier, createCodeChallenge } from "@bb-labs/pkce/expo";
|
|
41
|
-
|
|
42
|
-
// Make sure expo-crypto is installed in your React Native app
|
|
43
|
-
// npm install expo-crypto
|
|
44
|
-
|
|
45
|
-
const verifier = generateCodeVerifier();
|
|
46
|
-
const challenge = await createCodeChallenge(verifier);
|
|
47
|
-
```
|
|
48
|
-
|
|
49
|
-
#### Node.js
|
|
20
|
+
const challenge = createCodeChallenge(verifier);
|
|
50
21
|
|
|
51
|
-
|
|
52
|
-
import { generateCodeVerifier, createCodeChallenge } from "@bb-labs/pkce/nodejs";
|
|
53
|
-
|
|
54
|
-
const verifier = generateCodeVerifier();
|
|
55
|
-
const challenge = await createCodeChallenge(verifier);
|
|
22
|
+
// Use in OAuth flow...
|
|
56
23
|
```
|
|
57
24
|
|
|
58
25
|
## API
|
|
59
26
|
|
|
60
27
|
### `generateCodeVerifier(): string`
|
|
61
28
|
|
|
62
|
-
Generates a cryptographically secure random code verifier (43
|
|
29
|
+
Generates a cryptographically secure random code verifier (43 characters, base64url encoded).
|
|
63
30
|
|
|
64
|
-
### `createCodeChallenge(verifier: string):
|
|
31
|
+
### `createCodeChallenge(verifier: string): string`
|
|
65
32
|
|
|
66
33
|
Creates a code challenge by SHA-256 hashing the verifier and base64url encoding the result.
|
|
67
34
|
|
|
68
35
|
## Requirements
|
|
69
36
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
- Install `expo-crypto`: `npm install expo-crypto`
|
|
73
|
-
- The library will automatically detect and use expo-crypto
|
|
74
|
-
|
|
75
|
-
### Node.js Apps
|
|
76
|
-
|
|
77
|
-
- Node.js built-in `crypto` module (available in all modern Node.js versions)
|
|
78
|
-
- No additional dependencies required
|
|
79
|
-
|
|
80
|
-
### Browser Apps
|
|
37
|
+
Depends on [`@noble/hashes`](https://github.com/paulmillr/noble-hashes) for crypto functions.
|
|
81
38
|
|
|
82
|
-
|
|
83
|
-
- Or use a polyfill for older browsers
|
|
84
|
-
|
|
85
|
-
## PKCE Flow Example
|
|
39
|
+
**React Native Setup:**
|
|
86
40
|
|
|
87
41
|
```typescript
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
const verifier = generateCodeVerifier();
|
|
92
|
-
const challenge = await createCodeChallenge(verifier);
|
|
93
|
-
|
|
94
|
-
// 2. Send challenge to authorization server
|
|
95
|
-
const authUrl =
|
|
96
|
-
`https://auth.example.com/oauth/authorize?` +
|
|
97
|
-
`client_id=your_client_id&` +
|
|
98
|
-
`redirect_uri=your_redirect_uri&` +
|
|
99
|
-
`response_type=code&` +
|
|
100
|
-
`code_challenge=${challenge}&` +
|
|
101
|
-
`code_challenge_method=S256`;
|
|
102
|
-
|
|
103
|
-
// 3. After user authorization, exchange code for tokens using the verifier
|
|
104
|
-
const tokenResponse = await fetch("https://auth.example.com/oauth/token", {
|
|
105
|
-
method: "POST",
|
|
106
|
-
body: new URLSearchParams({
|
|
107
|
-
grant_type: "authorization_code",
|
|
108
|
-
code: authorizationCode,
|
|
109
|
-
redirect_uri: redirectUri,
|
|
110
|
-
client_id: clientId,
|
|
111
|
-
code_verifier: verifier, // ← Send the original verifier
|
|
112
|
-
}),
|
|
113
|
-
});
|
|
42
|
+
// In your app/_layout.tsx (Expo) or App.tsx
|
|
43
|
+
import { polyfillWebCrypto } from "expo-standard-web-crypto";
|
|
44
|
+
polyfillWebCrypto();
|
|
114
45
|
```
|
|
115
46
|
|
|
116
|
-
|
|
47
|
+
For bare React Native:
|
|
117
48
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
49
|
+
```bash
|
|
50
|
+
npm install react-native-get-random-values
|
|
51
|
+
import 'react-native-get-random-values';
|
|
52
|
+
```
|
|
122
53
|
|
|
123
54
|
## License
|
|
124
55
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Universal PKCE implementation using @noble/hashes
|
|
3
|
+
* Works in Node.js, React Native, Expo, and browsers
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Generates a cryptographically secure random code verifier for PKCE
|
|
7
|
+
* Uses @noble/hashes randomBytes (works in Node.js, browsers, React Native)
|
|
8
|
+
*/
|
|
9
|
+
export declare function generateCodeVerifier(): string;
|
|
10
|
+
/**
|
|
11
|
+
* Creates a code challenge from a code verifier using SHA-256
|
|
12
|
+
* Uses @noble/hashes sha256 (works in Node.js, browsers, React Native)
|
|
13
|
+
*/
|
|
14
|
+
export declare function createCodeChallenge(verifier: string): string;
|
package/dist/index.js
CHANGED
|
@@ -1,23 +1,74 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Universal PKCE implementation using @noble/hashes
|
|
3
|
+
* Works in Node.js, React Native, Expo, and browsers
|
|
4
|
+
*/
|
|
5
|
+
import { sha256 } from "@noble/hashes/sha2";
|
|
6
|
+
import { randomBytes } from "@noble/hashes/utils";
|
|
7
|
+
/**
|
|
8
|
+
* Generates a cryptographically secure random code verifier for PKCE
|
|
9
|
+
* Uses @noble/hashes randomBytes (works in Node.js, browsers, React Native)
|
|
10
|
+
*/
|
|
11
|
+
export function generateCodeVerifier() {
|
|
12
|
+
// Generate 32 bytes (256 bits) of random data
|
|
13
|
+
const randomBytesData = randomBytes(32);
|
|
14
|
+
// Convert to base64url encoding (RFC 4648)
|
|
15
|
+
return base64UrlEncode(randomBytesData);
|
|
10
16
|
}
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
17
|
+
/**
|
|
18
|
+
* Creates a code challenge from a code verifier using SHA-256
|
|
19
|
+
* Uses @noble/hashes sha256 (works in Node.js, browsers, React Native)
|
|
20
|
+
*/
|
|
21
|
+
export function createCodeChallenge(verifier) {
|
|
22
|
+
// Convert string to bytes
|
|
23
|
+
const bytes = stringToBytes(verifier);
|
|
24
|
+
// Hash using SHA-256 from @noble/hashes
|
|
25
|
+
const hash = sha256(bytes);
|
|
26
|
+
// Convert to base64url encoding
|
|
27
|
+
return base64UrlEncode(hash);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Converts a string to UTF-8 bytes
|
|
31
|
+
*/
|
|
32
|
+
function stringToBytes(str) {
|
|
33
|
+
const bytes = [];
|
|
34
|
+
for (let i = 0; i < str.length; i++) {
|
|
35
|
+
const code = str.charCodeAt(i);
|
|
36
|
+
if (code < 0x80) {
|
|
37
|
+
bytes.push(code);
|
|
38
|
+
}
|
|
39
|
+
else if (code < 0x800) {
|
|
40
|
+
bytes.push(0xc0 | (code >> 6), 0x80 | (code & 0x3f));
|
|
41
|
+
}
|
|
42
|
+
else if (code < 0xd800 || code >= 0xe000) {
|
|
43
|
+
bytes.push(0xe0 | (code >> 12), 0x80 | ((code >> 6) & 0x3f), 0x80 | (code & 0x3f));
|
|
44
|
+
}
|
|
45
|
+
else {
|
|
46
|
+
// Surrogate pair
|
|
47
|
+
i++;
|
|
48
|
+
const cp = 0x10000 + (((code & 0x3ff) << 10) | (str.charCodeAt(i) & 0x3ff));
|
|
49
|
+
bytes.push(0xf0 | (cp >> 18), 0x80 | ((cp >> 12) & 0x3f), 0x80 | ((cp >> 6) & 0x3f), 0x80 | (cp & 0x3f));
|
|
50
|
+
}
|
|
16
51
|
}
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
52
|
+
return new Uint8Array(bytes);
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* Converts Uint8Array to base64url encoding (RFC 4648)
|
|
56
|
+
*/
|
|
57
|
+
function base64UrlEncode(bytes) {
|
|
58
|
+
const chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-_";
|
|
59
|
+
let result = "";
|
|
60
|
+
for (let i = 0; i < bytes.length; i += 3) {
|
|
61
|
+
const b1 = bytes[i];
|
|
62
|
+
const b2 = i + 1 < bytes.length ? bytes[i + 1] : 0;
|
|
63
|
+
const b3 = i + 2 < bytes.length ? bytes[i + 2] : 0;
|
|
64
|
+
result += chars[b1 >> 2];
|
|
65
|
+
result += chars[((b1 & 0x03) << 4) | (b2 >> 4)];
|
|
66
|
+
if (i + 1 < bytes.length) {
|
|
67
|
+
result += chars[((b2 & 0x0f) << 2) | (b3 >> 6)];
|
|
68
|
+
}
|
|
69
|
+
if (i + 2 < bytes.length) {
|
|
70
|
+
result += chars[b3 & 0x3f];
|
|
71
|
+
}
|
|
20
72
|
}
|
|
73
|
+
return result;
|
|
21
74
|
}
|
|
22
|
-
export const generateCodeVerifier = defaultExport.generateCodeVerifier;
|
|
23
|
-
export const createCodeChallenge = defaultExport.createCodeChallenge;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@bb-labs/pkce",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.3",
|
|
4
4
|
"description": "A library for PKCE",
|
|
5
5
|
"homepage": "https://github.com/beepbop-labs/pkce",
|
|
6
6
|
"keywords": [
|
|
@@ -19,10 +19,18 @@
|
|
|
19
19
|
"dist",
|
|
20
20
|
"README.md"
|
|
21
21
|
],
|
|
22
|
+
"exports": {
|
|
23
|
+
".": "./dist/index.js",
|
|
24
|
+
"./node": "./dist/node/index.js",
|
|
25
|
+
"./expo": "./dist/expo/index.js"
|
|
26
|
+
},
|
|
22
27
|
"scripts": {
|
|
23
28
|
"clean": "rm -rf dist",
|
|
24
29
|
"build": "npm run clean && tsc -p tsconfig.json",
|
|
25
|
-
"pack": "npm run build &&
|
|
30
|
+
"pack": "npm run build && rm -rf archive && mkdir -p archive && bun pm pack --destination ./archive && find ./archive -name '*.tgz' -exec mv {} ./archive/archive2.tgz \\;"
|
|
31
|
+
},
|
|
32
|
+
"dependencies": {
|
|
33
|
+
"@noble/hashes": "^1.3.0"
|
|
26
34
|
},
|
|
27
35
|
"devDependencies": {
|
|
28
36
|
"@types/bun": "latest"
|
package/dist/pkce/expo.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a cryptographically secure random code verifier for PKCE
|
|
3
|
-
* Uses expo-crypto if available, otherwise throws an error
|
|
4
|
-
*/
|
|
5
|
-
export declare function generateCodeVerifier(): string;
|
|
6
|
-
/**
|
|
7
|
-
* Creates a code challenge from a code verifier using SHA-256
|
|
8
|
-
* Uses expo-crypto if available, otherwise throws an error
|
|
9
|
-
*/
|
|
10
|
-
export declare function createCodeChallenge(verifier: string): Promise<string>;
|
package/dist/pkce/expo.js
DELETED
|
@@ -1,55 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a cryptographically secure random code verifier for PKCE
|
|
3
|
-
* Uses expo-crypto if available, otherwise throws an error
|
|
4
|
-
*/
|
|
5
|
-
export function generateCodeVerifier() {
|
|
6
|
-
try {
|
|
7
|
-
const Crypto = require("expo-crypto");
|
|
8
|
-
// Generate 32 bytes (256 bits) of random data
|
|
9
|
-
const randomBytes = Crypto.getRandomBytes(32);
|
|
10
|
-
// Convert to base64url encoding (RFC 4648)
|
|
11
|
-
return base64UrlEncode(randomBytes);
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
throw new Error("expo-crypto is required for React Native PKCE operations. Install expo-crypto or use a different PKCE implementation.");
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Creates a code challenge from a code verifier using SHA-256
|
|
19
|
-
* Uses expo-crypto if available, otherwise throws an error
|
|
20
|
-
*/
|
|
21
|
-
export async function createCodeChallenge(verifier) {
|
|
22
|
-
try {
|
|
23
|
-
const Crypto = require("expo-crypto");
|
|
24
|
-
// Hash the verifier using SHA-256
|
|
25
|
-
const hashBuffer = await Crypto.digestStringAsync(Crypto.CryptoDigestAlgorithm.SHA256, verifier, {
|
|
26
|
-
encoding: Crypto.CryptoEncoding.BASE64,
|
|
27
|
-
});
|
|
28
|
-
// Return base64url encoded hash
|
|
29
|
-
return base64UrlEncode(hashBuffer);
|
|
30
|
-
}
|
|
31
|
-
catch (error) {
|
|
32
|
-
throw new Error("expo-crypto is required for React Native PKCE operations. Install expo-crypto or use a different PKCE implementation.");
|
|
33
|
-
}
|
|
34
|
-
}
|
|
35
|
-
/**
|
|
36
|
-
* Converts input to base64url encoding (RFC 4648)
|
|
37
|
-
* Replaces '+' with '-', '/' with '_', and removes padding
|
|
38
|
-
*/
|
|
39
|
-
function base64UrlEncode(input) {
|
|
40
|
-
let base64;
|
|
41
|
-
if (typeof input === "string") {
|
|
42
|
-
// If input is already a base64 string, use it directly
|
|
43
|
-
base64 = input;
|
|
44
|
-
}
|
|
45
|
-
else {
|
|
46
|
-
// Convert Uint8Array to base64
|
|
47
|
-
base64 = "";
|
|
48
|
-
for (let i = 0; i < input.length; i++) {
|
|
49
|
-
base64 += String.fromCharCode(input[i]);
|
|
50
|
-
}
|
|
51
|
-
base64 = btoa(base64);
|
|
52
|
-
}
|
|
53
|
-
// Convert to base64url
|
|
54
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
55
|
-
}
|
package/dist/pkce/nodejs.d.ts
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a cryptographically secure random code verifier for PKCE
|
|
3
|
-
* Node.js implementation using built-in crypto
|
|
4
|
-
*/
|
|
5
|
-
export declare function generateCodeVerifier(): string;
|
|
6
|
-
/**
|
|
7
|
-
* Creates a code challenge from a code verifier using SHA-256
|
|
8
|
-
* Node.js implementation using built-in crypto
|
|
9
|
-
*/
|
|
10
|
-
export declare function createCodeChallenge(verifier: string): Promise<string>;
|
package/dist/pkce/nodejs.js
DELETED
|
@@ -1,49 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generates a cryptographically secure random code verifier for PKCE
|
|
3
|
-
* Node.js implementation using built-in crypto
|
|
4
|
-
*/
|
|
5
|
-
export function generateCodeVerifier() {
|
|
6
|
-
try {
|
|
7
|
-
const { randomBytes } = require("crypto");
|
|
8
|
-
// Generate 32 bytes (256 bits) of random data
|
|
9
|
-
const randomBytesData = randomBytes(32);
|
|
10
|
-
// Convert to base64url encoding (RFC 4648)
|
|
11
|
-
return base64UrlEncode(randomBytesData);
|
|
12
|
-
}
|
|
13
|
-
catch (error) {
|
|
14
|
-
throw new Error("Node.js crypto is required for server-side PKCE operations.");
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
/**
|
|
18
|
-
* Creates a code challenge from a code verifier using SHA-256
|
|
19
|
-
* Node.js implementation using built-in crypto
|
|
20
|
-
*/
|
|
21
|
-
export async function createCodeChallenge(verifier) {
|
|
22
|
-
try {
|
|
23
|
-
const { createHash } = require("crypto");
|
|
24
|
-
// Hash the verifier using SHA-256
|
|
25
|
-
const hash = createHash("sha256").update(verifier).digest();
|
|
26
|
-
// Return base64url encoded hash
|
|
27
|
-
return base64UrlEncode(hash);
|
|
28
|
-
}
|
|
29
|
-
catch (error) {
|
|
30
|
-
throw new Error("Node.js crypto is required for server-side PKCE operations.");
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
/**
|
|
34
|
-
* Converts input to base64url encoding (RFC 4648)
|
|
35
|
-
* Replaces '+' with '-', '/' with '_', and removes padding
|
|
36
|
-
*/
|
|
37
|
-
function base64UrlEncode(input) {
|
|
38
|
-
let base64;
|
|
39
|
-
if (typeof input === "string") {
|
|
40
|
-
// If input is already a base64 string, use it directly
|
|
41
|
-
base64 = input;
|
|
42
|
-
}
|
|
43
|
-
else {
|
|
44
|
-
// Convert Buffer to base64
|
|
45
|
-
base64 = input.toString("base64");
|
|
46
|
-
}
|
|
47
|
-
// Convert to base64url
|
|
48
|
-
return base64.replace(/\+/g, "-").replace(/\//g, "_").replace(/=+$/, "");
|
|
49
|
-
}
|