Skip to content

132 security dialog#155

Open
d-alex171 wants to merge 5 commits intomainfrom
132-security-dialog
Open

132 security dialog#155
d-alex171 wants to merge 5 commits intomainfrom
132-security-dialog

Conversation

@d-alex171
Copy link
Contributor

No description provided.

@theosiemensrhodes
Copy link
Collaborator

@greptile

@greptile-apps
Copy link

greptile-apps bot commented Jan 31, 2026

Greptile Summary

This PR implements the security settings page with password change, session management, and logout functionality. The implementation correctly imports the shared authClient, uses react-hook-form for validation, and properly handles loading states.

Key changes:

  • Added password change form with validation (current password, new password, confirmation)
  • Added session management with "Revoke All Sessions" functionality
  • Added logout button
  • Changed settings dialog overflow behavior to enable scrolling

Issues to address:

  • Several previously flagged issues remain unresolved (security concern with revokeOtherSessions: false, nested scrollbar behavior, button validation UX)
  • Minor cleanup needed: unused isPasswordSuccess variable on line 95
  • Success message state could behave unexpectedly if user modifies then clears the form

Confidence Score: 3/5

  • Safe to merge with cleanup recommended - core functionality works but several UX and code quality issues should be addressed
  • The security settings implementation is functional and follows good patterns (proper auth client usage, form validation, error handling). However, several issues from previous review comments remain unaddressed, including a security concern with session handling and UX issues with form validation. The new code also contains minor issues like unused variables and edge cases in success message display.
  • src/components/settings/pages/security-settings-content.tsx needs attention to address previous security and UX feedback

Important Files Changed

Filename Overview
src/components/settings/pages/security-settings-content.tsx Implements password change, session management, and logout functionality with form validation, though contains unused code and a minor UX issue with success message state
src/components/settings/settings-dialog.tsx Changed DialogContent from overflow-hidden to overflow-y-auto to enable scrolling in settings dialog

Last reviewed commit: 96edc26

Copy link

@greptile-apps greptile-apps bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

2 files reviewed, 9 comments

Edit Code Review Agent Settings | Greptile

const [loading, setLoading] = useState(false);
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);

const authClient = createAuthClient()
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creates a new auth client instance on every render. Move outside component or memoize.

Suggested change
const authClient = createAuthClient()
const authClient = React.useMemo(() => createAuthClient(), []);
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/settings/pages/security-settings-content.tsx
Line: 20:20

Comment:
Creates a new auth client instance on every render. Move outside component or memoize.

```suggestion
  const authClient = React.useMemo(() => createAuthClient(), []);
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is really important, you should just import it from where greptile mentioned

@@ -1,3 +1,188 @@
import React, { useState } from 'react';
import { createAuthClient } from 'better-auth/client';
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Importing from better-auth/client but existing codebase uses better-auth/react (see src/lib/auth/client.ts:3). Use the shared authClient from @/lib/auth/client instead of creating a new instance.

Suggested change
import { createAuthClient } from 'better-auth/client';
import { authClient } from '@/lib/auth/client';

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/settings/pages/security-settings-content.tsx
Line: 2:2

Comment:
Importing from `better-auth/client` but existing codebase uses `better-auth/react` (see `src/lib/auth/client.ts:3`). Use the shared `authClient` from `@/lib/auth/client` instead of creating a new instance.

```suggestion
import { authClient } from '@/lib/auth/client';
```

<sub>Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!</sub>

How can I resolve this? If you propose a fix, please make it concise.

disabled={loading}
/>
</div>
<Button onClick={handleChangePassword} disabled={loading}>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Button is not disabled when required fields are empty. Users can submit with empty passwords.

Suggested change
<Button onClick={handleChangePassword} disabled={loading}>
<Button onClick={handleChangePassword} disabled={loading || !currentPassword || !newPassword || !confirmPassword}>
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/components/settings/pages/security-settings-content.tsx
Line: 128:128

Comment:
Button is not disabled when required fields are empty. Users can submit with empty passwords.

```suggestion
            <Button onClick={handleChangePassword} disabled={loading || !currentPassword || !newPassword || !confirmPassword}>
```

How can I resolve this? If you propose a fix, please make it concise.

Copy link
Collaborator

@theosiemensrhodes theosiemensrhodes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks mostly good just fix the comments

<div className="space-y-4">
<div className="space-y-2">
<Label htmlFor="current-password">Current Password</Label>
<Input
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We have a way to do forms that isn't manual. See the login and signup pages for examples.

const [loading, setLoading] = useState(false);
const [message, setMessage] = useState<{ type: 'success' | 'error'; text: string } | null>(null);

const authClient = createAuthClient()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah this is really important, you should just import it from where greptile mentioned

@d-alex171
Copy link
Contributor Author

@greptile

const { error } = await authClient.changePassword({
currentPassword: data.currentPassword,
newPassword: data.newPassword,
revokeOtherSessions: false,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

revokeOtherSessions: false is a security risk

When a user changes their password, failing to revoke other active sessions means any compromised session (e.g., an attacker who already has a valid token on another device) remains valid after the password change. Revoking other sessions on password change is a widely accepted security best practice.

Consider setting this to true by default — the user can still use the separate "Revoke All Sessions" button if needed, but a password change should at minimum invalidate all sessions besides the current one.

Suggested change
revokeOtherSessions: false,
revokeOtherSessions: true,

}

reset();
setError("root", { type: "success" } as any);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Misusing errors.root to track success state

Setting a "success" sentinel value into errors.root via setError("root", { type: "success" } as any) is a hack that misuses react-hook-form's error system. It also requires the as any cast to silence TypeScript, which is a red flag.

A cleaner approach is to use a separate useState variable (similar to how sessionMessage is handled in the session management section):

const [passwordSuccess, setPasswordSuccess] = useState(false);

// on success:
reset();
setPasswordSuccess(true);

Then render the success alert based on passwordSuccess instead of isPasswordSuccess, and clear it when the form becomes dirty again.

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

const isPasswordSuccess = (errors.root as any)?.type === "success";

return (
<div className="space-y-6 max-h-[calc(100vh-200px)] overflow-y-auto px-1">
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nested scrollbars due to conflicting overflow rules

This wrapper div applies max-h-[calc(100vh-200px)] overflow-y-auto, giving the security content its own independent scrollbar. At the same time, settings-dialog.tsx was changed in this PR from overflow-hidden to overflow-y-auto on the DialogContent. The result is two stacked scrollable containers on the security tab — one inside the other — producing a double-scrollbar experience.

The security content's max-h + overflow-y-auto is redundant because the parent dialog already constrains height and scrolls. Remove these from the content wrapper and let the dialog handle vertical overflow (which is already what the settings-dialog change enables):

Suggested change
<div className="space-y-6 max-h-[calc(100vh-200px)] overflow-y-auto px-1">
<div className="space-y-6 px-1">

Comment on lines 98 to 104
<div>
<h3 className="text-lg font-medium">Security</h3>
<p className="text-sm text-muted-foreground">
Manage your account security and sessions
</p>
</div>
<Separator />
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Duplicate "Security" heading

settings-dialog.tsx already renders a DialogTitle containing the tab's label (i.e. "Security") for every settings pane. This block renders a second <h3>Security</h3> heading directly inside the content, resulting in two "Security" headings visible in the UI.

The other content components (e.g. AvailabilitySettingsContent) do not add their own headings and instead rely on the DialogTitle from the parent. Consider removing this block and instead adding a description to the security entry in the settingsItems array in settings-dialog.tsx if the subtitle is needed.

@d-alex171
Copy link
Contributor Author

@greptile

}
};

const isPasswordSuccess = (errors.root as any)?.type === "success";
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused variable - isPasswordSuccess is never referenced. Remove this line.

Suggested change
const isPasswordSuccess = (errors.root as any)?.type === "success";

</Alert>
)}

{passwordSuccess && !isDirty && (
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Success message can reappear if user types then clears form back to empty. Once user starts editing (isDirty becomes true), passwordSuccess should be set to false so success doesn't reappear when form returns to default state.

Add useEffect to clear success state:

useEffect(() => {
  if (isDirty) setPasswordSuccess(false);
}, [isDirty]);

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants