147 lines
6.2 KiB
JavaScript
147 lines
6.2 KiB
JavaScript
export async function GetBlueskyDID(PDS, Handle) {
|
|
let request = fetch(PDS + "/xrpc/com.atproto.identity.resolveDid?handle=" + Handle, { method: "GET"})
|
|
.then((response) => response.json());
|
|
return request;
|
|
}
|
|
|
|
// Component 1/4
|
|
export async function GetPDSWellKnown() {
|
|
let Data = await fetch("https://bsky.social/.well-known/oauth-authorization-server", {method: "GET"})
|
|
.then((response) => response.json());
|
|
return Data;
|
|
}
|
|
|
|
// Component 2/4
|
|
// Many thanks to https://github.com/tonyxu-io/pkce-generator. It was the base for this code.
|
|
export async function CreatePKCECodeVerifier() {
|
|
// Generate some Numbers
|
|
let Numbers = new Uint8Array(32);
|
|
crypto.getRandomValues(Numbers);
|
|
// Generate a random string of characters.
|
|
let CodeVerifier = "";
|
|
for (let i in Numbers) {
|
|
CodeVerifier += String.fromCharCode(Numbers[i]);
|
|
}
|
|
// Put this random string into Base64URL format.
|
|
CodeVerifier = btoa(CodeVerifier).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
return CodeVerifier;
|
|
}
|
|
|
|
export async function CreatePKCECodeChallenge(CodeVerifier) {
|
|
// Generate a code challenge with the code verifier.
|
|
// This is done by first SHA256 encrypting the CodeVerifier, then putting the outputted string into Base64URL format.
|
|
let CodeChallenge = btoa(await sha256(CodeVerifier)).replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
|
return CodeChallenge;
|
|
}
|
|
|
|
// Component 3/4
|
|
export async function PARrequest(PAREndpoint, State, ChallengeCode) {
|
|
return fetch(PAREndpoint, {method: "POST", body: new URLSearchParams({ response_type: "code", code_challenge_method: "S256", scope: "atproto transition:generic", client_id: "https://fedi.crowdedgames.group/oauth/client-metadata.json", redirect_uri: "https://fedi.crowdedgames.group/HTML/setting.html", code_challenge: ChallengeCode, state: State, login_hint: "crowdedgames.group" }), "Content-Type": "application/x-www-form-urlencoded"});
|
|
}
|
|
|
|
// Component 4/4
|
|
export async function ClientDPoP(POSTorGET, RequestURL, DPoPNonce, publicKey) {
|
|
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]);
|
|
|
|
// Header
|
|
var Header = {alg: "HS256", typ: "dpop+jwt", jwk: await crypto.subtle.exportKey("jwk", KeyPair.publicKey).then(function(response) {return response})};
|
|
// Payload
|
|
var Payload = {};
|
|
Payload.jti = GenerateToken(64);
|
|
Payload.htm = POSTorGET;
|
|
Payload.htu = RequestURL;
|
|
Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000);
|
|
Payload.nonce = DPoPNonce;
|
|
|
|
var sHeader = JSON.stringify(Header);
|
|
var sPayload = JSON.stringify(Payload);
|
|
var JWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, "616161");
|
|
console.log(JWT);
|
|
return JWT;
|
|
}
|
|
|
|
// So far does nothing? Don't touch :3
|
|
export async function AssertionJWT() {
|
|
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]);
|
|
|
|
// Header
|
|
var Header = {alg: "HS256", kid: await crypto.subtle.exportKey("jwk", KeyPair.publicKey).then(function(response) {return response})};
|
|
// Payload
|
|
var Payload = {};
|
|
|
|
Payload.iss = "https://fedi.crowdedgames.group/oauth/client-metadata.json";
|
|
Payload.sub = "https://fedi.crowdedgames.group/oauth/client-metadata.json";
|
|
// Payload.aud
|
|
Payload.jti = GenerateToken(64);
|
|
Payload.iat = Math.floor(new Date(Date.now()).getTime() / 1000);
|
|
|
|
var sHeader = JSON.stringify(Header);
|
|
var sPayload = JSON.stringify(Payload);
|
|
var JWT = KJUR.jws.JWS.sign("HS256", sHeader, sPayload, "838383");
|
|
}
|
|
|
|
export async function HandleAuthorization(BlueskyPKCEverifer, BlueskyPKCEchallenge, BlueskyNonce) {
|
|
// Declare Variables
|
|
let KeyPair = await crypto.subtle.generateKey({name: "ECDSA", namedCurve: "P-256"}, true, ["sign", "verify"]);
|
|
|
|
let WellKnown = await GetPDSWellKnown();
|
|
let PAREndpoint = WellKnown.pushed_authorization_request_endpoint;
|
|
|
|
let State = GenerateToken(64);
|
|
|
|
let PKCEverifier = await CreatePKCECodeVerifier();
|
|
let PKCEchallenge = await CreatePKCECodeChallenge(PKCEverifier);
|
|
// PAR request (beginning)
|
|
let PAR = PARrequest(WellKnown.pushed_authorization_request_endpoint, State, PKCEchallenge);
|
|
let body = await PAR.then((response) => response.json());
|
|
let nonce = await PAR.then((response) => response.headers.get("dpop-nonce"));
|
|
|
|
let ExportedKey1 = await crypto.subtle.exportKey("raw", KeyPair.publicKey);
|
|
// Save these things.
|
|
document.cookie = BlueskyPKCEverifer + "=" + PKCEverifier + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;";
|
|
document.cookie = BlueskyPKCEchallenge + "=" + PKCEchallenge + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;";
|
|
document.cookie = BlueskyNonce + "=" + nonce + ";samesite=strict;path=/;expires=Fri, 31 Dec 9999 23:59:59 GMT;";
|
|
// 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;";
|
|
// }
|
|
// 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;
|
|
}
|
|
|
|
export async function GainTokens() {
|
|
|
|
}
|
|
|
|
// Stolen from elsewhere.
|
|
// Firefox snippet; Slightly edited.
|
|
async function sha256(message) {
|
|
// encode as UTF-8
|
|
const MessageBuffer = new TextEncoder().encode(message);
|
|
|
|
// hash the message
|
|
const HashBuffer = await crypto.subtle.digest('SHA-256', MessageBuffer);
|
|
|
|
// convert ArrayBuffer to Array
|
|
const HashArray = Array.from(new Uint8Array(HashBuffer));
|
|
|
|
// convert this hashArray to a string
|
|
let string = "";
|
|
for (let i in HashArray) {
|
|
string += String.fromCharCode(HashArray[i]);
|
|
}
|
|
|
|
return string;
|
|
}
|
|
|
|
// Stolen from Search
|
|
// TODO: implement my own function.
|
|
export function GenerateToken(length) {
|
|
var chars = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789';
|
|
var token = '';
|
|
for(var i = 0; i < length; i++) {
|
|
token += chars[Math.floor(Math.random() * chars.length)];
|
|
}
|
|
return token;
|
|
}
|
|
|