diff --git a/JS/BlueskyAPI.js b/JS/BlueskyAPI.js index b00a3c5..b221667 100644 --- a/JS/BlueskyAPI.js +++ b/JS/BlueskyAPI.js @@ -1,8 +1,8 @@ import * as Cookie from "./Cookies.js"; export async function GetBlueskyDID(PDS, Handle) { - let DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/com.atproto.identity.resolveDid"); - return fetch(PDS + "/xrpc/com.atproto.identity.resolveDid?handle=" + Handle, { method: "GET", headers: {"Authorization": "DPoP " + Cookie.BlueskyAccessTokenCookie, "DPoP": DPoP}}); + let DPoP = await ClientDPoPPDS("GET", PDS + "/xrpc/com.atproto.identity.resolveHandle?handle=" + Handle); + return fetch(PDS + "/xrpc/com.atproto.identity.resolveHandle?handle=" + Handle, { method: "GET", headers: {"Authorization": "DPoP " + Cookie.BlueskyAccessTokenCookie, "DPoP": DPoP}}); } // Added after all the components: in case of nonce mismatch... @@ -51,12 +51,13 @@ export async function AuthRequest(TokenEndpoint, code, DPoP, Verify) { } // Component 4/4 -export async function ClientDPoPToken(POSTorGET, RequestURL) { - let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]); +export async function ClientDPoPToken(POSTorGET, RequestURL) { + let PublicKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPublicKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"]); + let PrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPrivateKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["sign"]); // Header var Header = {typ: "dpop+jwt", alg: "ES256", jwk: - await crypto.subtle.exportKey("jwk", KeyPair.publicKey) + await crypto.subtle.exportKey("jwk", PublicKey) .then(function(response) { delete response["key_ops"]; delete response["ext"]; @@ -75,7 +76,7 @@ export async function ClientDPoPToken(POSTorGET, RequestURL) { var sHeader = JSON.stringify(Header); var sPayload = JSON.stringify(Payload); var JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, - await crypto.subtle.exportKey("jwk", KeyPair.privateKey) + await crypto.subtle.exportKey("jwk", PrivateKey) .then(function(response) { delete response["key_ops"]; delete response["ext"]; @@ -87,11 +88,12 @@ export async function ClientDPoPToken(POSTorGET, RequestURL) { // So far does nothing? Don't touch :3 export async function ClientDPoPPDS(POSTorGET, RequestURL) { - let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]); + let PublicKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPublicKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["verify"]); + let PrivateKey = await crypto.subtle.importKey("jwk", JSON.parse(Cookie.BlueskyPrivateKeyCookie), {name: "ECDSA", namedCurve: "P-256"}, true, ["sign"]); // Header var Header = {typ: "dpop+jwt", alg: "ES256", jwk: - await crypto.subtle.exportKey("jwk", KeyPair.publicKey) + await crypto.subtle.exportKey("jwk", PublicKey) .then(function(response) { delete response["key_ops"]; delete response["ext"]; @@ -111,7 +113,7 @@ export async function ClientDPoPPDS(POSTorGET, RequestURL) { var sHeader = JSON.stringify(Header); var sPayload = JSON.stringify(Payload); var JWT = KJUR.jws.JWS.sign("ES256", sHeader, sPayload, - await crypto.subtle.exportKey("jwk", KeyPair.privateKey) + await crypto.subtle.exportKey("jwk", PrivateKey) .then(function(response) { delete response["key_ops"]; delete response["ext"]; @@ -140,12 +142,12 @@ export async function HandleAuthorization() { let nonce = await PAR.then((response) => response.headers.get("dpop-nonce")); // Save nonce Cookie.InputCookie(Cookie.BlueskyNonceName, nonce); - - let ExportedKey1 = await crypto.subtle.exportKey("raw", KeyPair.publicKey); - // Don't remove the keys. They are important. - // if (document.cookie.split("; ").find((row) => row.startsWith(BlueskyPublicKey + "="))?.split("=").length == 1 || document.cookie.split("; ").find((row) => row.startsWith(BlueskyPrivateKey + "="))?.split("=").length == 1) { - // document.cookie = BlueskyPublicKey + "=" + ExportedKey1 + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;"; - // } + // Export keys + let ExportedKey1 = await crypto.subtle.exportKey("jwk", KeyPair.publicKey); + let ExportedKey2 = await crypto.subtle.exportKey("jwk", KeyPair.privateKey); + // Convert them into a good format. + Cookie.InputCookie(Cookie.BlueskyPublicKeyName, JSON.stringify(ExportedKey1)); + Cookie.InputCookie(Cookie.BlueskyPrivateKeyName, JSON.stringify(ExportedKey2)); // Now we need to authenticate. Make sure the State stays the same throughout this whole process :] document.location.href = "https://bsky.social/oauth/authorize?client_id=https://fedi.crowdedgames.group/oauth/client-metadata.json&request_uri=" + body.request_uri; } @@ -187,3 +189,18 @@ async function sha256(message) { return string; } + +// Firefox snippet. +function ab2str(buf) { + return String.fromCharCode.apply(null, new Uint8Array(buf)); +} + +// Firefox snippet. +function str2ab(str) { + const buf = new ArrayBuffer(str.length); + const bufView = new Uint8Array(buf); + for (let i = 0, strLen = str.length; i < strLen; i++) { + bufView[i] = str.charCodeAt(i); + } + return buf; +} diff --git a/JS/Cookies.js b/JS/Cookies.js index e28a82e..ce5b587 100644 --- a/JS/Cookies.js +++ b/JS/Cookies.js @@ -12,6 +12,7 @@ export const BlueskyPDSName = "https://bsky.social"; export const BlueskyPKCEVeriferName = "bluesky_pkce_verifier"; export const BlueskyPKCEChallengeName = "bluesky_pkce_challenge"; export const BlueskyPublicKeyName = "bluesky_public_key"; +export const BlueskyPrivateKeyName = "bluesky_private_key"; export const BlueskyNonceName = "bluesky_nonce"; export const BlueskyAccessTokenName = "bluesky_access_token"; export const BlueskyRefreshTokenName = "bluesky_refresh_token"; @@ -30,6 +31,7 @@ export const MastodonTokenTypeCookie = GetCookie(MastodonTokenTypeName); export const BlueskyPKCEVeriferCookie = GetCookie(BlueskyPKCEVeriferName); export const BlueskyPKCEChallengeCookie = GetCookie(BlueskyPKCEChallengeName); export const BlueskyPublicKeyCookie = GetCookie(BlueskyPublicKeyName); +export const BlueskyPrivateKeyCookie = GetCookie(BlueskyPrivateKeyName); export const BlueskyNonceCookie = GetCookie(BlueskyNonceName); export const BlueskyAccessTokenCookie = GetCookie(BlueskyAccessTokenName); export const BlueskyRefreshTokenCookie = GetCookie(BlueskyRefreshTokenName); diff --git a/JS/setting.js b/JS/setting.js index 9d42584..4a8abd3 100644 --- a/JS/setting.js +++ b/JS/setting.js @@ -63,6 +63,10 @@ BlueskyLogoutButton.onclick = (event) => { Cookie.ExpireCookie(Cookie.BlueskyPKCEVeriferName); Cookie.ExpireCookie(Cookie.BlueskyPKCEChallengeName); Cookie.ExpireCookie(Cookie.BlueskyNonceName); + Cookie.ExpireCookie(Cookie.BlueskyAccessTokenName); + Cookie.ExpireCookie(Cookie.BlueskyRefreshTokenName); + Cookie.ExpireCookie(Cookie.BlueskyPublicKeyName); + Cookie.ExpireCookie(Cookie.BlueskyPrivateKeyName); document.location.href = document.location.href; } @@ -98,12 +102,14 @@ CheckLogin(); async function TESTING() { let response = BlueskyAPI.GetBlueskyDID("https://woodear.us-west.host.bsky.network", "crowdedgames.group"); let body = await response.then((res) => res.json()); - if (response.status == 401) { - await BlueskyAPI.FixNonceMismatch(response.headers.get("dpop-nonce")); + let status = await response.then((res) => res.status); + let header = await response.then((res) => res.headers.get("dpop-nonce")); + if (status == 401) { + await BlueskyAPI.FixNonceMismatch(header); response = BlueskyAPI.GetBlueskyDID("https://woodear.us-west.host.bsky.network", "crowdedgames.group"); body = await response.then((res) => res.json()); } - console.log(response); + console.log(body); } TESTING();