Skip to content

Commit e6d7ec5

Browse files
committed
refactor(oauth-browser-client): use browser-native base64 serialization when possible
1 parent a04cfc9 commit e6d7ec5

File tree

5 files changed

+24
-36
lines changed

5 files changed

+24
-36
lines changed

.changeset/easy-parents-rest.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@atcute/oauth-browser-client': patch
3+
---
4+
5+
use browser-native base64 serialization when possible

packages/oauth/browser-client/lib/dpop.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
1+
import { fromBase64Url, toBase64Url } from '@atcute/multibase';
2+
13
import { database } from './environment.js';
24
import type { DPoPKey } from './types/dpop.js';
35
import { extractContentType } from './utils/response.js';
4-
import { encoder, fromBase64Url, generateJti, toBase64Url, toSha256 } from './utils/runtime.js';
6+
import { encoder, generateJti, stringToSha256 } from './utils/runtime.js';
57

68
const ES256_ALG = { name: 'ECDSA', namedCurve: 'P-256' } as const;
79

@@ -67,7 +69,7 @@ export const createDPoPFetch = (issuer: string, dpopKey: DPoPKey, isAuthServer?:
6769

6870
const authorizationHeader = request.headers.get('authorization');
6971
const ath = authorizationHeader?.startsWith('DPoP ')
70-
? await toSha256(authorizationHeader.slice(5))
72+
? await stringToSha256(authorizationHeader.slice(5))
7173
: undefined;
7274

7375
const { method, url } = request;

packages/oauth/browser-client/lib/utils/runtime.ts

+6-30
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,13 @@
1+
import { toBase64Url } from '@atcute/multibase';
2+
import { toSha256 } from '@atcute/uint8array';
3+
14
export const encoder = new TextEncoder();
25

36
export const locks: LockManager | undefined = typeof navigator !== 'undefined' ? navigator.locks : undefined;
47

5-
export const toBase64Url = (input: Uint8Array): string => {
6-
const CHUNK_SIZE = 0x8000;
7-
const arr = [];
8-
9-
for (let i = 0; i < input.byteLength; i += CHUNK_SIZE) {
10-
// @ts-expect-error
11-
arr.push(String.fromCharCode.apply(null, input.subarray(i, i + CHUNK_SIZE)));
12-
}
13-
14-
return btoa(arr.join('')).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
15-
};
16-
17-
export const fromBase64Url = (input: string): Uint8Array => {
18-
try {
19-
const binary = atob(input.replace(/-/g, '+').replace(/_/g, '/').replace(/\s/g, ''));
20-
const bytes = new Uint8Array(binary.length);
21-
22-
for (let i = 0; i < binary.length; i++) {
23-
bytes[i] = binary.charCodeAt(i);
24-
}
25-
26-
return bytes;
27-
} catch (err) {
28-
throw new TypeError(`invalid base64url`, { cause: err });
29-
}
30-
};
31-
32-
export const toSha256 = async (input: string): Promise<string> => {
8+
export const stringToSha256 = async (input: string): Promise<string> => {
339
const bytes = encoder.encode(input);
34-
const digest = await crypto.subtle.digest('SHA-256', bytes);
10+
const digest = await toSha256(bytes);
3511

3612
return toBase64Url(new Uint8Array(digest));
3713
};
@@ -49,7 +25,7 @@ export const generatePKCE = async (): Promise<{ verifier: string; challenge: str
4925

5026
return {
5127
verifier: verifier,
52-
challenge: await toSha256(verifier),
28+
challenge: await stringToSha256(verifier),
5329
method: 'S256',
5430
};
5531
};

packages/oauth/browser-client/package.json

+3-1
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,9 @@
2424
"prepublish": "rm -rf dist; pnpm run build"
2525
},
2626
"dependencies": {
27-
"@atcute/client": "workspace:^"
27+
"@atcute/client": "workspace:^",
28+
"@atcute/multibase": "workspace:^",
29+
"@atcute/uint8array": "workspace:^"
2830
},
2931
"devDependencies": {
3032
"@types/bun": "^1.2.1"

pnpm-lock.yaml

+6-3
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

0 commit comments

Comments
 (0)