Discord sign-in: the browser route is the password route, and says so
The choice reads QR code / Password, and the Password side signs in on discord.com in a window moho opens - 'Sign in on discord.com' - saying that this is where an email or phone and password go. The two-factor prompt and its state are gone with the daemon flow they answered: Discord's own page asks for the code now. Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
+1
-1
Submodule nobilis updated: 3a5b747401...c3c399dc2b
@@ -950,11 +950,9 @@ function DiscordForm({ accountId }: { accountId?: string }): JSX.Element {
|
||||
const store = useStore()
|
||||
const qrPath = useChat((s) => s.discordQrPath)
|
||||
const status = useChat((s) => s.discordLoginStatus)
|
||||
const mfa = useChat((s) => s.discordMfa)
|
||||
const [method, setMethod] = useState<'qr' | 'browser'>('qr')
|
||||
// No password state any more, deliberately: the password is typed into
|
||||
// Discord's own page in the sign-in window, so this process never holds one.
|
||||
const [code, setCode] = useState('')
|
||||
const [busy, setBusy] = useState(false)
|
||||
// Only for a new account: re-authenticating keeps the account's own setting.
|
||||
const [useTor, setUseTor] = useState(false)
|
||||
@@ -965,65 +963,6 @@ function DiscordForm({ accountId }: { accountId?: string }): JSX.Element {
|
||||
setBusy(false)
|
||||
}
|
||||
|
||||
// The two-factor step replaces the form: the login is already in flight and
|
||||
// only needs the code to finish.
|
||||
if (mfa) {
|
||||
return (
|
||||
<div className="add-form">
|
||||
<p className="small muted">
|
||||
{mfa.totp
|
||||
? 'Enter the 6-digit code from your authenticator app.'
|
||||
: 'Enter your verification code.'}
|
||||
{mfa.backup && ' A backup code works here too.'}
|
||||
</p>
|
||||
<div className="field-row">
|
||||
<input
|
||||
className="text-field"
|
||||
autoFocus
|
||||
inputMode="numeric"
|
||||
autoComplete="one-time-code"
|
||||
placeholder="123456"
|
||||
value={code}
|
||||
onChange={(e) => setCode(e.target.value)}
|
||||
onKeyDown={(e) => {
|
||||
if (e.key === 'Enter' && code.trim()) {
|
||||
void window.moho
|
||||
.rpc('submitDiscordMfa', { loginId: mfa.loginId, code })
|
||||
.catch(fail)
|
||||
}
|
||||
}}
|
||||
/>
|
||||
<button
|
||||
type="button"
|
||||
className="button"
|
||||
disabled={!code.trim()}
|
||||
onClick={() =>
|
||||
void window.moho.rpc('submitDiscordMfa', { loginId: mfa.loginId, code }).catch(fail)
|
||||
}
|
||||
>
|
||||
Verify
|
||||
</button>
|
||||
</div>
|
||||
{/* When re-authenticating, the surrounding block already offers a
|
||||
Cancel that backs out of the whole thing - two stacked Cancels
|
||||
would just be a question of which one you meant. */}
|
||||
{!accountId && (
|
||||
<button
|
||||
type="button"
|
||||
className="button subtle"
|
||||
onClick={() => {
|
||||
store.clearDiscordMfa()
|
||||
setCode('')
|
||||
}}
|
||||
>
|
||||
Cancel
|
||||
</button>
|
||||
)}
|
||||
{status && <p className="small muted">{status}</p>}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="add-form">
|
||||
{!accountId && <TorSwitch service="discord" on={useTor} onChange={setUseTor} />}
|
||||
@@ -1040,7 +979,7 @@ function DiscordForm({ accountId }: { accountId?: string }): JSX.Element {
|
||||
className={method === 'browser' ? 'active' : undefined}
|
||||
onClick={() => setMethod('browser')}
|
||||
>
|
||||
Browser
|
||||
Password
|
||||
</button>
|
||||
</div>
|
||||
|
||||
@@ -1069,11 +1008,15 @@ function DiscordForm({ accountId }: { accountId?: string }): JSX.Element {
|
||||
</>
|
||||
) : (
|
||||
<>
|
||||
{/* The password route, and the only one: a password typed into a
|
||||
form of moho's own was tried and removed, because Discord
|
||||
refuses it from a third-party client and counts each attempt
|
||||
against the account. */}
|
||||
<p className="small muted">
|
||||
Opens Discord's own sign-in page in a browser window. Your password goes into
|
||||
their form and never passes through moho — and because it is their page, their
|
||||
captcha, two-factor and device checks all work normally. The window is thrown away
|
||||
afterwards, session and all.
|
||||
Sign in with your email or phone and password on Discord's own page, in a window
|
||||
moho opens. Your password goes into their form and never passes through moho — and
|
||||
because it is their page, their captcha, two-factor and new-device checks all work as
|
||||
they do anywhere. The window is thrown away afterwards, session and all.
|
||||
</p>
|
||||
<button
|
||||
type="button"
|
||||
@@ -1095,7 +1038,7 @@ function DiscordForm({ accountId }: { accountId?: string }): JSX.Element {
|
||||
.catch(fail)
|
||||
}}
|
||||
>
|
||||
{busy ? 'Waiting…' : accountId ? 'Re-authenticate in a browser' : 'Sign in with a browser'}
|
||||
{busy ? 'Waiting…' : accountId ? 'Re-authenticate on discord.com' : 'Sign in on discord.com'}
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
@@ -637,8 +637,6 @@ export interface ChatState {
|
||||
// Login flows
|
||||
discordQrPath: string
|
||||
discordLoginStatus: string
|
||||
/** Set when a Discord password login stops at the two-factor step. */
|
||||
discordMfa: { loginId: string; totp: boolean; sms: boolean; backup: boolean } | null
|
||||
/** The account being re-authenticated, or '' for a brand-new one. */
|
||||
discordReauthAccountId: string
|
||||
sneedChatLoginStatus: string
|
||||
@@ -727,7 +725,6 @@ const INITIAL: ChatState = {
|
||||
matrixVerification: null,
|
||||
discordQrPath: '',
|
||||
discordLoginStatus: '',
|
||||
discordMfa: null,
|
||||
discordReauthAccountId: '',
|
||||
sneedChatLoginStatus: '',
|
||||
matrixLoginStatus: ''
|
||||
@@ -3278,24 +3275,12 @@ export class ChatStore {
|
||||
case 'discordLoginScanned':
|
||||
this.set({ discordLoginStatus: 'Scanned - approve it on your phone' })
|
||||
break
|
||||
case 'discordLoginMfa':
|
||||
this.set({
|
||||
discordMfa: {
|
||||
loginId: data.loginId,
|
||||
totp: !!data.totp,
|
||||
sms: !!data.sms,
|
||||
backup: !!data.backup
|
||||
},
|
||||
discordLoginStatus: ''
|
||||
})
|
||||
break
|
||||
|
||||
case 'discordLoginResult':
|
||||
this.set({
|
||||
discordQrPath: '',
|
||||
discordLoginStatus: data.error || '',
|
||||
// A failure leaves the form open to try again; success clears it.
|
||||
discordMfa: data.error ? this.state.discordMfa : null,
|
||||
discordReauthAccountId: data.error ? this.state.discordReauthAccountId : ''
|
||||
})
|
||||
if (!data.error) {
|
||||
@@ -4538,14 +4523,10 @@ export class ChatStore {
|
||||
this.set({
|
||||
discordReauthAccountId: accountId,
|
||||
discordQrPath: '',
|
||||
discordLoginStatus: '',
|
||||
discordMfa: null
|
||||
discordLoginStatus: ''
|
||||
})
|
||||
}
|
||||
|
||||
clearDiscordMfa(): void {
|
||||
this.set({ discordMfa: null })
|
||||
}
|
||||
setSneedChatLoginStatus(status: string): void {
|
||||
this.set({ sneedChatLoginStatus: status })
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user