Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 11 additions & 19 deletions vtc-service/admin-ui/src/lib/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -125,28 +125,23 @@ export function isWalletAvailable(): boolean {
);
}

/** True iff the wallet additionally exposes the proxy-login + vault-list
* APIs (newer extension builds). */
/** True iff the wallet exposes the whole proxy-login surface this page drives:
* `walletProfile` to resolve the identity, `proxyLogin` to mint as it, and
* `vaultList` for the pick-a-different-identity path.
*
* Presence detection, not version negotiation — the extension may simply not
* be installed. There is deliberately no separate probe per method: every
* build that has one has all three, so a second probe would only describe a
* wallet that does not exist. */
export function isWalletProxyAvailable(): boolean {
return (
isWalletAvailable() &&
typeof window.vtaWallet?.walletProfile === "function" &&
typeof window.vtaWallet?.proxyLogin === "function" &&
typeof window.vtaWallet?.vaultList === "function"
);
}

/** True iff the wallet can resolve-or-bind a persona for this origin itself.
*
* A capability probe, not a compatibility fold: without it the proxy path can
* only work for an operator who has already bound an entry by hand, and the
* difference is worth an accurate message rather than a `TypeError`. */
export function isWalletProfileAvailable(): boolean {
return (
isWalletProxyAvailable() &&
typeof window.vtaWallet?.walletProfile === "function"
);
}

/** API base for the wallet's auth round-trip. Points at the VTC's
* header-exempt wallet surface, served same-origin with the admin UI. */
function walletApiBase(): string {
Expand Down Expand Up @@ -243,11 +238,8 @@ export async function loginWithWalletProxy(
* `proxyLogin` as a single call.
*/
export async function loginWithWalletProfile(): Promise<VtaWalletLoginResult> {
if (!isWalletProfileAvailable()) {
throw new Error(
"This VTA wallet build cannot choose an identity for a site. " +
"Update the extension, or pin a did-self-issued vault entry to this VTC by hand.",
);
if (!isWalletProxyAvailable()) {
throw new Error("VTA wallet doesn't expose proxy-login APIs.");
}
const rp = await rpDid();
const profile = await window.vtaWallet!.walletProfile!({
Expand Down
51 changes: 23 additions & 28 deletions vtc-service/admin-ui/src/pages/Login.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ import {
} from "@/lib/webauthn";
import {
isWalletAvailable,
isWalletProfileAvailable,
isWalletProxyAvailable,
listProxyCandidates,
loginWithWallet,
Expand Down Expand Up @@ -54,7 +53,6 @@ export function Login() {

const walletAvailable = isWalletAvailable();
const proxyAvailable = isWalletProxyAvailable();
const profileAvailable = isWalletProfileAvailable();
const busy = phase.kind === "running" || walletPhase.kind === "running";

// Shared success tail: the wallet returned a bearer; mirror it into the
Expand Down Expand Up @@ -267,33 +265,30 @@ export function Login() {
</p>
)}

{profileAvailable && (
<button
type="button"
className="secondary"
onClick={handleProxyStart}
disabled={busy}
>
Sign in via VTA-proxied SIOP
</button>
)}

{/* Secondary, and worded as the exception it is. The primary button
uses whichever identity the wallet has bound to this site; this is
for an operator holding more than one here. On a wallet too old to
resolve an identity itself it is the only proxy route, so it stays
visible in that case. */}
{proxyAvailable && (
<button
type="button"
className="link"
onClick={handleChooseIdentity}
disabled={busy}
>
{profileAvailable
? "Sign in as a different identity…"
: "Sign in via VTA-proxied SIOP (choose an entry)"}
</button>
<>
<button
type="button"
className="secondary"
onClick={handleProxyStart}
disabled={busy}
>
Sign in via VTA-proxied SIOP
</button>

{/* Secondary, and worded as the exception it is. The button above
uses whichever identity the wallet has bound to this site; this
is for an operator holding more than one here, and it costs a
consent prompt that enumerates the vault to this page. */}
<button
type="button"
className="link"
onClick={handleChooseIdentity}
disabled={busy}
>
Sign in as a different identity…
</button>
</>
)}

{candidates && (
Expand Down