diff --git a/package.json b/package.json index e775a18..08a08f9 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ }, "dependencies": { "@atcute/client": "^2.0.4", + "@atcute/oauth-browser-client": "^1.0.5", "@solidjs/router": "^0.15.1", "hls.js": "^1.5.17", "public-transport": "file:pkg/pt.tgz", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 70909eb..a1c2ab4 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -11,6 +11,9 @@ importers: '@atcute/client': specifier: ^2.0.4 version: 2.0.4 + '@atcute/oauth-browser-client': + specifier: ^1.0.5 + version: 1.0.5 '@solidjs/router': specifier: ^0.15.1 version: 0.15.1(solid-js@1.9.3) @@ -61,6 +64,9 @@ packages: '@atcute/client@2.0.4': resolution: {integrity: sha512-bKA6KEOmrdhU2CDRNp13M4WyKN0EdrVLKJffzPo62ANSTMacz5hRJhmvQYwuo7BZSGIoDql4sH+QR6Xbk3DERg==} + '@atcute/oauth-browser-client@1.0.5': + resolution: {integrity: sha512-UUs2WFMh22rXOapRM848WfWtvgaxV/ji0tEupFrrBYe2i+/UlwhXcphlqdwm43LBsFtMWtV1Xsy2zmnItf0Akg==} + '@babel/code-frame@7.26.2': resolution: {integrity: sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==} engines: {node: '>=6.9.0'} @@ -914,6 +920,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + nanoid@5.0.8: + resolution: {integrity: sha512-TcJPw+9RV9dibz1hHUzlLVy8N4X9TnwirAjrU08Juo6BNKggzVfP2ZJ/3ZUSq15Xl5i85i+Z89XBO90pB2PghQ==} + engines: {node: ^18 || >=20} + hasBin: true + node-fetch-native@1.6.4: resolution: {integrity: sha512-IhOigYzAKHd244OC0JIMIUrjzctirCmPkaIfhDeGcEETWof5zKYUW7e7MYvChGWh/4CJeXEgsRyGzuF334rOOQ==} @@ -1204,6 +1215,11 @@ snapshots: '@atcute/client@2.0.4': {} + '@atcute/oauth-browser-client@1.0.5': + dependencies: + '@atcute/client': 2.0.4 + nanoid: 5.0.8 + '@babel/code-frame@7.26.2': dependencies: '@babel/helper-validator-identifier': 7.25.9 @@ -2031,6 +2047,8 @@ snapshots: nanoid@3.3.7: {} + nanoid@5.0.8: {} + node-fetch-native@1.6.4: {} node-releases@2.0.18: {} diff --git a/public/client-metadata.json b/public/client-metadata.json new file mode 100644 index 0000000..638b744 --- /dev/null +++ b/public/client-metadata.json @@ -0,0 +1,12 @@ +{ + "client_id": "https://pdsls.dev/client-metadata.json", + "client_name": "pdsls", + "client_uri": "https://pdsls.dev", + "redirect_uris": ["https://pdsls.dev/"], + "scope": "atproto transition:generic", + "grant_types": ["authorization_code", "refresh_token"], + "response_types": ["code"], + "token_endpoint_auth_method": "none", + "application_type": "web", + "dpop_bound_access_tokens": true +} diff --git a/src/lib/json.tsx b/src/components/json.tsx similarity index 100% rename from src/lib/json.tsx rename to src/components/json.tsx diff --git a/src/components/login.tsx b/src/components/login.tsx new file mode 100644 index 0000000..2962d9a --- /dev/null +++ b/src/components/login.tsx @@ -0,0 +1,159 @@ +import { createSignal, onMount, Show, type Component } from "solid-js"; +import { + configureOAuth, + createAuthorizationUrl, + finalizeAuthorization, + getSession, + OAuthUserAgent, + resolveFromIdentity, + type Session, +} from "@atcute/oauth-browser-client"; +import { At } from "@atcute/client/lexicons"; + +configureOAuth({ + metadata: { + client_id: import.meta.env.VITE_OAUTH_CLIENT_ID, + redirect_uri: import.meta.env.VITE_OAUTH_REDIRECT_URL, + }, +}); + +const [loginState, setLoginState] = createSignal(false); +const [notice, setNotice] = createSignal(""); +const [handle, setHandle] = createSignal(""); +let agent: OAuthUserAgent; + +const resolveDid = async (did: string) => { + const res = await fetch( + did.startsWith("did:web") ? + `https://${did.split(":")[2]}/.well-known/did.json` + : "https://plc.directory/" + did, + ); + + return res + .json() + .then((doc) => { + for (const alias of doc.alsoKnownAs) { + if (alias.includes("at://")) { + return alias.split("//")[1]; + } + } + }) + .catch(() => ""); +}; + +const Login: Component = () => { + const [loginInput, setLoginInput] = createSignal(""); + + const loginBsky = async (handle: string) => { + try { + setNotice(`Resolving your identity...`); + const resolved = await resolveFromIdentity(handle); + + setNotice(`Contacting your data server...`); + const authUrl = await createAuthorizationUrl({ + scope: import.meta.env.VITE_OAUTH_SCOPE, + ...resolved, + }); + + setNotice(`Redirecting...`); + await new Promise((resolve) => setTimeout(resolve, 250)); + + location.assign(authUrl); + } catch { + setNotice("Error during OAuth login"); + } + }; + + return ( +