Summary
Add an optional Entra ID (Azure AD) integration via a Unity Catalog HTTP Connection, plus a unified PrincipalPicker component that replaces ~14 inconsistent principal-entry UIs across the app with a Gmail-style badge input.
- Configured mode: type-ahead + popup search dialog backed by Microsoft Graph (Users + Groups). Each result row shows
displayName on top and email/UPN (users) or GUID (groups) underneath, so homonyms are always disambiguated.
- Unconfigured mode: typed values become removable badges; clicking a badge reverts it to editable text.
- Auth: UC HTTP Connection only — UC handles OAuth2 client credentials and token refresh via
ws.serving_endpoints.http_request(conn=name, ...). No app-side token cache, no client secret stored in the app database.
- Storage: keep existing
string / List[str] fields untouched. No Alembic migration for v1.
Full PRD: docs/prds/prd-entra-id-graph-integration.md
Why
Today, ~14 different forms each implement principal entry differently (plain Input, comma-separated string, hard-coded Select, even unwired wizard placeholders). There is no validation, no display-name resolution, and no defense against typos. Operators with an Entra tenant cannot pick from a directory; consumers are guessing whether a group is data-eng or data_engineering.
The existing UC HTTP Connection plumbing (used by the workflow webhook step) already gives us a clean, secret-free path to Microsoft Graph. The pieces are in place; what is missing is one shared component, a small Graph search backend, and migration of every call site.
Decisions locked in (from PRD Q&A)
- Replacement scope: all ~14 call sites in v1.
- Principal types: Users + Groups (defer service principals).
- Storage: keep strings (
List[str] of emails / group names).
- Search UI: both type-ahead AND popup dialog in the same component.
- Pre-existing data: no re-validation; renders as plain badges.
- Manual-mode badges: click to edit (true Gmail-like).
- Disambiguation: two-line rows (name + email/GUID) plus tooltip on selected badges.
Sub-issues (to be filed)
Backend
Frontend — shared component
Frontend — call-site migration (15 fields across 13 files)
Testing
Out of scope (v1)
- Service principals
- Storage migration to structured Principal records
- Bulk re-validation of pre-existing values
- OBO / delegated Graph queries
- Server-side display-name caching beyond the 5-min query cache
- Replacing existing role/team Selects in the workflow designer (additive toggle only)
Acceptance criteria
- Admin can configure a UC HTTP Connection in Settings → Integrations → Entra ID and the test button reports success/failure of a real Graph probe.
- With Entra configured, every principal-entry field across the app uses the new picker; type-ahead returns results after 2 chars, debounced 250ms.
- Every search result row shows the friendly name plus the email/UPN/GUID underneath; selected badges expose the email/UPN/GUID via tooltip + `title` attribute.
- With Entra not configured, every field falls back to the same picker in manual mode (Enter/Tab/comma → badge, click badge → editable text).
- Existing API payloads continue to use the same string identifiers; existing DB rows render as plain badges with no error decoration.
- No client secret is stored in the app database; UC handles OAuth.
Summary
Add an optional Entra ID (Azure AD) integration via a Unity Catalog HTTP Connection, plus a unified
PrincipalPickercomponent that replaces ~14 inconsistent principal-entry UIs across the app with a Gmail-style badge input.displayNameon top and email/UPN (users) or GUID (groups) underneath, so homonyms are always disambiguated.ws.serving_endpoints.http_request(conn=name, ...). No app-side token cache, no client secret stored in the app database.string/List[str]fields untouched. No Alembic migration for v1.Full PRD:
docs/prds/prd-entra-id-graph-integration.mdWhy
Today, ~14 different forms each implement principal entry differently (plain
Input, comma-separated string, hard-codedSelect, even unwired wizard placeholders). There is no validation, no display-name resolution, and no defense against typos. Operators with an Entra tenant cannot pick from a directory; consumers are guessing whether a group isdata-engordata_engineering.The existing UC HTTP Connection plumbing (used by the workflow webhook step) already gives us a clean, secret-free path to Microsoft Graph. The pieces are in place; what is missing is one shared component, a small Graph search backend, and migration of every call site.
Decisions locked in (from PRD Q&A)
List[str]of emails / group names).Sub-issues (to be filed)
Backend
list_http_connections(ws)helper fromworkflows_routes.pyand exposeGET /api/settings/uc-http-connectionsEntraIdManager(src/backend/src/controller/entra_id_manager.py) withsearch_users,search_groups,get_user,get_group,test, OData escaping, in-memory TTL cacheGET /api/entra/status,GET /api/entra/search,POST /api/entra/testENTRA_UC_HTTP_CONNECTION_NAMEinSettingsManager(`_load_persisted_settings` + `update_settings` + `get_settings`)Principalmodel + tests for normalisationFrontend — shared component
PrincipalPickercomponent atsrc/frontend/src/components/common/principal-picker.tsxwith both modes, two-line result rows, tooltip on badges, Zustandentra-storefor statusentra-settings.tsx+ view) with UC connection select + test button + clear buttonentranamespace +common.principalPicker.*)Frontend — call-site migration (15 fields across 13 files)
Testing
Out of scope (v1)
Acceptance criteria