pax_global_header 0000666 0000000 0000000 00000000064 15206345071 0014515 g ustar 00root root 0000000 0000000 52 comment=ebb980f1636e1b3a097ec6dbfd197fb88c5b9e7a
microsoft-authentication-library-for-python-1.37.0/ 0000775 0000000 0000000 00000000000 15206345071 0022374 5 ustar 00root root 0000000 0000000 microsoft-authentication-library-for-python-1.37.0/.Pipelines/ 0000775 0000000 0000000 00000000000 15206345071 0024402 5 ustar 00root root 0000000 0000000 microsoft-authentication-library-for-python-1.37.0/.Pipelines/CI-AND-RELEASE-PIPELINES.md 0000664 0000000 0000000 00000015225 15206345071 0030230 0 ustar 00root root 0000000 0000000 # CI/CD Pipelines
This document describes the pipeline structure for the `msal` Python package,
including what each pipeline does, when it runs, and how to trigger a release.
---
## Pipeline Files
| File | ADO Pipeline | Purpose |
|------|-------------|---------|
| [`azure-pipelines.yml`](../azure-pipelines.yml) | [MSAL.Python-PR-OneBranch-Official (3064)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3064) | PR gate, post-merge CI, and performance benchmarks — calls the shared template with `runPublish: false`; runs benchmarks on post-merge pushes to `dev` |
| [`pipeline-publish.yml`](pipeline-publish.yml) | [MSAL.Python-Publish (3067)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3067) | Release pipeline — manually queued, builds and publishes to PyPI |
| [`template-pipeline-stages.yml`](template-pipeline-stages.yml) | — | Shared stages template — PreBuildCheck, Validate, UnitTests, and E2ETests stages reused by both pipelines |
| [`credscan-exclusion.json`](credscan-exclusion.json) | — | CredScan suppression file for known test fixtures |
---
## PR / CI Pipeline — [MSAL.Python-PR-OneBranch-Official (3064)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3064)
### Triggers
| Event | Branches |
|-------|----------|
| Pull request opened / updated | `dev` (PRs targeting `dev` only) |
| Push / merge | `dev` |
| Scheduled | Daily at 11:45 PM Pacific, `dev` branch (only when there are new changes) |
Fast unit-test feedback for PRs targeting **other** branches (e.g. `release-x.y.z`)
is provided separately by the GitHub Actions workflow
[`.github/workflows/python-package.yml`](../.github/workflows/python-package.yml),
which runs the package build and unit tests on every PR.
### Stages
```
PreBuildCheck ─► UnitTests ─► E2ETests ─► Benchmark (post-merge to dev only)
```
| Stage | What it does | When it runs |
|-------|-------------|-------------|
| **PreBuildCheck** | Runs SDL security scans: PoliCheck (policy/offensive content), CredScan (leaked credentials), and PostAnalysis (breaks the build on findings) | Always |
| **UnitTests** | Runs the unit test suite on Python 3.9, 3.10, 3.11, 3.12, 3.13, and 3.14 (no Key Vault required) | After PreBuildCheck |
| **E2ETests** | Fetches the MSID Lab certificate from Key Vault and runs `tests/test_e2e.py` + `tests/test_fmi_e2e.py` on the same Python matrix. On forked PRs the stage still runs, but the Key Vault tasks are skipped and the E2E tests self-skip (because `LAB_APP_CLIENT_CERT_PFX_PATH` is unset), so the stage reports green with all E2E tests marked Skipped in the Tests tab. | After UnitTests |
| **Benchmark** | Runs performance benchmarks on Python 3.9 and publishes `benchmark-results` artifact | Post-merge pushes to `dev` and manual runs only |
The `Validate` stage is **skipped** on PR/CI runs (it only applies to release builds).
> **SDL coverage:** The PreBuildCheck stage satisfies the OneBranch SDL requirement.
> It runs on every PR, every merge to `dev`, and on the daily schedule — ensuring
> continuous security scanning without a separate dedicated SDL pipeline.
---
## Release Pipeline — [MSAL.Python-Publish (3067)](https://dev.azure.com/IdentityDivision/IDDP/_build?definitionId=3067)
### Triggers
**Manual only** — no automatic branch or tag triggers. Must be queued explicitly
with both parameters filled in.
### Parameters
| Parameter | Description | Example values |
|-----------|-------------|----------------|
| **Package version to publish** | Must exactly match `msal/sku.py __version__`. [PEP 440](https://peps.python.org/pep-0440/) format. | `1.36.0`, `1.36.0rc1`, `1.36.0b1` |
| **Publish target** | Destination for this release. | `test.pypi.org (Preview / RC)` or `pypi.org (ESRP Production)` |
### Stage Flow
```
PreBuildCheck ─► Validate ─► UnitTests ─► E2ETests ─► Build ─┬─► PublishMSALPython (publishTarget == 'test.pypi.org (Preview / RC)')
└─► PublishPyPI (publishTarget == 'pypi.org (ESRP Production)')
```
| Stage | What it does | Condition |
|-------|-------------|-----------|
| **PreBuildCheck** | PoliCheck + CredScan scans | Always |
| **Validate** | Asserts the `packageVersion` parameter matches `msal/sku.py __version__` | Always (release runs only) |
| **UnitTests** | Unit test matrix (Python 3.9–3.14) | After Validate passes |
| **E2ETests** | E2E test matrix (Python 3.9–3.14) with MSID Lab cert from Key Vault | After UnitTests passes |
| **Build** | Builds `sdist` and `wheel` via `python -m build`; publishes `python-dist` artifact | After E2ETests passes |
| **PublishMSALPython** | Uploads to test.pypi.org | `publishTarget == test.pypi.org (Preview / RC)` |
| **PublishPyPI** | Uploads to PyPI via ESRP (`EsrpRelease@12`); requires manual approval | `publishTarget == pypi.org (ESRP Production)` |
> ⚠️ **TestPyPI publishing is currently a no-op.** The `MSAL-Test-Python-Upload`
> service connection has not yet been created (pending a test.pypi.org API
> token), so the `PublishMSALPython` stage prints a skip message rather than
> uploading. Until the SC exists, use the `pypi.org (ESRP Production)` path
> with an RC version (e.g. `1.36.0rc1`) for end-to-end validation.
---
## How to Publish a Release
### Step 1 — Update the version
Edit `msal/sku.py` and set `__version__` to the target version:
```python
__version__ = "1.36.0rc1" # RC / preview
__version__ = "1.36.0" # production release
```
Push the change to the branch you intend to release from.
### Step 2 — Queue the pipeline
1. Go to the **MSAL.Python-Publish** pipeline in ADO.
2. Click **Run pipeline**.
3. Select the branch to release from.
4. Enter the **Package version to publish** (must match `msal/sku.py` exactly).
5. Select the **Publish target**:
- `test.pypi.org (Preview / RC)` — for release candidates and previews
- `pypi.org (ESRP Production)` — for final releases (requires approval gate)
6. Click **Run**.
### Step 3 — Approve (production releases only)
The `pypi.org (ESRP Production)` path includes a required manual approval before
the package is uploaded. An approver must review and approve in the ADO
**Environments** panel before the `PublishPyPI` stage proceeds.
### Step 4 — Verify
- **test.pypi.org:** https://test.pypi.org/project/msal/
- **PyPI:** https://pypi.org/project/msal/
---
## Version Format
PyPI enforces [PEP 440](https://peps.python.org/pep-0440/). Versions with `-` (e.g. `1.36.0-Preview`) are rejected at upload time. Use standard suffixes:
| Release type | Format |
|-------------|--------|
| Production | `1.36.0` |
| Release candidate | `1.36.0rc1` |
| Beta | `1.36.0b1` |
| Alpha | `1.36.0a1` |
microsoft-authentication-library-for-python-1.37.0/.Pipelines/credscan-exclusion.json 0000664 0000000 0000000 00000000641 15206345071 0031067 0 ustar 00root root 0000000 0000000 {
"tool": "Credential Scanner",
"suppressions": [
{
"file": "tests/certificate-with-password.pfx",
"_justification": "Self-signed certificate used only in unit tests. Not a production credential."
},
{
"file": "tests/test_mi.py",
"_justification": "WWW-Authenticate challenge header value used as a mock HTTP response fixture in unit tests. Not a real credential."
}
]
}
microsoft-authentication-library-for-python-1.37.0/.Pipelines/pipeline-publish.yml 0000664 0000000 0000000 00000016551 15206345071 0030406 0 ustar 00root root 0000000 0000000 # pipeline-publish.yml
#
# Release pipeline for the msal Python package — manually triggered only.
# Source: https://github.com/AzureAD/microsoft-authentication-library-for-python
#
# Publish targets:
# test.pypi.org (Preview / RC) — preview releases via MSAL-Test-Python-Upload SC
# (SC creation pending test.pypi.org API token)
# pypi.org (ESRP Production) — production releases via ESRP (EsrpRelease@12) using MSAL-ESRP-AME SC
#
# For pipeline documentation, see .Pipelines/CI-AND-RELEASE-PIPELINES.md.
parameters:
- name: packageVersion
displayName: 'Package version to publish (must match msal/sku.py, e.g. 1.36.0 or 1.36.0rc1)'
type: string
- name: publishTarget
displayName: 'Publish target'
type: string
values:
- 'test.pypi.org (Preview / RC)'
- 'pypi.org (ESRP Production)'
trigger: none # manual runs only — no automatic branch or tag triggers
pr: none
# Stage flow:
#
# PreBuildCheck ─► Validate ─► UnitTests ─► E2ETests ─► Build ─► PublishMSALPython (publishTarget == Preview)
# └─► PublishPyPI (publishTarget == ESRP Production)
stages:
# PreBuildCheck, Validate, UnitTests, and E2ETests stages are defined in the shared template.
- template: template-pipeline-stages.yml
parameters:
packageVersion: ${{ parameters.packageVersion }}
runPublish: true
# ══════════════════════════════════════════════════════════════════════════════
# Stage 3 · Build — build sdist + wheel
# ══════════════════════════════════════════════════════════════════════════════
- stage: Build
displayName: 'Build package'
dependsOn: E2ETests
condition: eq(dependencies.E2ETests.result, 'Succeeded')
jobs:
- job: BuildDist
displayName: 'Build sdist + wheel (Python 3.12)'
pool:
vmImage: ubuntu-latest
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.12'
displayName: 'Use Python 3.12'
- script: |
python -m pip install --upgrade pip build twine
displayName: 'Install build toolchain'
- script: |
python -m build
displayName: 'Build sdist and wheel'
- script: |
python -m twine check dist/*
displayName: 'Verify distribution (twine check)'
- task: PublishPipelineArtifact@1
displayName: 'Publish dist/ as pipeline artifact'
inputs:
targetPath: dist/
artifact: python-dist
# ══════════════════════════════════════════════════════════════════════════════
# Stage 4a · Publish to test.pypi.org (Preview / RC)
# Note: requires MSAL-Test-Python-Upload SC in ADO (pending test.pypi.org API token)
# ══════════════════════════════════════════════════════════════════════════════
- stage: PublishMSALPython
displayName: 'Publish to test.pypi.org (Preview)'
dependsOn: Build
condition: >
and(
eq(dependencies.Build.result, 'Succeeded'),
eq('${{ parameters.publishTarget }}', 'test.pypi.org (Preview / RC)')
)
jobs:
- deployment: DeployMSALPython
displayName: 'Upload to test.pypi.org'
pool:
vmImage: ubuntu-latest
environment: MSAL-Python
strategy:
runOnce:
deploy:
steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download python-dist artifact'
inputs:
artifactName: python-dist
targetPath: $(Pipeline.Workspace)/python-dist
- task: UsePythonVersion@0
inputs:
versionSpec: '3.12'
displayName: 'Use Python 3.12'
- script: |
python -m pip install --upgrade pip twine
displayName: 'Install twine'
# TODO: create MSAL-Test-Python-Upload SC with test.pypi.org API token, then uncomment:
# - task: TwineAuthenticate@1
# displayName: 'Authenticate with MSAL-Test-Python-Upload'
# inputs:
# pythonUploadServiceConnection: MSAL-Test-Python-Upload
# - script: |
# python -m twine upload \
# -r "MSAL-Test-Python-Upload" \
# --config-file $(PYPIRC_PATH) \
# --skip-existing \
# $(Pipeline.Workspace)/python-dist/*
# displayName: 'Upload to test.pypi.org'
- script: echo "Publish to test.pypi.org skipped — MSAL-Test-Python-Upload SC not yet created."
displayName: 'Skip upload (SC pending)'
# ══════════════════════════════════════════════════════════════════════════════
# Stage 4b · Publish to PyPI (ESRP Production)
# Uses EsrpRelease@12 via the MSAL-ESRP-AME service connection.
# IMPORTANT: configure a required manual approval on this environment in
# ADO → Pipelines → Environments → MSAL-Python-Release → Approvals and checks.
# IMPORTANT: EsrpRelease@12 requires a Windows agent.
# ══════════════════════════════════════════════════════════════════════════════
- stage: PublishPyPI
displayName: 'Publish to PyPI (ESRP Production)'
dependsOn: Build
condition: >
and(
eq(dependencies.Build.result, 'Succeeded'),
eq('${{ parameters.publishTarget }}', 'pypi.org (ESRP Production)')
)
jobs:
- deployment: DeployPyPI
displayName: 'Upload to PyPI via ESRP'
pool:
vmImage: windows-latest
environment: MSAL-Python-Release
strategy:
runOnce:
deploy:
steps:
- task: DownloadPipelineArtifact@2
displayName: 'Download python-dist artifact'
inputs:
artifactName: python-dist
targetPath: $(Pipeline.Workspace)/python-dist
- task: EsrpRelease@12
displayName: 'Publish to PyPI via ESRP'
inputs:
connectedservicename: 'MSAL-ESRP-AME'
usemanagedidentity: true
keyvaultname: 'MSALVault'
signcertname: 'MSAL-ESRP-Release-Signing'
clientid: '8650ce2b-38d4-466a-9144-bc5c19c88112'
intent: 'PackageDistribution'
contenttype: 'PyPi'
contentsource: 'Folder'
folderlocation: '$(Pipeline.Workspace)/python-dist'
waitforreleasecompletion: true
owners: 'ryauld@microsoft.com,avdunn@microsoft.com'
approvers: 'avdunn@microsoft.com,bogavril@microsoft.com'
serviceendpointurl: 'https://api.esrp.microsoft.com'
mainpublisher: 'ESRPRELPACMAN'
domaintenantid: '33e01921-4d64-4f8c-a055-5bdaffd5e33d'
microsoft-authentication-library-for-python-1.37.0/.Pipelines/template-pipeline-stages.yml 0000664 0000000 0000000 00000032267 15206345071 0032041 0 ustar 00root root 0000000 0000000 # template-pipeline-stages.yml
#
# Shared stages template for the msal Python package.
#
# Called from:
# pipeline-publish.yml — release build (runPublish: true)
# azure-pipelines.yml — PR gate and post-merge CI (runPublish: false)
#
# Parameters:
# packageVersion - Version to validate against msal/sku.py
# Required when runPublish is true; unused otherwise.
# runPublish - When true: also runs the Validate stage before UnitTests.
# When false (PR / merge builds): only PreBuildCheck + tests run.
#
# Stage flow:
#
# runPublish: true → PreBuildCheck ─► Validate ─► UnitTests ─► E2ETests
# runPublish: false → PreBuildCheck ─► UnitTests ─► E2ETests (Validate is skipped)
#
# Build and Publish stages are defined in pipeline-publish.yml (not here),
# so that the PR build never references PyPI service connections.
parameters:
- name: packageVersion
type: string
default: ''
- name: runPublish
type: boolean
default: false
stages:
# ══════════════════════════════════════════════════════════════════════════════
# Stage 0 · PreBuildCheck — SDL security scans (PoliCheck + CredScan)
# Always runs, mirrors MSAL.NET pre-build analysis.
# ══════════════════════════════════════════════════════════════════════════════
- stage: PreBuildCheck
displayName: 'Pre-build security checks'
jobs:
- job: SecurityScan
displayName: 'PoliCheck + CredScan'
pool:
vmImage: windows-latest
variables:
Codeql.SkipTaskAutoInjection: true
steps:
- task: NodeTool@0
displayName: 'Install Node.js (includes npm)'
inputs:
versionSpec: '20.x'
- task: securedevelopmentteam.vss-secure-development-tools.build-task-policheck.PoliCheck@2
displayName: 'Run PoliCheck'
inputs:
targetType: F
continueOnError: true
- task: securedevelopmentteam.vss-secure-development-tools.build-task-credscan.CredScan@3
displayName: 'Run CredScan'
inputs:
suppressionsFile: '$(Build.SourcesDirectory)/.Pipelines/credscan-exclusion.json'
toolMajorVersion: V2
debugMode: false
- task: securedevelopmentteam.vss-secure-development-tools.build-task-postanalysis.PostAnalysis@2
displayName: 'Post Analysis'
inputs:
GdnBreakGdnToolCredScan: true
GdnBreakGdnToolPoliCheck: true
- task: securedevelopmentteam.vss-secure-development-tools.build-task-publishsecurityanalysislogs.PublishSecurityAnalysisLogs@3
displayName: 'Publish Security Analysis Logs (TSA)'
condition: succeededOrFailed()
inputs:
tsaConfigFile: '$(Build.SourcesDirectory)/.Pipelines/tsaConfig.json'
- task: mspremier.PostBuildCleanup.PostBuildCleanup-task.PostBuildCleanup@3
displayName: 'Clean agent directories'
condition: always()
# ══════════════════════════════════════════════════════════════════════════════
# Stage 1 · Validate — verify packageVersion matches msal/sku.py __version__
# Skipped when runPublish is false (PR / merge builds).
# ══════════════════════════════════════════════════════════════════════════════
- stage: Validate
displayName: 'Validate version'
dependsOn: PreBuildCheck
condition: and(${{ parameters.runPublish }}, eq(dependencies.PreBuildCheck.result, 'Succeeded'))
jobs:
- job: ValidateVersion
displayName: 'Check version matches source'
pool:
vmImage: ubuntu-latest
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.12'
displayName: 'Set up Python'
- script: |
python - <<'EOF'
import sys, runpy
ns = runpy.run_path("msal/sku.py")
sku_ver = ns.get("__version__", "")
param_ver = "${{ parameters.packageVersion }}"
if not param_ver:
print("##vso[task.logissue type=error]packageVersion is required. Enter the version to publish (must match msal/sku.py __version__).")
sys.exit(1)
elif param_ver != sku_ver:
print(f"##vso[task.logissue type=error]Version mismatch: parameter '{param_ver}' != msal/sku.py '{sku_ver}'")
print("Update msal/sku.py __version__ to match the packageVersion parameter, or correct the parameter.")
sys.exit(1)
else:
print(f"Version validated: {param_ver}")
EOF
displayName: 'Verify version parameter matches msal/sku.py'
# ══════════════════════════════════════════════════════════════════════════════
# Stage 2 · UnitTests — run unit tests across all supported Python versions.
# No Key Vault or service connection needed.
# Waits for Validate when runPublish is true;
# runs immediately when Validate is skipped (PR / merge builds).
# ══════════════════════════════════════════════════════════════════════════════
- stage: UnitTests
displayName: 'Unit test'
dependsOn:
- PreBuildCheck
- Validate
condition: |
and(
eq(dependencies.PreBuildCheck.result, 'Succeeded'),
in(dependencies.Validate.result, 'Succeeded', 'Skipped')
)
jobs:
- job: Pytest
displayName: 'pytest'
pool:
vmImage: ubuntu-22.04
timeoutInMinutes: 30
strategy:
matrix:
Python39: { python.version: '3.9' }
Python310: { python.version: '3.10' }
Python311: { python.version: '3.11' }
Python312: { python.version: '3.12' }
Python313: { python.version: '3.13' }
Python314: { python.version: '3.14' }
maxParallel: 6
steps:
- task: UsePythonVersion@0
displayName: 'Use Python $(python.version)'
inputs:
versionSpec: '$(python.version)'
- bash: |
set -euo pipefail
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-azurepipelines pytest-timeout
displayName: 'Install Python dependencies'
- bash: |
set -o pipefail
mkdir -p test-results
pytest -vv \
--benchmark-skip \
--timeout=120 \
--junitxml=test-results/junit-unit.xml \
--ignore=tests/test_e2e.py \
--ignore=tests/test_e2e_manual.py \
--ignore=tests/test_fmi_e2e.py \
--deselect tests/test_cryptography.py::CryptographyTestCase::test_ceiling_should_be_latest_cryptography_version_plus_three \
--deselect tests/test_cryptography.py::CryptographyTestCase::test_should_be_run_with_latest_version_of_cryptography \
2>&1 | tee test-results/pytest-unit.log
displayName: 'Run pytest (unit)'
env:
# Force unbuffered stdout so ADO logs stream in real time through the tee pipe.
PYTHONUNBUFFERED: '1'
# Run cryptography version-gating tests separately as a warning-only check.
# These tests fail whenever a new `cryptography` release ships (signalling a
# required ceiling bump in setup.cfg). We deliberately swallow the non-zero
# exit code with `|| true` so the step always succeeds at the shell level —
# this keeps the stage result as 'Succeeded' (not 'SucceededWithIssues') so
# downstream publish gates can stay strict. Failures are still surfaced as
# JUnit test failures in the Tests tab via PublishTestResults below
# (failTaskOnFailedTests: false), giving maintainers visibility without
# blocking unrelated PRs or releases.
- bash: |
pytest -vv \
--timeout=60 \
tests/test_cryptography.py::CryptographyTestCase::test_ceiling_should_be_latest_cryptography_version_plus_three \
tests/test_cryptography.py::CryptographyTestCase::test_should_be_run_with_latest_version_of_cryptography \
--junitxml=test-results/junit-crypto-ceiling.xml \
|| true
displayName: 'Check cryptography ceiling (warning only)'
env:
PYTHONUNBUFFERED: '1'
- task: PublishTestResults@2
displayName: 'Publish unit test results'
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: 'test-results/junit-unit.xml'
failTaskOnFailedTests: true
testRunTitle: 'Unit · Python $(python.version)'
- task: PublishTestResults@2
displayName: 'Publish cryptography ceiling results'
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: 'test-results/junit-crypto-ceiling.xml'
failTaskOnFailedTests: false
testRunTitle: 'Cryptography ceiling check $(python.version)'
# ══════════════════════════════════════════════════════════════════════════════
# Stage 3 · E2ETests — runs only if unit tests pass. Fetches the MSID Lab
# certificate from Key Vault (mirrors MSAL.NET's
# build/template-install-keyvault-secrets.yaml).
# Fork behaviour: the stage still runs on forked PRs, but the
# Key Vault retrieval and certificate decoding steps are skipped
# via `ne(System.PullRequest.IsFork, 'True')`. The pytest step then
# self-skips each test because LAB_APP_CLIENT_CERT_PFX_PATH is unset
# (see tests/test_e2e.py). Result: green stage on forks with all
# E2E tests reported as Skipped in the Tests tab.
# ═══════════════════════════════════════════════════════════════════════════
- stage: E2ETests
displayName: 'E2E tests'
dependsOn: UnitTests
condition: eq(dependencies.UnitTests.result, 'Succeeded')
jobs:
- job: Pytest
displayName: 'pytest'
pool:
vmImage: ubuntu-22.04
timeoutInMinutes: 60
strategy:
matrix:
Python39: { python.version: '3.9' }
Python310: { python.version: '3.10' }
Python311: { python.version: '3.11' }
Python312: { python.version: '3.12' }
Python313: { python.version: '3.13' }
Python314: { python.version: '3.14' }
maxParallel: 6
steps:
- task: UsePythonVersion@0
displayName: 'Use Python $(python.version)'
inputs:
versionSpec: '$(python.version)'
- task: AzureKeyVault@2
displayName: 'Fetch MSID Lab certificate from Key Vault'
condition: ne(variables['System.PullRequest.IsFork'], 'True')
inputs:
azureSubscription: 'AuthSdkResourceManager'
KeyVaultName: 'msidlabs'
SecretsFilter: 'LabAuth'
RunAsPreJob: false
- bash: |
set -euo pipefail
if [ -z "${LAB_AUTH_B64:-}" ]; then
echo "##vso[task.logissue type=error]LabAuth secret is empty — Key Vault retrieval failed."
exit 1
fi
CERT_PATH="$(Agent.TempDirectory)/lab-auth.pfx"
printf '%s' "$LAB_AUTH_B64" | base64 -d > "$CERT_PATH"
echo "##vso[task.setvariable variable=LAB_APP_CLIENT_CERT_PFX_PATH]$CERT_PATH"
echo "Lab cert written to: $CERT_PATH ($(wc -c < "$CERT_PATH") bytes)"
displayName: 'Decode lab certificate to PFX'
condition: ne(variables['System.PullRequest.IsFork'], 'True')
env:
LAB_AUTH_B64: $(LabAuth)
- bash: |
set -euo pipefail
python -m pip install --upgrade pip
pip install -r requirements.txt
pip install pytest pytest-azurepipelines pytest-timeout
displayName: 'Install Python dependencies'
- bash: |
set -o pipefail
mkdir -p test-results
pytest -vv \
--timeout=300 \
--junitxml=test-results/junit-e2e.xml \
tests/test_e2e.py tests/test_fmi_e2e.py \
2>&1 | tee test-results/pytest-e2e.log
displayName: 'Run pytest (E2E)'
env:
# Force unbuffered stdout so ADO logs stream in real time through the tee pipe.
PYTHONUNBUFFERED: '1'
LAB_APP_CLIENT_CERT_PFX_PATH: $(LAB_APP_CLIENT_CERT_PFX_PATH)
- task: PublishTestResults@2
displayName: 'Publish E2E test results'
condition: succeededOrFailed()
inputs:
testResultsFormat: 'JUnit'
testResultsFiles: 'test-results/junit-e2e.xml'
failTaskOnFailedTests: true
testRunTitle: 'E2E · Python $(python.version)'
- bash: rm -f "$(Agent.TempDirectory)/lab-auth.pfx"
displayName: 'Remove lab certificate from agent'
condition: always()
microsoft-authentication-library-for-python-1.37.0/.Pipelines/tsaConfig.json 0000664 0000000 0000000 00000000645 15206345071 0027217 0 ustar 00root root 0000000 0000000 {
"codebaseName": "MSAL Python",
"notificationAliases": [
"IdentityDevExDotnet@microsoft.com"
],
"codebaseAdmins": [
"EUROPE\\aadidagt"
],
"instanceUrl": "https://identitydivision.visualstudio.com",
"projectName": "IDDP",
"areaPath": "IDDP\\DevEx-Client-SDK\\Python",
"iterationPath": "IDDP\\Unscheduled",
"tools": [
"credscan",
"policheck"
]
}
microsoft-authentication-library-for-python-1.37.0/.github/ 0000775 0000000 0000000 00000000000 15206345071 0023734 5 ustar 00root root 0000000 0000000 microsoft-authentication-library-for-python-1.37.0/.github/ISSUE_TEMPLATE/ 0000775 0000000 0000000 00000000000 15206345071 0026117 5 ustar 00root root 0000000 0000000 microsoft-authentication-library-for-python-1.37.0/.github/ISSUE_TEMPLATE/bug_report.md 0000664 0000000 0000000 00000002373 15206345071 0030616 0 ustar 00root root 0000000 0000000 ---
name: Bug report
about: Create a report to help us improve
title: "[Bug] "
labels: needs attention, untriaged
assignees: ''
---
**Describe the bug**
A clear and concise description of what the bug is.
**To Reproduce**
Steps to reproduce the behavior:
1. Go to our [off-the-shelf samples](https://github.com/AzureAD/microsoft-authentication-library-for-python/tree/dev/sample) and pick one that is closest to your usage scenario. You should not need to modify the sample.
2. Follow the description of the sample, typically at the beginning of it, to prepare a `config.json` containing your test configurations
3. Run such sample, typically by `python sample.py config.json`
4. See the error
5. In this bug report, tell us the sample you choose, paste the content of the config.json with your test setup (which you can choose to skip your credentials, and/or mail it to our developer's email).
**Expected behavior**
A clear and concise description of what you expected to happen.
**What you see instead**
Paste the sample output, or add screenshots to help explain your problem.
**The MSAL Python version you are using**
Paste the output of this
`python -c "import msal; print(msal.__version__)"`
**Additional context**
Add any other context about the problem here.
microsoft-authentication-library-for-python-1.37.0/.github/ISSUE_TEMPLATE/feature_request.yaml 0000664 0000000 0000000 00000002350 15206345071 0032206 0 ustar 00root root 0000000 0000000 name: Feature request
description: Suggest a new feature for MSAL Python.
labels: ["feature request", "untriaged", "needs attention"]
title : '[Feature Request] '
body:
- type: markdown
attributes:
value: |
## Before submitting your feature request
Please make sure that your question or issue is not already covered in [MSAL documentation](https://learn.microsoft.com/entra/msal/python/) or [samples](https://learn.microsoft.com/azure/active-directory/develop/sample-v2-code?tabs=apptype).
- type: markdown
attributes:
value: |
## Feature request for MSAL Python
- type: dropdown
attributes:
label: MSAL client type
description: Are you using Public Client (desktop apps, CLI apps) or Confidential Client (web apps, web APIs, service-to-service, managed identity)?
multiple: true
options:
- "Public"
- "Confidential"
validations:
required: true
- type: textarea
attributes:
label: Problem Statement
description: "Describe the problem or context for this feature request."
validations:
required: true
- type: textarea
attributes:
label: Proposed solution
description: "Describe the solution you'd like."
validations:
required: false
microsoft-authentication-library-for-python-1.37.0/.github/workflows/ 0000775 0000000 0000000 00000000000 15206345071 0025771 5 ustar 00root root 0000000 0000000 microsoft-authentication-library-for-python-1.37.0/.github/workflows/RunIssueSentinel.yml 0000664 0000000 0000000 00000000731 15206345071 0031774 0 ustar 00root root 0000000 0000000 name: Run issue sentinel
on:
issues:
types: [opened, edited, closed]
jobs:
Issue:
permissions:
issues: write
runs-on: ubuntu-latest
steps:
- name: Run Issue Sentinel
uses: Azure/issue-sentinel@v1
with:
password: ${{secrets.ISSUE_SENTINEL_PASSWORD}}
enable-similar-issues-scanning: true # Scan for similar issues
enable-security-issues-scanning: true # Scan for security issues microsoft-authentication-library-for-python-1.37.0/.github/workflows/python-package.yml 0000664 0000000 0000000 00000005175 15206345071 0031436 0 ustar 00root root 0000000 0000000 # Build verification + unit tests for the msal Python package.
#
# This workflow runs on every PR (against any target branch) to give contributors
# fast feedback that the package still builds and that the unit tests pass across
# all supported Python versions.
#
# Post-merge validation on dev, E2E tests, benchmarks, SDL scans, and PyPI
# publishing are NOT run here. Those run in the ADO pipelines:
# - azure-pipelines.yml (PRs + pushes to dev: unit + E2E + SDL)
# - .Pipelines/pipeline-publish.yml (manual release to TestPyPI / PyPI)
name: Build and Unit Tests
on:
pull_request:
# No `branches` filter — run on PRs against any target branch.
jobs:
build:
name: Build package (sdist + wheel)
permissions:
contents: read
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
cache: 'pip'
- name: Install build tooling
run: |
python -m pip install --upgrade pip
python -m pip install build twine
- name: Build sdist and wheel
run: python -m build --sdist --wheel --outdir dist/ .
- name: Verify built artifacts
# `twine check` catches broken long_description / metadata that would fail PyPI upload.
run: twine check dist/*
- name: Upload built artifacts
uses: actions/upload-artifact@v4
with:
name: dist
path: dist/
retention-days: 7
ci:
name: Unit tests Python ${{ matrix.python-version }}
permissions:
contents: read
runs-on: ubuntu-22.04
strategy:
fail-fast: false
matrix:
python-version: ['3.9', '3.10', '3.11', '3.12', '3.13', '3.14']
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
# It automatically takes care of pip cache, according to
# https://docs.github.com/en/actions/using-workflows/caching-dependencies-to-speed-up-workflows#about-caching-workflow-dependencies
with:
python-version: ${{ matrix.python-version }}
cache: 'pip'
- name: Install dependencies
run: |
python -m pip install --upgrade pip
python -m pip install pytest
if [ -f requirements.txt ]; then pip install -r requirements.txt; fi
- name: Run unit tests
# Skip benchmarks and E2E tests — those require lab credentials and run in ADO.
run: |
pytest tests/ \
--benchmark-skip \
--ignore=tests/test_e2e.py \
--ignore=tests/test_e2e_manual.py \
--ignore=tests/test_fmi_e2e.py
microsoft-authentication-library-for-python-1.37.0/.gitignore 0000664 0000000 0000000 00000001435 15206345071 0024367 0 ustar 00root root 0000000 0000000 # Python cache
__pycache__/
*.pyc
# PTVS analysis
.ptvs/
*.pyproj
# Build results
/bin/
/obj/
/dist/
/MANIFEST
# Result of running python setup.py install/pip install -e
/build/
/msal.egg-info/
# Test results
/TestResults/
# User-specific files
*.suo
*.user
*.sln.docstates
/tests/config.py
# Windows image file caches
Thumbs.db
ehthumbs.db
# Folder config file
Desktop.ini
# Recycle Bin used on file shares
$RECYCLE.BIN/
# Mac desktop service store files
.DS_Store
.idea
src/build
*.iml
/doc/_build
# Virtual Environments
/env*
.venv/
docs/_build/
# Visual Studio Files
/.vs/*
/tests/.vs/*
# vim files
*.swp
# The test configuration file(s) could potentially contain credentials
tests/config.json
# Token Cache files
msal_cache.bin
.env
.perf.baseline
*.pfx
.vscode/settings.json
microsoft-authentication-library-for-python-1.37.0/.readthedocs.yaml 0000664 0000000 0000000 00000001073 15206345071 0025624 0 ustar 00root root 0000000 0000000 # .readthedocs.yaml
# Read the Docs configuration file
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
# Required
version: 2
# Set the version of Python and other tools you might need
build:
os: ubuntu-22.04
tools:
python: "3.12"
# Build documentation in the docs/ directory with Sphinx
sphinx:
configuration: docs/conf.py
# We recommend specifying your dependencies to enable reproducible builds:
# https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html
python:
install:
- requirements: docs/requirements.txt
microsoft-authentication-library-for-python-1.37.0/CODEOWNERS 0000664 0000000 0000000 00000000032 15206345071 0023762 0 ustar 00root root 0000000 0000000 * @AzureAD/id4s-msal-team
microsoft-authentication-library-for-python-1.37.0/LICENSE 0000664 0000000 0000000 00000002200 15206345071 0023373 0 ustar 00root root 0000000 0000000 The MIT License (MIT)
Copyright (c) Microsoft Corporation.
All rights reserved.
This code is licensed under the MIT License.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files(the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and / or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions :
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE. microsoft-authentication-library-for-python-1.37.0/README.md 0000664 0000000 0000000 00000022501 15206345071 0023653 0 ustar 00root root 0000000 0000000 # Microsoft Authentication Library (MSAL) for Python
| `dev` branch | Reference Docs | # of Downloads per different platforms | # of Downloads per recent MSAL versions | Benchmark Diagram |
|:------------:|:--------------:|:--------------------------------------:|:---------------------------------------:|:-----------------:|
[](https://github.com/AzureAD/microsoft-authentication-library-for-python/actions) | [](https://msal-python.readthedocs.io/en/latest/?badge=latest) | [](https://pypistats.org/packages/msal) | [](https://pepy.tech/project/msal) | [📉](https://azuread.github.io/microsoft-authentication-library-for-python/dev/bench/)
The Microsoft Authentication Library for Python enables applications to integrate with the [Microsoft identity platform](https://aka.ms/aaddevv2). It allows you to sign in users or apps with Microsoft identities ([Microsoft Entra ID](https://www.microsoft.com/security/business/identity-access/microsoft-entra-id), [External identities](https://www.microsoft.com/security/business/identity-access/microsoft-entra-external-id), [Microsoft Accounts](https://account.microsoft.com) and [Azure AD B2C](https://azure.microsoft.com/services/active-directory-b2c/) accounts) and obtain tokens to call Microsoft APIs such as [Microsoft Graph](https://graph.microsoft.io/) or your own APIs registered with the Microsoft identity platform. It is built using industry standard OAuth2 and OpenID Connect protocols
Not sure whether this is the SDK you are looking for your app? There are other Microsoft Identity SDKs
[here](https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Microsoft-Authentication-Client-Libraries).
Quick links:
| [Getting Started](https://learn.microsoft.com/azure/active-directory/develop/web-app-quickstart?pivots=devlang-python)| [Docs](https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki) | [Samples](https://aka.ms/aaddevsamplesv2) | [Support](README.md#community-help-and-support) | [Feedback](https://forms.office.com/r/TMjZkDbzjY) |
| --- | --- | --- | --- | --- |
## Scenarios supported
Click on the following thumbnail to visit a large map with clickable links to proper samples.
[](https://msal-python.readthedocs.io/en/latest/)
## Installation
You can find MSAL Python on [Pypi](https://pypi.org/project/msal/).
1. If you haven't already, [install and/or upgrade the pip](https://pip.pypa.io/en/stable/installing/)
of your Python environment to a recent version. We tested with pip 18.1.
1. As usual, just run `pip install msal`.
## Versions
This library follows [Semantic Versioning](http://semver.org/).
You can find the changes for each version under
[Releases](https://github.com/AzureAD/microsoft-authentication-library-for-python/releases).
## Usage
Before using MSAL Python (or any MSAL SDKs, for that matter), you will have to
[register your application with the Microsoft identity platform](https://docs.microsoft.com/azure/active-directory/develop/quickstart-v2-register-an-app).
Acquiring tokens with MSAL Python follows this 3-step pattern.
(Note: That is the high level conceptual pattern.
There will be some variations for different flows. They are demonstrated in
[runnable samples hosted right in this repo](https://github.com/AzureAD/microsoft-authentication-library-for-python/tree/dev/sample).
)
1. MSAL proposes a clean separation between
[public client applications, and confidential client applications](https://tools.ietf.org/html/rfc6749#section-2.1).
So you will first create either a `PublicClientApplication` or a `ConfidentialClientApplication` instance,
and ideally reuse it during the lifecycle of your app. The following example shows a `PublicClientApplication`:
```python
from msal import PublicClientApplication
app = PublicClientApplication(
"your_client_id",
authority="https://login.microsoftonline.com/Enter_the_Tenant_Name_Here")
```
Later, each time you would want an access token, you start by:
```python
result = None # It is just an initial value. Please follow instructions below.
```
2. The API model in MSAL provides you explicit control on how to utilize token cache.
This cache part is technically optional, but we highly recommend you to harness the power of MSAL cache.
It will automatically handle the token refresh for you.
```python
# We now check the cache to see
# whether we already have some accounts that the end user already used to sign in before.
accounts = app.get_accounts()
if accounts:
# If so, you could then somehow display these accounts and let end user choose
print("Pick the account you want to use to proceed:")
for a in accounts:
print(a["username"])
# Assuming the end user chose this one
chosen = accounts[0]
# Now let's try to find a token in cache for this account
result = app.acquire_token_silent(["your_scope"], account=chosen)
```
3. Either there is no suitable token in the cache, or you chose to skip the previous step,
now it is time to actually send a request to AAD to obtain a token.
There are different methods based on your client type and scenario. Here we demonstrate a placeholder flow.
```python
if not result:
# So no suitable token exists in cache. Let's get a new one from AAD.
result = app.acquire_token_by_one_of_the_actual_method(..., scopes=["User.Read"])
if "access_token" in result:
print(result["access_token"]) # Yay!
else:
print(result.get("error"))
print(result.get("error_description"))
print(result.get("correlation_id")) # You may need this when reporting a bug
```
Refer the [Wiki](https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki) pages for more details on the MSAL Python functionality and usage.
## Migrating from ADAL
If your application is using ADAL Python, we recommend you to update to use MSAL Python. No new feature work will be done in ADAL Python.
See the [ADAL to MSAL migration](https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Migrate-to-MSAL-Python) guide.
## Roadmap
You can follow the latest updates and plans for MSAL Python in the [Roadmap](https://github.com/AzureAD/microsoft-authentication-library-for-python/wiki/Roadmap) published on our Wiki.
## Samples and Documentation
MSAL Python supports multiple [application types and authentication scenarios](https://docs.microsoft.com/azure/active-directory/develop/authentication-flows-app-scenarios).
The generic documents on
[Auth Scenarios](https://docs.microsoft.com/azure/active-directory/develop/authentication-scenarios)
and
[Auth protocols](https://docs.microsoft.com/azure/active-directory/develop/active-directory-v2-protocols)
are recommended reading.
We provide a [full suite of sample applications](https://aka.ms/aaddevsamplesv2) and [documentation](https://aka.ms/aaddevv2) to help you get started with learning the Microsoft identity platform.
## Community Help and Support
We leverage Stack Overflow to work with the community on supporting Microsoft Entra and its SDKs, including this one!
We highly recommend you ask your questions on Stack Overflow (we're all on there!)
Also browser existing issues to see if someone has had your question before.
We recommend you use the "msal" tag so we can see it!
Here is the latest Q&A on Stack Overflow for MSAL:
[http://stackoverflow.com/questions/tagged/msal](http://stackoverflow.com/questions/tagged/msal)
## Submit Feedback
We'd like your thoughts on this library. Please complete [this short survey.](https://forms.office.com/r/TMjZkDbzjY)
## Security Reporting
If you find a security issue with our libraries or services please report it to [secure@microsoft.com](mailto:secure@microsoft.com) with as much detail as possible. Your submission may be eligible for a bounty through the [Microsoft Bounty](http://aka.ms/bugbounty) program. Please do not post security issues to GitHub Issues or any other public site. We will contact you shortly upon receiving the information. We encourage you to get notifications of when security incidents occur by visiting [this page](https://technet.microsoft.com/security/dd252948) and subscribing to Security Advisory Alerts.
## Contributing
All code is licensed under the MIT license and we triage actively on GitHub. We enthusiastically welcome contributions and feedback. Please read the [contributing guide](./contributing.md) before starting.
## We Value and Adhere to the Microsoft Open Source Code of Conduct
This project has adopted the [Microsoft Open Source Code of Conduct](https://opensource.microsoft.com/codeofconduct/). For more information see the [Code of Conduct FAQ](https://opensource.microsoft.com/codeofconduct/faq/) or contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additional questions or comments.
microsoft-authentication-library-for-python-1.37.0/RELEASES.md 0000664 0000000 0000000 00000014345 15206345071 0024130 0 ustar 00root root 0000000 0000000 # Microsoft Identity SDK Versioning and Servicing FAQ
We have adopted the semantic versioning flow that is industry standard for OSS projects. It gives the maximum amount of control on what risk you take with what versions. If you know how semantic versioning works with node.js, java, and ruby none of this will be new.
## Semantic Versioning and API stability promises
Microsoft Authentication libraries are independent open source libraries that are used by partners both internal and external to Microsoft. As with the rest of Microsoft, we have moved to a rapid iteration model where bugs are fixed daily and new versions are produced as required. To communicate these frequent changes to external partners and customers, we use semantic versioning for all our public Microsoft Authentication SDK libraries. This follows the practices of other open source libraries on the internet. This allows us to support our downstream partners which will lock on certain versions for stability purposes, as well as providing for the distribution over NuGet, CocoaPods, and Maven.
The semantics are: MAJOR.MINOR.PATCH (example 1.1.5)
We will update our code distributions to use the latest PATCH semantic version number in order to make sure our customers and partners get the latest bug fixes. Downstream partner needs to pull the latest PATCH version. Most partners should try lock on the latest MINOR version number in their builds and accept any updates in the PATCH number.
Example:
Using NuGet, this ensures all 1.1.0 to 1.1.x updates are included when building your code, but not 1.2.
```
```
| Version | Description | Example |
|:-------:|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------:|:---------------------------------------------------------------------------------------------------------:|
| x.x.x | PATCH version number. Incrementing these numbers is for bug fixes and updates but do not introduce new features. This is used for close partners who build on our platform release (ex. Azure AD Fabric, Office, etc.),In addition, Cocoapods, NuGet, and Maven use this number to deliver the latest release to customers. This will update frequently (sometimes within the same day),There is no new features, and no regressions or API surface changes. Code will continue to work unless affected by a particular code fix. | MSAL for iOS 1.0.10,(this was a fix for the Storyboard display that was fixed for a specific Office team) |
| x.x | MINOR version numbers. Incrementing these second numbers are for new feature additions that do not impact existing features or introduce regressions. They are purely additive, but may require testing to ensure nothing is impacted.,All x.x.x bug fixes will also roll up in to this number.,There is no regressions or API surface changes. Code will continue to work unless affected by a particular code fix or needs this new feature. | MSAL for iOS 1.1.0,(this added WPJ capability to MSAL, and rolled all the updates from 1.0.0 to 1.0.12) |
| x | MAJOR version numbers. This should be considered a new, supported version of Microsoft Authentication SDK and begins the Azure two year support cycle anew. Major new features are introduced and API changes can occur.,This should only be used after a large amount of testing and used only if those features are needed.,We will continue to service MAJOR version numbers with bug fixes up to the two year support cycle. | MSAL for iOS 1.0,(our first official release of MSAL) |
## Serviceability
When we release a new MINOR version, the previous MINOR version is abandoned.
When we release a new MAJOR version, we will continue to apply bug fixes to the existing features in the previous MAJOR version for up to the 2 year support cycle for Azure.
Example: We release MSALiOS 2.0 in the future which supports unified Auth for AAD and MSA. Later, we then have a fix in Conditional Access for MSALiOS. Since that feature exists both in MSALiOS 1.1 and MSALiOS 2.0, we will fix both. It will roll up in a PATCH number for each. Customers that are still locked down on MSALiOS 1.1 will receive the benefit of this fix.
## Microsoft Authentication SDKs and Azure Active Directory
Microsoft Authentication SDKs major versions will maintain backwards compatibility with Azure Active Directory web services through the support period. This means that the API surface area defined in a MAJOR version will continue to work for 2 years after release.
We will respond to bugs quickly from our partners and customers submitted through GitHub and through our private alias (tellaad@microsoft.com) for security issues and update the PATCH version number. We will also submit a change summary for each PATCH number.
Occasionally, there will be security bugs or breaking bugs from our partners that will require an immediate fix and a publish of an update to all partners and customers. When this occurs, we will do an emergency roll up to a PATCH version number and update all our distribution methods to the latest.
microsoft-authentication-library-for-python-1.37.0/RELEASE_GUIDE.md 0000664 0000000 0000000 00000010045 15206345071 0024713 0 ustar 00root root 0000000 0000000 # MSAL Python — Release Guide
How to ship a new version of `msal` to PyPI. Everything happens in Azure DevOps
(no GitHub Releases, no Git-tag-triggered automation).
---
## Before you start — one-time prerequisites
Confirm these are set up in ADO (`IdentityDivision` → `IDDP` project) — the
release will fail at runtime if any are missing:
- [ ] Pipeline **MSAL.Python-Publish** (definition `3067`) exists
- [ ] Service connection **`MSAL-ESRP-AME`** exists and is authorized
- [ ] Environment **`MSAL-Python-Release`** has a **required manual approval**
configured under *Approvals and checks*
- [ ] Key Vault **`MSALVault`** contains cert **`MSAL-ESRP-Release-Signing`**
> **Note on TestPyPI:** The TestPyPI publish path
> (`publishTarget = test.pypi.org (Preview / RC)`) is currently a **no-op** —
> the `MSAL-Test-Python-Upload` service connection has not been created yet,
> so that stage prints a skip message and uploads nothing. Until it's wired up,
> use an RC version (e.g. `1.36.0rc1`) on the production path for dry runs.
---
## Release in 4 steps
### 1. Bump the version on `dev`
The package version lives in **one file**: [msal/sku.py](msal/sku.py).
```python
__version__ = "1.36.0" # final release
# or
__version__ = "1.36.0rc1" # RC / dry run
```
Open a PR, get it merged into `dev`.
### 2. Cut the release branch
```bash
git checkout dev && git pull
git checkout -b release-1.36.0
git push origin release-1.36.0
```
Pushing the branch does **not** publish anything — the pipeline is manual.
### 3. Queue the publish pipeline
ADO → **IDDP** → **MSAL.Python-Publish (3067)** → **Run pipeline**:
| Field | Value |
|-------|-------|
| Branch | `release-1.36.0` |
| **Package version to publish** | `1.36.0` (must match `msal/sku.py` exactly) |
| **Publish target** | `pypi.org (ESRP Production)` |
The pipeline runs:
```
PreBuildCheck → Validate → UnitTests → E2ETests → Build → PublishPyPI
```
### 4. Approve, then verify
When `PublishPyPI` starts, it pauses for approval on the
`MSAL-Python-Release` environment. An approver clicks **Approve** in
ADO → Pipelines → Environments → MSAL-Python-Release.
ESRP signs the artifact and uploads to PyPI. Verify:
- https://pypi.org/project/msal/
- `pip install msal==1.36.0`
---
## Dry run (before the real release)
Recommended flow to exercise the pipeline end-to-end without burning a real
version number:
1. Set `msal/sku.py` to an RC version: `__version__ = "1.36.0rc1"`
2. Open a PR, merge to `dev`
3. Cut `release-1.36.0` from `dev`
4. Queue **MSAL.Python-Publish**:
- Package version: `1.36.0rc1`
- Publish target: `pypi.org (ESRP Production)`
5. Approve when prompted
6. Confirm `pip install msal==1.36.0rc1 --pre` works
7. Then bump to `1.36.0` and run the real release
RC versions are not installed by default by `pip install msal` (they require
`--pre`), so they're safe to push to production PyPI for verification.
---
## Hotfix (patch a released version)
```bash
# Fix on dev first
git checkout dev
# ...PR, merge fix into dev...
# Merge into the existing release branch
git checkout release-1.36.0
git merge dev
# Bump patch version in msal/sku.py: 1.36.0 → 1.36.1
git commit -am "bump to 1.36.1"
git push origin release-1.36.0
```
Then re-queue **MSAL.Python-Publish** with `packageVersion = 1.36.1` and
`pypi.org (ESRP Production)`. Approve.
---
## What changed from the old flow
| Old (GitHub-Actions) | New (ADO) |
|---|---|
| Push to `release-*` auto-published to TestPyPI | Manual queue with `test.pypi.org` target (currently no-op) |
| Push a Git tag auto-published to PyPI | Manual queue with `pypi.org (ESRP Production)` target |
| `PYPI_API_TOKEN` GitHub secret | ESRP service connection `MSAL-ESRP-AME` |
| Anyone with tag-push could ship | Required approver on `MSAL-Python-Release` environment |
Git tags are still allowed for source-tracking, but they trigger nothing.
---
## Pipeline architecture
See [.Pipelines/CI-AND-RELEASE-PIPELINES.md](.Pipelines/CI-AND-RELEASE-PIPELINES.md)
for stage-by-stage detail, triggers, and the shared template structure.
microsoft-authentication-library-for-python-1.37.0/azure-pipelines.yml 0000664 0000000 0000000 00000004424 15206345071 0026237 0 ustar 00root root 0000000 0000000 # PR gate and branch CI for the msal Python package.
# Runs on pushes to dev, on all PRs to dev, and on a daily schedule.
# Delegates test stages to .Pipelines/template-pipeline-stages.yml with
# runPublish: false — PreBuildCheck (SDL scans) + UnitTests + E2ETests only.
trigger:
branches:
include:
- dev
pr:
branches:
include:
- dev
drafts: false
schedules:
- cron: '45 7 * * *' # 07:45 UTC daily (11:45 PM PST / 12:45 AM PDT)
displayName: 'Daily CI (dev)'
branches:
include:
- dev
always: false # only run when there are new changes
stages:
- template: .Pipelines/template-pipeline-stages.yml
parameters:
runPublish: false
- stage: Benchmark
displayName: 'Run benchmarks'
dependsOn: E2ETests
# Only run on post-merge pushes to dev — not on PRs or scheduled runs.
condition: |
and(
succeeded('E2ETests'),
eq(variables['Build.SourceBranch'], 'refs/heads/dev'),
or(
eq(variables['Build.Reason'], 'IndividualCI'),
eq(variables['Build.Reason'], 'BatchedCI'),
eq(variables['Build.Reason'], 'Manual')
)
)
jobs:
- job: Benchmark
displayName: 'Performance benchmarks (Python 3.9)'
pool:
vmImage: ubuntu-latest
steps:
- task: UsePythonVersion@0
inputs:
versionSpec: '3.9'
displayName: 'Set up Python 3.9'
- script: |
python -m pip install --upgrade pip
pip install pytest -r requirements.txt
displayName: 'Install dependencies'
- task: Cache@2
displayName: 'Restore performance baseline cache'
inputs:
key: 'perf-baseline | "$(Agent.OS)" | tests/test_benchmark.py'
path: .perf.baseline
- bash: |
pytest --benchmark-only --benchmark-json benchmark.json --log-cli-level INFO tests/test_benchmark.py
displayName: 'Run benchmarks'
- bash: |
[ -f benchmark.json ] && echo "##vso[task.setvariable variable=benchmarkFileExists]true"
displayName: 'Check benchmark output exists'
condition: succeededOrFailed()
- task: PublishPipelineArtifact@1
displayName: 'Publish benchmark results'
condition: and(succeededOrFailed(), eq(variables['benchmarkFileExists'], 'true'))
inputs:
targetPath: 'benchmark.json'
artifact: 'benchmark-results'
microsoft-authentication-library-for-python-1.37.0/contributing.md 0000664 0000000 0000000 00000010401 15206345071 0025421 0 ustar 00root root 0000000 0000000 # CONTRIBUTING
Azure Active Directory SDK projects welcomes new contributors. This document will guide you
through the process.
### SUPPORTED PYTHON VERSIONS
The set of Python versions that MSAL Python supports, and the policy for
adding/removing support, is documented in
[doc/python_version_support_policy.md](doc/python_version_support_policy.md).
Any change that adds or removes a Python version must update both that
policy document and the supported-version declarations it lists
(`setup.cfg`, the GitHub Actions matrix, and the cryptography test).
### CONTRIBUTOR LICENSE AGREEMENT
Please visit [https://cla.microsoft.com/](https://cla.microsoft.com/) and sign the Contributor License
Agreement. You only need to do that once. We can not look at your code until you've submitted this request.
### FORK
Fork this project on GitHub and check out your copy.
Example for Project Foo (which can be any ADAL or MSAL or just any library):
```
$ git clone git@github.com:username/project-foo.git
$ cd project-foo
$ git remote add upstream git@github.com:AzureAD/project-foo.git
```
No need to decide if you want your feature or bug fix to go into the dev branch
or the master branch. **All bug fixes and new features should go into the dev branch.**
The master branch is effectively frozen; patches that change the SDKs
protocols or API surface area or affect the run-time behavior of the SDK will be rejected.
Some of our SDKs have bundled dependencies that are not part of the project proper.
Any changes to files in those directories or its subdirectories should be sent to their respective projects.
Do not send your patch to us, we cannot accept it.
In case of doubt, open an issue in the [issue tracker](issues).
Especially do so if you plan to work on a major change in functionality. Nothing is more
frustrating than seeing your hard work go to waste because your vision
does not align with our goals for the SDK.
### BRANCH
Okay, so you have decided on the proper branch. Create a feature branch
and start hacking:
```
$ git checkout -b my-feature-branch
```
### COMMIT
Make sure git knows your name and email address:
```
$ git config --global user.name "J. Random User"
$ git config --global user.email "j.random.user@example.com"
```
Writing good commit logs is important. A commit log should describe what
changed and why. Follow these guidelines when writing one:
1. The first line should be 50 characters or less and contain a short
description of the change prefixed with the name of the changed
subsystem (e.g. "net: add localAddress and localPort to Socket").
2. Keep the second line blank.
3. Wrap all other lines at 72 columns.
A good commit log looks like this:
```
fix: explaining the commit in one line
Body of commit message is a few lines of text, explaining things
in more detail, possibly giving some background about the issue
being fixed, etc etc.
The body of the commit message can be several paragraphs, and
please do proper word-wrap and keep columns shorter than about
72 characters or so. That way `git log` will show things
nicely even when it is indented.
```
The header line should be meaningful; it is what other people see when they
run `git shortlog` or `git log --oneline`.
Check the output of `git log --oneline files_that_you_changed` to find out
what directories your changes touch.
### REBASE
Use `git rebase` (not `git merge`) to sync your work from time to time.
```
$ git fetch upstream
$ git rebase upstream/v0.1 # or upstream/master
```
### TEST
Bug fixes and features should come with tests. Add your tests in the
test directory. This varies by repository but often follows the same convention of /src/test. Look at other tests to see how they should be
structured (license boilerplate, common includes, etc.).
Make sure that all tests pass.
### PUSH
```
$ git push origin my-feature-branch
```
Go to https://github.com/username/microsoft-authentication-library-for-***.git and select your feature branch. Click
the 'Pull Request' button and fill out the form.
Pull requests are usually reviewed within a few days. If there are comments
to address, apply your changes in a separate commit and push that to your
feature branch. Post a comment in the pull request afterwards; GitHub does
not send out notifications when you add commits.
microsoft-authentication-library-for-python-1.37.0/dependency-management.md 0000664 0000000 0000000 00000006167 15206345071 0027160 0 ustar 00root root 0000000 0000000 # Best Practices for Managing Dependency Versions
The best practices for managing dependency versions depend on
whether you are maintaining an application or a library.
## If you are maintaining an application
There are two suggestions that you shall follow.
1. Pinning dependencies in a production environment.
This refers to explicitly declaring the exact version of your application's dependencies,
including those of its dependencies (a.k.a. transitive dependencies).
This ensures consistent and predictable deployments by preventing accidental upgrades that might introduce errors or regressions.
To achieve this, the steps can be different, based on the tooling you choose.
For example, if you are using the common `pip` tool,
you shall specify the version like this `pip install PackageName==x.y.z`,
where x.y.z is the latest stable version that you have tested with your app.
Another approach is to start from a clean virtual environment,
install all the direct dependencies using the method described above,
and then run a `pip freeze > requirements.txt`.
It will generate a requirements.txt file containing all your dependencies
(including the transitive dependencies) pinned to their current versions.
Now you commit that requirements.txt.
From now on, you can consistently recreate your production environment by
`pip install -r requirements.txt`.
You may also use advanced dependency management tools to simplify the work flow.
For example,
[pip-tools](https://pypi.org/project/pip-tools).
2. Keep the dependencies updated.
The purpose of pinning dependencies in the production is not about
sticking with old versions forever.
It is about allowing you to control when to update what.
In the long run, you are still recommended to keep the dependencies updated because:
* The digital landscape is rife with threats.
Outdated dependencies are prime targets for malicious actors.
By staying updated, you fortify your project against known vulnerabilities
and significantly reduce the risk of exploitation.
* Software is rarely perfect. New versions of dependencies often include bug fixes,
improving the stability and performance of your application.
Ignoring updates means missing out on these enhancements.
You shall maintain a non-production environment,
periodically upgrade its dependencies to latest versions, test it out.
If the test passes, you can update the requirements.txt accordingly,
and deploy it to your production environment.
## If you are maintaining a library
If you are maintaining a library (meaning it will be used by its downstream applications),
you shall avoid pinning your dependencies.
Instead, you shall declare your dependencies by range, such as `msal>=1.33, <2.0`.
The lower bound is the smallest version that started to provide the API you are using,
the upper bound is the version that is expected to contain breaking changes.
microsoft-authentication-library-for-python-1.37.0/doc/ 0000775 0000000 0000000 00000000000 15206345071 0023141 5 ustar 00root root 0000000 0000000 microsoft-authentication-library-for-python-1.37.0/doc/python_version_support_policy.md 0000664 0000000 0000000 00000011450 15206345071 0031725 0 ustar 00root root 0000000 0000000 # MSAL Python Version Support Policy
This page describes the Python version support policy for the
Microsoft Authentication Library for Python (MSAL Python), including
end-of-support timelines for each Python version.
This policy is aligned with the
[Azure SDK for Python version support policy](https://github.com/Azure/azure-sdk-for-python/blob/main/doc/python_version_support_policy.md)
so that MSAL Python and the Azure SDK can be consumed together without
version conflicts.
End of support means, in the MSAL Python context, that **new MSAL Python
releases will no longer install on, be tested against, or accept bug
fixes for those Python versions**. Older MSAL Python releases that did
support those Python versions remain installable from PyPI via pip's
`requires-python` resolution, so existing applications continue to work
without change — they simply stop receiving new features and security
fixes.
## Policy
MSAL Python supports a Python version while it is supported upstream by
the Python core team (PSF), plus an additional **6-month grace window**
after the PSF end-of-support date to give applications time to migrate.
Concretely:
- MSAL Python adds support for a new Python release as soon as practical
after that Python release ships a stable `.0`.
- MSAL Python drops support for a Python version on the **first MSAL
Python release published on or after the SDK end-of-support date** for
that Python version (PSF end-of-support + ~6 months).
- Dropping a Python version is a **breaking change** and is delivered in
a new minor or major release of MSAL Python, never in a patch.
- The release notes (`RELEASES.md`) call out every Python-version
removal, and `setup.cfg` is updated in the same change to bump
`python_requires`, the trove classifiers, and any environment markers.
> **Note:** The "MSAL Python End Of Support" date is inclusive — the
> listed day is the last supported day, and the next day is the first
> unsupported day.
## Currently supported versions
| Python Version | PSF End of Support | MSAL Python End Of Support |
|----------------|--------------------|----------------------------|
| 3.9 ([PEP 596](https://peps.python.org/pep-0596/#lifespan)) | October 2025 | April 30, 2026 *(see note)* |
| 3.10 ([PEP 619](https://peps.python.org/pep-0619/#lifespan)) | October 2026 | April 30, 2027 |
| 3.11 ([PEP 664](https://peps.python.org/pep-0664/#lifespan)) | October 2027 | April 30, 2028 |
| 3.12 ([PEP 693](https://peps.python.org/pep-0693/#lifespan)) | October 2028 | April 30, 2029 |
| 3.13 ([PEP 719](https://peps.python.org/pep-0719/#lifespan)) | October 2029 | April 30, 2030 |
| 3.14 ([PEP 745](https://peps.python.org/pep-0745/#lifespan)) | October 2030 | April 30, 2031 |
> **Note on Python 3.9:** Python 3.9 is past its policy end-of-support
> date but is granted a one-time transition grace window in MSAL Python
> while we adopt this policy and complete the removal of Python 3.8. It
> will be removed in a subsequent MSAL Python release; the date will be
> announced in `RELEASES.md` ahead of removal.
## End-of-life versions (no longer supported)
| Python Version | PSF End of Support | MSAL Python End Of Support |
|----------------|--------------------|----------------------------|
| 3.8 ([PEP 569](https://peps.python.org/pep-0569/#lifespan)) | October 2024 | April 2026 |
| 3.7 ([PEP 537](https://peps.python.org/pep-0537/#lifespan)) | June 2023 | December 2023 |
| 3.6 ([PEP 494](https://peps.python.org/pep-0494/#lifespan)) | December 2021 | August 2022 |
| 2.7 ([PEP 373](https://peps.python.org/pep-0373/)) | April 2020 | January 2022 |
## Implementation
The supported Python versions are encoded in three places, which must be
kept in sync with this policy:
1. **`setup.cfg`** — `python_requires`, the `Programming Language ::
Python :: 3.x` trove classifiers, and any `python_version`
environment markers on optional dependencies (e.g. `pymsalruntime`).
2. **`.github/workflows/python-package.yml`** — the `python-version`
matrix used by the `pytest` test job.
3. **`tests/test_cryptography.py`** — the N+3 ceiling test that enforces
tracking the latest `cryptography` release. Newer `cryptography`
versions routinely drop EOL Python versions, which is the most common
forcing function for this policy.
## Rationale
MSAL Python depends transitively on `cryptography`, `requests`, and
`PyJWT`. These libraries follow a similar policy and drop EOL Python
versions roughly six months after PSF end-of-support. Continuing to
support an EOL Python version in MSAL Python forces us to either pin
those dependencies to old, unmaintained versions — exposing MSAL users
to known CVEs — or to maintain conditional install metadata that breaks
on every dependency bump. Aligning with the Azure SDK and upstream
policies keeps MSAL Python simple, secure, and predictable.
microsoft-authentication-library-for-python-1.37.0/docker_run.sh 0000775 0000000 0000000 00000001345 15206345071 0025071 0 ustar 00root root 0000000 0000000 #!/usr/bin/bash
# Error out if there is less than 1 argument
if [ "$#" -lt 1 ]; then
echo "Usage: $0 [command]"
echo "Example: $0 python:3.14.0a2-slim bash"
exit 1
fi
# We will get a standard Python image from the input,
# so that we don't need to hard code one in a Dockerfile
IMAGE_NAME=$1
echo "=== Starting $IMAGE_NAME (especially those which have no AppImage yet) ==="
echo "After seeing the bash prompt, run the following to test:"
echo " apt update && apt install -y gcc libffi-dev # Needed in Python 3.14.0a2-slim"
echo " pip install -e ."
echo " pytest --capture=no -s tests/chosen_test_file.py"
docker run --rm -it \
--privileged \
-w /home -v $PWD:/home \
$IMAGE_NAME \
$2
microsoft-authentication-library-for-python-1.37.0/docs/ 0000775 0000000 0000000 00000000000 15206345071 0023324 5 ustar 00root root 0000000 0000000 microsoft-authentication-library-for-python-1.37.0/docs/Makefile 0000664 0000000 0000000 00000001104 15206345071 0024760 0 ustar 00root root 0000000 0000000 # Minimal makefile for Sphinx documentation
#
# You can set these variables from the command line.
SPHINXOPTS =
SPHINXBUILD = sphinx-build
SOURCEDIR = .
BUILDDIR = _build
# Put it first so that "make" without argument is like "make help".
help:
@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O)
.PHONY: help Makefile
# Catch-all target: route all unknown targets to Sphinx using the new
# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS).
%: Makefile
@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) microsoft-authentication-library-for-python-1.37.0/docs/conf.py 0000664 0000000 0000000 00000013122 15206345071 0024622 0 ustar 00root root 0000000 0000000 # -*- coding: utf-8 -*-
#
# Configuration file for the Sphinx documentation builder.
#
# This file does only contain a selection of the most common options. For a
# full list see the documentation:
# http://www.sphinx-doc.org/en/master/config
# -- Path setup --------------------------------------------------------------
# If extensions (or modules to document with autodoc) are in another directory,
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
from datetime import date
import os
import sys
sys.path.insert(0, os.path.abspath('..'))
# -- Project information -----------------------------------------------------
project = u'MSAL Python'
copyright = u'{0}, Microsoft'.format(date.today().year)
author = u'Microsoft'
# The short X.Y version
from msal import __version__ as version
# The full version, including alpha/beta/rc tags
release = version
# -- General configuration ---------------------------------------------------
# If your documentation needs a minimal Sphinx version, state it here.
#
# needs_sphinx = '1.0'
# Add any Sphinx extension module names here, as strings. They can be
# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom
# ones.
extensions = [
'sphinx.ext.autodoc', # This seems need to be the first extension to load
'sphinx.ext.githubpages',
'sphinx_paramlinks',
]
# Add any paths that contain templates here, relative to this directory.
templates_path = ['_templates']
# The suffix(es) of source filenames.
# You can specify multiple suffix as a list of string:
#
# source_suffix = ['.rst', '.md']
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# The language for content autogenerated by Sphinx. Refer to documentation
# for a list of supported languages.
#
# This is also used if you do content translation via gettext catalogs.
# Usually you set "language" from the command line for these cases.
language = "en"
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
# This pattern also affects html_static_path and html_extra_path.
exclude_patterns = [u'_build', 'Thumbs.db', '.DS_Store']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = None
# -- Options for HTML output -------------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
#
# html_theme = 'alabaster'
html_theme = 'furo'
# Theme options are theme-specific and customize the look and feel of a theme
# further. For a list of options available for each theme, see the
# documentation.
#
html_theme_options = {
"light_css_variables": {
"font-stack": "'Segoe UI', SegoeUI, 'Helvetica Neue', Helvetica, Arial, sans-serif",
"font-stack--monospace": "SFMono-Regular, Consolas, 'Liberation Mono', Menlo, Courier, monospace",
},
}
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
#html_static_path = ['_static']
# Custom sidebar templates, must be a dictionary that maps document names
# to template names.
#
# The default sidebars (for documents that don't match any pattern) are
# defined by theme itself. Builtin themes are using these templates by
# default: ``['localtoc.html', 'relations.html', 'sourcelink.html',
# 'searchbox.html']``.
#
# html_sidebars = {}
# -- Options for HTMLHelp output ---------------------------------------------
# Output file base name for HTML help builder.
htmlhelp_basename = 'MSALPythondoc'
# -- Options for LaTeX output ------------------------------------------------
latex_elements = {
# The paper size ('letterpaper' or 'a4paper').
#
# 'papersize': 'letterpaper',
# The font size ('10pt', '11pt' or '12pt').
#
# 'pointsize': '10pt',
# Additional stuff for the LaTeX preamble.
#
# 'preamble': '',
# Latex figure (float) alignment
#
# 'figure_align': 'htbp',
}
# Grouping the document tree into LaTeX files. List of tuples
# (source start file, target name, title,
# author, documentclass [howto, manual, or own class]).
latex_documents = [
(master_doc, 'MSALPython.tex', u'MSAL Python Documentation',
u'Microsoft', 'manual'),
]
# -- Options for manual page output ------------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
(master_doc, 'msalpython', u'MSAL Python Documentation',
[author], 1)
]
# -- Options for Texinfo output ----------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
texinfo_documents = [
(master_doc, 'MSALPython', u'MSAL Python Documentation',
author, 'MSALPython', 'One line description of project.',
'Miscellaneous'),
]
# -- Options for Epub output -------------------------------------------------
# Bibliographic Dublin Core info.
epub_title = project
# The unique identifier of the text. This can be a ISBN number
# or the project homepage.
#
# epub_identifier = ''
# A unique identification for the text.
#
# epub_uid = ''
# A list of files that should not be packed into the epub file.
epub_exclude_files = ['search.html']
# -- Extension configuration -------------------------------------------------
microsoft-authentication-library-for-python-1.37.0/docs/daemon-app.svg 0000664 0000000 0000000 00000105011 15206345071 0026064 0 ustar 00root root 0000000 0000000