MAINT Migrate Azure Cognitive Services from API key to Entra ID authentication#1404
MAINT Migrate Azure Cognitive Services from API key to Entra ID authentication#1404romanlutz wants to merge 23 commits intoAzure:mainfrom
Conversation
- Update AIRTTargetInitializer to use get_azure_openai_auth() for Azure OpenAI targets and get_azure_token_provider() for PromptShieldTarget instead of API key environment variables - Remove key_var from all Azure target configs in TARGET_CONFIGS - Update 15 notebook .py files to use Entra auth for Azure endpoints - Non-Azure targets (platform OpenAI, Groq, Google, etc.) keep API keys - Update unit tests to mock Entra auth providers Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove Azure CogSvc API key lines from .env_example - Update .env_example header to reflect Entra auth is the default - Re-execute 13 notebooks with jupytext --execute to verify Entra auth works and populate cell outputs All notebooks pass except: - 10_http_target: second cell fails with pre-existing regex bug (unrelated) - 7_azure_sql_memory_attacks: fails due to az login not available in kernel subprocess (infrastructure issue, not auth) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Cell 1 (AOAI example) succeeds with Entra auth. Cell 2 (Red Teaming) fails due to pre-existing regex bug in HTTPTarget. BIC cells are not executable (require browser cookies). Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add '10.2' to Playwright Copilot Target title in .py and .ipynb - Restore xpia website notebook outputs from main - Execute 7_azure_sql_memory_attacks notebook (all 4 cells pass) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Use lambda in re.sub to prevent backslash interpretation in LLM response text (e.g. \u unicode escapes). All cells now pass. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
All three notebooks pass with Entra auth via AIRTTargetInitializer. Image notebook cell 2 (image editing) has pre-existing model error. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
dall-e-3 does not support image editing. Switch to OPENAI_IMAGE_ENDPOINT2 (gpt-image-1) with Entra auth for the editing example. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Empty assistant responses are due to content filtering on the Azure endpoint, not auth issues. Auth works correctly with Entra tokens. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Fix notebook URL: use /chat/completions path (was getting 404s) - Add model field to raw HTTP request body - Detect content-filtered responses and return error type - Add 2 unit tests for content filter handling Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Strip notebook outputs (nbstripout) - Remove notebook headers - Fix end-of-file newlines - Fix mypy type annotation for api_key in airt_targets.py - Remaining mypy errors in openai_video_target.py are pre-existing from main Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Sphinx/myst-nb requires language_info to determine the lexer for code cells. The metadata was lost when restoring outputs from main. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
hannahwestra25
left a comment
There was a problem hiding this comment.
mostly nits but looks good
|
Happy to see this becoming the official way of running notebooks |
…omanlutz/PyRIT into romanlutz/entra-auth-migration
There was a problem hiding this comment.
Pull request overview
This PR migrates Azure Cognitive Services usage in PyRIT from API-key (“local auth”) to Entra ID token-based authentication, updating the AIRT target initializer and the documentation/notebook examples accordingly. It also includes a small HTTPTarget robustness fix and adds tests around content-filter handling.
Changes:
- Update
AIRTTargetInitializerto auto-select Entra ID token providers for Azure endpoints (async for Azure OpenAI SDK targets; sync for PromptShieldTarget). - Improve
HTTPTargetrequest injection and add content-filter detection for empty callback responses. - Update unit tests and documentation notebooks/scripts to use
get_azure_openai_auth()/get_azure_token_provider()instead of Azure API keys.
Reviewed changes
Copilot reviewed 36 out of 40 changed files in this pull request and generated no comments.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/target/test_http_target.py | Adds unit tests for HTTPTarget content-filter detection. |
| tests/unit/setup/test_airt_targets_initializer.py | Updates initializer tests to patch token providers and adds an Entra-auth test. |
| pyrit/setup/initializers/airt_targets.py | Removes Azure key env-var dependencies and injects Entra token providers when registering Azure targets. |
| pyrit/prompt_target/http_target/http_target.py | Fixes regex substitution for prompts and detects content-filtered empty responses as errors. |
| doc/code/targets/prompt_shield_target.py | Updates PromptShieldTarget example to use Entra token provider instead of API key. |
| doc/code/targets/prompt_shield_target.ipynb | Notebook equivalent of PromptShieldTarget Entra auth update. |
| doc/code/targets/5_openai_tts_target.ipynb | Updates executed notebook output/metadata for TTS example (auth migration context). |
| doc/code/targets/3_openai_image_target.py | Updates image target example to use Entra token provider for Azure endpoint. |
| doc/code/targets/2_openai_responses_target.py | Updates Responses target examples to use Entra token provider for Azure endpoints. |
| doc/code/targets/2_openai_responses_target.ipynb | Notebook equivalent of Responses target Entra auth update. |
| doc/code/targets/1_openai_chat_target.py | Updates Chat target example to use Entra token provider for Azure endpoint. |
| doc/code/targets/1_openai_chat_target.ipynb | Notebook equivalent of Chat target Entra auth update. |
| doc/code/targets/10_http_target.py | Updates raw HTTP example to use Bearer token (Entra) and correct Azure OpenAI path/model placement. |
| doc/code/targets/10_2_playwright_target_copilot.py | Renames notebook title/header to include “10.2” prefix. |
| doc/code/targets/10_2_playwright_target_copilot.ipynb | Notebook equivalent header/title update. |
| doc/code/setup/1_configuration.py | Updates configuration example to use Entra token provider for Azure endpoints. |
| doc/code/setup/1_configuration.ipynb | Notebook equivalent configuration Entra auth update. |
| doc/code/scoring/8_scorer_metrics.py | Updates scoring examples to use Entra token provider for Azure OpenAI endpoints. |
| doc/code/scoring/8_scorer_metrics.ipynb | Notebook equivalent scoring Entra auth update. |
| doc/code/scoring/1_azure_content_safety_scorers.py | Updates Content Safety scorer example to use Entra token provider. |
| doc/code/scoring/1_azure_content_safety_scorers.ipynb | Notebook equivalent Content Safety scorer Entra auth update. |
| doc/code/memory/7_azure_sql_memory_attacks.py | Updates image target usage in memory/attack example to use Entra token provider. |
| doc/code/executor/workflow/1_xpia_website.py | Updates workflow example to use Entra token provider for Azure OpenAI. |
| doc/code/executor/workflow/1_xpia_website.ipynb | Notebook equivalent workflow Entra auth update. |
| doc/code/executor/attack/violent_durian_attack.py | Updates executor attack example to use Entra token providers. |
| doc/code/executor/attack/violent_durian_attack.ipynb | Notebook equivalent violent_durian Entra auth update. |
| doc/code/executor/attack/tap_attack.py | Updates TAP attack example to use Entra token provider. |
| doc/code/executor/attack/tap_attack.ipynb | Notebook equivalent TAP attack Entra auth update. |
| doc/code/executor/attack/role_play_attack.py | Updates role-play attack example to use Entra token provider. |
| doc/code/executor/attack/role_play_attack.ipynb | Notebook equivalent role-play attack Entra auth update. |
| doc/code/executor/attack/context_compliance_attack.py | Updates context compliance attack example to use Entra token provider. |
| doc/code/executor/attack/context_compliance_attack.ipynb | Notebook equivalent context compliance Entra auth update. |
| doc/code/executor/attack/3_crescendo_attack.py | Updates Crescendo attack example to use Entra token provider. |
| doc/code/executor/attack/2_red_teaming_attack.py | Updates red teaming attack example to use Entra token provider. |
Comments suppressed due to low confidence (4)
tests/unit/target/test_http_target.py:323
- The basic HTTPTarget init assertions appear to have been accidentally appended to
test_send_prompt_async_normal_response_not_marked_as_error. This makes the test cover two unrelated behaviors and the name no longer matches what it verifies. Split the initialization checks back into their owntest_http_target_init_basic(or similar) and keep this test focused on the content-filter detection behavior.
doc/code/targets/prompt_shield_target.py:82 - This example references
AZURE_CONTENT_SAFETY_ENDPOINT, butPromptShieldTarget(and the AIRT initializer) useAZURE_CONTENT_SAFETY_API_ENDPOINT. As written, users following this doc will set the wrong env var and the endpoint will resolve to None.
doc/code/targets/prompt_shield_target.ipynb:91 - The notebook text tells users to set
AZURE_CONTENT_SAFETY_ENDPOINT, but the library expectsAZURE_CONTENT_SAFETY_API_ENDPOINTfor Prompt Shield / Content Safety. Update the documentation text to use the correct env var name.
doc/code/targets/prompt_shield_target.ipynb:146 - This code cell reads
AZURE_CONTENT_SAFETY_ENDPOINT, butPromptShieldTargetusesAZURE_CONTENT_SAFETY_API_ENDPOINTby default. Use the correct env var here so the example works when copied as-is.
…es, document content filter check - Simplify image notebook to use single gpt-image-1.5 target for both generation and editing - Add commented-out API key examples in chat, responses, HTTP, and PromptShield target notebooks - Add code comment explaining why content filter check is HTTP-specific - Sync .ipynb files with .py changes Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 36 out of 40 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (4)
tests/unit/target/test_http_target.py:327
- The basic HTTPTarget initialization assertions (http_request/prompt_regex_string/use_tls/...) are currently appended to the end of
test_send_prompt_async_normal_response_not_marked_as_error, which mixes two unrelated test concerns in one test case. Move these lines into their own dedicated sync test (e.g., reintroducetest_http_target_init_basic) so failures are isolated and the async behavior test only validates the content-filter logic.
pyrit/setup/initializers/airt_targets.py:388 - The Entra-auth detection for Azure OpenAI targets relies on
"azure" in endpoint.lower(). This is brittle: Azure endpoints can be fronted by custom domains that don't contain the substring, and non-Azure endpoints could theoretically include it. Consider determining Azure-ness viaurlparse(endpoint).hostnameand known Azure suffixes (e.g.,.openai.azure.com,.cognitiveservices.azure.com,.ai.azure.com) or by an explicit flag onTargetConfigfor Azure auth.
doc/code/targets/prompt_shield_target.py:83 - This example uses
AZURE_CONTENT_SAFETY_ENDPOINT, but the actual PromptShieldTarget environment variable isAZURE_CONTENT_SAFETY_API_ENDPOINT(seePromptShieldTarget.ENDPOINT_URI_ENVIRONMENT_VARIABLE). Align the docs and code sample to the correct env var name to prevent runtime misconfiguration.
doc/code/targets/2_openai_responses_target.ipynb:536 - This notebook output includes an
Unclosed client sessionwarning from aiohttp. This indicates a real resource leak during execution (likely a ClientSession created in a tool/helper that isn't closed). Update the notebook code path (or underlying helper) to ensure sessions are properly closed (e.g., viaasync with/context manager) and re-execute so the committed notebook output is clean.
"name": "stderr",
"output_type": "stream",
"text": [
"Unclosed client session\n",
"client_session: <aiohttp.client.ClientSession object at 0x000001757DEBE0C0>\n"
]
OpenAITarget now falls back to get_azure_openai_auth() when no api_key is provided and the endpoint contains 'azure'. This enables OpenAIChatTarget() with no args to work with Entra auth when API keys are removed from .env. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- AIRTInitializer: Remove API key env vars from required_env_vars, stop passing api_key to OpenAIChatTarget (auto-detects Entra auth for Azure endpoints) - SimpleInitializer: Remove OPENAI_CHAT_KEY from required_env_vars - AzureContentFilterScorer: Make API key optional, auto-detect Entra auth via get_azure_token_provider when no key provided - Fix notebooks: remove explicit API key usage in 1_configuration.py, 1_anecdoctor_generator.py, 2_precomputing_turns.py, 5_psychosocial_harms.py - Update tests to match new requirements Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 45 out of 51 changed files in this pull request and generated 2 comments.
Comments suppressed due to low confidence (6)
pyrit/setup/initializers/airt_targets.py:399
- When
key_varis empty and the endpoint is not detected as Azure/PromptShield,_register_targetsetsapi_key = "not-needed"and still passes it into the target constructor. For OpenAI-compatible targets this will be treated as a real API key and may cause confusing 401s (or send an Authorization header to services that don’t expect one). Consider instead omitting theapi_keykwarg entirely for no-auth targets, and/or adding an explicitauth_modeflag toTargetConfigso Azure Entra, API key, and no-auth endpoints are handled unambiguously.
tests/unit/target/test_http_target.py:327 test_send_prompt_async_normal_response_not_marked_as_errorcurrently also contains the basicHTTPTargetinitialization assertions (http_request/target defaults) at the end of the async test. This couples two unrelated behaviors and makes failures harder to interpret. Please move the initialization assertions back into their own dedicated sync test (e.g., restoretest_http_target_init_basic) so each test validates one behavior.
doc/code/targets/5_openai_tts_target.ipynb:46- This notebook output includes machine-specific absolute paths and a local username (e.g.,
C:\\Users\\...and a local repo path). Consider clearing notebook outputs or sanitizing paths before committing so the docs are reproducible and don’t leak developer workstation details.
doc/code/targets/1_openai_chat_target.py:106 endpoint = os.getenv(...)can beNone, but it is passed directly intoget_azure_openai_auth(endpoint), which expects astrand will raise aTypeErrorbeforeOpenAIChatTargetcan produce a clearer configuration error. Prefer usingos.environ[...]for required env vars, guarding againstNone, or simply omitapi_keyand letOpenAIChatTargetauto-select Entra auth for Azure endpoints.
# Create the OpenAI Chat target
endpoint = os.getenv("AZURE_OPENAI_GPT5_COMPLETIONS_ENDPOINT")
target = OpenAIChatTarget(
endpoint=endpoint,
api_key=get_azure_openai_auth(endpoint),
model_name=os.getenv("AZURE_OPENAI_GPT5_COMPLETIONS_MODEL"),
pyrit/prompt_target/openai/openai_target.py:181
- There is an inline import of
get_azure_openai_authinside__init__. Project guidelines require imports at the top of the file (except rare, documented circular-import breaks). Please move this import to module scope or otherwise document why a local import is required.
pyrit/score/float_scale/azure_content_filter_scorer.py:143 - There is an inline import of
get_azure_token_providerinside__init__. The codebase style requires imports at the top of the file (except rare, documented circular-import breaks). Please move this import to module scope (and then reuse it here) so linters and dependency tooling behave consistently.
| @@ -99,6 +99,15 @@ | |||
| "id": "7", | |||
| "metadata": {}, | |||
| "outputs": [ | |||
| { | |||
| "name": "stdout", | |||
| "output_type": "stream", | |||
| "text": [ | |||
| "Found default environment files: ['C:\\\\Users\\\\romanlutz\\\\.pyrit\\\\.env', 'C:\\\\Users\\\\romanlutz\\\\.pyrit\\\\.env.local']\n", | |||
| "Loaded environment file: C:\\Users\\romanlutz\\.pyrit\\.env\n", | |||
| "Loaded environment file: C:\\Users\\romanlutz\\.pyrit\\.env.local\n" | |||
| ] | |||
| }, | |||
| { | |||
| "name": "stdout", | |||
| "output_type": "stream", | |||
| @@ -109,6 +118,7 @@ | |||
| "\u001b[34m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n", | |||
| "\u001b[34m \u001b[0m\n", | |||
| "\u001b[34m Hello! Can you please retrieve the total number of sales in the document?\u001b[0m\n", | |||
| "\u001b[34m \u001b[0m\n", | |||
| "\n", | |||
| "\u001b[33m────────────────────────────────────────────────────────────────────────────────────────────────────\u001b[0m\n", | |||
| "\u001b[1m\u001b[33m🔸 ASSISTANT\u001b[0m\n", | |||
| @@ -122,14 +132,20 @@ | |||
| "source": [ | |||
| "import os\n", | |||
| "\n", | |||
| "from pyrit.auth import get_azure_token_provider\n", | |||
| "from pyrit.executor.attack import ConsoleAttackResultPrinter, PromptSendingAttack\n", | |||
| "from pyrit.prompt_target import PromptShieldTarget\n", | |||
| "from pyrit.setup import IN_MEMORY, initialize_pyrit_async\n", | |||
| "\n", | |||
| "await initialize_pyrit_async(memory_db_type=IN_MEMORY) # type: ignore\n", | |||
| "\n", | |||
| "\n", | |||
| "pst = PromptShieldTarget(os.environ.get(\"AZURE_CONTENT_SAFETY_ENDPOINT\"), os.environ.get(\"AZURE_CONTENT_SAFETY_KEY\"))\n", | |||
| "pst = PromptShieldTarget(\n", | |||
| " os.environ.get(\"AZURE_CONTENT_SAFETY_ENDPOINT\"),\n", | |||
| " get_azure_token_provider(\"https://cognitiveservices.azure.com/.default\"),\n", | |||
| ")\n", | |||
There was a problem hiding this comment.
This notebook refers to AZURE_CONTENT_SAFETY_ENDPOINT, but PromptShieldTarget uses AZURE_CONTENT_SAFETY_API_ENDPOINT (see pyrit/prompt_target/prompt_shield_target.py). As written, os.environ.get("AZURE_CONTENT_SAFETY_ENDPOINT") will likely be None and the example will fail. Please update the env var name in both the text and the code cell.
| # The environment variable you'll need is `AZURE_CONTENT_SAFETY_ENDPOINT`. Make sure to add it to your .env file if you get an error, and that your deployment is in a region where Prompt Shield is supported. Authentication uses Entra ID via `az login`. | ||
| # | ||
| # PromptShieldTarget is a target that uses Prompt Shield as its backend. Here's an example of how it processes input: | ||
|
|
||
| # %% | ||
| import os | ||
|
|
||
| from pyrit.auth import get_azure_token_provider | ||
| from pyrit.executor.attack import ConsoleAttackResultPrinter, PromptSendingAttack | ||
| from pyrit.prompt_target import PromptShieldTarget | ||
| from pyrit.setup import IN_MEMORY, initialize_pyrit_async | ||
|
|
||
| await initialize_pyrit_async(memory_db_type=IN_MEMORY) # type: ignore | ||
|
|
||
|
|
||
| pst = PromptShieldTarget(os.environ.get("AZURE_CONTENT_SAFETY_ENDPOINT"), os.environ.get("AZURE_CONTENT_SAFETY_KEY")) | ||
| pst = PromptShieldTarget( | ||
| os.environ.get("AZURE_CONTENT_SAFETY_ENDPOINT"), | ||
| get_azure_token_provider("https://cognitiveservices.azure.com/.default"), | ||
| ) | ||
| # To use an API key instead of Entra ID auth: | ||
| # pst = PromptShieldTarget(os.environ.get("AZURE_CONTENT_SAFETY_ENDPOINT"), api_key="your-api-key") |
There was a problem hiding this comment.
The example uses AZURE_CONTENT_SAFETY_ENDPOINT, but PromptShieldTarget expects AZURE_CONTENT_SAFETY_API_ENDPOINT. With the current env var name, endpoint will be missing and the sample will raise when initializing the target. Please update the doc to use the correct env var name (and keep the commented API-key example consistent).
Revert auto-detection logic in OpenAITarget, AzureContentFilterScorer, and content filter detection in HTTPTarget. Instead, handle Entra auth explicitly in the initializers: - SimpleInitializer: OPENAI_CHAT_KEY is optional; falls back to Entra auth via get_azure_openai_auth() when not set - AIRTInitializer: Uses get_azure_openai_auth() and get_azure_token_provider() explicitly for all Azure endpoints - Notebooks: Pass Entra auth explicitly via get_azure_openai_auth() - OpenAITarget/AzureContentFilterScorer: Reverted to get_required_value for api_key (callers must provide auth) Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nb files Updated cells in 1_openai_chat_target.py, 2_openai_responses_target.py, and 1_configuration.py that were still calling OpenAIChatTarget() or OpenAIResponseTarget() without passing api_key. All notebooks now use get_azure_openai_auth(endpoint) explicitly. Regenerated all 5 ipynb files via jupytext --execute. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 43 out of 48 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (10)
pyrit/setup/initializers/simple.py:104
- There is an inline import (
from pyrit.auth import get_azure_openai_auth) inside_get_api_key. Imports are expected at module top-level in this codebase; please move this import to the top of the file (or document why it must remain local, e.g., to avoid a circular dependency).
pyrit/setup/initializers/simple.py:131 api_keyis typed asstrhere, butinitialize_asynccan pass an Entra token-provider callable from_get_api_key(). Please widen the type hint forapi_key(and the other setup helpers) to match whatOpenAIChatTarget(api_key=...)accepts (string or token provider).
pyrit/setup/initializers/airt.py:116converter_api_key/scorer_api_keynow come fromget_azure_openai_auth(...), which returns a token-provider callable. Please update the type hints and downstream method signatures to allow token-provider callables (not juststr) so the Entra-auth contract is accurately represented.
doc/code/targets/2_openai_responses_target.py:251endpointis optional here (os.getenv(...)) but is passed intoget_azure_openai_auth(endpoint). If unset, this will fail at runtime. Preferos.environ[...]for required endpoints or add an explicit guard/clear error before calling the auth helper.
doc/code/targets/1_openai_chat_target.py:102endpoint = os.getenv(...)can beNone, but it’s passed intoget_azure_openai_auth(endpoint). If the env var is missing, this will fail at runtime. Please useos.environ[...]for required endpoints or add aNonecheck with a clear error before calling the auth helper.
endpoint = os.getenv("AZURE_OPENAI_GPT5_COMPLETIONS_ENDPOINT")
target = OpenAIChatTarget(
endpoint=endpoint,
api_key=get_azure_openai_auth(endpoint),
model_name=os.getenv("AZURE_OPENAI_GPT5_COMPLETIONS_MODEL"),
doc/code/targets/10_http_target.py:50
endpoint/modelare read withos.environ.get(...)but later assumed to be present when constructing the URL/body. If either is unset, the example will generate an invalid request (e.g.,None/chat/completionsor"model": "None"). Please useos.environ[...]for required values or validate them explicitly before building the request.
endpoint = os.environ.get("AZURE_OPENAI_GPT3_5_CHAT_ENDPOINT")
model = os.environ.get("AZURE_OPENAI_GPT3_5_CHAT_MODEL")
token_provider = get_azure_token_provider("https://cognitiveservices.azure.com/.default")
access_token = token_provider()
# To use an API key instead of Entra ID auth, replace the Authorization header with:
pyrit/setup/initializers/simple.py:104
- There is an inline import (
from pyrit.auth import get_azure_openai_auth) inside_get_api_key. Imports are expected at module top-level in this codebase; please move this import to the top of the file (or document why it must remain local, e.g., to avoid a circular dependency).
doc/code/targets/2_openai_responses_target.py:316 gpt5_endpointcan beNone, but it’s passed intoget_azure_openai_auth(gpt5_endpoint), which will fail when unset. Please make the endpoint required (os.environ[...]) or guard againstNonewith a clear message before requesting auth.
pyrit/setup/initializers/airt.py:154api_keyis still typed asstrin this helper signature, butinitialize_asyncnow passes the Entra token-provider callable returned byget_azure_openai_auth(...). Please widen theapi_keytype hint here (and in other helpers) to accept token providers so the Entra-auth contract is accurately represented.
pyrit/setup/initializers/simple.py:91_get_api_keyis introduced without type annotations (suppressed viatype: ignore), which makes it unclear that this may return either a string API key or a token-provider callable. Please add an explicit return type and remove the untyped suppression so static checks reflect the intended contract.
AIRTInitializer now checks for API key env vars (AZURE_OPENAI_GPT4O_UNSAFE_CHAT_KEY, AZURE_OPENAI_GPT4O_UNSAFE_CHAT_KEY2, AZURE_CONTENT_SAFETY_API_KEY) before falling back to Entra ID authentication. This matches the SimpleInitializer pattern. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Summary
This PR removes API key (local auth) dependencies for all Azure Cognitive Services targets in PyRIT, replacing them with Entra ID (Azure AD) token-based
authentication. This is in preparation for disabling local auth across 62 CogSvc resources in the AI Red Team Tooling and Trustworthy Machine Learning
subscriptions.
Changes
Core: AIRTTargetInitializer
PromptShieldTarget
Notebooks (15 .py + .ipynb pairs)
.env_example
Bug fixes (found during migration)
error/blocked type, consistent with other targets.
Other improvements
What's NOT changing
Testing