Skip to content

Commit a30ba9d

Browse files
committed
feat: b64urlDecode() Decode URL-safe base64 to original string
1 parent cf267ff commit a30ba9d

3 files changed

Lines changed: 58 additions & 4 deletions

File tree

src/lib/helper.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,16 +170,26 @@ export function b64toURLSafe(base64: string): string {
170170
export function b64fromURLSafe(base64: string): string {
171171
validateB64URL(base64)
172172
const str = base64.replace(/-/g, '+').replace(/_/g, '/')
173+
return b64PadSuffix(str)
174+
}
175+
176+
177+
export function b64PadSuffix(input: string): string {
173178
let num = 0
174-
const mo = str.length % 4
179+
const mo = input.length % 4
175180
switch (mo) {
176181
case 3:
177182
num = 1
178183
break
179184
case 2:
180185
num = 2
181186
break
187+
case 0:
188+
num = 0
189+
break
190+
default:
191+
throw new Error(ErrMsg.notValidB64URLLength)
182192
}
183193

184-
return str + '='.repeat(num)
194+
return input + '='.repeat(num)
185195
}

src/lib/index.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import {
44
fromBuffer as browserFromBuffer,
55
} from './browser'
66
import { defaultConfig } from './config'
7-
import { b64toURLSafe, isRunningInNodejs } from './helper'
7+
import { b64toURLSafe, b64PadSuffix, isRunningInNodejs } from './helper'
88
import { TextDecoderFn, TextEncoderFn } from './model'
99
import {
1010
fromBuffer as nodeFromBuffer,
@@ -79,3 +79,21 @@ export function b64urlEncode(
7979
const b64 = b64encode(input, textEncoder)
8080
return b64toURLSafe(b64)
8181
}
82+
83+
84+
/**
85+
* Decode URL-safe base64 to original string.
86+
*
87+
* Note: using b64fromURLSafe() for converting URL-safe base64 string to base64 string
88+
*
89+
* @see https://en.wikipedia.org/wiki/Base64#URL_applications
90+
*/
91+
export function b64urlDecode(
92+
input: string,
93+
outputEncoding: string = 'utf-8',
94+
textDecoder?: TextDecoderFn,
95+
): string {
96+
97+
const str = b64PadSuffix(input) // for URL-safe
98+
return b64decode(str, outputEncoding, textDecoder)
99+
}

test/10_index.test.ts

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import * as assert from 'power-assert'
99
import rewire = require('rewire')
1010
import { TextDecoder, TextEncoder } from 'util'
1111

12-
import { b64decode, b64encode, b64fromBuffer, b64urlEncode } from '../src/index'
12+
import { b64decode, b64encode, b64fromBuffer, b64urlDecode, b64urlEncode } from '../src/index'
1313
import { defaultConfig, ErrMsg } from '../src/lib/config'
1414

1515
import { input1, input2, input3, input4, input44, input5, input8, inputURLSafe, inputURLSafe2 } from './config'
@@ -115,4 +115,30 @@ describe(filename, () => {
115115
})
116116
})
117117

118+
119+
describe('Should b64urlDecode() works', () => {
120+
it('with valid input', () => {
121+
inputURLSafe.forEach(([input, b64, b64url]) => {
122+
const ret1 = b64urlDecode(b64, 'utf8', TextDecoder)
123+
const ret2 = b64urlDecode(b64url, (void 0), TextDecoder)
124+
125+
assert(ret1 === ret2, `0: "${ret1}" !== "${ret2}"`)
126+
assert(ret1 === input, `1: "${ret1}" !== "${input}"`)
127+
})
128+
})
129+
130+
it('with invalid input', () => {
131+
inputURLSafe2.forEach(b64 => {
132+
try {
133+
b64urlDecode(b64, 'utf8', TextDecoder)
134+
assert(false, 'Should throw error, but NOT')
135+
}
136+
catch (ex) {
137+
assert(true)
138+
}
139+
})
140+
})
141+
})
142+
143+
118144
})

0 commit comments

Comments
 (0)