diff options
author | Vika <vika@fireburn.ru> | 2022-10-02 11:54:30 +0300 |
---|---|---|
committer | Vika <vika@fireburn.ru> | 2022-10-02 11:54:30 +0300 |
commit | 568f98589b6c30bb3f807517d28039b12dd54be3 (patch) | |
tree | 885f918885b246d3760cf3b1f2a252b88485f358 /kittybox-rs/companion-lite/src/indieauth.ts | |
parent | b7d4e5c4686bc8aac41d832567190002542a1743 (diff) | |
download | kittybox-568f98589b6c30bb3f807517d28039b12dd54be3.tar.zst |
companion-lite: rewrite to use IndieAuth
This is a naive implementation that doesn't have some security checks. It's ok tho, should work fine... can refine it later
Diffstat (limited to 'kittybox-rs/companion-lite/src/indieauth.ts')
-rw-r--r-- | kittybox-rs/companion-lite/src/indieauth.ts | 113 |
1 files changed, 113 insertions, 0 deletions
diff --git a/kittybox-rs/companion-lite/src/indieauth.ts b/kittybox-rs/companion-lite/src/indieauth.ts new file mode 100644 index 0000000..40facab --- /dev/null +++ b/kittybox-rs/companion-lite/src/indieauth.ts @@ -0,0 +1,113 @@ +// @ts-ignore +import { mf2 } from "https://esm.sh/microformats-parser@1.4.1?pin=v96" +import { MF2 } from "./micropub_api.js" +import base64 from "./base64.js" + /* + const { mf2 }: { + mf2: (html: string, options: { + baseUrl: string, + experimental?: { lang?: boolean, textContent?: boolean } + }) => { + items: MF2[], + rels: {[key: string]: string[]}, + "rel-urls": {[key: string]: { rels: string[], text?: string }} + } + } = + // @ts-ignore + await import("https://esm.sh/microformats-parser@1.4.1?pin=v96"); + */ + +interface IndieauthMetadata { + authorization_endpoint: string, + token_endpoint: string, + issuer: string, + introspection_endpoint?: string, + introspection_endpoint_auth_methods_supported?: ("Bearer")[], + revocation_endpoint?: string, + revocation_endpoint_auth_methods_supported?: ["none"], + scopes_supported?: string[], + response_types_supported: ["code"], + grant_types_supported: ("authorization_code" | "refresh_token")[] + code_challenge_methods_supported: ("S256")[] + authorization_response_iss_parameter_supported: true, + userinfo_endpoint?: string +} + +interface MF2ParsedData { + items: MF2[], + rels: {[key: string]: string[]}, + "rel-urls": {[key: string]: { rels: string[], text?: string }} +} + +export interface IndiewebEndpoints { + authorization_endpoint: URL, + token_endpoint: URL, + userinfo_endpoint: URL | null, + revocation_endpoint: URL | null, + micropub: URL, + +} + +export function create_verifier() { + const array = new Uint8Array(64) + crypto.getRandomValues(array) + + return array.reduce((str, byte) => str + byte.toString(16).padStart(2, '0'), '') +} + +export async function create_challenge(verifier: string): Promise<string> { + return await crypto.subtle.digest('SHA-256', Uint8Array.from(verifier, c => c.charCodeAt(0))) + .then((buf) => base64.encode(new Uint8Array(buf))) + .then(s => { + return s + .replaceAll("+", "-") + .replaceAll("/", "_") + .replaceAll(/=$/g, "") + }) +} + +export async function discover_endpoints(me: URL): Promise<IndiewebEndpoints | null> { + const response = await fetch(me); + const data: MF2ParsedData = mf2(await response.text(), { baseUrl: me.toString() }); + let endpoints: Partial<IndiewebEndpoints> = {}; + if ("micropub" in data.rels) { + endpoints.micropub = new URL(data.rels.micropub[0]) + } else { + return null + } + if ("indieauth_metadata" in data.rels) { + const metadata_response = await fetch(data.rels.indieauth_metadata[0], { + headers: { + "Accept": "application/json" + } + }); + + const metadata = await metadata_response.json() as IndieauthMetadata; + endpoints.authorization_endpoint = new URL(metadata.authorization_endpoint) + endpoints.token_endpoint = new URL(metadata.token_endpoint) + if (metadata.userinfo_endpoint != null) { + endpoints.userinfo_endpoint = new URL(metadata.userinfo_endpoint) + } else { + endpoints.userinfo_endpoint = null + } + if (metadata.revocation_endpoint != null) { + endpoints.revocation_endpoint = new URL(metadata.revocation_endpoint) + } else { + endpoints.revocation_endpoint = null + } + + return endpoints as IndiewebEndpoints + } else if ( + "authorization_endpoint" in data.rels + && "token_endpoint" in data.rels + ) { + endpoints.authorization_endpoint = new URL(data.rels.authorization_endpoint[0]) + endpoints.token_endpoint = new URL(data.rels.token_endpoint[0]) + endpoints.userinfo_endpoint = null + endpoints.revocation_endpoint = null + + return endpoints as IndiewebEndpoints + } else { + return null + } +} |