Skip to content

fix: resolve plugin registration and cross-tenant auth issues (#59)#60

Merged
joshsmithxrm merged 6 commits intomainfrom
fix/issue-59-multi-tenant-auth
Jan 1, 2026
Merged

fix: resolve plugin registration and cross-tenant auth issues (#59)#60
joshsmithxrm merged 6 commits intomainfrom
fix/issue-59-multi-tenant-auth

Conversation

@joshsmithxrm
Copy link
Copy Markdown
Owner

Summary

Resolves all items in #59:

Plugin Registration Fixes

  • Add entityAlias field to image definitions (defaults to name if not specified)
  • Add unsecureConfiguration field for step configuration
  • Add MainOperation stage (30) support for Custom API plugin steps
  • Add JSON schema for plugin-registration.json
  • Use Zulu time format for generatedAt timestamps
  • Fix solution component addition on UPDATE path (was only on CREATE)
  • Fix cross-platform path normalization (use forward slashes)
  • Fix runtime ETC lookup for pluginpackage entity

Cross-Tenant Auth Fix

Root cause: MSAL token cache shared by all profiles, but account lookup used accounts.FirstOrDefault() which picked an arbitrary cached account regardless of which tenant the profile belonged to.

Fix:

  • Add HomeAccountId property to AuthProfile and ICredentialProvider
  • Store account identifier (format: {objectId}.{tenantId}) after authentication
  • Use GetAccountAsync(homeAccountId) for precise account lookup
  • Fall back to filtering by HomeAccountId.TenantId if no identifier stored
  • Never silently use random cached account - force re-auth if no match found

Applied to: InteractiveBrowserCredentialProvider, DeviceCodeCredentialProvider, GlobalDiscoveryService

Test plan

  • Build succeeds with no errors
  • All 1,897 tests pass across all target frameworks
  • Manual test: verified cross-tenant auth now works correctly

Closes #59

🤖 Generated with Claude Code

joshsmithxrm and others added 4 commits January 1, 2026 04:25
- Add unsecureConfiguration field (renamed from configuration for clarity)
- Add entityAlias field to image output
- Add MainOperation (stage 30) support for custom actions
- Add ZuluTimeConverter for consistent timestamp formatting
- Fix solution component handling for updates (add even on update)
- Fix cross-platform path normalization (use forward slashes)
- Add JSON schema for plugin-registration.json

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
When silent token acquisition fails (e.g., tokens cached under different
tenant's authority), the fallback now respects the profile's original
AuthMethod instead of always defaulting to device code flow.

- Add preferredAuthMethod parameter to GlobalDiscoveryService
- Check if interactive browser is available before falling back
- Use interactive browser for profiles created with that method
- Only fall back to device code for SP profiles or when interactive unavailable
- Always use "organizations" authority for discovery (enables token reuse)

Fixes item #9 in issue #59.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Root cause: MSAL token cache shared by all profiles, but account lookup
used `accounts.FirstOrDefault()` which picked arbitrary cached account
regardless of which tenant the profile belonged to.

Fix:
- Add HomeAccountId property to AuthProfile and ICredentialProvider
- Store account identifier (format: {objectId}.{tenantId}) after auth
- Use GetAccountAsync(homeAccountId) for precise account lookup
- Fall back to filtering by HomeAccountId.TenantId if no identifier
- Never silently use random cached account - force re-auth if needed
- Apply fix to: InteractiveBrowserCredentialProvider,
  DeviceCodeCredentialProvider, GlobalDiscoveryService

The fix ensures each profile's token lookup finds only tokens from the
correct tenant, preventing cross-tenant environment discovery.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings January 1, 2026 10:56
@gemini-code-assist
Copy link
Copy Markdown

Summary of Changes

Hello @joshsmithxrm, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request delivers significant improvements in two core areas: plugin registration and cross-tenant authentication. It enhances the reliability and usability of plugin registration by introducing a formal schema, supporting new configuration options, and fixing component management. Concurrently, it resolves a critical cross-tenant authentication bug by implementing a more precise MSAL account lookup strategy, ensuring users are always authenticated against the correct tenant and preventing unintended data access.

Highlights

  • Enhanced Plugin Registration: Introduced a JSON schema for 'plugin-registration.json' for better validation, added 'entityAlias' for plugin images, supported 'MainOperation' stage for Custom API plugins, and included 'unsecureConfiguration' for step configurations.
  • Improved Solution Component Management: Fixed an issue where solution components were only added on creation, now ensuring they are added or updated correctly even on subsequent modifications.
  • Cross-Platform Path Normalization: Standardized path handling within plugin configurations to use forward slashes, improving compatibility across different operating systems.
  • Resolved Cross-Tenant Authentication Issues: Addressed a critical bug where MSAL token caching led to incorrect account lookups across different tenants by introducing 'HomeAccountId' for precise account identification and lookup, preventing silent use of arbitrary cached accounts.
  • Refined MSAL Account Lookup: Implemented a robust account lookup mechanism in 'InteractiveBrowserCredentialProvider', 'DeviceCodeCredentialProvider', and 'GlobalDiscoveryService' that prioritizes 'HomeAccountId', falls back to tenant filtering, and then username, ensuring the correct user context is always used.
  • Zulu Time Format for Timestamps: Ensured 'generatedAt' timestamps in plugin configurations are consistently formatted in Zulu time (UTC with 'Z' suffix).
  • Accurate Plugin Package ETC Lookup: Corrected the runtime entity type code (ETC) lookup for 'pluginpackage' entities, which can vary by environment.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This is an excellent pull request that addresses several important issues. The fix for the cross-tenant authentication issue is critical and the implementation is robust, correctly using HomeAccountId for precise account lookups and preventing the use of incorrect cached tokens. The addition of the FindAccountAsync method is the right approach to centralize this logic within each provider.

The improvements to plugin registration are also very valuable. Adding support for the MainOperation stage, unsecureConfiguration, and entityAlias enhances the tool's capabilities. The fixes for adding components to solutions on update, normalizing paths for cross-platform compatibility, and dynamically looking up entity type codes all contribute to a more reliable and robust tool. The new JSON schema is a great addition for validation and discoverability.

I have one suggestion regarding code duplication to improve maintainability, but overall this is a high-quality contribution.

Copy link
Copy Markdown

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 resolves plugin registration issues and fixes a critical cross-tenant authentication bug. The plugin registration improvements add missing fields and features, while the auth fix ensures MSAL correctly identifies cached accounts per tenant instead of using arbitrary cached accounts.

Key Changes:

  • Add entityAlias, unsecureConfiguration, and MainOperation stage support to plugin registration
  • Fix cross-tenant auth by storing and using HomeAccountId for precise MSAL account lookup
  • Add runtime entity type code lookup for dynamic entities like pluginpackage

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
src/PPDS.Cli/Plugins/Models/PluginRegistrationConfig.cs Added ZuluTimeConverter for timestamps, renamed Configuration to UnsecureConfiguration, added EntityAlias property
src/PPDS.Cli/Plugins/Registration/PluginRegistrationService.cs Added ETC cache for runtime lookup, MainOperation stage mapping, solution component addition on UPDATE path
src/PPDS.Cli/Plugins/Extraction/AssemblyExtractor.cs Added entityAlias defaulting logic and MainOperation stage support
src/PPDS.Cli/Commands/Plugins/ExtractCommand.cs Normalized paths to forward slashes for cross-platform compatibility
src/PPDS.Cli/Commands/Plugins/ListCommand.cs Added unsecureConfiguration and entityAlias to JSON output
src/PPDS.Cli/Commands/Auth/AuthCommandGroup.cs Store HomeAccountId after authentication
src/PPDS.Auth/Profiles/AuthProfile.cs Added HomeAccountId property for MSAL account tracking
src/PPDS.Auth/Discovery/GlobalDiscoveryService.cs Implemented FindAccountAsync with HomeAccountId lookup and tenant filtering
src/PPDS.Auth/Credentials/InteractiveBrowserCredentialProvider.cs Added FindAccountAsync method with HomeAccountId support
src/PPDS.Auth/Credentials/DeviceCodeCredentialProvider.cs Added FindAccountAsync method with HomeAccountId support
src/PPDS.Auth/Credentials/ICredentialProvider.cs Added HomeAccountId property to interface
src/PPDS.Auth/Credentials/*CredentialProvider.cs Implemented HomeAccountId property in all credential providers
schemas/plugin-registration.schema.json New JSON schema for plugin registration configuration validation
src/PPDS.Cli/CHANGELOG.md Documented plugin registration additions and fixes
src/PPDS.Auth/CHANGELOG.md Documented cross-tenant auth fix
tests/PPDS.Cli.Tests/Plugins/Models/PluginRegistrationConfigTests.cs Updated tests for UnsecureConfiguration rename

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
- Extract MsalAccountHelper for DRY account lookup (Gemini feedback)
- Remove entityAlias from schema required array (Copilot feedback)
- Add CultureInfo.InvariantCulture to DateTimeOffset.Parse (Copilot feedback)

Generic catch clause deferred to #61.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
@gemini-code-assist
Copy link
Copy Markdown

Thanks for the update, @joshsmithxrm! I appreciate you addressing the DRY violation in FindAccountAsync by extracting MsalAccountHelper.cs. This is a great improvement for code maintainability and consistency. Excellent work on these updates!

@joshsmithxrm joshsmithxrm merged commit 06eca4b into main Jan 1, 2026
5 checks passed
@github-project-automation github-project-automation bot moved this from Todo to Done in PPDS Roadmap Jan 1, 2026
@joshsmithxrm joshsmithxrm deleted the fix/issue-59-multi-tenant-auth branch January 1, 2026 11:17
@joshsmithxrm joshsmithxrm self-assigned this Jan 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

bug: registrations.json output has multiple issues

3 participants