Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New Auth Methods #8119

Merged
merged 74 commits into from
Oct 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
74 commits
Select commit Hold shift + click to select a range
113212a
wip: show all login methods
douxc Aug 20, 2024
38bfc3d
wip: update sign methods
douxc Aug 21, 2024
a8a47e7
wip: auth methods
douxc Aug 23, 2024
d64507d
feat: add check verification code page
douxc Aug 23, 2024
5508666
fix: use constant variable replace magic number
douxc Aug 23, 2024
137edf4
fix: merge previous version sso login component
douxc Aug 23, 2024
d0af586
fix: style update
douxc Aug 26, 2024
bfd2966
wip: update invite process
douxc Aug 26, 2024
caf842a
wip: update invite logic
douxc Aug 28, 2024
cea867c
wip: stash
douxc Aug 29, 2024
a2214e3
feat: supports email & code login
douxc Aug 29, 2024
b398dbf
feat: supports sso login
douxc Aug 29, 2024
8eb9a01
feat: now you can reset your password
douxc Aug 30, 2024
1b648fa
fix: hide integrations in settings menu
douxc Aug 30, 2024
db67e22
feat: new login methods
douxc Sep 3, 2024
db7119d
fix: page redirect to invite-settings when url not contain invite_token
douxc Sep 3, 2024
cab439a
chore: use remixicon
douxc Sep 4, 2024
870d278
fix: update translate
douxc Sep 4, 2024
27d5182
fix: add white space between words\symbols\numbers
douxc Sep 4, 2024
36efee4
fix: redirect to reset password page when account not exist
douxc Sep 4, 2024
ee5ef32
--amend
douxc Sep 4, 2024
c35aab5
fix: countdown not update when rensend mail
douxc Sep 9, 2024
a3bd688
fix: Username is required now when you set your account info
douxc Sep 9, 2024
1708735
fix: link error while click forgot password at login page
douxc Sep 9, 2024
7278fd6
fix: lint
douxc Sep 9, 2024
0d4b180
fix: move countdown to components
douxc Sep 9, 2024
82656d4
fix: OAuth link update
douxc Sep 9, 2024
0e72b7b
revert yarn.lock
douxc Sep 9, 2024
afc489d
fix: use enum define protocols
douxc Sep 11, 2024
5641348
chore: use base component Input instead native html input
douxc Sep 11, 2024
d74409b
Merge branch 'main' into feat/auth-methods
douxc Sep 11, 2024
01400b8
Chore: use Button instead native button
douxc Sep 12, 2024
cedba21
fix: use constant variable instead magic number
douxc Sep 12, 2024
6e7b6e9
fix: page will rediect to signin in 5s after reset password
douxc Sep 12, 2024
8526c73
fix: use color variable instead of hex value
douxc Sep 12, 2024
0483cc8
style: use css class name instead of multi tailwind varibles
douxc Sep 12, 2024
3cdc352
Merge branch 'main' into feat/auth-methods
douxc Sep 19, 2024
badcf56
Merge branch 'main' into feat/auth-methods
douxc Sep 23, 2024
c5fa49a
feat:support use tab change focus between inputs at login page
douxc Sep 23, 2024
519fcbc
fix: disable login button if email or password is empty
douxc Sep 26, 2024
3a84c21
fix: update the translations for the reset-password page
douxc Sep 26, 2024
4dad2ec
fix: fix the issue that if there is only one input field in the page …
douxc Sep 26, 2024
ed9853b
fix: add a language parameter to the send email code API
douxc Sep 26, 2024
6e36bc8
fix: add a whitespace between the words
douxc Sep 27, 2024
7096aa6
fix: if the invite link is expired,the page will show tips
douxc Sep 27, 2024
1062bd3
fix: Handle the logic of mutual redirection between the email verific…
douxc Sep 27, 2024
0b0e0e3
fix: update the invite logic so that the page redirects to the sign-i…
douxc Sep 29, 2024
64f1588
fix: the page redirects to the activation page, so that we can rechec…
douxc Sep 29, 2024
8e33929
fix: The default value of authType is 'password', which will cause an…
douxc Sep 30, 2024
26dc51a
fix: Prioritize display of email password login
douxc Sep 30, 2024
0c5404d
fix: if the email and password login feature is enabled, show the res…
douxc Sep 30, 2024
e28af42
fix: use the browser timezone as the value of Timezone field, America…
douxc Sep 30, 2024
ef4988c
Merge branch 'main' into feat/auth-methods
douxc Oct 14, 2024
5d12b19
fix: merge refresh_token
douxc Oct 14, 2024
d572ad3
Merge branch 'main' into feat/auth-methods
douxc Oct 14, 2024
5dd29d8
fix: save the access_token cause an error when login with email&password
douxc Oct 14, 2024
7f9278e
fix: save access_token & refresh_token throw an error when set invite…
douxc Oct 15, 2024
f557a67
fix: add the 'language' param to the login API
douxc Oct 15, 2024
1e11c64
fix: update the set-password Page's translate;fix: the email & code l…
douxc Oct 15, 2024
8c65050
fix: validate password before login
douxc Oct 15, 2024
5473efa
fix: add show/hide control for password inputs at sign-in and set-pas…
douxc Oct 15, 2024
f9891b9
style: show underline on link hover
douxc Oct 15, 2024
d3fbe10
fix: tooltip error while password invalid
douxc Oct 15, 2024
9f3147d
feat: add show/hide supports for all password input
douxc Oct 15, 2024
c0d757c
fix: show the reset password button when email and password login is …
douxc Oct 15, 2024
344662f
fix: display message if no auth methods are enabled
douxc Oct 15, 2024
055b27b
feat: show tooltip when registration is not allowed
douxc Oct 16, 2024
ef6c114
fix: get registration config from system features
douxc Oct 16, 2024
2077e35
style: update style of the login page when there is no auth methods e…
douxc Oct 17, 2024
097486f
fix: show error tip if reset-password API returns 'account_not_found'…
douxc Oct 17, 2024
eb83336
fix: remove console.log
douxc Oct 18, 2024
e1deeac
fix: check if setup is finished before validating access_token
douxc Oct 18, 2024
8b7f7c8
fix: page will redirect to /apps when request account page
douxc Oct 18, 2024
1bbe0c5
fix: use 'code' as the unique key for errors
douxc Oct 18, 2024
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
79 changes: 57 additions & 22 deletions web/app/account/account-page/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { ToastContext } from '@/app/components/base/toast'
import AppIcon from '@/app/components/base/app-icon'
import Avatar from '@/app/components/base/avatar'
import { IS_CE_EDITION } from '@/config'
import Input from '@/app/components/base/input'

const titleClassName = `
text-sm font-medium text-gray-900
Expand All @@ -31,6 +32,7 @@ const validPassword = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/

export default function AccountPage() {
const { t } = useTranslation()
const { systemFeatures } = useAppContext()
const { mutateUserProfile, userProfile, apps } = useAppContext()
const { notify } = useContext(ToastContext)
const [editNameModalVisible, setEditNameModalVisible] = useState(false)
Expand All @@ -41,6 +43,9 @@ export default function AccountPage() {
const [password, setPassword] = useState('')
const [confirmPassword, setConfirmPassword] = useState('')
const [showDeleteAccountModal, setShowDeleteAccountModal] = useState(false)
const [showCurrentPassword, setShowCurrentPassword] = useState(false)
const [showPassword, setShowPassword] = useState(false)
const [showConfirmPassword, setShowConfirmPassword] = useState(false)

const handleEditName = () => {
setEditNameModalVisible(true)
Expand Down Expand Up @@ -158,8 +163,8 @@ export default function AccountPage() {
</div>
</div>
{
IS_CE_EDITION && (
<div className='mb-8 flex justify-between'>
systemFeatures.enable_email_password_login && (
<div className='mb-8 flex justify-between gap-2'>
<div>
<div className='mb-1 text-sm font-medium text-gray-900'>{t('common.account.password')}</div>
<div className='mb-2 text-xs text-gray-500'>{t('common.account.passwordTip')}</div>
Expand Down Expand Up @@ -191,8 +196,7 @@ export default function AccountPage() {
>
<div className='mb-6 text-lg font-medium text-gray-900'>{t('common.account.editName')}</div>
<div className={titleClassName}>{t('common.account.name')}</div>
<input
className={inputClassName}
<Input className='mt-2'
value={editName}
onChange={e => setEditName(e.target.value)}
/>
Expand Down Expand Up @@ -223,30 +227,61 @@ export default function AccountPage() {
{userProfile.is_password_set && (
<>
<div className={titleClassName}>{t('common.account.currentPassword')}</div>
<input
type="password"
className={inputClassName}
value={currentPassword}
onChange={e => setCurrentPassword(e.target.value)}
/>
<div className='relative mt-2'>
<Input
type={showCurrentPassword ? 'text' : 'password'}
value={currentPassword}
onChange={e => setCurrentPassword(e.target.value)}
/>

<div className="absolute inset-y-0 right-0 flex items-center">
<Button
type="button"
variant='ghost'
onClick={() => setShowCurrentPassword(!showCurrentPassword)}
>
{showCurrentPassword ? '👀' : '😝'}
</Button>
</div>
</div>
</>
)}
<div className='mt-8 text-sm font-medium text-gray-900'>
{userProfile.is_password_set ? t('common.account.newPassword') : t('common.account.password')}
</div>
<input
type="password"
className={inputClassName}
value={password}
onChange={e => setPassword(e.target.value)}
/>
<div className='relative mt-2'>
<Input
type={showPassword ? 'text' : 'password'}
value={password}
onChange={e => setPassword(e.target.value)}
/>
<div className="absolute inset-y-0 right-0 flex items-center">
<Button
type="button"
variant='ghost'
onClick={() => setShowPassword(!showPassword)}
>
{showPassword ? '👀' : '😝'}
</Button>
</div>
</div>
<div className='mt-8 text-sm font-medium text-gray-900'>{t('common.account.confirmPassword')}</div>
<input
type="password"
className={inputClassName}
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
/>
<div className='relative mt-2'>
<Input
type={showConfirmPassword ? 'text' : 'password'}
value={confirmPassword}
onChange={e => setConfirmPassword(e.target.value)}
/>
<div className="absolute inset-y-0 right-0 flex items-center">
<Button
type="button"
variant='ghost'
onClick={() => setShowConfirmPassword(!showConfirmPassword)}
>
{showConfirmPassword ? '👀' : '😝'}
</Button>
</div>
</div>
<div className='flex justify-end mt-10'>
<Button className='mr-2' onClick={() => {
setEditPasswordModalVisible(false)
Expand Down
2 changes: 1 addition & 1 deletion web/app/account/avatar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ export default function AppSelector() {
>
<Menu.Items
className="
absolute -right-3 -top-3 w-60 max-w-80
absolute -right-2 -top-1 w-60 max-w-80
divide-y divide-gray-100 origin-top-right rounded-lg bg-white
shadow-lg
"
Expand Down
202 changes: 14 additions & 188 deletions web/app/activate/activateForm.tsx
Original file line number Diff line number Diff line change
@@ -1,27 +1,16 @@
'use client'
import { useCallback, useState } from 'react'
import { useContext } from 'use-context-selector'
import { useTranslation } from 'react-i18next'
import useSWR from 'swr'
import { useSearchParams } from 'next/navigation'
import Link from 'next/link'
import { CheckCircleIcon } from '@heroicons/react/24/solid'
import style from './style.module.css'
import { useRouter, useSearchParams } from 'next/navigation'
import cn from '@/utils/classnames'
import Button from '@/app/components/base/button'

import { SimpleSelect } from '@/app/components/base/select'
import { timezones } from '@/utils/timezone'
import { LanguagesSupported, languages } from '@/i18n/language'
import { activateMember, invitationCheck } from '@/service/common'
import Toast from '@/app/components/base/toast'
import { invitationCheck } from '@/service/common'
import Loading from '@/app/components/base/loading'
import I18n from '@/context/i18n'
const validPassword = /^(?=.*[a-zA-Z])(?=.*\d).{8,}$/

const ActivateForm = () => {
const router = useRouter()
const { t } = useTranslation()
const { locale, setLocaleOnClient } = useContext(I18n)
const searchParams = useSearchParams()
const workspaceID = searchParams.get('workspace_id')
const email = searchParams.get('email')
Expand All @@ -35,64 +24,20 @@ const ActivateForm = () => {
token,
},
}
const { data: checkRes, mutate: recheck } = useSWR(checkParams, invitationCheck, {
const { data: checkRes } = useSWR(checkParams, invitationCheck, {
revalidateOnFocus: false,
onSuccess(data) {
if (data.is_valid) {
const params = new URLSearchParams(searchParams)
const { email, workspace_id } = data.data
params.set('email', encodeURIComponent(email))
params.set('workspace_id', encodeURIComponent(workspace_id))
params.set('invite_token', encodeURIComponent(token as string))
router.replace(`/signin?${params.toString()}`)
}
},
})

const [name, setName] = useState('')
const [password, setPassword] = useState('')
const [timezone, setTimezone] = useState(Intl.DateTimeFormat().resolvedOptions().timeZone)
const [language, setLanguage] = useState(locale)
const [showSuccess, setShowSuccess] = useState(false)

const showErrorMessage = useCallback((message: string) => {
Toast.notify({
type: 'error',
message,
})
}, [])

const valid = useCallback(() => {
if (!name.trim()) {
showErrorMessage(t('login.error.nameEmpty'))
return false
}
if (!password.trim()) {
showErrorMessage(t('login.error.passwordEmpty'))
return false
}
if (!validPassword.test(password)) {
showErrorMessage(t('login.error.passwordInvalid'))
return false
}

return true
}, [name, password, showErrorMessage, t])

const handleActivate = useCallback(async () => {
if (!valid())
return
try {
await activateMember({
url: '/activate',
body: {
workspace_id: workspaceID,
email,
token,
name,
password,
interface_language: language,
timezone,
},
})
setLocaleOnClient(language, false)
setShowSuccess(true)
}
catch {
recheck()
}
}, [email, language, name, password, recheck, setLocaleOnClient, timezone, token, valid, workspaceID])

return (
<div className={
cn(
Expand All @@ -115,125 +60,6 @@ const ActivateForm = () => {
</div>
</div>
)}
{checkRes && checkRes.is_valid && !showSuccess && (
<div className='flex flex-col md:w-[400px]'>
<div className="w-full mx-auto">
<div className={`mb-3 flex justify-center items-center w-20 h-20 p-5 rounded-[20px] border border-gray-100 shadow-lg text-[40px] font-bold ${style.logo}`}>
</div>
<h2 className="text-[32px] font-bold text-gray-900">
{`${t('login.join')} ${checkRes.workspace_name}`}
</h2>
<p className='mt-1 text-sm text-gray-600 '>
{`${t('login.joinTipStart')} ${checkRes.workspace_name} ${t('login.joinTipEnd')}`}
</p>
</div>

<div className="w-full mx-auto mt-6">
<div className="bg-white">
{/* username */}
<div className='mb-5'>
<label htmlFor="name" className="my-2 flex items-center justify-between text-sm font-medium text-gray-900">
{t('login.name')}
</label>
<div className="mt-1 relative rounded-md shadow-sm">
<input
id="name"
type="text"
value={name}
onChange={e => setName(e.target.value)}
placeholder={t('login.namePlaceholder') || ''}
className={'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'}
tabIndex={1}
/>
</div>
</div>
{/* password */}
<div className='mb-5'>
<label htmlFor="password" className="my-2 flex items-center justify-between text-sm font-medium text-gray-900">
{t('login.password')}
</label>
<div className="mt-1 relative rounded-md shadow-sm">
<input
id="password"
type='password'
value={password}
onChange={e => setPassword(e.target.value)}
placeholder={t('login.passwordPlaceholder') || ''}
className={'appearance-none block w-full rounded-lg pl-[14px] px-3 py-2 border border-gray-200 hover:border-gray-300 hover:shadow-sm focus:outline-none focus:ring-primary-500 focus:border-primary-500 placeholder-gray-400 caret-primary-600 sm:text-sm pr-10'}
tabIndex={2}
/>
</div>
<div className='mt-1 text-xs text-gray-500'>{t('login.error.passwordInvalid')}</div>
</div>
{/* language */}
<div className='mb-5'>
<label htmlFor="name" className="my-2 flex items-center justify-between text-sm font-medium text-gray-900">
{t('login.interfaceLanguage')}
</label>
<div className="relative mt-1 rounded-md shadow-sm">
<SimpleSelect
defaultValue={LanguagesSupported[0]}
items={languages.filter(item => item.supported)}
onSelect={(item) => {
setLanguage(item.value as string)
}}
/>
</div>
</div>
{/* timezone */}
<div className='mb-4'>
<label htmlFor="timezone" className="block text-sm font-medium text-gray-700">
{t('login.timezone')}
</label>
<div className="relative mt-1 rounded-md shadow-sm">
<SimpleSelect
defaultValue={timezone}
items={timezones}
onSelect={(item) => {
setTimezone(item.value as string)
}}
/>
</div>
</div>
<div>
<Button
variant='primary'
className='w-full !text-sm'
onClick={handleActivate}
>
{`${t('login.join')} ${checkRes.workspace_name}`}
</Button>
</div>
<div className="block w-hull mt-2 text-xs text-gray-600">
{t('login.license.tip')}
&nbsp;
<Link
className='text-primary-600'
target='_blank' rel='noopener noreferrer'
href={`https://docs.dify.ai/${language !== LanguagesSupported[1] ? 'user-agreement' : `v/${locale.toLowerCase()}/policies`}/open-source`}
>{t('login.license.link')}</Link>
</div>
</div>
</div>
</div>
)}
{checkRes && checkRes.is_valid && showSuccess && (
<div className="flex flex-col md:w-[400px]">
<div className="w-full mx-auto">
<div className="mb-3 flex justify-center items-center w-20 h-20 p-5 rounded-[20px] border border-gray-100 shadow-lg text-[40px] font-bold">
<CheckCircleIcon className='w-10 h-10 text-[#039855]' />
</div>
<h2 className="text-[32px] font-bold text-gray-900">
{`${t('login.activatedTipStart')} ${checkRes.workspace_name} ${t('login.activatedTipEnd')}`}
</h2>
</div>
<div className="w-full mx-auto mt-6">
<Button variant='primary' className='w-full !text-sm'>
<a href="/signin">{t('login.activated')}</a>
</Button>
</div>
</div>
)}
</div>
)
}
Expand Down
5 changes: 5 additions & 0 deletions web/app/components/base/icons/assets/public/common/lock.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading
Loading