Skip to content

Commit

Permalink
Persist the parameters in the client implementation guide
Browse files Browse the repository at this point in the history
  • Loading branch information
sandhose committed Oct 17, 2024
1 parent d7b0ce0 commit 4a54e6a
Show file tree
Hide file tree
Showing 4 changed files with 73 additions and 27 deletions.
19 changes: 19 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
"@astrojs/react": "^3.6.2",
"@fontsource/inconsolata": "^5.1.0",
"@fontsource/inter": "^5.1.0",
"@nanostores/persistent": "^0.10.2",
"@nanostores/react": "^0.7.3",
"@tanstack/react-query": "^5.56.2",
"@vector-im/compound-design-tokens": "^1.8.0",
Expand All @@ -26,5 +27,6 @@
"react": "^18.3.1",
"react-dom": "^18.3.1",
"typescript": "^5.6.2"
}
},
"packageManager": "[email protected]+sha512.faf344af2d6ca65c4c5c8c2224ea77a81a5e8859cbc4e06b1511ddce2f0151512431dd19e6aff31f2c6a8f5f2aced9bd2273e1fed7dd4de1868984059d2c4247"
}
2 changes: 1 addition & 1 deletion src/pages/client-implementation-guide/callback.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { CallbackParameters } from "./interactive";
This is the callback page.
We received the following parameters:

<CallbackParameters client:only />
<CallbackParameters client:only="react" />

You can now get the `code` parameter and close this window.
75 changes: 50 additions & 25 deletions src/pages/client-implementation-guide/interactive.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,14 @@ import {
} from "@vector-im/compound-design-tokens/assets/web/icons";
import { useStore } from "@nanostores/react";
import { Alert, Button, Form } from "@vector-im/compound-web";
import { atom, type WritableAtom } from "nanostores";
import { atom, computed, task, type WritableAtom } from "nanostores";
import { persistentAtom } from "@nanostores/persistent";
import type React from "react";
import { useEffect, useState } from "react";
import cx from "classnames";
import { QueryClient, useMutation, useQuery } from "@tanstack/react-query";

import styles from "./style.module.css";
import { useEffect, useState } from "react";

const baseUrl = new URL(import.meta.env.BASE_URL, import.meta.env.SITE);
const clientUri = baseUrl.toString();
Expand All @@ -20,27 +21,42 @@ const redirectUri = new URL(
).toString();

const queryClient = atom(new QueryClient());
const csApi = atom("https://synapse-oidc.element.dev/");
const issuer = atom("https://auth-oidc.element.dev/");
const state = atom("ieXei8ohb7miesie");
const serverMetadata = atom<ServerMetadata>({
authorization_endpoint: "https://auth-oidc.element.dev/authorize",
token_endpoint: "https://auth-oidc.element.dev/oauth2/token",
registration_endpoint: "https://auth-oidc.element.dev/oauth2/registration",
const csApi = persistentAtom<string>(
"cs-api",
"https://synapse-oidc.element.dev/",
);
const issuer = persistentAtom<string>(
"issuer",
"https://auth-oidc.element.dev/",
);
const state = persistentAtom<string>("state", "ieXei8ohb7miesie");
const serverMetadata = persistentAtom<ServerMetadata>(
"server-metadata",
{
authorization_endpoint: "https://auth-oidc.element.dev/authorize",
token_endpoint: "https://auth-oidc.element.dev/oauth2/token",
registration_endpoint: "https://auth-oidc.element.dev/oauth2/registration",
},
{ encode: JSON.stringify, decode: JSON.parse },
);
const clientId = persistentAtom<string | null>("client-id", null, {
encode: JSON.stringify,
decode: JSON.parse,
});
const clientId = atom<string | null>(null);
const codeVerifier = atom<string>(
const codeVerifier = persistentAtom<string>(
"code-verifier",
"ahlae7FuMahCeeseip6Shooqu6aefai5xoocea5gav2",
);
const codeChallenge = atom<string>(
"KjgOR3AZvytATpbxHdwNEYRdpwWMF7Va2zfAauJyoYo",
const codeChallenge = computed(codeVerifier, (codeVerifier) =>
task(() => computeCodeChallenge(codeVerifier)),
);
const code = persistentAtom("code");

const computeCodeChallenge = async (codeVerifier: string): Promise<string> => {
// Hash the verifier
const encoder = new TextEncoder();
const data = encoder.encode(codeVerifier);
const hash = await window.crypto.subtle.digest("SHA-256", data);
const hash = await globalThis.crypto.subtle.digest("SHA-256", data);
// Base64 encode the hash
let str = "";
const bytes = new Uint8Array(hash);
Expand Down Expand Up @@ -218,15 +234,6 @@ export const RandomStringField: React.FC<RandomStringFormProps> = ({

export const AuthParametersForm = () => {
const $codeChallenge = useStore(codeChallenge);
const $codeVerifier = useStore(codeVerifier);

useEffect(() => {
const compute = async () => {
const newCodeChallenge = await computeCodeChallenge($codeVerifier);
codeChallenge.set(newCodeChallenge);
};
compute();
}, [$codeVerifier]);

return (
<div className={cx(styles["form-wrapper"])}>
Expand Down Expand Up @@ -397,6 +404,7 @@ export const CodeExchangeForm = () => {
const $clientId = useStore(clientId) || "";
const $serverMetadata = useStore(serverMetadata);
const $codeVerifier = useStore(codeVerifier);
const $code = useStore(code);
const $queryClient = useStore(queryClient);

const [response, setResponse] = useState<null | object>(null);
Expand Down Expand Up @@ -437,6 +445,12 @@ export const CodeExchangeForm = () => {
mutation.mutate(code);
};

const onCodeInput = (e: React.KeyboardEvent<HTMLInputElement>) => {
e.preventDefault();
const input = e.currentTarget.value;
code.set(input);
};

return (
<div className={cx(styles["form-wrapper"])}>
<Form.Root className={cx(styles.form)} onSubmit={onSubmit}>
Expand Down Expand Up @@ -467,7 +481,12 @@ export const CodeExchangeForm = () => {
</Form.Field>
<Form.Field name="code">
<Form.Label>Code</Form.Label>
<Form.TextControl type="text" required />
<Form.TextControl
type="text"
value={$code}
onInput={onCodeInput}
required
/>
<Form.ErrorMessage match="valueMissing">
This field is required
</Form.ErrorMessage>
Expand Down Expand Up @@ -519,7 +538,7 @@ export const DisplayAuthorizationUrl: React.FC = () => {
"urn:matrix:org.matrix.msc2967.client:api:* urn:matrix:org.matrix.msc2967.client:device:ABCDEFGHIJKL",
state: $state,
code_challenge_method: "S256",
code_challenge: $codeChallenge,
code_challenge: $codeChallenge || "",
} satisfies Record<string, string>;

const query = new URLSearchParams(params).toString();
Expand Down Expand Up @@ -556,5 +575,11 @@ export const CallbackParameters = () => {
const stripped = hash.startsWith("#") ? hash.slice(1) : hash;
const params = new URLSearchParams(stripped);

useEffect(() => {
if (params.get("code")) {
code.set(params.get("code") as string);
}
}, [params]);

return <DataViewer data={Object.fromEntries(params)} />;
};

0 comments on commit 4a54e6a

Please sign in to comment.