* Public File Sharing This implements public file sharing (read-only) with and without passwords (#57). It also fixes a problem with filenames including special characters like `#` not working properly (#71). You can share a directory or a single file, by using the new share icon on the right of the directories/files, and click on it to manage an existing file share (setting a new password, or deleting the file share). There is some other minor cleanup and other copy updates in the README. Closes #57 Fixes #71 * Hide UI elements when sharing isn't allowed
138 lines
3.8 KiB
TypeScript
138 lines
3.8 KiB
TypeScript
import { MultiFactorAuthMethodType } from '/lib/types.ts';
|
|
import PasswordlessPasskeyLogin from '/islands/auth/PasswordlessPasskeyLogin.tsx';
|
|
|
|
interface MultiFactorAuthVerifyFormProps {
|
|
email: string;
|
|
redirectUrl: string;
|
|
availableMethods: MultiFactorAuthMethodType[];
|
|
error?: { title: string; message: string };
|
|
}
|
|
|
|
export default function MultiFactorAuthVerifyForm(
|
|
{ email, redirectUrl, availableMethods, error }: MultiFactorAuthVerifyFormProps,
|
|
) {
|
|
const hasPasskey = availableMethods.includes('passkey');
|
|
const hasTotp = availableMethods.includes('totp');
|
|
const hasEmail = availableMethods.includes('email');
|
|
|
|
return (
|
|
<section class='max-w-md w-full mb-12'>
|
|
<section class='mb-6'>
|
|
<h2 class='mt-6 text-center text-3xl font-extrabold text-white'>
|
|
Multi-Factor Authentication
|
|
</h2>
|
|
<p class='mt-2 text-center text-sm text-gray-300'>
|
|
You are required to authenticate with an additional method
|
|
</p>
|
|
</section>
|
|
|
|
{error
|
|
? (
|
|
<section class='notification-error'>
|
|
<h3>{error.title}</h3>
|
|
<p>{error.message}</p>
|
|
</section>
|
|
)
|
|
: null}
|
|
|
|
{hasEmail
|
|
? (
|
|
<form
|
|
class='mb-6'
|
|
method='POST'
|
|
action={`/mfa-verify?redirect=${encodeURIComponent(redirectUrl)}`}
|
|
>
|
|
<fieldset class='block mb-4'>
|
|
<label class='text-slate-300 block pb-1' for='token'>
|
|
Email Verification Code
|
|
</label>
|
|
<input
|
|
type='text'
|
|
id='code'
|
|
name='code'
|
|
placeholder='123456'
|
|
class='mt-1 input-field'
|
|
autocomplete='off'
|
|
required
|
|
/>
|
|
</fieldset>
|
|
|
|
<section class='flex justify-center mt-8 mb-4'>
|
|
<button
|
|
type='submit'
|
|
class='button'
|
|
>
|
|
Verify Code
|
|
</button>
|
|
</section>
|
|
</form>
|
|
)
|
|
: null}
|
|
|
|
{hasEmail && hasTotp
|
|
? (
|
|
<section class='text-center -mt-10 mb-6 block'>
|
|
<p class='text-gray-400 text-sm'>or</p>
|
|
</section>
|
|
)
|
|
: null}
|
|
|
|
{hasTotp
|
|
? (
|
|
<form
|
|
class='mb-6'
|
|
method='POST'
|
|
action={`/mfa-verify?redirect=${encodeURIComponent(redirectUrl)}`}
|
|
>
|
|
<fieldset class='block mb-4'>
|
|
<label class='text-slate-300 block pb-1' for='token'>
|
|
Authentication Token or Backup Code
|
|
</label>
|
|
<input
|
|
type='text'
|
|
id='token'
|
|
name='token'
|
|
placeholder='123456 or backup code'
|
|
class='mt-1 input-field'
|
|
autocomplete='one-time-code'
|
|
required
|
|
/>
|
|
</fieldset>
|
|
|
|
<section class='flex justify-center mt-8 mb-4'>
|
|
<button
|
|
type='submit'
|
|
class='button'
|
|
>
|
|
Verify Code
|
|
</button>
|
|
</section>
|
|
</form>
|
|
)
|
|
: null}
|
|
|
|
{(hasEmail || hasTotp) && hasPasskey
|
|
? (
|
|
<section class='text-center -mt-10 mb-6 block'>
|
|
<p class='text-gray-400 text-sm'>or</p>
|
|
</section>
|
|
)
|
|
: null}
|
|
|
|
{hasPasskey && email
|
|
? (
|
|
<section class='mb-8'>
|
|
<PasswordlessPasskeyLogin email={email} redirectUrl={redirectUrl} />
|
|
</section>
|
|
)
|
|
: null}
|
|
|
|
<section class='text-center mt-6'>
|
|
<a href='/login' class='text-blue-400 hover:text-blue-300 text-sm'>
|
|
Back to Login
|
|
</a>
|
|
</section>
|
|
</section>
|
|
);
|
|
}
|