Skip to content

Conversation

@ccastrotrejo
Copy link
Contributor

@ccastrotrejo ccastrotrejo commented Dec 2, 2025

Commit Type

  • feature - New functionality
  • fix - Bug fix
  • refactor - Code restructuring without behavior change
  • perf - Performance improvement
  • docs - Documentation update
  • test - Test-related changes
  • chore - Maintenance/tooling

Risk Level

  • Low - Minor changes, limited scope
  • Medium - Moderate changes, some user impact
  • High - Major changes, significant user/system impact

What & Why

This PR implements a comprehensive authentication flow for the iframe chat application. It adds:

  1. Login UI Component: A new LoginPrompt component with Fluent UI styling that displays when users need to authenticate
  2. Enhanced Auth Handler: Improved authentication handling with popup-based login flow that monitors auth status
  3. Component Reorganization: Refactored multi-session chat components and session list into organized subdirectories with separated styles
  4. Theme Integration: Wrapped components in FluentProvider for consistent theming across light/dark modes

The changes improve the user experience by providing a clear, branded login interface instead of abrupt redirects when authentication is required.

Impact of Change

  • Users: Users will now see a professional login prompt when authentication is required, with better visual feedback during the login process. The authentication flow is more seamless with popup-based login instead of full page redirects.
  • Developers:
    • New LoginPrompt component available for reuse
    • Updated authHandler API with onLoginRequired callback replacing onLogoutComplete
    • Reorganized component structure in apps/iframe-app/src/components/ with subdirectories for MultiSessionChat and SessionList
    • Enhanced authentication utilities including openLoginPopup and checkAuthStatus functions
  • System:
    • Better authentication state management with polling-based status checks
    • Improved error handling for authentication failures
    • FluentProvider wrapping ensures consistent theme application

Test Plan

  • Unit tests added/updated
  • E2E tests added/updated
  • Manual testing completed
  • Tested in: Local development environment with mock authentication scenarios

Tests Updated:

  • Updated authHandler.test.ts with new test cases for openLoginPopup and checkAuthStatus
  • Updated config-parser.test.ts with comprehensive tests for getAgentBaseUrl utility function
  • Tests cover popup success/failure scenarios, cross-origin handling, and URL parsing edge cases

Manual Testing:

  • Verified login prompt displays correctly in both light and dark themes
  • Tested authentication flow with popup opening, user login, and successful return
  • Verified component reorganization doesn't break existing functionality
  • Confirmed FluentProvider theming works across all chat components

Contributors

@ccastrotrejo

Screenshots/Videos

New LoginPrompt component that displays when authentication is required:

  • Clean, centered card layout with company branding support
  • "Sign in" button with loading state
  • Responsive design with Fluent UI styling
  • Consistent with existing chat UI patterns

@github-actions
Copy link

github-actions bot commented Dec 2, 2025

🤖 AI PR Validation Report

PR Review Results

Thank you for your submission! Here's detailed feedback on your PR title and body compliance:

PR Title

  • Current: feat(chat): Implement authentication flow with login UI and session management
  • Issue: Title is specific and clear, describing the main changes and affected area.
  • Recommendation: No change needed.

Commit Type

  • Properly selected (feature - New functionality).
  • Only one selected, which is correct.

Risk Level

  • The PR body marks risk as Medium and only the medium box is checked, but the code diff indicates this is a High-risk change:
    • Introduces a new authentication flow (UI/popup, session management)
    • Refactors core chat components and reorganizes them
    • Changes how authentication failures are handled, impacting user login experience
    • Touches many files and core logic (over 20 files changed, >2k additions, plus deletions)
    • Adds and updates test coverage and E2E specs, indicating surface area expansion.
  • Recommendation:
    • Please update the Risk Level in the PR body to High and ensure the label on the PR is set to risk:high.

What & Why

  • Current:

    This PR implements a comprehensive authentication flow for the iframe chat application. It adds:

    1. Login UI Component: ...
      ...
      The changes improve the user experience by providing a clear, branded login interface instead of abrupt redirects when authentication is required.
  • Issue: Content is clear and sufficiently detailed; rationale is appropriate.
  • Recommendation: No change needed.

Impact of Change

  • Users: Clear explanation of impacts, including new login flow and improved experience.
  • Developers: Notes API changes, new reusable components, and reorganization.
  • System: Mentions improved state management, theme handling, and error handling.
  • Recommendation: No required changes. (Optional: Call out any major breaking changes if downstream consumers are expected to upgrade API usage.)

Test Plan

  • Unit tests, manual tests, and specific E2E file added. Comprehensively described with concrete coverage for new auth code.
  • Assessment: Pass, good coverage for high-risk change.

⚠️ Contributors

  • Current: Only @ccastrotrejo tagged.
  • Recommendation: Remember to give credit to PMs, designers, or others if they contributed (optional, not required).

⚠️ Screenshots/Videos

  • Current: Textual description of new login UI and visual changes; no direct screenshot or link included.
  • Assessment: This is a UI change, so, for completeness, strongly recommend adding a screenshot/gif of the new login prompt as a courtesy for reviewers and for historical documentation. If possible, provide a screenshot or video.

Summary Table

Section Status Recommendation
Title
Commit Type
Risk Level Update to High in body & label
What & Why
Impact of Change
Test Plan
Contributors ⚠️ Tag relevant collaborators if any
Screenshots/Videos ⚠️ Add screenshot/gif of new UI if possible

Please update the PR body to set Risk Level to HIGH if you agree with the above assessment, and set the GitHub label to risk:high. Also, consider including a screenshot/video for the new login experience. Thank you for your thorough documentation and coverage on this high-impact change!


Last updated: Wed, 03 Dec 2025 01:52:36 GMT

@ccastrotrejo ccastrotrejo marked this pull request as ready for review December 2, 2025 23:26
Copilot AI review requested due to automatic review settings December 2, 2025 23:26
@ccastrotrejo ccastrotrejo changed the title feat(chat): Implement authentication feat(chat): Implement authentication flow with login UI and session management Dec 2, 2025
Copilot finished reviewing on behalf of ccastrotrejo December 2, 2025 23:29
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR implements authentication functionality for the iframe chat application, introducing login/logout handling through Azure App Service EasyAuth. The changes include refactoring the authentication flow, restructuring the SessionList component into separate files for better maintainability, and adding a new LoginPrompt component for user authentication.

Key Changes

  • Authentication Implementation: Added openLoginPopup() function and checkAuthStatus() to handle Azure AD login flows with popup-based authentication
  • Component Restructuring: Split monolithic SessionList component into modular files (SessionList.tsx, SessionItem.tsx, SessionListStyles.ts) following Fluent UI v9 patterns
  • Auth Flow Integration: Updated createUnauthorizedHandler to trigger login UI instead of automatic logout, integrated with IframeWrapper and MultiSessionChat components

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 6 comments.

Show a summary per file
File Description
turbo.json Added dist/** to build:lib outputs for library distribution
apps/iframe-app/src/lib/utils/config-parser.ts Added getAgentBaseUrl() helper to extract base URL from agent card URLs
apps/iframe-app/src/lib/utils/tests/config-parser.test.ts Added comprehensive tests for getAgentBaseUrl() covering edge cases
apps/iframe-app/src/lib/index.tsx Removed unused CSS import for logicAppsChat styles
apps/iframe-app/src/lib/iframe.tsx Removed unused CSS import for logicAppsChat styles
apps/iframe-app/src/lib/authHandler.ts Refactored authentication: replaced logout flow with login popup, added openLoginPopup() and checkAuthStatus() functions
apps/iframe-app/src/lib/tests/authHandler.test.ts Added tests for new auth functions, but tests contain critical bugs (incorrect callback names, mismatched expectations)
apps/iframe-app/src/components/SessionList/SessionListStyles.ts New file: Fluent UI v9 styles for SessionList (migrated from CSS modules)
apps/iframe-app/src/components/SessionList/SessionList.tsx Extracted SessionList component from monolithic file, using makeStyles pattern
apps/iframe-app/src/components/SessionList/SessionItem.tsx Extracted SessionItem component for better modularity and performance
apps/iframe-app/src/components/SessionList.tsx Deleted: Split into separate modular files
apps/iframe-app/src/components/SessionList.module.css Deleted: Migrated to Fluent UI v9 makeStyles
apps/iframe-app/src/components/MultiSessionChat/MultiSessionChatWithAuth.tsx Updated import paths after component restructuring
apps/iframe-app/src/components/MultiSessionChat/MultiSessionChatStyles.ts New file: Extracted styles using makeStyles pattern
apps/iframe-app/src/components/MultiSessionChat/MultiSessionChat.tsx Refactored to use extracted styles, added 401 handling for agent card fetch, removed FluentProvider wrapper
apps/iframe-app/src/components/MultiSessionChat.module.css Deleted: Migrated to makeStyles
apps/iframe-app/src/components/LoginPrompt/index.ts New file: Exports for LoginPrompt component
apps/iframe-app/src/components/LoginPrompt/LoginPromptStyles.ts New file: Styles for login prompt using makeStyles
apps/iframe-app/src/components/LoginPrompt/LoginPrompt.tsx New component: Login UI shown when authentication is required
apps/iframe-app/src/components/IframeWrapper.tsx Integrated authentication flow with LoginPrompt, added FluentProvider at top level, uses new auth handlers
Comments suppressed due to low confidence (3)

apps/iframe-app/src/components/MultiSessionChat/MultiSessionChat.tsx:157

  • Checking response.statusText === 'Unauthorized' is unreliable. HTTP status text varies between servers and may be empty or different strings. Use response.status === 401 instead to reliably detect unauthorized responses.
    apps/iframe-app/src/components/MultiSessionChat/MultiSessionChat.tsx:187
  • The dependency array includes both config and individual properties config.apiKey, config.apiUrl, and config.oboUserToken. Including the parent object along with its properties is redundant and can cause unnecessary re-renders. Remove config from the dependency array and keep only the specific properties that the effect uses: [config.apiKey, config.apiUrl, config.oboUserToken, config.onUnauthorized].
    apps/iframe-app/src/components/MultiSessionChat/MultiSessionChat.tsx:161
  • When the response status is 401 (Unauthorized), the code calls onUnauthorized() but doesn't set setIsLoadingAgent(false) or clear the loading state. This will leave the UI in a perpetual loading state. After calling onUnauthorized(), either return early or set an appropriate error state so the UI can respond appropriately.

Comment on lines +186 to +205
it('should call onLoginSuccess when login popup succeeds', async () => {
vi.useFakeTimers();

mockFetch
.mockResolvedValueOnce({ ok: false }) // refresh fails
.mockResolvedValueOnce({
// checkAuthStatus succeeds
ok: true,
json: () => Promise.resolve([{ provider_name: 'aad' }]),
});

const mockPopup = { closed: false, close: vi.fn(), location: { href: '' } };
mockWindowOpen.mockReturnValueOnce(mockPopup);

const onLoginSuccess = vi.fn();

// Simulate popup being closed
const handler = createUnauthorizedHandler({
baseUrl: 'https://example.com',
onLoginSuccess,
});
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The test expects onLoginSuccess callback, but AuthHandlerConfig interface doesn't define this callback. The implementation calls onLoginRequired() when refresh fails, not when login succeeds. This test doesn't match the actual implementation behavior and will fail.

Copilot uses AI. Check for mistakes.
Comment on lines +220 to 265
it('should fall back to logout when login fails', async () => {
vi.useFakeTimers();

mockFetch.mockResolvedValueOnce({ ok: false });
const mockPopup = {
closed: false,
close: vi.fn(),
location: { href: 'https://example.com/.auth/logout' },
};
mockWindowOpen.mockReturnValueOnce(mockPopup);
mockFetch
.mockResolvedValueOnce({ ok: false }) // refresh fails
.mockResolvedValueOnce({
// checkAuthStatus fails (not authenticated)
ok: true,
json: () => Promise.resolve([]),
});

const mockLoginPopup = { closed: false, close: vi.fn(), location: { href: '' } };
const mockLogoutPopup = { closed: false, close: vi.fn(), location: { href: '' } };
mockWindowOpen.mockReturnValueOnce(mockLoginPopup).mockReturnValueOnce(mockLogoutPopup);

const onLoginFailed = vi.fn();
const onLogoutComplete = vi.fn();

const handler = createUnauthorizedHandler({
baseUrl: 'https://example.com',
onLoginFailed,
onLogoutComplete,
});

await handler();

// Change popup URL to completion URL
mockPopup.location.href = 'https://example.com/.auth/logout/complete';
// Simulate login popup being closed without success
mockLoginPopup.closed = true;

// Advance timers to trigger interval check
vi.advanceTimersByTime(600);
// Advance timers to trigger interval check and auth status check
await vi.advanceTimersByTimeAsync(600);

expect(onLoginFailed).toHaveBeenCalled();

// Now logout popup should be opened
expect(mockWindowOpen).toHaveBeenCalledTimes(2);
expect(mockWindowOpen).toHaveBeenLastCalledWith('https://example.com/.auth/logout', 'auth-logout', 'width=600,height=700,popup=true');

// Simulate logout popup being closed
mockLogoutPopup.closed = true;

await vi.advanceTimersByTimeAsync(600);

expect(mockPopup.close).toHaveBeenCalled();
expect(onLogoutComplete).toHaveBeenCalled();

vi.useRealTimers();
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

This test expects the unauthorized handler to open a logout popup when login fails, but the implementation (authHandler.ts lines 263-293) only calls onLoginRequired() when refresh fails - it doesn't handle login popup success/failure or open logout popups. The test logic doesn't match the actual implementation.

Copilot uses AI. Check for mistakes.
Comment on lines 130 to +276
mockFetch.mockResolvedValueOnce({ ok: false });
mockWindowOpen.mockReturnValueOnce(null);

const onLoginFailed = vi.fn();

const handler = createUnauthorizedHandler({
baseUrl: 'https://example.com',
onLoginFailed,
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The test expects onLoginFailed callback, but AuthHandlerConfig interface doesn't define this callback. The test should use onLoginRequired instead, which is the actual callback that exists in the interface.

Copilot uses AI. Check for mistakes.
icon={<AddFilled fontSize={16} />}
onClick={onNewSession}
size="medium"
title="New chat"
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

[nitpick] The button title uses lowercase "New chat" while the button text uses "New chat" (title case). For consistency with UI standards and accessibility (screen readers will read the title), both should use the same case. Consider changing the title to match: title="New Chat".

Suggested change
title="New chat"
title="New Chat"

Copilot uses AI. Check for mistakes.
expect(result).toBe(true);
expect(mockFetch).toHaveBeenCalledWith('https://example.com/.auth/me', {
method: 'GET',
credentials: 'same-origin',
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The test expects credentials: 'same-origin', but the implementation in authHandler.ts line 245 uses credentials: 'include'. This mismatch will cause the test to fail. The implementation should use 'include' for cross-origin cookie support, so the test expectation should be updated to match.

Suggested change
credentials: 'same-origin',
credentials: 'include',

Copilot uses AI. Check for mistakes.
Comment on lines +162 to 174
it('should open login popup when refresh fails', async () => {
mockFetch.mockResolvedValueOnce({ ok: false });
const mockPopup = { closed: false, close: vi.fn(), location: { href: '' } };
mockWindowOpen.mockReturnValueOnce(mockPopup);

const onRefreshFailed = vi.fn();
const onLogoutComplete = vi.fn();
const onLoginSuccess = vi.fn();

const handler = createUnauthorizedHandler({
baseUrl: 'https://example.com',
onRefreshFailed,
onLogoutComplete,
onLoginSuccess,
});
Copy link

Copilot AI Dec 2, 2025

Choose a reason for hiding this comment

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

The test expects onLoginSuccess and onLoginFailed callbacks, but the AuthHandlerConfig interface (line 11 in authHandler.ts) only defines onLoginRequired, not onLoginSuccess or onLoginFailed. This test will fail because the handler implementation doesn't support these callbacks. The test should be updated to use the actual onLoginRequired callback.

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants