Skip to content

Commit

Permalink
fix: encode-decode payload with urf-8
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomontalbano committed Oct 24, 2024
1 parent 7dbd97b commit acbb77f
Show file tree
Hide file tree
Showing 7 changed files with 115 additions and 45 deletions.
40 changes: 40 additions & 0 deletions packages/js-auth/src/jwtDecode.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -277,4 +277,44 @@ describe('jwtDecode', () => {
'RhXDw7UajFr9Bxl18Q8Es82MlbXw1YcJCRe0g1-mPpDaxcBDtvHyu8GQYLpRYm-4xQ5fbg57Pzqxue0jOxv2Kcsl7XhOUHwEs9jTyS5_tfeJTQ_Ab4zKi27EFfb9NmA78xXEa1wyznVDoYvUy-PzemPPchEDezx1qrJkd0zMqnr5CJntSmfPCP22g0ljLscNUtUlbACT7xpIVXAe37XZ6_DBHOuAToleupFoyUKbNH3fRTc3FIrzexWt1m8RQALQ-QGDPljjFpnWjo3aiJQMZAu9FoZgdJn-qlbW0iYRFl91TAu8VAJ8bJJo8o3jbNdlggs9kNYFy3h15Zx3rnOUyA'
})
})

it('should be able to parse an access token with custom claims and special chars.', () => {
const accessToken =
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJvcmdhbml6YXRpb24iOnsiaWQiOiJlbldveEZNT25wIiwic2x1ZyI6InRoZS1ibHVlLWJyYW5kLTMiLCJlbnRlcnByaXNlIjpmYWxzZSwicmVnaW9uIjoiZXUtd2VzdC0xIn0sImFwcGxpY2F0aW9uIjp7ImlkIjoiR1J2RGlySmxERyIsImtpbmQiOiJ3ZWJhcHAiLCJwdWJsaWMiOmZhbHNlfSwiY3VzdG9tX2NsYWltIjp7ImN1c3RvbWVyIjp7ImZpcnN0X25hbWUiOiJKw7ZyZyIsImxhc3RfbmFtZSI6IkRvZSJ9fSwic2NvcGUiOiJtYXJrZXQ6YWxsIiwiZXhwIjoxNzI5Nzc5MTQ0LCJ0ZXN0Ijp0cnVlLCJyYW5kIjowLjY5NzY2MjgwNDM2NTg2OTUsImlhdCI6MTcyOTc3MTk0NCwiaXNzIjoiaHR0cHM6Ly9hdXRoLmNvbW1lcmNlbGF5ZXIuY28ifQ.jtOrSfSZMITiiYLqmu5p5oiY3pZnQSGNvDeN7nMQ7vpTByMvy-XQvY9YVCVxCwqDy3I_57c3HiEhgEz47crmv-4g4TpcfFjFqg4i2TQGHf0VNJJ5LOsh_cTUYNf13Q7Vy5jmrJ-sC8MJ0tEcs-f2ACizCJ_uRCqvybB-WwmeIf9A-ExiKN24Ku1Tw9aE0zjuTL3rmwNuR_6H1Umx6XzAQIRjNGL0vOTPTQ52-MrTwV3MYqFxlaWw0HZfr-0RgnUBhXN1LnW4sFYVmLLeYPJ1khYs3blcbKOJo5BQ8Fbnyj7E33Mx2B-3Z63y6uA8vTf45GzucZvIiHBXPeYLtUnglHv9KkhZYksb6xj4WMddZzKGC0r8LVl3ac8ZdWf00epLCBnIDkY8T94NgYF9xCWESB477x8rVLUs8WoKrnvrMzc7OZF2xk22q6Ajc24q9FOhnvymS674N_e1yI5QjNxxZHA5R-W79P-pXbm_nmCRqhKnewZupUKKehbm3SyLYmUndDilJNPkD7qxYZhlDr4rpd5VUpFONAU7qZznGgAak92RY-lJHh-RycyRRf8y-M2Q2jeQfXOrsTXAbK35-c2zvCDXBG7fYTc_fzGZFFMEvMpcfFw4PWfEFZTwxLRWWUHw2WYjg5da94c09z0oUWd2Lmp6P6Yw5vAqeHQn9A5x_y0'

expect(jwtDecode(accessToken)).toStrictEqual({
header: {
alg: 'RS512',
typ: 'JWT',
kid: 'aba4cc628d1fce3fb93a3ee55826e41cfaf18dc2dff3b07222740380e9199d5d'
},
payload: {
organization: {
id: 'enWoxFMOnp',
slug: 'the-blue-brand-3',
enterprise: false,
region: 'eu-west-1'
},
application: {
id: 'GRvDirJlDG',
kind: 'webapp',
public: false
},
custom_claim: {
customer: {
first_name: 'Jörg',
last_name: 'Doe'
}
},
scope: 'market:all',
exp: 1729779144,
test: true,
rand: 0.6976628043658695,
iat: 1729771944,
iss: 'https://auth.commercelayer.co'
},
signature:
'jtOrSfSZMITiiYLqmu5p5oiY3pZnQSGNvDeN7nMQ7vpTByMvy-XQvY9YVCVxCwqDy3I_57c3HiEhgEz47crmv-4g4TpcfFjFqg4i2TQGHf0VNJJ5LOsh_cTUYNf13Q7Vy5jmrJ-sC8MJ0tEcs-f2ACizCJ_uRCqvybB-WwmeIf9A-ExiKN24Ku1Tw9aE0zjuTL3rmwNuR_6H1Umx6XzAQIRjNGL0vOTPTQ52-MrTwV3MYqFxlaWw0HZfr-0RgnUBhXN1LnW4sFYVmLLeYPJ1khYs3blcbKOJo5BQ8Fbnyj7E33Mx2B-3Z63y6uA8vTf45GzucZvIiHBXPeYLtUnglHv9KkhZYksb6xj4WMddZzKGC0r8LVl3ac8ZdWf00epLCBnIDkY8T94NgYF9xCWESB477x8rVLUs8WoKrnvrMzc7OZF2xk22q6Ajc24q9FOhnvymS674N_e1yI5QjNxxZHA5R-W79P-pXbm_nmCRqhKnewZupUKKehbm3SyLYmUndDilJNPkD7qxYZhlDr4rpd5VUpFONAU7qZznGgAak92RY-lJHh-RycyRRf8y-M2Q2jeQfXOrsTXAbK35-c2zvCDXBG7fYTc_fzGZFFMEvMpcfFw4PWfEFZTwxLRWWUHw2WYjg5da94c09z0oUWd2Lmp6P6Yw5vAqeHQn9A5x_y0'
})
})
})
4 changes: 2 additions & 2 deletions packages/js-auth/src/jwtDecode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,8 @@ export function jwtDecode(accessToken: string): CommerceLayerJWT {
}

return {
header: JSON.parse(decodeBase64URLSafe(encodedHeader)),
payload: JSON.parse(decodeBase64URLSafe(encodedPayload)),
header: JSON.parse(decodeBase64URLSafe(encodedHeader, 'binary')),
payload: JSON.parse(decodeBase64URLSafe(encodedPayload, 'utf-8')),
signature
}
}
Expand Down
10 changes: 7 additions & 3 deletions packages/js-auth/src/jwtEncode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,13 +62,14 @@ async function jwtEncode(
): Promise<string> {
const header = { alg: 'HS512', typ: 'JWT' }

const encodedHeader = encodeBase64URLSafe(JSON.stringify(header))
const encodedHeader = encodeBase64URLSafe(JSON.stringify(header), 'binary')

const encodedPayload = encodeBase64URLSafe(
JSON.stringify({
...payload,
iat: Math.floor(new Date().getTime() / 1000)
})
}),
'utf-8'
)

const unsignedToken = `${encodedHeader}.${encodedPayload}`
Expand Down Expand Up @@ -96,5 +97,8 @@ async function createSignature(data: string, secret: string): Promise<string> {
enc.encode(data)
)

return encodeBase64URLSafe(String.fromCharCode(...new Uint8Array(signature)))
return encodeBase64URLSafe(
String.fromCharCode(...new Uint8Array(signature)),
'binary'
)
}
19 changes: 17 additions & 2 deletions packages/js-auth/src/jwtVerify.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,18 @@ describe('jwtVerify', () => {
expect(verification).toStrictEqual(jsonwebtokenDecoded)
})

it('should be able to verify a JWT with custom claims and special chars.', async () => {
const jsonwebtokenDecoded = jwt.decode(accessTokenCustomClaims, {
complete: true
})

const verification = await jwtVerify(accessTokenCustomClaims, {
ignoreExpiration: true
})

expect(verification).toStrictEqual(jsonwebtokenDecoded)
})

it('should cache in-memory the response from "jwks.json".', async () => {
// adds the 'fetchMock' global variable and rewires 'fetch' global to call 'fetchMock' instead of the real implementation
fetchMocker.enableMocks()
Expand Down Expand Up @@ -134,7 +146,7 @@ describe('jwtVerify', () => {

const newAccessToken = [
header,
encodeBase64URLSafe(JSON.stringify(newPayload)),
encodeBase64URLSafe(JSON.stringify(newPayload), 'utf-8'),
signature
].join('.')

Expand All @@ -155,7 +167,7 @@ describe('jwtVerify', () => {

const newAccessToken = [
header,
encodeBase64URLSafe(JSON.stringify(newPayload)),
encodeBase64URLSafe(JSON.stringify(newPayload), 'utf-8'),
signature
].join('.')

Expand All @@ -177,6 +189,9 @@ const accessTokenIo =
const accessTokenCo =
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJvcmdhbml6YXRpb24iOnsiaWQiOiJXWGxFT0ZiT25yIiwic2x1ZyI6ImRyb3AtaW4tanMtc3RnIiwiZW50ZXJwcmlzZSI6dHJ1ZSwicmVnaW9uIjoiZXUtd2VzdC0xIn0sImFwcGxpY2F0aW9uIjp7ImlkIjoiZ3BaYkRpZEtZcCIsImNsaWVudF9pZCI6ImdRTVNJTkx5TW0yVHJabzBVR0VFZHViQzd1U2dtOS1RVEc0ZVRWVVRWMW8iLCJraW5kIjoic2FsZXNfY2hhbm5lbCIsInB1YmxpYyI6dHJ1ZX0sIm1hcmtldCI6eyJpZCI6WyJxZ0xkQmhkbWdBIl0sInN0b2NrX2xvY2F0aW9uX2lkcyI6WyJKblZQZ3VEVmthIiwiQm5hSlF1dndHdyJdLCJnZW9jb2Rlcl9pZCI6bnVsbCwiYWxsb3dzX2V4dGVybmFsX3ByaWNlcyI6ZmFsc2V9LCJzY29wZSI6Im1hcmtldDppZDpxZ0xkQmhkbWdBIiwiZXhwIjoxNzI3MzkzMDM3LCJ0ZXN0Ijp0cnVlLCJyYW5kIjowLjY3MTMxNzc5Mjc4MTc0MjYsImlhdCI6MTcyNzM3ODYzNywiaXNzIjoiaHR0cHM6Ly9hdXRoLmNvbW1lcmNlbGF5ZXIuY28ifQ.kOf-6mwLCjn_dlFxc5SeaTE-4mFSq1JaVW7GCX_afUWSb5FZtb1OAjogqLqOBcm0nLb5XWdl8ZZyTvcgLlQenb8Cg-XX4r1Znd63nkBuHE3cRdbaMqlsGdbzixGzL3-puGCO1RmGBO2GcYoFQQMgSGMeLVLiadu4-NmSelMwQuLMGWmVVUFDZ99tn_6nWGInfBP_slKMwTrF7N3hXJHQIh3ZnwfTxGDC-rA_NZlHdWNMWFvLbfhwv_MrPkv0-sD0sTpndolK95ZKXm7L90dgL2HIrzpdS_gaWbCoqJTKLUPODHRYW6MWLoKwvo1pWT7biZncKF_4REGQiMVW7MivA4B-R5C_GRCEmDChdl9420f5cGXW1tZOge4r7mzYWyy5tIyiSjxg3MTpmCSvMadrtXgZ5d0ZRrQttPlr6B1Fi_6Um8WmImg64UQOYI4GgO3hJ23washNMW3O2M6pQMMcM1OaH3S7p2qtmlmqbYjXqeBrthDHpdjTPdsQzIc33fyg9GPSOIbCUGYzEFRnlXEpJer9E1Rm1FAlX8t5dWTUJcw_73broWzjd6VKwAnVWMNb6WjMc2xkfQu-8bJhM5hScY_Iy1Ui-HRBcoSfmrqXlhgM258ZamU6huiWzqQXUZOqWtupjQUz_K358mpSL_WuMHOfj-pZ70W7cnMxJCa3rbY'

const accessTokenCustomClaims =
'eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCIsImtpZCI6ImFiYTRjYzYyOGQxZmNlM2ZiOTNhM2VlNTU4MjZlNDFjZmFmMThkYzJkZmYzYjA3MjIyNzQwMzgwZTkxOTlkNWQifQ.eyJvcmdhbml6YXRpb24iOnsiaWQiOiJXWGxFT0ZiT25yIiwic2x1ZyI6ImRyb3AtaW4tanMtc3RnIiwiZW50ZXJwcmlzZSI6dHJ1ZSwicmVnaW9uIjoiZXUtd2VzdC0xIn0sImFwcGxpY2F0aW9uIjp7ImlkIjoiYU5hS21pZW9rTSIsImNsaWVudF9pZCI6IkVELVNYa3gybzJpTUUyM3lXc1dHY1M4VzFyWURrVVRBOW5UYW1TdkFBdEkiLCJraW5kIjoiaW50ZWdyYXRpb24iLCJwdWJsaWMiOmZhbHNlfSwiY3VzdG9tX2NsYWltIjp7ImN1c3RvbWVyIjp7ImZpcnN0X25hbWUiOiJKw7ZyZyIsImxhc3RfbmFtZSI6IkRvZSJ9fSwic2NvcGUiOiJtYXJrZXQ6YWxsIiwiZXhwIjoxNzI5Nzg5Mjk5LCJ0ZXN0Ijp0cnVlLCJyYW5kIjowLjgxMzE0MjI2ODMxNDY2NzcsImlhdCI6MTcyOTc4MjA5OSwiaXNzIjoiaHR0cHM6Ly9hdXRoLmNvbW1lcmNlbGF5ZXIuY28ifQ.Al5NKzAlka2V42c1nx8AQu7xX5DAx-elQvAiEsI5cXEBB3ZoBX4YEolizATj9e3eTuoQ6rolgPmNdR2_WF8-BnDvhiIDpUpdJ7O3DryNPauH0974d6UGBP9lXoIcHkosUtEu88yyPWhRAoIOPcoxgHVbKJCnREBqRSexPLuARImphYPex7VQwDKsoN1KE-fz40pPgFawr-OLx5nd0xQlkh1HW5wV-7WOtFKPX24ofzSy7pna8yiEsWSQ67_2NJ0XZ5_fhRYxUiy6ZSEWwPV8kXJHAYCbWSyX1gcwKrVnfO7-QuTjImZ7LzSTwYoQv1U68h6DPoy0kjd1q7K6htklcW7gjdDjowE8EP0_ZNpzQ1oErP8A70z4bnLV7SqU6zaTzEeJ4r1dj90luoY4l7Zo-12gDOtOZAd32SWDdJk_PvnZyt44050Zkx6a2qXO7EaSq2w-LcOSihDTI-NRQPLdbS4nRvRbM0UEOTGVWZlQ3iUFduImDaek3Vbi-BdRs37Gm-48pBb9_mfSzF4KQVPZM10FIKkX6R27OSw7oJ-_UkTKFfdUE2ifsJMhG7Q_ZqZnKcqICKID5TBL2GBbTQj62i6Nreq2pRtglkNHMoVEaJ4-t_i7J3og4a5Vg2zIujE2MhwSyhQP01YoEt7i61-UhorBA-ngXZTmpT_GkxtJZrk'

/**
* Generate private key: openssl genrsa -out ./packages/js-auth/src/private.key 4096
* Generate public key: openssl rsa -in ./packages/js-auth/src/private.key -pubout -outform PEM -out ./packages/js-auth/src/public.key
Expand Down
2 changes: 1 addition & 1 deletion packages/js-auth/src/jwtVerify.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export async function jwtVerify(
)

const rawSignature = new Uint8Array(
Array.from(decodeBase64URLSafe(decodedJWT.signature), (c) =>
Array.from(decodeBase64URLSafe(decodedJWT.signature, 'binary'), (c) =>
c.charCodeAt(0)
)
)
Expand Down
59 changes: 28 additions & 31 deletions packages/js-auth/src/utils/base64.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,72 +35,69 @@ describe('Using `btoa` and `atob`', () => {
function runTests(): void {
describe('encodeBase64UrlSafe', () => {
it('should be able to create a Base64 URL safe encoded ASCII string from a binary string.', () => {
expect(encodeBase64URLSafe('')).toEqual('')
expect(encodeBase64URLSafe('Hello, world')).toEqual('SGVsbG8sIHdvcmxk')
expect(encodeBase64URLSafe('', 'utf-8')).toEqual('')
expect(encodeBase64URLSafe('Hello, world', 'utf-8')).toEqual(
'SGVsbG8sIHdvcmxk'
)

expect(encodeBase64URLSafe(stringifiedObject)).toEqual(
expect(encodeBase64URLSafe(stringifiedObject, 'utf-8')).toEqual(
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSJ9fQ'
)

expect(encodeBase64URLSafe(stringifiedObjectWithSpecialChar)).toEqual(
expect(
encodeBase64URLSafe(stringifiedObjectWithSpecialChar, 'utf-8')
).toEqual(
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSsO2cmciLCJsYXN0X25hbWUiOiJEb2UifX0'
)

expect(
encodeBase64URLSafe(
'0\x82\x0760\x82\x06\x1E \x03\x02\x01\x02\x02\x10\tW¸\x13HxölÈÐ×\x12¨Ìµú0'
)
).toEqual('MIIHNjCCBh6gAwIBAgIQCVe4E0h49mzI0NcSqMy1-jA')

expect(encodeBase64URLSafe('subjects?_d=1')).toEqual('c3ViamVjdHM_X2Q9MQ')
expect(encodeBase64URLSafe('subjects?_d=1', 'utf-8')).toEqual(
'c3ViamVjdHM_X2Q9MQ'
)
})
})

describe('decodeBase64UrlSafe', () => {
it('should be able to decode a string of data which has been encoded using Base64 encoding.', () => {
expect(decodeBase64URLSafe('')).toEqual('')
expect(decodeBase64URLSafe('SGVsbG8sIHdvcmxk')).toEqual('Hello, world')
expect(decodeBase64URLSafe('', 'utf-8')).toEqual('')
expect(decodeBase64URLSafe('SGVsbG8sIHdvcmxk', 'utf-8')).toEqual(
'Hello, world'
)

expect(
decodeBase64URLSafe(
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSJ9fQ=='
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSJ9fQ==',
'utf-8'
)
).toEqual(stringifiedObject)

expect(
decodeBase64URLSafe('MIIHNjCCBh6gAwIBAgIQCVe4E0h49mzI0NcSqMy1+jA=')
).toEqual(
'0\x82\x0760\x82\x06\x1E \x03\x02\x01\x02\x02\x10\tW¸\x13HxölÈÐ×\x12¨Ìµú0'
)

expect(
decodeBase64URLSafe(
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSsO2cmciLCJsYXN0X25hbWUiOiJEb2UifX0'
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSsO2cmciLCJsYXN0X25hbWUiOiJEb2UifX0',
'utf-8'
)
).toEqual(stringifiedObjectWithSpecialChar)

expect(decodeBase64URLSafe('c3ViamVjdHM/X2Q9MQ==')).toEqual(
expect(decodeBase64URLSafe('c3ViamVjdHM/X2Q9MQ==', 'utf-8')).toEqual(
'subjects?_d=1'
)
})

it('should be able to decode a string of data which has been encoded using Base64 URL safe encoding.', () => {
expect(decodeBase64URLSafe('')).toEqual('')
expect(decodeBase64URLSafe('SGVsbG8sIHdvcmxk')).toEqual('Hello, world')
expect(decodeBase64URLSafe('', 'utf-8')).toEqual('')
expect(decodeBase64URLSafe('SGVsbG8sIHdvcmxk', 'utf-8')).toEqual(
'Hello, world'
)

expect(
decodeBase64URLSafe(
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSJ9fQ'
'eyJjdXN0b21lciI6eyJmaXJzdF9uYW1lIjoiSm9obiIsImxhc3RfbmFtZSI6IkRvZSJ9fQ',
'utf-8'
)
).toEqual(stringifiedObject)

expect(
decodeBase64URLSafe('MIIHNjCCBh6gAwIBAgIQCVe4E0h49mzI0NcSqMy1-jA')
).toEqual(
'0\x82\x0760\x82\x06\x1E \x03\x02\x01\x02\x02\x10\tW¸\x13HxölÈÐ×\x12¨Ìµú0'
expect(decodeBase64URLSafe('c3ViamVjdHM_X2Q9MQ', 'utf-8')).toEqual(
'subjects?_d=1'
)

expect(decodeBase64URLSafe('c3ViamVjdHM_X2Q9MQ')).toEqual('subjects?_d=1')
})
})
}
26 changes: 20 additions & 6 deletions packages/js-auth/src/utils/base64.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,19 @@
* @param stringToEncode The binary string to encode.
* @returns An ASCII string containing the Base64 URL safe representation of `stringToEncode`.
*/
export function encodeBase64URLSafe(stringToEncode: string): string {
export function encodeBase64URLSafe(
stringToEncode: string,
encoding: 'utf-8' | 'binary'
): string {
if (typeof btoa !== 'undefined') {
// Convert the string to a UTF-8 byte sequence before encoding
const utf8String =
encoding === 'utf-8'
? unescape(encodeURIComponent(stringToEncode))
: stringToEncode

return (
btoa(stringToEncode)
btoa(utf8String)
// Remove padding equal characters
.replaceAll('=', '')
// Replace characters according to base64url specifications
Expand All @@ -21,7 +30,7 @@ export function encodeBase64URLSafe(stringToEncode: string): string {
)
}

return Buffer.from(stringToEncode, 'binary').toString('base64url')
return Buffer.from(stringToEncode, encoding).toString('base64url')
}

/**
Expand All @@ -35,15 +44,20 @@ export function encodeBase64URLSafe(stringToEncode: string): string {
* @param encodedData A binary string (i.e., a string in which each character in the string is treated as a byte of binary data) containing Base64 URL safe -encoded data.
* @returns An ASCII string containing decoded data from `encodedData`.
*/
export function decodeBase64URLSafe(encodedData: string): string {
export function decodeBase64URLSafe(
encodedData: string,
encoding: 'utf-8' | 'binary'
): string {
if (typeof atob !== 'undefined') {
return atob(
const decoded = atob(
encodedData
// Replace characters according to base64url specifications
.replaceAll('-', '+')
.replaceAll('_', '/')
)

return encoding === 'utf-8' ? decodeURIComponent(escape(decoded)) : decoded
}

return Buffer.from(encodedData, 'base64url').toString('binary')
return Buffer.from(encodedData, 'base64url').toString(encoding)
}

0 comments on commit acbb77f

Please sign in to comment.