feat(Login): Migrate login page to frontend#33255
Conversation
|
Based on your review schedule, I'll hold off on reviewing this PR until it's marked as ready for review. If you'd like me to take a look now, comment
|
|
Yay!!!! Is this the magic moment we've all been waiting for that kills the little gray close buttons that show up before the React frontend is built/loaded? |
| { required: true, message: t('Please enter your username') }, | ||
| ]} | ||
| > | ||
| <Input prefix={<Icons.UserOutlined size={1} />} /> |
There was a problem hiding this comment.
Should it be one of these: 'xs', 's', 'm', 'l', 'xl', 'xxl'?
Well, i think those will show up anyways until the frontend is loaded. Sorry! |
There was a problem hiding this comment.
Review by Korbit AI
Korbit automatically attempts to detect when you fix issues in new commits.
| Category | Issue | Status |
|---|---|---|
| Unused OAuth Provider Parameter ▹ view | 🧠 Incorrect | |
| Unmemoized Bootstrap Data Fetch ▹ view | 🧠 Incorrect | |
| Form Submission Logic Not Separated ▹ view | 🧠 Not in standard | |
| Incorrect Component for Logout Route ▹ view | ||
| Unhandled URL Generation Error in Redirect ▹ view | 🧠 Incorrect | |
| Missing Interface Documentation ▹ view | 🧠 Not in standard | |
| Potential Circular Import Risk ▹ view | ✅ Fix detected | |
| Unexplained Layout Constants ▹ view | 🧠 Not in standard | |
| Unclear API Parameter ▹ view | 🧠 Incorrect | |
| Unmemoized Form Handler ▹ view | 🧠 Incorrect |
Files scanned
| File Path | Reviewed |
|---|---|
| superset/connectors/sqla/init.py | ✅ |
| superset-frontend/cypress-base/cypress/utils/urls.ts | ✅ |
| superset/views/alerts.py | ✅ |
| superset/views/auth.py | ✅ |
| superset-frontend/src/components/Icons/AntdEnhanced.tsx | ✅ |
| superset-frontend/src/views/routes.tsx | ✅ |
| superset-frontend/src/pages/Register/index.tsx | ✅ |
| superset-frontend/src/pages/Login/index.tsx | ✅ |
| superset-frontend/cypress-base/cypress/support/e2e.ts | ✅ |
| superset/views/base.py | ✅ |
| superset/views/utils.py | ✅ |
| superset/initialization/init.py | ✅ |
| superset/security/manager.py | ✅ |
Explore our documentation to understand the languages and file types we support and the files we ignore.
Check out our docs on how you can make Korbit work best for you and your team.
superset/views/alerts.py
Outdated
| @has_access | ||
| @permission_name("read") | ||
| def list(self) -> FlaskResponse: | ||
| from superset import is_feature_enabled |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| { | ||
| path: '/logout/', | ||
| Component: Login, | ||
| }, |
There was a problem hiding this comment.
Incorrect Component for Logout Route 
Tell me more
What is the issue?
The logout route is incorrectly using the Login component, which doesn't handle the logout functionality.
Why this matters
Users attempting to logout will be shown the login page without their session being terminated, leading to an inconsistent authentication state.
Suggested change ∙ Feature Preview
Create and use a dedicated Logout component that handles session termination before redirecting to the login page. Example:
const Logout = lazy(
() => import(/* webpackChunkName: "Logout" */ 'src/pages/Logout'),
);
// In routes array
{
path: '/logout/',
Component: Logout,
}Provide feedback to improve future suggestions
💬 Looking for more details? Reply to this comment to chat with Korbit.
| @no_cache | ||
| def login(self, provider: Optional[str] = None) -> WerkzeugResponse: | ||
| if g.user is not None and g.user.is_authenticated: | ||
| return redirect(self.appbuilder.get_url_for_index) |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| conf_password: values.confirmPassword, | ||
| 'g-recaptcha-response': captchaResponse, | ||
| }; | ||
| SupersetClient.postForm('/register/form', payload, '').finally(() => { |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
|
|
||
| @expose("/") | ||
| @no_cache | ||
| def login(self, provider: Optional[str] = None) -> WerkzeugResponse: |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| const [loading, setLoading] = useState(false); | ||
| const [captchaResponse, setCaptchaResponse] = useState<string | null>(null); | ||
|
|
||
| const bootstrapData = getBootstrapData(); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| const onFinish = (values: RegisterForm) => { | ||
| setLoading(true); | ||
| const payload = { | ||
| username: values.username, | ||
| first_name: values.firstName, | ||
| last_name: values.lastName, | ||
| email: values.email, | ||
| password: values.password, | ||
| conf_password: values.confirmPassword, | ||
| 'g-recaptcha-response': captchaResponse, | ||
| }; |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| const onFinish = (values: RegisterForm) => { | ||
| setLoading(true); | ||
| const payload = { | ||
| username: values.username, | ||
| first_name: values.firstName, | ||
| last_name: values.lastName, | ||
| email: values.email, | ||
| password: values.password, | ||
| conf_password: values.confirmPassword, | ||
| 'g-recaptcha-response': captchaResponse, | ||
| }; | ||
| SupersetClient.postForm('/register/form', payload, '').finally(() => { | ||
| setLoading(false); | ||
| }); |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| interface RegisterForm { | ||
| username: string; | ||
| firstName: string; | ||
| lastName: string; | ||
| email: string; | ||
| password: string; | ||
| confirmPassword: string; | ||
| } |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| const formItemLayout = { | ||
| labelCol: { | ||
| xs: { span: 24 }, | ||
| sm: { span: 6 }, | ||
| }, | ||
| wrapperCol: { | ||
| xs: { span: 24 }, | ||
| sm: { span: 24 }, | ||
| }, | ||
| }; |
This comment was marked as resolved.
This comment was marked as resolved.
Sorry, something went wrong.
| ); | ||
|
|
||
| before(() => { | ||
| if (Cypress.currentTest.title.toLowerCase().includes('login')) { |
There was a problem hiding this comment.
Can we make a small util for this?
| href={`/login/${provider.name}`} | ||
| block | ||
| iconPosition="start" | ||
| icon={AuthIconMap[provider.name]} |
There was a problem hiding this comment.
Can new providers be introduced? What happens if the icon isn't available?
There was a problem hiding this comment.
Since we can get the icon from user config as 'fa-google' etc, I can create an icon map that has all available providers, and maybe we can have fallback to <i> tag for new providers?
There was a problem hiding this comment.
It seems that there is a pattern in naming convention for social icons on Ant Design. We can make a util function that gets the provider name, capitalizes it and adds either Outlined or Filled (to check with Kasia) and if it does not exist can fallback to a button or a fallback icon.
| /> | ||
| </Form.Item> | ||
| <Form.Item label={null}> | ||
| <Flex |
There was a problem hiding this comment.
You have a LoginContainer that is the same as this. I prefer inline, so maybe we can get rid of LoginContainer?
|
YES! should the login box be |
|
While at it, wondering if we can reduce the width of the login card, and maybe remove the icon at the top-right, the word "Log in" speaks for itself, no need for an icon here. @kasiazjc may have some input here too. |
|
|
||
| const StyledCard = styled(Card)` | ||
| ${({ theme }) => css` | ||
| width: 40%; |
There was a problem hiding this comment.
I would avoid using percentages as I don't think it would look great on wide screens, let me know what you think
| label={t('Email')} | ||
| name="email" | ||
| rules={[{ required: true, message: t('Please enter your email') }]} | ||
| > |
There was a problem hiding this comment.
| > | |
| >rules={[ | |
| { required: true, message: t('Email is required') }, | |
| { | |
| type: 'email', | |
| message: t('Please enter a valid email address'), | |
| }, | |
| ]} |
There was a problem hiding this comment.
So that it can check if t s valid format for the email adress as well
| elif security_manager.is_guest_user(user): | ||
| user.roles = (current_app.appbuilder.sm.find_role("Public"),) | ||
| elif current_app.appbuilder.sm.is_guest_user(user): | ||
| payload = { |
There was a problem hiding this comment.
Why do you need to use the current_app.app_builder.sm instead of security_manager
There was a problem hiding this comment.
You are correct, these were measures i took to avoid circular imports but they are not necessary anymore
|
Great work @msyavuz ! The page is looking good, I just have a few nits here and there.
|
dpgaspar
left a comment
There was a problem hiding this comment.
Tested AUTH_DB and AUTH_OAUTH,
using OAUTH and AUTH_USER_REGISTRATION = True login breaks, it seems RECAPTCHA_PUBLIC_KEY is now required, although this is a good measure it was not a required config before.
This does not seem specifically related with this PR, but a possible issue
superset/views/alerts.py
Outdated
| @has_access | ||
| @permission_name("read") | ||
| def log(self, pk: int) -> FlaskResponse: # pylint: disable=unused-argument | ||
| from superset import is_feature_enabled |
There was a problem hiding this comment.
nit: is it possible to move this import to the module level?
@dpgaspar I think it was required when registration is enabled but it was implicit, Recaptcha would be broken and you couldn't register. I am not sure what was the intended flow with fab as https://flask-appbuilder.readthedocs.io/en/latest/user_registration.html doesn't mention oauth. |
That button is technically a link so not sure about removing the underline
I think looks pretty similar to the fab view we had now, we would again need a design input for this if we want to change more stuff.
Thought about maybe having less padding on the left but decided to go with less custom styles |
I think input of @kasiazjc is relevant here. I tried to replicate what it was before but if want to change these stuff we can do that as well. |




SUMMARY
This PR tries to move the Login fab view to the frontend spa.
BEFORE/AFTER SCREENSHOTS OR ANIMATED GIF
AuthDB Login:
Before:

After:

TESTING INSTRUCTIONS
Follow the instructions on setting up auth methods in flask-appbuilder to see different auth methods working. The configuration should be something like this for different auth methods:
ADDITIONAL INFORMATION