mirror of
https://github.com/supabase/supabase.git
synced 2026-05-31 18:03:33 +08:00
feat: add password strength configs (#19330)
* feat: add password strength configs * Revert the changes to RadioGroup since they may cause issues in other usecases. Add a string to represent the empty string state for PASSWORD_REQUIRED_CHARACTERS property. --------- Co-authored-by: Ivan Vasilov <vasilov.ivan@gmail.com>
This commit is contained in:
committed by
GitHub
parent
4e48e3c831
commit
eed4e29db5
@@ -31,7 +31,13 @@ import UpgradeToPro from 'components/ui/UpgradeToPro'
|
||||
import { useAuthConfigQuery } from 'data/auth/auth-config-query'
|
||||
import { useAuthConfigUpdateMutation } from 'data/auth/auth-config-update-mutation'
|
||||
import { useOrgSubscriptionQuery } from 'data/subscriptions/org-subscription-query'
|
||||
import { useCheckPermissions, useSelectedOrganization, useStore, useFlag } from 'hooks'
|
||||
import { useCheckPermissions, useFlag, useSelectedOrganization, useStore } from 'hooks'
|
||||
|
||||
// Use a const string to represent no chars option. Represented as empty string on the backend side.
|
||||
const NO_REQUIRED_CHARACTERS = 'NO_REQUIRED_CHARS'
|
||||
const LETTERS_AND_DIGITS = 'abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
|
||||
const LOWER_UPPER_DIGITS = 'abcdefghijklmnopqrstuvwxyz:ABCDEFGHIJKLMNOPQRSTUVWXYZ:0123456789'
|
||||
const LOWER_UPPER_DIGITS_SYMBOLS = LOWER_UPPER_DIGITS + ':!@#$%^&*()_+-=[]{};\'\\\\:"|<>?,./`~'
|
||||
|
||||
const schema = object({
|
||||
DISABLE_SIGNUP: boolean().required(),
|
||||
@@ -50,6 +56,8 @@ const schema = object({
|
||||
SESSIONS_TIMEBOX: number().min(0, 'Must be a positive number'),
|
||||
SESSIONS_INACTIVITY_TIMEOUT: number().min(0, 'Must be a positive number'),
|
||||
SESSIONS_SINGLE_PER_USER: boolean(),
|
||||
PASSWORD_MIN_LENGTH: number().min(6, 'Must be greater or equal to 6.'),
|
||||
PASSWORD_REQUIRED_CHARACTERS: string(),
|
||||
})
|
||||
|
||||
function HoursOrNeverText({ value }: { value: number }) {
|
||||
@@ -85,6 +93,7 @@ const BasicAuthSettingsForm = observer(() => {
|
||||
|
||||
const isProPlanAndUp = isSuccessSubscription && subscription?.plan?.id !== 'free'
|
||||
const singlePerUserReleased = useFlag('authSingleSessionPerUserReleased')
|
||||
const passwordStrengthReleased = useFlag('authPasswordStrengthReleased')
|
||||
|
||||
const INITIAL_VALUES = {
|
||||
DISABLE_SIGNUP: !authConfig?.DISABLE_SIGNUP,
|
||||
@@ -100,11 +109,23 @@ const BasicAuthSettingsForm = observer(() => {
|
||||
SESSIONS_SINGLE_PER_USER: authConfig?.SESSIONS_SINGLE_PER_USER || false,
|
||||
}
|
||||
: null),
|
||||
|
||||
...(passwordStrengthReleased
|
||||
? {
|
||||
PASSWORD_MIN_LENGTH: authConfig?.PASSWORD_MIN_LENGTH || 6,
|
||||
PASSWORD_REQUIRED_CHARACTERS:
|
||||
authConfig?.PASSWORD_REQUIRED_CHARACTERS || NO_REQUIRED_CHARACTERS,
|
||||
}
|
||||
: null),
|
||||
}
|
||||
|
||||
const onSubmit = (values: any, { resetForm }: any) => {
|
||||
const payload = { ...values }
|
||||
payload.DISABLE_SIGNUP = !values.DISABLE_SIGNUP
|
||||
// The backend uses empty string to represent no required characters in the password
|
||||
if (payload.PASSWORD_REQUIRED_CHARACTERS === NO_REQUIRED_CHARACTERS) {
|
||||
payload.PASSWORD_REQUIRED_CHARACTERS = ''
|
||||
}
|
||||
|
||||
updateAuthConfig(
|
||||
{ projectRef: projectRef!, config: payload },
|
||||
@@ -184,6 +205,56 @@ const BasicAuthSettingsForm = observer(() => {
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
<div className="border-t border-muted"></div>
|
||||
{passwordStrengthReleased && (
|
||||
<>
|
||||
<FormSection header={<FormSectionLabel>Passwords</FormSectionLabel>}>
|
||||
<FormSectionContent loading={isLoading}>
|
||||
<InputNumber
|
||||
id="PASSWORD_MIN_LENGTH"
|
||||
size="small"
|
||||
label="Minimum password length"
|
||||
descriptionText="Passwords shorter than this value will be rejected as weak. Minimum 6, recommended 8 or more."
|
||||
actions={<span className="mr-3 text-foreground-lighter">characters</span>}
|
||||
disabled={!canUpdateConfig}
|
||||
/>
|
||||
<>
|
||||
<Radio.Group
|
||||
id="PASSWORD_REQUIRED_CHARACTERS"
|
||||
name="PASSWORD_REQUIRED_CHARACTERS"
|
||||
label="Required characters"
|
||||
descriptionText="Passwords that do not have at least one of each will be rejected as weak."
|
||||
>
|
||||
<Radio
|
||||
label="No required characters"
|
||||
value={NO_REQUIRED_CHARACTERS}
|
||||
checked={values.PASSWORD_REQUIRED_CHARACTERS === NO_REQUIRED_CHARACTERS}
|
||||
description="(default)"
|
||||
/>
|
||||
<Radio
|
||||
label="Letters and digits"
|
||||
value={LETTERS_AND_DIGITS}
|
||||
checked={values.PASSWORD_REQUIRED_CHARACTERS === LETTERS_AND_DIGITS}
|
||||
/>
|
||||
<Radio
|
||||
label="Lowercase, uppercase letters and digits"
|
||||
value={LOWER_UPPER_DIGITS}
|
||||
checked={values.PASSWORD_REQUIRED_CHARACTERS === LOWER_UPPER_DIGITS}
|
||||
/>
|
||||
<Radio
|
||||
label="Lowercase, uppercase letters, digits and symbols"
|
||||
value={LOWER_UPPER_DIGITS_SYMBOLS}
|
||||
checked={
|
||||
values.PASSWORD_REQUIRED_CHARACTERS === LOWER_UPPER_DIGITS_SYMBOLS
|
||||
}
|
||||
description="(recommended)"
|
||||
/>
|
||||
</Radio.Group>
|
||||
</>
|
||||
</FormSectionContent>
|
||||
</FormSection>
|
||||
<div className="border-t border-muted"></div>
|
||||
</>
|
||||
)}
|
||||
<FormSection header={<FormSectionLabel>User Sessions</FormSectionLabel>}>
|
||||
<FormSectionContent loading={isLoading}>
|
||||
{isProPlanAndUp ? (
|
||||
|
||||
Reference in New Issue
Block a user