Account page
A ready-made settings page where users manage their profile, password, two-step verification and devices.
Every app eventually needs an "account settings" screen. Daywalker Auth hosts one for you at /account, with nothing to build.
What users can do there
| Section | What it does |
|---|---|
| Profile | Edit first and last name |
| Email address | See their address and whether it's verified |
| Password | Change it (needs the current password). Other devices are signed out. |
| Passkeys | Add one (needs a recent sign-in), rename or remove it |
| Two-step verification | Turn it on with a QR code, save backup codes, create new codes, or turn it off |
| Connected accounts | See which Google/GitHub accounts can sign in |
| Where you're signed in | See every device with browser, OS, approximate location and last activity; sign any of them out |
Linking to it
The easiest way is <UserButton>: its menu already has a Manage account item that opens the page and links back to where the user came from.
"use client";
import { UserButton } from "@daywalker/auth/react";
export function Header() {
return <UserButton afterSignOutUrl="/" />;
}For your own link, use hostedUrl:
"use client";
import { useAuthClient } from "@daywalker/auth/react";
export function SettingsLink() {
const client = useAuthClient();
return <a href={client.hostedUrl("account", window.location.href)}>Account settings</a>;
}The second argument becomes a ← Back to your app link at the top of the page. Like every redirect, it must be on your instance's allowed origins; otherwise the back link is hidden.
If the user isn't signed in when they open the page, they're asked to sign in first and then brought back to it.
Building your own settings screen
Everything the hosted page does is available in the SDK, so you can build the same screens in your own design:
"use client";
import { useEffect, useState } from "react";
import { useAuth, type SessionInfo } from "@daywalker/auth/react";
export function Devices() {
const { listSessions, revokeSession, revokeOtherSessions } = useAuth();
const [sessions, setSessions] = useState<SessionInfo[]>([]);
const reload = () => listSessions().then(setSessions);
useEffect(() => { reload(); }, []);
return (
<div>
<ul>
{sessions.map((s) => (
<li key={s.id}>
{s.device} {s.current ? "(this device)" : ""}, {s.ipHint ?? "unknown location"}
{!s.current && <button onClick={async () => { await revokeSession(s.id); reload(); }}>Sign out</button>}
</li>
))}
</ul>
<button onClick={async () => { await revokeOtherSessions(); reload(); }}>Sign out everywhere else</button>
</div>
);
}The other building blocks:
const { updateProfile, changePassword, getSecurity } = useAuth();
await updateProfile({ firstName: "Grace", lastName: null }); // null clears a field
const { otherSessionsSignedOut } = await changePassword({ currentPassword, newPassword });
const { hasPassword, twoFactorEnabled, backupCodesRemaining, connectedAccounts } = await getSecurity();For two-step verification setup, see Two-factor authentication.
Why it behaves the way it does
- Changing your password needs the current one. Otherwise someone who briefly got into a signed-in browser could lock the real owner out.
- Changing your password signs out every other device. If you're changing it because you think it leaked, the intruder loses access straight away.
- Turning off 2FA needs a current code, for the same reason.
- Locations are approximate. Only the start of the IP address is shown, enough to spot "that's not my city" without revealing the full address.
- Accounts created with Google/GitHub have no password. For them the Password section offers Set a password, which goes through the email-code reset flow, so a hijacked session can't quietly add one.