a tiny oauth browser client for atproto using a service worker
11
fork

Configure Feed

Select the types of activity you want to include in your feed.

Clean up code a bit

+19 -23
+1 -1
README.md
··· 1 1 # atsw.js 2 2 3 - A very minimal OAuth browser client for atproto using a service worker. Make authenticated requests to your PDS using plain `‌fetch` calls. Check out a [live demo](https://jake.tngl.io/atsw/). 3 + A tiny OAuth browser client for atproto using a service worker. Make authenticated requests to your PDS using plain `‌fetch` calls. Check out a [live demo](https://jake.tngl.io/atsw/). 4 4 5 5 **This is very experimental** — you should probably use [atcute](https://codeberg.org/mary-ext/atcute/src/branch/trunk/packages/oauth/browser-client) if you're building something real. 6 6
+9 -11
atsw.js
··· 82 82 * @param {string} [nonce] 83 83 * @param {string} [ath] 84 84 */ 85 - async function createDPoP(dpopKey, htm, htu, nonce, ath) { 86 - const header = { alg: "ES256", typ: "dpop+jwt", jwk: dpopKey.jwk }; 87 - 88 - const jti = randomB64url(16); 85 + async function createDPoP({ privateKey, jwk }, htm, htu, nonce, ath) { 86 + const header = { alg: "ES256", typ: "dpop+jwt", jwk }; 89 87 90 88 /** @type {Record<string, string | number>} */ 91 - const payload = { jti, htm, htu, iat: Math.floor(Date.now() / 1000) }; 89 + const payload = { htm, htu, jti: randomB64url(16), iat: Math.floor(Date.now() / 1000) }; 92 90 if (nonce) payload["nonce"] = nonce; 93 91 if (ath) payload["ath"] = ath; 94 92 95 - const toSign = [ 96 - b64url(enc.encode(JSON.stringify(header)).buffer), 97 - b64url(enc.encode(JSON.stringify(payload)).buffer), 98 - ].join("."); 99 - 100 - const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, dpopKey.privateKey, enc.encode(toSign)); 93 + const toSign = [header, payload] 94 + .map((part) => JSON.stringify(part)) 95 + .map((part) => enc.encode(part)) 96 + .map((part) => b64url(part.buffer)) 97 + .join("."); 98 + const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, privateKey, enc.encode(toSign)); 101 99 102 100 return toSign + "." + b64url(sig); 103 101 }
+9 -11
example/atsw.js
··· 82 82 * @param {string} [nonce] 83 83 * @param {string} [ath] 84 84 */ 85 - async function createDPoP(dpopKey, htm, htu, nonce, ath) { 86 - const header = { alg: "ES256", typ: "dpop+jwt", jwk: dpopKey.jwk }; 87 - 88 - const jti = randomB64url(16); 85 + async function createDPoP({ privateKey, jwk }, htm, htu, nonce, ath) { 86 + const header = { alg: "ES256", typ: "dpop+jwt", jwk }; 89 87 90 88 /** @type {Record<string, string | number>} */ 91 - const payload = { jti, htm, htu, iat: Math.floor(Date.now() / 1000) }; 89 + const payload = { htm, htu, jti: randomB64url(16), iat: Math.floor(Date.now() / 1000) }; 92 90 if (nonce) payload["nonce"] = nonce; 93 91 if (ath) payload["ath"] = ath; 94 92 95 - const toSign = [ 96 - b64url(enc.encode(JSON.stringify(header)).buffer), 97 - b64url(enc.encode(JSON.stringify(payload)).buffer), 98 - ].join("."); 99 - 100 - const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, dpopKey.privateKey, enc.encode(toSign)); 93 + const toSign = [header, payload] 94 + .map((part) => JSON.stringify(part)) 95 + .map((part) => enc.encode(part)) 96 + .map((part) => b64url(part.buffer)) 97 + .join("."); 98 + const sig = await crypto.subtle.sign({ name: "ECDSA", hash: "SHA-256" }, privateKey, enc.encode(toSign)); 101 99 102 100 return toSign + "." + b64url(sig); 103 101 }