Skip to content

417 hwioauthbundle implementation#65

Draft
iliaskhat wants to merge 2 commits into
sumocoders:mainfrom
iliaskhat:417-hwioauthbundle-implementation
Draft

417 hwioauthbundle implementation#65
iliaskhat wants to merge 2 commits into
sumocoders:mainfrom
iliaskhat:417-hwioauthbundle-implementation

Conversation

@iliaskhat
Copy link
Copy Markdown

@iliaskhat iliaskhat commented May 12, 2026

Added and documented use of hwi/oauthbundle

Summary by Sourcery

Integrate optional Azure SSO via HWIOAuthBundle with support for both client and SumoCoders tenants, including database changes and login flow updates.

New Features:

  • Add optional Azure Entra ID single sign-on support using HWI OAuthBundle, including Azure role syncing and auto-provisioning of users based on email domain.
  • Expose Microsoft and SumoCoders SSO login options on the existing login page when corresponding credentials are configured.
  • Emit a dedicated Azure login domain event for downstream listeners on successful SSO logins.

Enhancements:

  • Extend the User entity with Azure object ID support and helpers to link/unlink Azure accounts and synchronize roles.
  • Update security, routing, and service configuration to plug HWI OAuth into the main firewall and user provider.
  • Document setup and configuration steps for enabling Azure and SumoCoders SSO, including environment variables and Azure Portal instructions.

Build:

  • Add HWIOAuthBundle as a project dependency and register the bundle in the Symfony application configuration.

Tests:

  • Add unit tests for Azure-related behavior on the User entity.
  • Add comprehensive tests for the AzureUserProvider covering user lookup, provisioning, role synchronization, domain restrictions, and login event dispatch.

@iliaskhat iliaskhat self-assigned this May 12, 2026
@sourcery-ai
Copy link
Copy Markdown

sourcery-ai Bot commented May 12, 2026

Reviewer's Guide

Adds optional Azure Entra ID SSO support using HWIOAuthBundle, wiring it into security, user entity, login UI, and configuration, with domain-based auto-provisioning, role syncing, events, and tests; also adjusts migrations path in the README.

Sequence diagram for Azure SSO login via HWIOAuthBundle

sequenceDiagram
    actor User
    participant Browser
    participant HWIOAuthBundle
    participant AzureUserProvider
    participant UserRepository
    participant EventDispatcher

    User->>Browser: Click Sign in with Microsoft
    Browser->>HWIOAuthBundle: GET /login/check-azure
    HWIOAuthBundle->>AzureUserProvider: loadUserByOAuthUserResponse(response)

    alt user matched by azureObjectId
        AzureUserProvider->>UserRepository: findOneBy(azureObjectId)
        UserRepository-->>AzureUserProvider: User
        AzureUserProvider->>User: syncAzureRoles(roles)
        AzureUserProvider->>User: update(email, roles)
        AzureUserProvider->>UserRepository: save()
        AzureUserProvider->>EventDispatcher: dispatch(AzureLoginEvent)
        AzureUserProvider-->>HWIOAuthBundle: User
    else user matched by email
        AzureUserProvider->>UserRepository: findOneBy(email)
        UserRepository-->>AzureUserProvider: User
        AzureUserProvider->>User: linkAzureAccount(oid)
        AzureUserProvider->>User: syncAzureRoles(roles)
        AzureUserProvider->>UserRepository: save()
        AzureUserProvider->>EventDispatcher: dispatch(AzureLoginEvent)
        AzureUserProvider-->>HWIOAuthBundle: User
    else new user auto-provisioned
        AzureUserProvider->>AzureUserProvider: assertEmailDomainIsAllowed(email)
        AzureUserProvider->>User: createFromAzureProfile(email, oid, roles)
        AzureUserProvider->>UserRepository: add(User)
        AzureUserProvider->>EventDispatcher: dispatch(AzureLoginEvent)
        AzureUserProvider-->>HWIOAuthBundle: User
    end

    HWIOAuthBundle-->>Browser: Authenticated session
    Browser-->>User: Logged in via Azure
Loading

File-Level Changes

Change Details Files
Introduce Azure SSO support via HWIOAuthBundle, including Azure and SumoCoders OAuth resource owners and routing.
  • Require hwi/oauth-bundle via composer and register HWIOAuthBundle in the Symfony bundle configuration.
  • Configure Azure and SumoCoders resource owners using environment-based client/tenant IDs and scopes in a dedicated hwi_oauth.yaml.
  • Register HWIOAuthBundle routing for connect and login paths via a new routing configuration file.
composer.json
composer.lock
symfony.lock
config/bundles.php
config/packages/hwi_oauth.yaml
config/routes/hwi_oauth_routing.yaml
Extend the User domain model and persistence layer to track Azure identities and synchronize roles.
  • Add nullable unique azureObjectId field and column to the User entity to link Azure accounts.
  • Introduce factory and helper methods for Azure-based user creation, linking/unlinking accounts, checking Azure status, exposing the Azure object ID, and syncing roles.
  • Add a Doctrine migration that adds the azure_object_id column and unique index to the user table, with a reversible down migration.
src/Entity/User/User.php
migrations/Version20260512135528.php
Implement an AzureUserProvider that integrates with HWIOAuthBundle to load, provision, and update users from Azure OAuth responses, including domain restrictions and login events.
  • Create AzureUserProvider implementing UserProviderInterface and OAuthAwareUserProviderInterface, resolving users by Azure OID or email and auto-provisioning when allowed.
  • Enforce optional email-domain restrictions for Azure logins and throw AccessDeniedException for disallowed domains.
  • Sync Azure-provided roles on login, ensure ROLE_USER presence, persist changes, and dispatch a custom AzureLoginEvent on every successful login.
  • Provide identifier-based loading, refresh support, and class support checks for Symfony security integration.
src/Security/OAuth/AzureUserProvider.php
src/Event/User/AzureLoginEvent.php
Wire Azure SSO into the security firewall and service container, and expose toggles to the login controller and template.
  • Update main security firewall to use CustomAuthenticator as entry point and configure an oauth section using AzureUserProvider for azure and sumocoders resource owners.
  • Allow public access for HWIOAuthBundle login and connect endpoints via new access_control rules.
  • Register AzureUserProvider and inject the allowed email domain via environment, and inject Azure/SumoCoders client IDs into the LoginController via services configuration.
  • Extend LoginController to accept Azure/SumoCoders client IDs and expose boolean flags to the login template to conditionally render SSO buttons.
  • Update the login template to show Microsoft and SumoCoders sign-in buttons using HWI OAuth redirect routes when enabled.
config/packages/security.yaml
config/services.yaml
src/Controller/User/LoginController.php
templates/user/login.html.twig
Add tests to cover Azure-specific behavior for the User entity and AzureUserProvider, including domain checks, role syncing, and event dispatching.
  • Extend User tests to cover Azure user defaults, linking/unlinking accounts, Azure profile factory, and role behavior.
  • Add a comprehensive AzureUserProvider test suite that mocks the repository and event dispatcher to validate lookup paths, auto-provisioning, domain restrictions, role synchronization, and login event dispatching.
tests/Entity/User/UserTest.php
tests/Security/OAuth/AzureUserProviderTest.php
Document Azure SSO setup and adjust migration instructions for consumers of the project.
  • Update README migration instructions to reference the root-level migrations directory instead of src/Migrations.
  • Add extensive Azure SSO documentation describing Azure app registration, API permissions, app roles, user-role assignments, bundle installation, configuration steps, environment variables, migrations, and optional SumoCoders login setup.
  • Add Dutch translations for new Azure-related login strings and error messages.
README.md
translations/messages.nl.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

Copy link
Copy Markdown

@sourcery-ai sourcery-ai Bot left a comment

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The azure_object_id column is nullable but has a unique index in the migration; this can cause issues on some databases (e.g. PostgreSQL doesn’t allow multiple NULLs in a unique index), so consider using a partial/index-where non-null or enforcing uniqueness at the application level instead.
  • Injecting AZURE_CLIENT_ID and SUMOCODERS_CLIENT_ID as required constructor arguments in LoginController means the container will fail if these env vars are absent; if you want Azure/ Sumocoders login to be truly optional, consider using default env values (e.g. env(default::string: '')) or making the arguments nullable.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The `azure_object_id` column is nullable but has a unique index in the migration; this can cause issues on some databases (e.g. PostgreSQL doesn’t allow multiple NULLs in a unique index), so consider using a partial/index-where non-null or enforcing uniqueness at the application level instead.
- Injecting `AZURE_CLIENT_ID` and `SUMOCODERS_CLIENT_ID` as required constructor arguments in `LoginController` means the container will fail if these env vars are absent; if you want Azure/ Sumocoders login to be truly optional, consider using default env values (e.g. `env(default::string: '')`) or making the arguments nullable.

## Individual Comments

### Comment 1
<location path="src/Security/OAuth/AzureUserProvider.php" line_range="116-117" />
<code_context>
+
+        $allowedDomain = ltrim($this->allowedEmailDomain, '@');
+        if (!str_ends_with(strtolower($email), '@' . strtolower($allowedDomain))) {
+            throw new AccessDeniedException(
+                sprintf('Email domain of "%s" is not allowed to log in via Azure.', $email)
+            );
+        }
</code_context>
<issue_to_address>
**issue (bug_risk):** The email-domain error message won't match the existing translation key, so it likely won't be localized.

The NL translation you added is tied to the key `"Email domain is not allowed to log in via Azure."`, but the thrown message is `"Email domain of "%s" is not allowed to log in via Azure."`. Because these differ, this message won’t be localized. To make it translatable, either reuse the existing key and log the email separately, or introduce a translation key with a parameter instead of hardcoding the `sprintf` string.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +116 to +117
throw new AccessDeniedException(
sprintf('Email domain of "%s" is not allowed to log in via Azure.', $email)
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

issue (bug_risk): The email-domain error message won't match the existing translation key, so it likely won't be localized.

The NL translation you added is tied to the key "Email domain is not allowed to log in via Azure.", but the thrown message is "Email domain of "%s" is not allowed to log in via Azure.". Because these differ, this message won’t be localized. To make it translatable, either reuse the existing key and log the email separately, or introduce a translation key with a parameter instead of hardcoding the sprintf string.

@jonasdekeukelaere jonasdekeukelaere marked this pull request as draft May 13, 2026 05:39
@@ -1,8 +1,22 @@
hwi_oauth:
# https://github.com/hwi/HWIOAuthBundle/blob/master/docs/2-configuring_resource_owners.md
firewall_names: [main]
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Dat is niet meer nodig? Uit 2.0 changelog:

  • Deprecated: configuration parameter firewall_names, firewalls are now computed automatically - all firewalls that have defined oauth authenticator/provider will be collected.

Comment thread config/routes/hwi_oauth_routing.yaml Outdated
resource: "@HWIOAuthBundle/Resources/config/routing/redirect.php"
prefix: /connect

hwi_oauth_connect:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why?


public function up(Schema $schema): void
{
$table = $schema->getTable('user');
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Waarom geen SQL?

Comment thread config/services.yaml

App\Controller\User\LoginController:
arguments:
$azureClientId: '%env(AZURE_CLIENT_ID)%'
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Waarom niet via #[Autowire]


// 1. Match on azure_object_id — most common path after first login
$user = $this->userRepository->findOneBy(['azureObjectId' => $oid]);
if ($user !== null) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Ik zou werken met instanceof if ($user instanceof User) { }

}

$allowedDomain = ltrim($this->allowedEmailDomain, '@');
if (!str_ends_with(strtolower($email), '@' . strtolower($allowedDomain))) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Dat gaat ook nooit werken met meerdere domains? Bv bij LDR hebben we die van LDR en onze van sumocoders.

Comment thread README.md
arguments:
$allowedEmailDomain: '%env(AZURE_ALLOWED_EMAIL_DOMAIN)%'

App\Controller\User\LoginController:
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Mag dan weg als het via Autowire gaat

Comment thread README.md

Add the following redirect URIs to the SumoCoders app registration in the Azure Portal:

* `https://<project>.wip/login/check-sumocoders`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

project.client.wip

Comment thread README.md
Add the following redirect URIs to the SumoCoders app registration in the Azure Portal:

* `https://<project>.wip/login/check-sumocoders`
* `https://<project>.phpXX.sumocoders.eu/login/check-sumocoders`
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

project.client.phpXX.sumocoders.eu

Comment thread README.md
* `https://<project>.wip/login/check-sumocoders`
* `https://<project>.phpXX.sumocoders.eu/login/check-sumocoders`

> Replace `<project>` with the project name.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

En

replace <client> with client name.

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