feat: add Azure Federated Workload Identity support for OIDC SSO login#7966
feat: add Azure Federated Workload Identity support for OIDC SSO login#7966cosmincatalin wants to merge 6 commits intographql-hive:mainfrom
Conversation
#2) * feat: add Azure Federated Identity (OAuthBearer) support for Kafka/EventHub Add support for the `oauthbearer` SASL mechanism in both the usage and usage-ingestor services, enabling Azure Workload Identity authentication to Event Hub when deployed in Kubernetes. Changes: - Add `@azure/identity` dependency to usage and usage-ingestor packages - Add `oauthbearer` SASL mechanism to environment config (Zod schemas) - Create `oauth-bearer-provider.ts` using DefaultAzureCredential - Update Kafka client initialization to support oauthbearer mechanism - Update Pulumi deployment to conditionally configure oauthbearer Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/b3983d22-e4bf-4a52-90f1-c93895adeff8 Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com> * refactor: improve OAuth bearer provider error handling and documentation Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/b3983d22-e4bf-4a52-90f1-c93895adeff8 Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com> * revert: remove incorrect Kafka OAuthBearer changes Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/755ee5ba-e92c-4df8-b742-e04e182b5a13 Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com> * feat: add Azure Federated Workload Identity support for OIDC SSO login When deployed in Kubernetes with Azure Workload Identity, the OIDC integration can now authenticate to the identity provider using a federated token (client_assertion) instead of a client_secret. Changes: - DB migration: add use_federated_credential column, make client_secret nullable - Entity/model: update OIDCIntegration to include useFederatedCredential - GraphQL: make clientSecret optional in create input, add useFederatedCredential field - OIDC provider: allow creation without client secret when federated - Auth server: use client_assertion with AZURE_FEDERATED_TOKEN_FILE - UI: show federated credential status, allow saving without client secret Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/755ee5ba-e92c-4df8-b742-e04e182b5a13 Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com> * fix: use strict equality for null check in OIDC provider Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/755ee5ba-e92c-4df8-b742-e04e182b5a13 Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com>
There was a problem hiding this comment.
Code Review
This pull request introduces support for OIDC federated credentials, specifically Azure Workload Identity, allowing authentication without a client secret. Key changes include database migrations to make client secrets optional, GraphQL schema updates, and server-side logic to utilize federated token files during token exchange. Feedback highlights a style guide violation regarding direct process.env access, a performance concern with synchronous file I/O in an asynchronous handler, and a missing validation check in the update logic to ensure a valid authentication method is always configured.
packages/services/api/src/modules/oidc-integrations/providers/oidc-integrations.provider.ts
Show resolved
Hide resolved
Co-authored-by: gemini-code-assist[bot] <176961590+gemini-code-assist[bot]@users.noreply.github.com>
…irect process.env access (#3) Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/8b554117-9357-40a5-9879-83f5ac168987 Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com>
* Add validation in updateOIDCIntegration to ensure valid auth method after update Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/3b791a1d-2b1b-4152-af23-11dcd32bc977 Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com> * Improve error message for missing auth method validation Agent-Logs-Url: https://github.com/cosmincatalin/console/sessions/3b791a1d-2b1b-4152-af23-11dcd32bc977 Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com> --------- Co-authored-by: copilot-swe-agent[bot] <198982749+Copilot@users.noreply.github.com> Co-authored-by: cosmincatalin <525590+cosmincatalin@users.noreply.github.com>
n1ru4l
left a comment
There was a problem hiding this comment.
Thank you for this contribution! I added some initial comments with an initial review, it would be great if you could address these. We cannot give an exact timeline yet, but we will try to test this on our cluster and see if it feasible to merge and support soon.
| const tokenFilePath = process.env['AZURE_FEDERATED_TOKEN_FILE']; | ||
| if (!tokenFilePath) { | ||
| req.log.error('AZURE_FEDERATED_TOKEN_FILE environment variable is not set'); | ||
| broadcastLog( | ||
| oidcIntegration.id, | ||
| 'Federated credential is configured but AZURE_FEDERATED_TOKEN_FILE environment variable is not set. ' + | ||
| 'Ensure the pod has Azure Workload Identity configured.', | ||
| ); | ||
| return rep.status(200).send({ | ||
| status: 'SIGN_IN_UP_NOT_ALLOWED', | ||
| reason: 'Sign in failed. Please contact your organization administrator.', | ||
| }); | ||
| } | ||
|
|
||
| const { readFileSync } = await import('node:fs'); | ||
| const federatedToken = readFileSync(tokenFilePath, 'utf-8'); | ||
| grantParams['client_assertion_type'] = | ||
| 'urn:ietf:params:oauth:client-assertion-type:jwt-bearer'; |
There was a problem hiding this comment.
Can you please move the token file reading to the environment.ts file and model the configuration in a way that would allow also configuring other providers in the future (for self-hosters)?
E.g. the environment variable could look like the following:
OIDC_WORKLOAD_FEDERATION_IDENTITY_PROVIDER="azure"
Which then implies that we have to setup some job that polls the path at that env[AZURE_FEDERATED_TOKEN_FILE] in intervals?
Also I think it would be safe to fail service startup if OIDC_WORKLOAD_FEDERATION_IDENTITY_PROVIDER is provided but AZURE_FEDERATED_TOKEN_FILE is not set to a file path or cannot be read?
Attempting to read the tokenFilePath every time a request is authenticated seems a lot, so maybe we can have a container class/service that just polls or watches the file path?
| broadcastLog( | ||
| oidcIntegration.id, | ||
| 'Federated credential is configured but AZURE_FEDERATED_TOKEN_FILE environment variable is not set. ' + | ||
| 'Ensure the pod has Azure Workload Identity configured.', | ||
| ); |
There was a problem hiding this comment.
This is an internal implementation detail that should not be leaked to the outside world.
| ALTER TABLE "oidc_integrations" | ||
| ADD COLUMN IF NOT EXISTS "use_federated_credential" BOOLEAN NOT NULL DEFAULT FALSE; | ||
|
|
||
| ALTER TABLE "oidc_integrations" | ||
| ALTER COLUMN "client_secret" DROP NOT NULL; |
There was a problem hiding this comment.
NIT: this can be combined into a single statement
Background
When deployed in Kubernetes with Azure Workload Identity, the OIDC SSO integration should be able to authenticate to the identity provider (e.g., Microsoft Entra ID) using a federated token (
client_assertion) instead of storing aclient_secret. This eliminates the need for secrets management for the OAuth2 token exchange.Description
Adds a
useFederatedCredentialoption to OIDC integrations. When enabled, the token exchange uses theAZURE_FEDERATED_TOKEN_FILE(injected by the Azure Workload Identity webhook) as aclient_assertionJWT bearer instead ofclient_secret.use_federated_credentialboolean column onoidc_integrations,client_secretmade nullableOIDCIntegration.encryptedClientSecretnowstring | null, addeduseFederatedCredential: booleanclientSecretoptional inCreateOIDCIntegrationInput,useFederatedCredentialfield on type + inputsclientSecretoruseFederatedCredentialis provided on createsupertokens-at-home.ts): Reads federated token file and sendsclient_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer+client_assertion=<token>instead ofclient_secretclientSecretPreviewToken exchange with federated credentials: