Skip to content
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7bbc9b9
Update application to support secretless auth
BillWagner Jul 11, 2024
3cea37c
Doing the YAML thing.
BillWagner Jul 11, 2024
7c659a5
run the code from the new branch. (temp)
BillWagner Jul 11, 2024
3b18d88
Use the right branch.
BillWagner Jul 11, 2024
4474d46
Add logging for which auth in use.
BillWagner Jul 11, 2024
d9e608b
Apply suggestions from code review
BillWagner Jul 11, 2024
3a8c6ed
fix merge issue
BillWagner Jul 11, 2024
6904e35
logging and debugging
BillWagner Jul 11, 2024
82dd95e
encode token
BillWagner Jul 11, 2024
0ced444
revert last change
BillWagner Jul 11, 2024
b31b490
testung
BillWagner Jul 23, 2024
086857d
fix merge issues
BillWagner Mar 6, 2026
6b51169
auth test
Jul 6, 2026
23f8bed
test auth workflow
Jul 8, 2026
f6d740d
add auth-only quest path for oidc testing
Jul 8, 2026
2546c1f
separate audience and resource to test auth flow
Jul 8, 2026
f52ed66
test secrets for auth validation
Jul 16, 2026
8b51a02
rp not found: audience validation
Jul 17, 2026
3bea03c
devops auth permission validations
Jul 23, 2026
6fda1d7
remove suppressNotifications flag
Jul 23, 2026
df0a2ff
sequester workflow controlled test
Jul 28, 2026
832dda6
synchronizer
Jul 28, 2026
1820ca3
resolve oidc path
Jul 28, 2026
021c1cd
bulk quest path
Jul 28, 2026
0aeeb34
audience fix
Jul 28, 2026
20ddf48
review ready: rename test variables
Jul 28, 2026
c604b94
PR feedback
Aug 5, 2026
fff077a
Final cleanup of the PR.
BillWagner Aug 6, 2026
07eb4cc
2nd pass removing test code
BillWagner Aug 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 6 additions & 2 deletions .github/actions/oidc-auth-flow/action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,12 @@ inputs:
description: "The Azure AD tenant ID"
required: true
audience:
description: "The audience for the access token"
description: "The target resource audience for the access token"
required: true
oidc-audience:
description: "The GitHub OIDC audience used for Azure login"
required: false
default: "api://AzureADTokenExchange"

outputs:
access-token:
Expand All @@ -25,7 +29,7 @@ runs:
with:
client-id: ${{ inputs.client-id }}
tenant-id: ${{ inputs.tenant-id }}
audience: ${{ inputs.audience }}
audience: ${{ inputs.oidc-audience }}
allow-no-subscriptions: true

- name: OSMP API access
Expand Down
29 changes: 25 additions & 4 deletions .github/workflows/quest-bulk.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,9 @@ jobs:
if: ${{ github.repository_owner == 'dotnet' }}

steps:
- name: Checkout
uses: actions/checkout@v4

- name: "Print manual bulk import run reason"
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
Expand All @@ -34,19 +37,37 @@ jobs:

- name: Azure OpenID Connect
id: azure-oidc-auth
uses: dotnet/docs-tools/.github/actions/oidc-auth-flow@main
uses: ./.github/actions/oidc-auth-flow
with:
client-id: ${{ secrets.CLIENT_ID }}
tenant-id: ${{ secrets.TENANT_ID }}
audience: ${{ secrets.OSMP_API_AUDIENCE }}


- name: Azure DevOps OpenID Connect
if: ${{ github.event_name == 'workflow_dispatch' }}
id: azure-devops-oidc-auth
uses: ./.github/actions/oidc-auth-flow
with:
client-id: ${{ secrets.QUEST_APPLICATION_ID }}
tenant-id: ${{ secrets.QUEST_DIRECTORY_ID }}
audience: ${{ secrets.QUEST_AUDIENCE_DEVOPS_ID }}

- name: Azure DevOps OpenID Connect for scheduled runs
if: ${{ github.event_name != 'workflow_dispatch' }}
id: azure-devops-oidc-auth-prod
uses: ./.github/actions/oidc-auth-flow
with:
client-id: ${{ secrets.QUEST_CLIENT_ID }}
tenant-id: ${{ secrets.TENANT_ID }}
audience: ${{ secrets.QUEST_AUDIENCE }}

- name: bulk-sequester
id: bulk-sequester
uses: dotnet/docs-tools/actions/sequester@main
uses: ./actions/sequester
env:
Comment thread
ramiMSFT marked this conversation as resolved.
ImportOptions__ApiKeys__GitHubToken: ${{ secrets.GITHUB_TOKEN }}
ImportOptions__ApiKeys__QuestKey: ${{ secrets.QUEST_KEY }}
ImportOptions__ApiKeys__AzureAccessToken: ${{ steps.azure-oidc-auth.outputs.access-token }}
ImportOptions__ApiKeys__QuestAccessToken: ${{ github.event_name == 'workflow_dispatch' && steps.azure-devops-oidc-auth.outputs.access-token || steps.azure-devops-oidc-auth-prod.outputs.access-token }}
ImportOptions__ApiKeys__SequesterPrivateKey: ${{ secrets.SEQUESTER_PRIVATEKEY }}
ImportOptions__ApiKeys__SequesterAppID: ${{ secrets.SEQUESTER_APPID }}
with:
Expand Down
209 changes: 199 additions & 10 deletions .github/workflows/quest.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,30 @@ on:
description: "The reason for running the workflow"
required: true
default: "Manual run"
target-org:
description: "The GitHub organization to target for a manual run"
required: false
default: "dotnet"
target-repo:
description: "The GitHub repository to target for a manual run"
required: false
default: "docs"
Comment thread
ramiMSFT marked this conversation as resolved.
Outdated
issue:
description: "The issue number to manually test"
description: "The issue number to manually test. Ignored when auth-only is true."
required: true
auth-only:
description: "Run only the Azure DevOps OIDC auth probe"
required: false
default: "false"
test-work-item-id:
description: "Azure DevOps work item ID used by the auth-only probe. Ignored unless auth-only is true."
required: false
default: "562440"
Comment thread
ramiMSFT marked this conversation as resolved.
Outdated

jobs:
import:
if: |
github.event_name == 'workflow_dispatch' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.auth-only != 'true') ||
github.event.label.name == ':world_map: reQUEST' ||
github.event.label.name == ':pushpin: seQUESTered' ||
contains(github.event.issue.labels.*.name, ':world_map: reQUEST') ||
Expand All @@ -25,52 +41,225 @@ jobs:
pull-requests: write

steps:
- name: Checkout
uses: actions/checkout@v4

- name: "Print manual run reason"
if: ${{ github.event_name == 'workflow_dispatch' }}
run: |
echo "Reason: $REASON"
echo "Target org: $TARGETORG"
echo "Target repo: $TARGETREPO"
echo "Issue number: $ISSUENUMBER"
env:
TARGETORG: ${{ github.event.inputs.target-org || github.repository_owner }}
TARGETREPO: ${{ github.event.inputs.target-repo || github.event.repository.name }}
ISSUENUMBER: ${{ github.event.inputs.issue }}
REASON: ${{ github.event.inputs.reason }}

- name: Azure OpenID Connect
id: azure-oidc-auth
uses: dotnet/docs-tools/.github/actions/oidc-auth-flow@main
uses: ./.github/actions/oidc-auth-flow
with:
client-id: ${{ secrets.CLIENT_ID }}
tenant-id: ${{ secrets.TENANT_ID }}
audience: ${{ secrets.OSMP_API_AUDIENCE }}

- name: Azure DevOps OpenID Connect for manual test runs
if: ${{ github.event_name == 'workflow_dispatch' }}
id: azure-devops-oidc-auth
uses: ./.github/actions/oidc-auth-flow
with:
client-id: ${{ secrets.QUEST_APPLICATION_ID }}
tenant-id: ${{ secrets.QUEST_DIRECTORY_ID }}
audience: ${{ secrets.QUEST_AUDIENCE_DEVOPS_ID }}

- name: Azure DevOps OpenID Connect for automatic runs
if: ${{ github.event_name != 'workflow_dispatch' }}
id: azure-devops-oidc-auth-prod
uses: ./.github/actions/oidc-auth-flow
with:
client-id: ${{ secrets.QUEST_CLIENT_ID }}
tenant-id: ${{ secrets.TENANT_ID }}
audience: ${{ secrets.QUEST_AUDIENCE }}

# This step occurs when ran manually, passing the manual issue number input
- name: manual-sequester
if: ${{ github.event_name == 'workflow_dispatch' }}
id: manual-sequester
uses: dotnet/docs-tools/actions/sequester@main
uses: ./actions/sequester
env:
ImportOptions__ApiKeys__GitHubToken: ${{ secrets.GITHUB_TOKEN }}
ImportOptions__ApiKeys__AzureAccessToken: ${{ steps.azure-oidc-auth.outputs.access-token }}
ImportOptions__ApiKeys__QuestKey: ${{ secrets.QUEST_KEY }}
ImportOptions__ApiKeys__QuestAccessToken: ${{ steps.azure-devops-oidc-auth.outputs.access-token }}
ImportOptions__ApiKeys__SequesterPrivateKey: ${{ secrets.SEQUESTER_PRIVATEKEY }}
ImportOptions__ApiKeys__SequesterAppID: ${{ secrets.SEQUESTER_APPID }}
with:
org: ${{ github.repository_owner }}
repo: ${{ github.repository }}
org: ${{ github.event.inputs.target-org || github.repository_owner }}
repo: ${{ github.event.inputs.target-repo || github.event.repository.name }}
issue: ${{ github.event.inputs.issue }}

# This step occurs automatically, passing the issue number from the event
- name: auto-sequester
if: ${{ github.event_name != 'workflow_dispatch' }}
id: auto-sequester
uses: dotnet/docs-tools/actions/sequester@main
uses: ./actions/sequester
env:
ImportOptions__ApiKeys__GitHubToken: ${{ secrets.GITHUB_TOKEN }}
ImportOptions__ApiKeys__AzureAccessToken: ${{ steps.azure-oidc-auth.outputs.access-token }}
ImportOptions__ApiKeys__QuestKey: ${{ secrets.QUEST_KEY }}
ImportOptions__ApiKeys__QuestAccessToken: ${{ steps.azure-devops-oidc-auth-prod.outputs.access-token }}
ImportOptions__ApiKeys__SequesterPrivateKey: ${{ secrets.SEQUESTER_PRIVATEKEY }}
ImportOptions__ApiKeys__SequesterAppID: ${{ secrets.SEQUESTER_APPID }}
with:
org: ${{ github.repository_owner }}
repo: ${{ github.repository }}
issue: ${{ github.event.issue.number }}


test-auth:
if: ${{ github.event_name == 'workflow_dispatch' && github.event.inputs.auth-only == 'true' }}
runs-on: ubuntu-latest
permissions:
id-token: write
contents: read

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Run existing Azure DevOps OIDC action
id: quest-auth
uses: ./.github/actions/oidc-auth-flow
with:
client-id: ${{ secrets.QUEST_APPLICATION_ID }}
tenant-id: ${{ secrets.QUEST_DIRECTORY_ID }}
oidc-audience: api://AzureADTokenExchange
audience: ${{ secrets.QUEST_AUDIENCE_DEVOPS_ID }}

- name: Confirm token was returned
shell: bash
run: |
if [ -z "${{ steps.quest-auth.outputs.access-token }}" ]; then
echo "No access token returned."
exit 1
fi
echo "Access token was returned."

- name: Read Content work item
id: read-work-item
shell: bash
env:
AZDO_TOKEN: ${{ steps.quest-auth.outputs.access-token }}
TEST_WORK_ITEM_ID: ${{ github.event.inputs.test-work-item-id || '562440' }}
run: |
set -euo pipefail

work_item_url="https://dev.azure.com/msft-skilling/Content/_apis/wit/workitems/$TEST_WORK_ITEM_ID?api-version=7.1"

status_code=$(curl -sS -w '%{http_code}' -D headers.txt \
-H "Authorization: Bearer $AZDO_TOKEN" \
-H "Content-Type: application/json" \
"$work_item_url" \
-o body.json)

echo "HTTP status: $status_code"

echo "HTTP response headers:"
cat headers.txt

echo ""
echo "Response body:"
cat body.json

if [[ ! "$status_code" =~ ^2 ]]; then
echo "Azure DevOps work item read probe failed."
exit 1
fi

identity_query=$(jq -r '
.fields["System.AssignedTo"],
.fields["System.ChangedBy"],
.fields["System.CreatedBy"]
| select(. != null)
| if type == "object" then (.uniqueName // .displayName // .id // empty) else . end
' body.json | head -n 1)

if [ -z "$identity_query" ]; then
echo "Unable to derive an Azure DevOps identity from work item $TEST_WORK_ITEM_ID."
exit 1
fi

echo "identity-query=$identity_query" >> "$GITHUB_OUTPUT"

- name: Probe Azure DevOps identities API
shell: bash
env:
AZDO_TOKEN: ${{ steps.quest-auth.outputs.access-token }}
IDENTITY_QUERY: ${{ steps.read-work-item.outputs.identity-query }}
run: |
set -euo pipefail

status_code=$(curl -sS -w '%{http_code}' -D headers.txt \
-H "Authorization: Bearer $AZDO_TOKEN" \
-H "Content-Type: application/json" \
--get \
--data-urlencode "searchFilter=General" \
--data-urlencode "filterValue=$IDENTITY_QUERY" \
--data-urlencode "queryMembership=None" \
--data-urlencode "api-version=7.1-preview.1" \
"https://vssps.dev.azure.com/msft-skilling/_apis/identities" \
-o body.json)

echo "HTTP status: $status_code"

echo "HTTP response headers:"
cat headers.txt

echo ""
echo "Response body:"
cat body.json

if [[ ! "$status_code" =~ ^2 ]]; then
echo "Azure DevOps identity read probe failed."
exit 1
fi

- name: Add work item history comment
shell: bash
env:
AZDO_TOKEN: ${{ steps.quest-auth.outputs.access-token }}
TEST_WORK_ITEM_ID: ${{ github.event.inputs.test-work-item-id || '562440' }}
run: |
set -euo pipefail

cat > patch.json <<'EOF'
[
{
"op": "add",
"path": "/fields/System.History",
"value": "going-secretless auth validation test"
}
]
EOF

status_code=$(curl -sS -w '%{http_code}' -D headers.txt \
-X PATCH \
-H "Authorization: Bearer $AZDO_TOKEN" \
-H "Content-Type: application/json-patch+json" \
--data @patch.json \
"https://dev.azure.com/msft-skilling/Content/_apis/wit/workitems/$TEST_WORK_ITEM_ID?api-version=7.1" \
-o body.json)

echo "HTTP status: $status_code"

echo "HTTP response headers:"
cat headers.txt

echo ""
echo "Response body:"
cat body.json

if [[ ! "$status_code" =~ ^2 ]]; then
echo "Azure DevOps work item write probe failed."
exit 1
fi

Loading
Loading