@bb-labs/pkce 0.0.2 → 0.0.4

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 CHANGED
@@ -1,13 +1,6 @@
1
1
  # @bb-labs/pkce
2
2
 
3
- A lightweight, zero-dependency PKCE (Proof Key for Code Exchange) library for OAuth 2.0 that works across platforms.
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
+ Zero-dependency PKCE library with pure JavaScript SHA-256 implementation. Bring your own crypto - provide a random bytes generator for maximum compatibility.
11
4
 
12
5
  ## Installation
13
6
 
@@ -17,108 +10,54 @@ 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
 
25
- // Generate a cryptographically secure code verifier
26
- const verifier = generateCodeVerifier();
27
-
28
- // Create the corresponding code challenge
29
- const challenge = await createCodeChallenge(verifier);
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
16
+ // Node.js
17
+ import { randomBytes } from "crypto";
18
+ const verifier = generateCodeVerifier(randomBytes);
19
+ const challenge = createCodeChallenge(verifier);
50
20
 
51
- ```typescript
52
- import { generateCodeVerifier, createCodeChallenge } from "@bb-labs/pkce/nodejs";
21
+ // Browser
22
+ const verifier = generateCodeVerifier((length) => {
23
+ const bytes = new Uint8Array(length);
24
+ crypto.getRandomValues(bytes);
25
+ return bytes;
26
+ });
27
+ const challenge = createCodeChallenge(verifier);
53
28
 
54
- const verifier = generateCodeVerifier();
55
- const challenge = await createCodeChallenge(verifier);
29
+ // Expo
30
+ import * as Crypto from "expo-crypto";
31
+ const verifier = generateCodeVerifier(Crypto.getRandomBytes);
32
+ const challenge = createCodeChallenge(verifier);
56
33
  ```
57
34
 
58
35
  ## API
59
36
 
60
- ### `generateCodeVerifier(): string`
61
-
62
- Generates a cryptographically secure random code verifier (43-128 characters, base64url encoded).
63
-
64
- ### `createCodeChallenge(verifier: string): Promise<string>`
65
-
66
- Creates a code challenge by SHA-256 hashing the verifier and base64url encoding the result.
67
-
68
- ## Requirements
69
-
70
- ### React Native / Expo Apps
37
+ ### `generateCodeVerifier(getRandomBytes): string`
71
38
 
72
- - Install `expo-crypto`: `npm install expo-crypto`
73
- - The library will automatically detect and use expo-crypto
39
+ Generates a cryptographically secure code verifier (43 chars, base64url encoded).
74
40
 
75
- ### Node.js Apps
41
+ - `getRandomBytes`: Function returning random bytes (Uint8Array, ArrayBuffer, or Buffer)
76
42
 
77
- - Node.js built-in `crypto` module (available in all modern Node.js versions)
78
- - No additional dependencies required
43
+ ### `createCodeChallenge(verifier): string`
79
44
 
80
- ### Browser Apps
45
+ Creates SHA-256 code challenge from verifier (43 chars, base64url encoded).
81
46
 
82
- - Modern browsers with Web Crypto API support
83
- - Or use a polyfill for older browsers
47
+ ## Platform Support
84
48
 
85
- ## PKCE Flow Example
86
-
87
- ```typescript
88
- import { generateCodeVerifier, createCodeChallenge } from "@bb-labs/pkce";
89
-
90
- // 1. Generate verifier and challenge
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
- });
114
- ```
49
+ Works in all JavaScript environments. Provide your platform's crypto random bytes function:
115
50
 
116
- ## Error Handling
51
+ - **Node.js**: `crypto.randomBytes`
52
+ - **Browser**: `(length) => crypto.getRandomValues(new Uint8Array(length))`
53
+ - **Expo/React Native**: `expo-crypto.getRandomBytes`
117
54
 
118
- The library will throw descriptive errors if no suitable crypto implementation is found:
55
+ ## Implementation Details
119
56
 
120
- - React Native: `"expo-crypto is required for React Native PKCE operations. Install expo-crypto or use a different PKCE implementation."`
121
- - Node.js: `"Node.js crypto is required for server-side PKCE operations."`
57
+ - **SHA-256**: Pure JavaScript FIPS 180-4 implementation
58
+ - **Base64URL**: RFC 4648 compliant
59
+ - **Bundle size**: ~3KB minified
60
+ - **TypeScript**: Full type definitions included
122
61
 
123
62
  ## License
124
63
 
package/dist/index.d.ts CHANGED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Random bytes generator function type
3
+ * Can return Buffer, Uint8Array, or ArrayBuffer
4
+ */
5
+ export type RandomBytesGenerator = (length: number) => Uint8Array | ArrayBuffer | Buffer;
6
+ /**
7
+ * Generates a random code verifier for PKCE
8
+ * @param getRandomBytes - Function that generates cryptographically secure random bytes
9
+ * @returns Base64url-encoded code verifier (43 characters)
10
+ *
11
+ * @example
12
+ * // Node.js
13
+ * import { randomBytes } from 'crypto';
14
+ * const verifier = generateCodeVerifier(randomBytes);
15
+ *
16
+ * @example
17
+ * // Expo
18
+ * import * as Crypto from 'expo-crypto';
19
+ * const verifier = generateCodeVerifier(Crypto.getRandomBytes);
20
+ *
21
+ * @example
22
+ * // Browser
23
+ * const verifier = generateCodeVerifier((length) => {
24
+ * const bytes = new Uint8Array(length);
25
+ * crypto.getRandomValues(bytes);
26
+ * return bytes;
27
+ * });
28
+ */
29
+ export declare function generateCodeVerifier(getRandomBytes: RandomBytesGenerator): string;
30
+ /**
31
+ * Creates a code challenge from a code verifier using SHA-256
32
+ * Uses pure JavaScript SHA-256 implementation
33
+ * @param verifier - The code verifier to hash
34
+ * @returns Base64url-encoded SHA-256 hash of the verifier (43 characters)
35
+ */
36
+ export declare function createCodeChallenge(verifier: string): string;
package/dist/index.js CHANGED
@@ -1 +1,47 @@
1
- "use strict";
1
+ import { uint8ArrayToBase64Url } from "./utils/base64";
2
+ import { sha256 } from "./utils/sha256";
3
+ /**
4
+ * Generates a random code verifier for PKCE
5
+ * @param getRandomBytes - Function that generates cryptographically secure random bytes
6
+ * @returns Base64url-encoded code verifier (43 characters)
7
+ *
8
+ * @example
9
+ * // Node.js
10
+ * import { randomBytes } from 'crypto';
11
+ * const verifier = generateCodeVerifier(randomBytes);
12
+ *
13
+ * @example
14
+ * // Expo
15
+ * import * as Crypto from 'expo-crypto';
16
+ * const verifier = generateCodeVerifier(Crypto.getRandomBytes);
17
+ *
18
+ * @example
19
+ * // Browser
20
+ * const verifier = generateCodeVerifier((length) => {
21
+ * const bytes = new Uint8Array(length);
22
+ * crypto.getRandomValues(bytes);
23
+ * return bytes;
24
+ * });
25
+ */
26
+ export function generateCodeVerifier(getRandomBytes) {
27
+ // Generate 32 bytes (256 bits) of random data
28
+ const randomBytes = getRandomBytes(32);
29
+ // Ensure we have a Uint8Array
30
+ const bytes = randomBytes instanceof Uint8Array
31
+ ? randomBytes
32
+ : new Uint8Array(randomBytes);
33
+ // Convert to base64url encoding (RFC 4648)
34
+ return uint8ArrayToBase64Url(bytes);
35
+ }
36
+ /**
37
+ * Creates a code challenge from a code verifier using SHA-256
38
+ * Uses pure JavaScript SHA-256 implementation
39
+ * @param verifier - The code verifier to hash
40
+ * @returns Base64url-encoded SHA-256 hash of the verifier (43 characters)
41
+ */
42
+ export function createCodeChallenge(verifier) {
43
+ // Hash using pure JavaScript SHA-256
44
+ const hash = sha256(verifier);
45
+ // Convert to base64url encoding
46
+ return uint8ArrayToBase64Url(hash);
47
+ }
@@ -0,0 +1,5 @@
1
+ /**
2
+ * Converts a Uint8Array to base64url encoding (RFC 4648)
3
+ * Works in all JavaScript environments
4
+ */
5
+ export declare function uint8ArrayToBase64Url(bytes: Uint8Array): string;
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Converts a Uint8Array to base64url encoding (RFC 4648)
3
+ * Works in all JavaScript environments
4
+ */
5
+ export function uint8ArrayToBase64Url(bytes) {
6
+ let binary = '';
7
+ for (let i = 0; i < bytes.length; i++) {
8
+ binary += String.fromCharCode(bytes[i]);
9
+ }
10
+ return btoa(binary)
11
+ .replace(/\+/g, '-')
12
+ .replace(/\//g, '_')
13
+ .replace(/=/g, '');
14
+ }
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Pure JavaScript SHA-256 implementation
3
+ * Based on FIPS 180-4 specification
4
+ * Works in all JavaScript environments (Node.js, browsers, React Native, Expo)
5
+ */
6
+ /**
7
+ * Computes SHA-256 hash of input data
8
+ * @param data - Input string or byte array
9
+ * @returns SHA-256 hash as Uint8Array (32 bytes)
10
+ */
11
+ export declare function sha256(data: string | Uint8Array): Uint8Array;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Pure JavaScript SHA-256 implementation
3
+ * Based on FIPS 180-4 specification
4
+ * Works in all JavaScript environments (Node.js, browsers, React Native, Expo)
5
+ */
6
+ // SHA-256 constants (first 32 bits of the fractional parts of the cube roots of the first 64 primes)
7
+ const K = new Uint32Array([
8
+ 0x428a2f98, 0x71374491, 0xb5c0fbcf, 0xe9b5dba5, 0x3956c25b, 0x59f111f1, 0x923f82a4, 0xab1c5ed5,
9
+ 0xd807aa98, 0x12835b01, 0x243185be, 0x550c7dc3, 0x72be5d74, 0x80deb1fe, 0x9bdc06a7, 0xc19bf174,
10
+ 0xe49b69c1, 0xefbe4786, 0x0fc19dc6, 0x240ca1cc, 0x2de92c6f, 0x4a7484aa, 0x5cb0a9dc, 0x76f988da,
11
+ 0x983e5152, 0xa831c66d, 0xb00327c8, 0xbf597fc7, 0xc6e00bf3, 0xd5a79147, 0x06ca6351, 0x14292967,
12
+ 0x27b70a85, 0x2e1b2138, 0x4d2c6dfc, 0x53380d13, 0x650a7354, 0x766a0abb, 0x81c2c92e, 0x92722c85,
13
+ 0xa2bfe8a1, 0xa81a664b, 0xc24b8b70, 0xc76c51a3, 0xd192e819, 0xd6990624, 0xf40e3585, 0x106aa070,
14
+ 0x19a4c116, 0x1e376c08, 0x2748774c, 0x34b0bcb5, 0x391c0cb3, 0x4ed8aa4a, 0x5b9cca4f, 0x682e6ff3,
15
+ 0x748f82ee, 0x78a5636f, 0x84c87814, 0x8cc70208, 0x90befffa, 0xa4506ceb, 0xbef9a3f7, 0xc67178f2
16
+ ]);
17
+ // Right rotate
18
+ function rotr(n, x) {
19
+ return (x >>> n) | (x << (32 - n));
20
+ }
21
+ // Convert string to UTF-8 bytes
22
+ function stringToBytes(str) {
23
+ const bytes = new Uint8Array(str.length);
24
+ for (let i = 0; i < str.length; i++) {
25
+ const code = str.charCodeAt(i);
26
+ if (code > 127) {
27
+ // Handle UTF-8 encoding for non-ASCII characters
28
+ return new TextEncoder().encode(str);
29
+ }
30
+ bytes[i] = code;
31
+ }
32
+ return bytes;
33
+ }
34
+ /**
35
+ * Computes SHA-256 hash of input data
36
+ * @param data - Input string or byte array
37
+ * @returns SHA-256 hash as Uint8Array (32 bytes)
38
+ */
39
+ export function sha256(data) {
40
+ // Convert input to bytes
41
+ const message = typeof data === 'string' ? stringToBytes(data) : data;
42
+ // Pre-processing: adding padding bits
43
+ const msgLength = message.length;
44
+ const bitLength = msgLength * 8;
45
+ // Calculate padding length (message + 1 bit + zeros + 64-bit length = multiple of 512 bits)
46
+ const paddingLength = (msgLength + 9 + 63) & ~63; // Round up to nearest multiple of 64
47
+ const padded = new Uint8Array(paddingLength);
48
+ // Copy message
49
+ padded.set(message);
50
+ // Append '1' bit (0x80 = 10000000 in binary)
51
+ padded[msgLength] = 0x80;
52
+ // Append length in bits as 64-bit big-endian integer
53
+ const view = new DataView(padded.buffer);
54
+ view.setUint32(paddingLength - 4, bitLength >>> 0, false); // Low 32 bits
55
+ // Initialize hash values (first 32 bits of the fractional parts of the square roots of the first 8 primes)
56
+ const h = new Uint32Array([
57
+ 0x6a09e667, 0xbb67ae85, 0x3c6ef372, 0xa54ff53a,
58
+ 0x510e527f, 0x9b05688c, 0x1f83d9ab, 0x5be0cd19
59
+ ]);
60
+ // Process message in 512-bit (64-byte) chunks
61
+ const w = new Uint32Array(64);
62
+ for (let chunkStart = 0; chunkStart < padded.length; chunkStart += 64) {
63
+ // Break chunk into sixteen 32-bit big-endian words
64
+ for (let i = 0; i < 16; i++) {
65
+ const offset = chunkStart + i * 4;
66
+ w[i] = (padded[offset] << 24) | (padded[offset + 1] << 16) |
67
+ (padded[offset + 2] << 8) | padded[offset + 3];
68
+ }
69
+ // Extend the sixteen 32-bit words into sixty-four 32-bit words
70
+ for (let i = 16; i < 64; i++) {
71
+ const s0 = rotr(7, w[i - 15]) ^ rotr(18, w[i - 15]) ^ (w[i - 15] >>> 3);
72
+ const s1 = rotr(17, w[i - 2]) ^ rotr(19, w[i - 2]) ^ (w[i - 2] >>> 10);
73
+ w[i] = (w[i - 16] + s0 + w[i - 7] + s1) >>> 0;
74
+ }
75
+ // Initialize working variables
76
+ let a = h[0], b = h[1], c = h[2], d = h[3];
77
+ let e = h[4], f = h[5], g = h[6], hh = h[7];
78
+ // Main compression loop
79
+ for (let i = 0; i < 64; i++) {
80
+ const S1 = rotr(6, e) ^ rotr(11, e) ^ rotr(25, e);
81
+ const ch = (e & f) ^ (~e & g);
82
+ const temp1 = (hh + S1 + ch + K[i] + w[i]) >>> 0;
83
+ const S0 = rotr(2, a) ^ rotr(13, a) ^ rotr(22, a);
84
+ const maj = (a & b) ^ (a & c) ^ (b & c);
85
+ const temp2 = (S0 + maj) >>> 0;
86
+ hh = g;
87
+ g = f;
88
+ f = e;
89
+ e = (d + temp1) >>> 0;
90
+ d = c;
91
+ c = b;
92
+ b = a;
93
+ a = (temp1 + temp2) >>> 0;
94
+ }
95
+ // Add compressed chunk to current hash value
96
+ h[0] = (h[0] + a) >>> 0;
97
+ h[1] = (h[1] + b) >>> 0;
98
+ h[2] = (h[2] + c) >>> 0;
99
+ h[3] = (h[3] + d) >>> 0;
100
+ h[4] = (h[4] + e) >>> 0;
101
+ h[5] = (h[5] + f) >>> 0;
102
+ h[6] = (h[6] + g) >>> 0;
103
+ h[7] = (h[7] + hh) >>> 0;
104
+ }
105
+ // Produce the final hash value (big-endian)
106
+ const hash = new Uint8Array(32);
107
+ for (let i = 0; i < 8; i++) {
108
+ hash[i * 4 + 0] = (h[i] >>> 24) & 0xff;
109
+ hash[i * 4 + 1] = (h[i] >>> 16) & 0xff;
110
+ hash[i * 4 + 2] = (h[i] >>> 8) & 0xff;
111
+ hash[i * 4 + 3] = h[i] & 0xff;
112
+ }
113
+ return hash;
114
+ }
package/package.json CHANGED
@@ -1,12 +1,17 @@
1
1
  {
2
2
  "name": "@bb-labs/pkce",
3
- "version": "0.0.2",
4
- "description": "A library for PKCE",
3
+ "version": "0.0.4",
4
+ "description": "Zero-dependency PKCE library using Web Crypto API for Node.js, browsers, React Native, and Expo",
5
5
  "homepage": "https://github.com/beepbop-labs/pkce",
6
6
  "keywords": [
7
7
  "pkce",
8
8
  "pkce-challenge",
9
- "pkce-verifier"
9
+ "pkce-verifier",
10
+ "oauth",
11
+ "oauth2",
12
+ "web-crypto",
13
+ "react-native",
14
+ "expo"
10
15
  ],
11
16
  "author": "Beepbop",
12
17
  "license": "MIT",
@@ -20,15 +25,14 @@
20
25
  "README.md"
21
26
  ],
22
27
  "exports": {
23
- ".": "./dist/index.js",
24
- "./node": "./dist/node/index.js",
25
- "./expo": "./dist/expo/index.js"
28
+ ".": "./dist/index.js"
26
29
  },
27
30
  "scripts": {
28
31
  "clean": "rm -rf dist",
29
32
  "build": "npm run clean && tsc -p tsconfig.json",
30
- "pack": "npm run build && npm pack --pack-destination ./archive/"
33
+ "pack": "npm run build && rm -rf archive && mkdir -p archive && bun pm pack --destination ./archive && find ./archive -name '*.tgz' -exec mv {} ./archive/archive7.tgz \\;"
31
34
  },
35
+ "dependencies": {},
32
36
  "devDependencies": {
33
37
  "@types/bun": "latest"
34
38
  },
@@ -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>;
@@ -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
- }
@@ -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>;
@@ -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
- }