PatchHound needs Microsoft Entra ID for two separate concerns:
- User authentication into the PatchHound web application
- App-only access for tenant lookup and, optionally, Microsoft Defender ingestion
This guide creates a single Entra application that can handle sign-in and tenant lookup. You can also reuse it for Defender ingestion if that fits your security model, but many teams prefer a separate app registration for Defender.
PatchHound uses these values at runtime:
AZURE_AD_CLIENT_IDAZURE_AD_TENANT_IDAZURE_AD_AUDIENCEENTRA_CLIENT_SECRETFRONTEND_ORIGIN
In Docker, the frontend derives these values automatically:
ENTRA_CLIENT_ID=AZURE_AD_CLIENT_IDENTRA_TENANT_ID=AZURE_AD_TENANT_IDENTRA_REDIRECT_URI=${FRONTEND_ORIGIN}/auth/callback
- Open the Microsoft Entra admin center.
- Go to
Identity > Applications > App registrations. - Select
New registration. - Give the application a name such as
PatchHound. - Choose the supported account type:
- Use
Accounts in this organizational directory onlyfor a single-tenant deployment. - Use
Accounts in any organizational directoryif you want multi-tenant sign-in.
- Use
- Add a Web redirect URI:
- Local Docker/dev example:
http://localhost:3000/auth/callback - Replace the hostname for your real deployment later.
- Local Docker/dev example:
- Create the app registration.
From the app overview page, record:
Application (client) ID->AZURE_AD_CLIENT_IDDirectory (tenant) ID->AZURE_AD_TENANT_ID
For AZURE_AD_AUDIENCE, use the same application client ID unless you have configured a custom application ID URI and are intentionally validating that instead.
Recommended local example:
AZURE_AD_CLIENT_ID=<application-client-id>
AZURE_AD_TENANT_ID=common
AZURE_AD_AUDIENCE=<application-client-id>
ENTRA_CLIENT_SECRET=<generated-client-secret>
FRONTEND_ORIGIN=http://localhost:3000Notes:
AZURE_AD_TENANT_ID=commonis useful for multi-tenant sign-in.- For a single-tenant deployment, set
AZURE_AD_TENANT_IDto the tenant GUID instead.
- Open
Certificates & secrets. - Create a new client secret.
- Copy the secret value immediately.
- Store it as
ENTRA_CLIENT_SECRET.
PatchHound uses this secret for:
- Frontend server-side auth flows with MSAL
- Microsoft Graph app-only lookup of tenant display name during setup
- Defender ingestion as well, if you choose to reuse the same app for that purpose
Under Authentication:
- Keep the Web redirect URI for every PatchHound origin you will use.
- Local example:
http://localhost:3000/auth/callback - Production example:
https://patchhound.example.com/auth/callback
PatchHound also logs users out through Entra, so keep the public application origin stable and consistent with FRONTEND_ORIGIN.
PatchHound setup requires the signed-in user to have the Tenant.Admin app role.
To add it:
- Open
App rolesfor the app registration. - Create a new app role with these values:
Display name: Tenant Admin
Allowed member types: Users/Groups
Value: Tenant.Admin
Description: Allows the user to initialize and administer a PatchHound tenant
Do you want to enable this app role?: Yes
- Save the role.
Then assign it:
- Go to
Enterprise applications. - Open the service principal for your PatchHound app.
- Go to
Users and groups. - Assign the
Tenant Adminrole to the users or groups who should be allowed to complete setup.
Important:
- The setup flow checks for the exact role value
Tenant.Admin. - After assigning the role, the user should sign out and sign back in before retrying setup.
For normal login, PatchHound requests standard OpenID Connect scopes:
openidprofileemailoffline_access
These are standard delegated permissions and are usually present automatically for sign-in scenarios.
Configure these API permissions on the Entra application.
Add this delegated permission:
User.Read
This covers user sign-in and basic profile access.
Add these application permissions:
Machine.Read.AllScore.Read.AllSoftware.Read.AllVulnerability.Read.All
Then grant admin consent for the tenant.
These are the permissions PatchHound expects for Defender-based data retrieval.
PatchHound’s built-in Defender source uses:
- API base URL:
https://api.securitycenter.microsoft.com - Token scope:
https://api.securitycenter.microsoft.com/.default
If you want to reuse the same Entra application for Defender ingestion:
- Add the required
WindowsDefenderATPapplication permissions listed above. - Grant admin consent.
- Use the same tenant ID, client ID, and client secret when configuring the Defender source inside PatchHound.
If you prefer separation of duties, create a second app registration just for Defender and use that in the tenant source configuration instead. That is usually the cleaner production setup.
Set the auth values in .env:
AZURE_AD_CLIENT_ID=<application-client-id>
AZURE_AD_TENANT_ID=<tenant-id-or-common>
AZURE_AD_AUDIENCE=<application-client-id>
ENTRA_CLIENT_SECRET=<client-secret>
FRONTEND_ORIGIN=http://localhost:3000Docker Compose maps these into the API and frontend automatically.
After restarting PatchHound:
- Open the frontend.
- Sign in with a user assigned to the
Tenant.Adminapp role. - Confirm that setup can proceed without the
Tenant.Admin is requirederror. - Complete initial tenant setup.
- If you configured Defender credentials, test the source from the admin area.
AADSTS50011 or redirect mismatch:
- The Entra app redirect URI does not match
${FRONTEND_ORIGIN}/auth/callback.
Login succeeds but PatchHound setup is blocked:
- The user is missing the
Tenant.Adminapp role. - The role was assigned after the user signed in and the session needs to be refreshed.
API authentication fails with audience errors:
AZURE_AD_AUDIENCEdoes not match the token audience PatchHound is validating.- Start with the application client ID unless you intentionally use a custom app ID URI.
Sign-in or Graph-backed profile lookup fails:
Microsoft Graph > Delegated > User.Readis missing.- Admin consent or user consent has not been granted as required by your tenant policy.
Defender ingestion cannot get tokens:
- One or more required
WindowsDefenderATPapplication permissions are missing:Machine.Read.AllScore.Read.AllSoftware.Read.AllVulnerability.Read.All
- Admin consent has not been granted.
- The configured source credentials do not match the app registration you intended to use.