Publish - 2026-03-16 22:06:27 UTC

This commit is contained in:
Duende Bot 2026-03-16 22:08:02 +00:00
parent fee70ffce7
commit 3e811895ec
495 changed files with 2618 additions and 6471 deletions

12
.config/dotnet-tools.json vendored Normal file
View file

@ -0,0 +1,12 @@
{
"version": 1,
"isRoot": true,
"tools": {
"NuGetKeyVaultSignTool": {
"version": "3.2.3",
"commands": [
"NuGetKeyVaultSignTool"
]
}
}
}

View file

@ -20,6 +20,7 @@ csharp_style_expression_bodied_operators = true
csharp_style_expression_bodied_properties = true
csharp_style_namespace_declarations = file_scoped
csharp_style_prefer_switch_expression = true
csharp_style_unused_value_expression_statement_preference = discard_variable
csharp_style_var_elsewhere = true
csharp_style_var_for_built_in_types = true
csharp_style_var_when_type_is_apparent = true
@ -37,6 +38,7 @@ dotnet_diagnostic.IDE0027.severity = warning # Use expression body for accessors
dotnet_diagnostic.IDE0046.severity = suggestion # Use conditional expression for return
dotnet_diagnostic.IDE0049.severity = warning # Use language keywords instead of framework type names for type references
dotnet_diagnostic.IDE0055.severity = warning # Formatting rule
dotnet_diagnostic.IDE0058.severity = warning # Remove unnecessary expression value
dotnet_diagnostic.IDE0066.severity = warning # Use switch expressions
dotnet_diagnostic.IDE0073.severity = warning # Require file header
dotnet_diagnostic.IDE0161.severity = warning # Use file-scoped namespace

View file

@ -1,33 +0,0 @@
# Contributing to Duende Software Products
Thank you for your interest in Duende Software. As this repository contains proprietary licensed software, we have a specific policy regarding contributions.
**We do not generally accept unsolicited contributions to this repository.**
## How to Report Issues or Suggest Improvements
If you've identified an issue, have a suggestion, or want to discuss any aspect of our products:
1. Please use the [GitHub Discussions](https://github.com/orgs/DuendeSoftware/discussions) for the Duende Software organization.
2. Provide a clear description of your observation, suggestion, or question.
3. Our team will review and respond to appropriate discussion threads.
## Limited Contribution Scenarios
In specific cases where a contribution might be considered:
1. Only proceed after explicit invitation or approval from the Duende Software team.
2. Any contributed code must be compatible with our proprietary license.
3. You will be required to sign a Contributor License Agreement (CLA) before any contribution can be accepted.
## Code of Conduct
When participating in discussions related to Duende Software:
- Be respectful and professional in all communications
- Focus on technical details and specific product functionality
- Understand that not all suggestions will be implemented
## Thank You
We appreciate your understanding of our contribution policy. Our team works diligently to ensure the quality and integrity of our products, and we value your interest and feedback through the appropriate channels.

View file

@ -1,118 +0,0 @@
# This was generated by tool. Edits will be overwritten.
name: aspnetcore-authentication-jwtbearer/release
on:
workflow_dispatch:
inputs:
version:
description: 'Version in format X.Y.Z, X.Y.Z-preview.N, or X.Y.Z-rc.N'
type: string
required: true
default: '0.0.0'
branch:
description: '(Optional) the name of the branch to release from'
type: string
required: false
default: 'main'
remove-tag-if-exists:
description: 'If set, will remove the existing tag. Use this if you have issues with the previous release action'
type: boolean
required: false
default: false
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
tag:
name: Tag and Pack
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
defaults:
run:
shell: bash
working-directory: aspnetcore-authentication-jwtbearer
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate Version Input
run: echo '${{ github.event.inputs.version }}' | grep -P '^\d+\.\d+\.\d+(-preview\.\d+|-rc\.\d+)?$' || (echo 'Invalid version format' && exit 1)
- name: Checkout target branch
if: github.event.inputs.branch != 'main'
run: git checkout ${{ github.event.inputs.branch }}
- name: Git Config
run: |-
git config --global user.email "github-bot@duendesoftware.com"
git config --global user.name "Duende Software GitHub Bot"
- name: Git Config
if: github.event.inputs['remove-tag-if-exists'] == 'true'
run: |-
if git rev-parse aaj-${{ github.event.inputs.version }} >/dev/null 2>&1; then
git tag -d aaj-${{ github.event.inputs.version }}
git push --delete origin aaj-${{ github.event.inputs.version }}
else
echo 'Tag aaj-${{ github.event.inputs.version }} does not exist.'
fi
- name: Git Config
run: |-
git tag -a aaj-${{ github.event.inputs.version }} -m "Release v${{ github.event.inputs.version }}"
git push origin aaj-${{ github.event.inputs.version }}
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: Pack aspnetcore-authentication-jwtbearer.slnf
run: dotnet pack -c Release aspnetcore-authentication-jwtbearer.slnf -o artifacts
- name: Tool restore
run: dotnet tool restore
- name: Sign packages
run: |-
for file in artifacts/*.nupkg; do
dotnet NuGetKeyVaultSignTool sign "$file" --file-digest sha256 --timestamp-rfc3161 http://timestamp.digicert.com --azure-key-vault-url https://duendecodesigninghsm.vault.azure.net/ --azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 --azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 --azure-key-vault-client-secret ${{ secrets.SignClientSecret }} --azure-key-vault-certificate NuGetPackageSigning
done
- name: Push packages to GitHub
run: dotnet nuget push artifacts/*.nupkg --source https://nuget.pkg.github.com/DuendeSoftware/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Artifacts
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: artifacts
path: aspnetcore-authentication-jwtbearer/artifacts/*.nupkg
overwrite: true
retention-days: 15
publish:
name: Publish to nuget.org
needs:
- tag
runs-on: ubuntu-latest
environment:
name: nuget.org
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: artifacts
path: artifacts
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: List files
run: tree
shell: bash
- name: Push packages to nuget.org
run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_ORG_API_KEY }} --skip-duplicate

View file

@ -1,69 +0,0 @@
name: Auto-close external issues and PRs
on:
issues:
types: [opened]
pull_request_target:
types: [opened]
permissions:
issues: write
pull-requests: write
jobs:
close-external:
runs-on: ubuntu-latest
steps:
- uses: actions/github-script@v7
with:
script: |
const isIssue = context.eventName === 'issues';
const item = isIssue ? context.payload.issue : context.payload.pull_request;
const association = item.author_association;
const exempt = ['OWNER', 'MEMBER', 'COLLABORATOR'];
if (exempt.includes(association)) {
console.log(`Skipping — author_association is ${association}`);
return;
}
const itemType = isIssue ? 'issue' : 'pull request';
const number = item.number;
const { owner, repo } = context.repo;
const body = [
`Thank you for your interest in Duende Software!`,
``,
`This repository contains proprietary licensed software and does not accept unsolicited issues or pull requests. This ${itemType} has been automatically closed.`,
``,
`- Please review our [contributor guidelines](https://github.com/DuendeSoftware/products/blob/main/.github/CONTRIBUTING.md) for more details.`,
`- For questions, bug reports, or suggestions, please use [GitHub Discussions](https://github.com/orgs/DuendeSoftware/discussions).`,
``,
`We appreciate your understanding!`,
].join('\n');
await github.rest.issues.createComment({
owner,
repo,
issue_number: number,
body,
});
if (isIssue) {
await github.rest.issues.update({
owner,
repo,
issue_number: number,
state: 'closed',
state_reason: 'not_planned',
});
} else {
await github.rest.pulls.update({
owner,
repo,
pull_number: number,
state: 'closed',
});
}
console.log(`Closed ${itemType} #${number} (author_association: ${association})`);

View file

@ -1,118 +0,0 @@
# This was generated by tool. Edits will be overwritten.
name: bff/release
on:
workflow_dispatch:
inputs:
version:
description: 'Version in format X.Y.Z, X.Y.Z-preview.N, or X.Y.Z-rc.N'
type: string
required: true
default: '0.0.0'
branch:
description: '(Optional) the name of the branch to release from'
type: string
required: false
default: 'main'
remove-tag-if-exists:
description: 'If set, will remove the existing tag. Use this if you have issues with the previous release action'
type: boolean
required: false
default: false
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
tag:
name: Tag and Pack
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
defaults:
run:
shell: bash
working-directory: bff
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate Version Input
run: echo '${{ github.event.inputs.version }}' | grep -P '^\d+\.\d+\.\d+(-preview\.\d+|-rc\.\d+)?$' || (echo 'Invalid version format' && exit 1)
- name: Checkout target branch
if: github.event.inputs.branch != 'main'
run: git checkout ${{ github.event.inputs.branch }}
- name: Git Config
run: |-
git config --global user.email "github-bot@duendesoftware.com"
git config --global user.name "Duende Software GitHub Bot"
- name: Git Config
if: github.event.inputs['remove-tag-if-exists'] == 'true'
run: |-
if git rev-parse bff-${{ github.event.inputs.version }} >/dev/null 2>&1; then
git tag -d bff-${{ github.event.inputs.version }}
git push --delete origin bff-${{ github.event.inputs.version }}
else
echo 'Tag bff-${{ github.event.inputs.version }} does not exist.'
fi
- name: Git Config
run: |-
git tag -a bff-${{ github.event.inputs.version }} -m "Release v${{ github.event.inputs.version }}"
git push origin bff-${{ github.event.inputs.version }}
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: Pack bff.slnf
run: dotnet pack -c Release bff.slnf -o artifacts
- name: Tool restore
run: dotnet tool restore
- name: Sign packages
run: |-
for file in artifacts/*.nupkg; do
dotnet NuGetKeyVaultSignTool sign "$file" --file-digest sha256 --timestamp-rfc3161 http://timestamp.digicert.com --azure-key-vault-url https://duendecodesigninghsm.vault.azure.net/ --azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 --azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 --azure-key-vault-client-secret ${{ secrets.SignClientSecret }} --azure-key-vault-certificate NuGetPackageSigning
done
- name: Push packages to GitHub
run: dotnet nuget push artifacts/*.nupkg --source https://nuget.pkg.github.com/DuendeSoftware/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Artifacts
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: artifacts
path: bff/artifacts/*.nupkg
overwrite: true
retention-days: 15
publish:
name: Publish to nuget.org
needs:
- tag
runs-on: ubuntu-latest
environment:
name: nuget.org
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: artifacts
path: artifacts
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: List files
run: tree
shell: bash
- name: Push packages to nuget.org
run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_ORG_API_KEY }} --skip-duplicate

View file

@ -1,118 +0,0 @@
# This was generated by tool. Edits will be overwritten.
name: docs-mcp/release
on:
workflow_dispatch:
inputs:
version:
description: 'Version in format X.Y.Z, X.Y.Z-preview.N, or X.Y.Z-rc.N'
type: string
required: true
default: '0.0.0'
branch:
description: '(Optional) the name of the branch to release from'
type: string
required: false
default: 'main'
remove-tag-if-exists:
description: 'If set, will remove the existing tag. Use this if you have issues with the previous release action'
type: boolean
required: false
default: false
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
tag:
name: Tag and Pack
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
defaults:
run:
shell: bash
working-directory: docs-mcp
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate Version Input
run: echo '${{ github.event.inputs.version }}' | grep -P '^\d+\.\d+\.\d+(-preview\.\d+|-rc\.\d+)?$' || (echo 'Invalid version format' && exit 1)
- name: Checkout target branch
if: github.event.inputs.branch != 'main'
run: git checkout ${{ github.event.inputs.branch }}
- name: Git Config
run: |-
git config --global user.email "github-bot@duendesoftware.com"
git config --global user.name "Duende Software GitHub Bot"
- name: Git Config
if: github.event.inputs['remove-tag-if-exists'] == 'true'
run: |-
if git rev-parse dmcp-${{ github.event.inputs.version }} >/dev/null 2>&1; then
git tag -d dmcp-${{ github.event.inputs.version }}
git push --delete origin dmcp-${{ github.event.inputs.version }}
else
echo 'Tag dmcp-${{ github.event.inputs.version }} does not exist.'
fi
- name: Git Config
run: |-
git tag -a dmcp-${{ github.event.inputs.version }} -m "Release v${{ github.event.inputs.version }}"
git push origin dmcp-${{ github.event.inputs.version }}
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: Pack docs-mcp.slnf
run: dotnet pack -c Release docs-mcp.slnf -o artifacts
- name: Tool restore
run: dotnet tool restore
- name: Sign packages
run: |-
for file in artifacts/*.nupkg; do
dotnet NuGetKeyVaultSignTool sign "$file" --file-digest sha256 --timestamp-rfc3161 http://timestamp.digicert.com --azure-key-vault-url https://duendecodesigninghsm.vault.azure.net/ --azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 --azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 --azure-key-vault-client-secret ${{ secrets.SignClientSecret }} --azure-key-vault-certificate NuGetPackageSigning
done
- name: Push packages to GitHub
run: dotnet nuget push artifacts/*.nupkg --source https://nuget.pkg.github.com/DuendeSoftware/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Artifacts
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: artifacts
path: docs-mcp/artifacts/*.nupkg
overwrite: true
retention-days: 15
publish:
name: Publish to nuget.org
needs:
- tag
runs-on: ubuntu-latest
environment:
name: nuget.org
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: artifacts
path: artifacts
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: List files
run: tree
shell: bash
- name: Push packages to nuget.org
run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_ORG_API_KEY }} --skip-duplicate

View file

@ -1,118 +0,0 @@
# This was generated by tool. Edits will be overwritten.
name: identity-server/release
on:
workflow_dispatch:
inputs:
version:
description: 'Version in format X.Y.Z, X.Y.Z-preview.N, or X.Y.Z-rc.N'
type: string
required: true
default: '0.0.0'
branch:
description: '(Optional) the name of the branch to release from'
type: string
required: false
default: 'main'
remove-tag-if-exists:
description: 'If set, will remove the existing tag. Use this if you have issues with the previous release action'
type: boolean
required: false
default: false
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
tag:
name: Tag and Pack
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
defaults:
run:
shell: bash
working-directory: identity-server
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Validate Version Input
run: echo '${{ github.event.inputs.version }}' | grep -P '^\d+\.\d+\.\d+(-preview\.\d+|-rc\.\d+)?$' || (echo 'Invalid version format' && exit 1)
- name: Checkout target branch
if: github.event.inputs.branch != 'main'
run: git checkout ${{ github.event.inputs.branch }}
- name: Git Config
run: |-
git config --global user.email "github-bot@duendesoftware.com"
git config --global user.name "Duende Software GitHub Bot"
- name: Git Config
if: github.event.inputs['remove-tag-if-exists'] == 'true'
run: |-
if git rev-parse is-${{ github.event.inputs.version }} >/dev/null 2>&1; then
git tag -d is-${{ github.event.inputs.version }}
git push --delete origin is-${{ github.event.inputs.version }}
else
echo 'Tag is-${{ github.event.inputs.version }} does not exist.'
fi
- name: Git Config
run: |-
git tag -a is-${{ github.event.inputs.version }} -m "Release v${{ github.event.inputs.version }}"
git push origin is-${{ github.event.inputs.version }}
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: Pack identity-server.slnf
run: dotnet pack -c Release identity-server.slnf -o artifacts
- name: Tool restore
run: dotnet tool restore
- name: Sign packages
run: |-
for file in artifacts/*.nupkg; do
dotnet NuGetKeyVaultSignTool sign "$file" --file-digest sha256 --timestamp-rfc3161 http://timestamp.digicert.com --azure-key-vault-url https://duendecodesigninghsm.vault.azure.net/ --azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 --azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 --azure-key-vault-client-secret ${{ secrets.SignClientSecret }} --azure-key-vault-certificate NuGetPackageSigning
done
- name: Push packages to GitHub
run: dotnet nuget push artifacts/*.nupkg --source https://nuget.pkg.github.com/DuendeSoftware/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Artifacts
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: artifacts
path: identity-server/artifacts/*.nupkg
overwrite: true
retention-days: 15
publish:
name: Publish to nuget.org
needs:
- tag
runs-on: ubuntu-latest
environment:
name: nuget.org
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: artifacts
path: artifacts
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: List files
run: tree
shell: bash
- name: Push packages to nuget.org
run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_ORG_API_KEY }} --skip-duplicate

View file

@ -1,118 +0,0 @@
# This was generated by tool. Edits will be overwritten.
name: templates/release
on:
workflow_dispatch:
inputs:
version:
description: 'Version in format X.Y.Z, X.Y.Z-preview.N, or X.Y.Z-rc.N'
type: string
required: true
default: '0.0.0'
branch:
description: '(Optional) the name of the branch to release from'
type: string
required: false
default: 'main'
remove-tag-if-exists:
description: 'If set, will remove the existing tag. Use this if you have issues with the previous release action'
type: boolean
required: false
default: false
env:
DOTNET_NOLOGO: true
DOTNET_CLI_TELEMETRY_OPTOUT: true
jobs:
tag:
name: Tag and Pack
runs-on: ubuntu-latest
permissions:
contents: write
packages: write
defaults:
run:
shell: bash
working-directory: templates
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: Checkout target branch
if: github.event.inputs.branch != 'main'
run: git checkout ${{ github.event.inputs.branch }}
- name: Git Config
run: |-
git config --global user.email "github-bot@duendesoftware.com"
git config --global user.name "Duende Software GitHub Bot"
- name: Git Config
if: github.event.inputs['remove-tag-if-exists'] == 'true'
run: |-
if git rev-parse templates-${{ github.event.inputs.version }} >/dev/null 2>&1; then
git tag -d templates-${{ github.event.inputs.version }}
git push --delete origin templates-${{ github.event.inputs.version }}
else
echo 'Tag templates-${{ github.event.inputs.version }} does not exist.'
fi
- name: Git Config
run: |-
git tag -a templates-${{ github.event.inputs.version }} -m "Release v${{ github.event.inputs.version }}"
git push origin templates-${{ github.event.inputs.version }}
- name: Tool restore
run: dotnet tool restore
- name: build templates
run: dotnet run --project build
- name: Pack ../artifacts/templates.csproj
run: dotnet pack -c Release ../artifacts/templates.csproj -o artifacts
- name: Sign packages
run: |-
for file in artifacts/*.nupkg; do
dotnet NuGetKeyVaultSignTool sign "$file" --file-digest sha256 --timestamp-rfc3161 http://timestamp.digicert.com --azure-key-vault-url https://duendecodesigninghsm.vault.azure.net/ --azure-key-vault-client-id 18e3de68-2556-4345-8076-a46fad79e474 --azure-key-vault-tenant-id ed3089f0-5401-4758-90eb-066124e2d907 --azure-key-vault-client-secret ${{ secrets.SignClientSecret }} --azure-key-vault-certificate NuGetPackageSigning
done
- name: Push packages to GitHub
run: dotnet nuget push artifacts/*.nupkg --source https://nuget.pkg.github.com/DuendeSoftware/index.json --api-key ${{ secrets.GITHUB_TOKEN }} --skip-duplicate
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
NUGET_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Upload Artifacts
uses: actions/upload-artifact@b4b15b8c7c6ac21ea08fcf65892d2ee8f75cf882
with:
name: artifacts
path: templates/artifacts/*.nupkg
overwrite: true
retention-days: 15
publish:
name: Publish to nuget.org
needs:
- tag
runs-on: ubuntu-latest
environment:
name: nuget.org
steps:
- uses: actions/download-artifact@fa0a91b85d4f404e444e00e005971372dc801d16
with:
name: artifacts
path: artifacts
- name: List .net sdks
run: dotnet --list-sdks
- name: Setup Dotnet
uses: actions/setup-dotnet@3e891b0cb619bf60e2c25674b222b8940e2c1c25
with:
dotnet-version: |-
8.0.x
9.0.203
10.0.200
- name: List files
run: tree
shell: bash
- name: Push packages to nuget.org
run: dotnet nuget push artifacts/*.nupkg --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_ORG_API_KEY }} --skip-duplicate

9
.gitignore vendored
View file

@ -130,6 +130,7 @@ DocProject/Help/html
# Click-Once directory
publish/
!**/Commands/Publish/
# Publish Web Output
*.[Pp]ublish.xml
@ -170,6 +171,13 @@ bower_components/
# RIA/Silverlight projects
Generated_Code/
# source generated files
Logging.g.cs
LoggerMessage.g.cs
PublicTopLevelProgram.Generated.g.cs
RegexGenerator.g.cs
**/Generated/
# Backup & report files from converting an old project file
# to a newer Visual Studio version. Backup files are not needed,
# because we have git ;-)
@ -232,4 +240,5 @@ reports
*.db
*.db-shm
*.db-wal
nul
.weave

43
Directory.Build.props Normal file
View file

@ -0,0 +1,43 @@
<Project>
<PropertyGroup>
<Company>Duende Software</Company>
</PropertyGroup>
<PropertyGroup>
<AnalysisMode>All</AnalysisMode>
<Authors>$(Company)</Authors>
<ContinuousIntegrationBuild Condition="'$(GITHUB_ACTIONS)' == 'true'">True</ContinuousIntegrationBuild>
<Copyright>$(Company)</Copyright>
<DebugType>embedded</DebugType>
<Deterministic>true</Deterministic>
<!-- <EnforceCodeStyleInBuild>true</EnforceCodeStyleInBuild> -->
<ImplicitUsings>enable</ImplicitUsings>
<IsPackable>false</IsPackable>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<!-- <NoWarn>$(NoWarn);EnableGenerateDocumentationFile</NoWarn>--><!-- workaround for https://github.com/dotnet/roslyn/issues/41640 -->
<!-- <Nullable>enable</Nullable> -->
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<PropertyGroup>
<NoWarn>$(NoWarn);CA1034</NoWarn><!-- Nested types should not be visible -->
<NoWarn>$(NoWarn);CA1040</NoWarn><!-- Avoid empty interfaces -->
<NoWarn>$(NoWarn);CA2007</NoWarn><!-- Do not directly await a Task -->
<NoWarn>$(NoWarn);CS1591</NoWarn><!-- Missing XML comment for publicly visible type or member -->
<NoWarn>$(NoWarn);NU1507</NoWarn><!-- There are 2 package sources defined in your configuration -->
</PropertyGroup>
<!--Release builds do more work and are stricter. This allows for faster
development: devs can be messy/experimental initially and verify their
code easily by running a build in Release mode, and we also enforce
stricter rules in CI with the Release configuration.
-->
<PropertyGroup Condition="'$(Configuration)'=='Release'">
<!-- Workaround for https://github.com/dotnet/roslyn/issues/41640 -->
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<Compile Include="$(MSBuildThisFileDirectory)shared/Global.cs" Link="Global.cs" />
</ItemGroup>
</Project>

1
Directory.Build.targets Normal file
View file

@ -0,0 +1 @@
<Project />

View file

@ -1,79 +1,87 @@
<Project>
<ItemGroup>
<PackageVersion Include="AngleSharp" Version="1.1.2" />
<PackageVersion Include="Aspire.Hosting.Testing" Version="13.0.0" />
<PackageVersion Include="Aspire.Hosting.SqlServer" Version="13.0.0" />
<PackageVersion Include="Aspire.Hosting.PostgreSQL" Version="13.1.1" />
<PackageVersion Include="Aspire.Hosting.SqlServer" Version="13.1.1" />
<PackageVersion Include="Aspire.Hosting.Testing" Version="13.1.1" />
<PackageVersion Include="BenchmarkDotNet" Version="0.15.0" />
<PackageVersion Include="BullsEye" Version="5.0.0" />
<!-- Added aspire transitive package to resolve package vulnerability -->
<PackageVersion Include="KubernetesClient" Version="17.0.14" />
<PackageVersion Include="Bullseye" Version="6.0.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.4" />
<PackageVersion Include="Cronos" Version="0.11.1" />
<PackageVersion Include="Duende.AccessTokenManagement" Version="4.1.1" />
<PackageVersion Include="Duende.AccessTokenManagement.OpenIdConnect" Version="4.1.1" />
<PackageVersion Include="Duende.AspNetCore.Authentication.JwtBearer" Version="0.3.0" />
<PackageVersion Include="Duende.ConformanceReport" Version="0.1.0" />
<PackageVersion Include="Duende.IdentityModel" Version="8.0.0" />
<PackageVersion Include="Duende.IdentityModel.OidcClient" Version="7.0.0" />
<PackageVersion Include="Duende.IdentityServer" Version="7.4.0-preview.2" />
<PackageVersion Include="Duende.IdentityServer.Storage" Version="7.4.0-preview.2" />
<PackageVersion Include="Duende.Internal.ValueObjectSourceGenerator" Version="1.0.1" />
<PackageVersion Include="Duende.Private.Licensing" Version="1.0.0" />
<PackageVersion Include="Duende.RazorSlices" Version="1.0.0" />
<PackageVersion Include="Google.Protobuf" Version="3.31.1" />
<PackageVersion Include="Grpc.AspNetCore" Version="2.76.0" />
<PackageVersion Include="Grpc.AspNetCore.Web" Version="2.76.0" />
<PackageVersion Include="Grpc.Net.Client" Version="2.76.0" />
<PackageVersion Include="Grpc.Net.Client.Web" Version="2.76.0" />
<PackageVersion Include="Grpc.Tools" Version="2.78.0" />
<PackageVersion Include="IdentityModel.AspNetCore.OAuth2Introspection" Version="6.2.0" />
<PackageVersion Include="Markdig" Version="0.43.0" />
<PackageVersion Include="MartinCostello.Logging.XUnit.v3" Version="0.7.0" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.4.1" />
<!-- Review Packages -->
<PackageVersion Include="Microsoft.AspNetCore.Authentication.Certificate" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.Authorization" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Authentication" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.DevServer" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Components.WebAssembly.Server" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.DataProtection.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Identity.EntityFrameworkCore" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Razor.RuntimeCompilation" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.Mvc.Testing" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.TestHost" Version="10.0.0" />
<PackageVersion Include="Microsoft.AspNetCore.WebUtilities" Version="2.2.0" />
<PackageVersion Include="Microsoft.Build.Tasks.Core" Version="17.14.28" />
<PackageVersion Include="Microsoft.CodeAnalysis.PublicApiAnalyzers" Version="3.3.4" />
<PackageVersion Include="Microsoft.Data.SqlClient" Version="6.1.4" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Design" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.InMemory" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Relational" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Sqlite" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.SqlServer" Version="10.0.0" />
<PackageVersion Include="Microsoft.EntityFrameworkCore.Tools" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Hybrid" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Caching.Memory" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.DependencyInjection.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Diagnostics.Testing" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.FileProviders.Embedded" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Polly" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Hosting.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Http.Resilience" Version="10.1.0" />
<PackageVersion Include="Microsoft.Extensions.Logging" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Abstractions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Console" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Logging.Testing" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.ConfigurationExtensions" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Primitives" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.Options.DataAnnotations" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.ServiceDiscovery" Version="10.0.0" />
<PackageVersion Include="Microsoft.Extensions.TimeProvider.Testing" Version="10.0.0" />
<PackageVersion Include="Microsoft.IdentityModel.JsonWebTokens" Version="8.0.1" />
<PackageVersion Include="Microsoft.IdentityModel.Logging" Version="8.0.1" />
<PackageVersion Include="Microsoft.IdentityModel.Protocols.OpenIdConnect" Version="8.0.1" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.12.0" />
<PackageVersion Include="Microsoft.NETCore.Jit" Version="2.0.8" />
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="18.3.0" />
<PackageVersion Include="Microsoft.Playwright.Xunit.v3" Version="1.58.0" />
<PackageVersion Include="Microsoft.SourceLink.GitHub" Version="8.0.0" />
<PackageVersion Include="Microsoft.Testing.Extensions.CodeCoverage" Version="18.4.1" />
<PackageVersion Include="Microsoft.Testing.Extensions.TrxReport" Version="2.1.0" />
<PackageVersion Include="MimeKit" Version="4.15.1" />
<PackageVersion Include="MinVer" Version="6.0.0" />
<PackageVersion Include="ModelContextProtocol" Version="0.4.0-preview.3" />
<PackageVersion Include="ModelContextProtocol.AspNetCore" Version="0.4.0-preview.3" />
<PackageVersion Include="NBomber" Version="6.0.2" />
<PackageVersion Include="NBomber.Http" Version="6.0.2" />
<PackageVersion Include="NBomber.Sinks.Timescale" Version="0.8.0" />
<PackageVersion Include="netDumbster" Version="3.1.1" />
<PackageVersion Include="Newtonsoft.Json" Version="13.0.4" />
<PackageVersion Include="Npgsql" Version="10.0.1" />
<PackageVersion Include="NSubstitute" Version="5.1.0" />
<PackageVersion Include="OpenTelemetry" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Exporter.Console" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Exporter.OpenTelemetryProtocol" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Extensions.Hosting" Version="1.12.0" />
@ -81,33 +89,30 @@
<PackageVersion Include="OpenTelemetry.Instrumentation.Http" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.Runtime" Version="1.12.0" />
<PackageVersion Include="OpenTelemetry.Instrumentation.SqlClient" Version="1.12.0-beta.2" />
<PackageVersion Include="OpenTelemetry" Version="1.12.0" />
<PackageVersion Include="PublicApiGenerator" Version="11.1.0" />
<PackageVersion Include="Duende.RazorSlices" Version="1.0.0" />
<PackageVersion Include="PublicApiGenerator" Version="11.5.4" />
<PackageVersion Include="ReverseMarkdown" Version="4.7.0" />
<PackageVersion Include="RichardSzalay.MockHttp" Version="7.0.0" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.AspNetCore" Version="8.0.3" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="7.0.0" />
<PackageVersion Include="Serilog.Sinks.OpenTelemetry" Version="4.2.0" />
<PackageVersion Include="Serilog.Sinks.TextWriter" Version="3.0.0" />
<PackageVersion Include="Serilog.Extensions.Logging" Version="9.0.2" />
<PackageVersion Include="Shouldly" Version="4.2.1" />
<PackageVersion Include="Shouldly" Version="4.3.0" />
<PackageVersion Include="SimpleExec" Version="12.0.0" />
<PackageVersion Include="SimpleFeedReader" Version="2.0.4" />
<PackageVersion Include="Spectre.Console.Cli" Version="0.53.0" />
<PackageVersion Include="Spectre.Console.Json" Version="0.53.0" />
<PackageVersion Include="Sustainsys.Saml2.AspNetCore2" Version="2.11.0" />
<PackageVersion Include="System.CommandLine" Version="2.0.3" />
<PackageVersion Include="System.Drawing.Common" Version="10.0.3" />
<PackageVersion Include="System.Formats.Cbor" Version="10.0.3" />
<PackageVersion Include="System.IdentityModel.Tokens.Jwt" Version="8.0.1" />
<PackageVersion Include="System.Net.Http" Version="4.3.4" />
<PackageVersion Include="System.Text.Json" Version="10.0.0" />
<PackageVersion Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageVersion Include="xunit.v3.core" Version="3.2.2" />
<PackageVersion Include="Verify.XunitV3" Version="31.13.0" />
<PackageVersion Include="xunit.runner.visualstudio" Version="3.1.5" />
<PackageVersion Include="xunit.v3.core.mtp-v2" Version="3.2.2" />
<PackageVersion Include="xunit.v3.extensibility.core" Version="3.2.2" />
<PackageVersion Include="Verify.XunitV3" Version="31.13.0" />
<PackageVersion Include="Vogen" Version="7.0.3" />
<PackageVersion Include="Yarp.ReverseProxy" Version="2.1.0" />
</ItemGroup>
</Project>

View file

@ -2,7 +2,6 @@
"solution": {
"path": "..\\products.slnx",
"projects": [
".github\\workflow-gen\\workflow-gen.csproj",
"aspnetcore-authentication-jwtbearer\\src\\AspNetCore.Authentication.JwtBearer\\AspNetCore.Authentication.JwtBearer.csproj",
"aspnetcore-authentication-jwtbearer\\test\\AspNetCore.Authentication.JwtBearer.Tests\\AspNetCore.Authentication.JwtBearer.Tests.csproj"
]

View file

@ -0,0 +1,25 @@
#:project ../.github/build/BuildHelpers.csproj
using BuildHelpers;
using static Bullseye.Targets;
var repoRoot = Repo.FindRoot();
Targets.Shared(repoRoot, "aspnetcore-authentication-jwtbearer/aspnetcore-authentication-jwtbearer.slnf");
const string TestsAspNetCoreAuthenticationJwtBearerTests = "tests-asp-net-core-authentication-jwt-bearer-tests";
Targets.Test(TestsAspNetCoreAuthenticationJwtBearerTests, "aspnetcore-authentication-jwtbearer/test/AspNetCore.Authentication.JwtBearer.Tests", repoRoot);
Target(SharedTargets.Default, [
SharedTargets.CheckSolutions,
SharedTargets.CheckUnusedPackages,
SharedTargets.CheckSortedRefs,
SharedTargets.VerifyFormatting,
SharedTargets.Clean,
SharedTargets.VerifyNoChanges,
SharedTargets.DotnetDevCerts,
TestsAspNetCoreAuthenticationJwtBearerTests
]);
await RunTargetsAndExitAsync(args);

View file

@ -5,15 +5,12 @@
<RootNamespace>Duende.AspNetCore.Authentication.JwtBearer</RootNamespace>
<AssemblyName>Duende.AspNetCore.Authentication.JwtBearer</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Duende.IdentityModel" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.Extensions.Caching.Hybrid" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Duende.AspNetCore.Authentication.JwtBearer.Tests" />
</ItemGroup>
</Project>

View file

@ -30,7 +30,7 @@ internal class DPoPExpirationValidator
if (start < time)
{
var diff = time - now;
Logger.LogDebug("Expiration check failed. Creation time was too far in the future. The time being checked was {iat}, and clock is now {now}. The time difference is {diff}.", time, now, diff);
Logger.LogDebug("Expiration check failed. Creation time was too far in the future. The time being checked was {Iat}, and clock is now {Now}. The time difference is {Diff}.", time, now, diff);
return true;
}
@ -39,7 +39,7 @@ internal class DPoPExpirationValidator
if (expiration < end)
{
var diff = now - expiration;
Logger.LogDebug("Expiration check failed. Expiration has already happened. The expiration was at {exp}, and clock is now {now}. The time difference is {diff}.", expiration, now, diff);
Logger.LogDebug("Expiration check failed. Expiration has already happened. The expiration was at {Exp}, and clock is now {Now}. The time difference is {Diff}.", expiration, now, diff);
return true;
}

View file

@ -100,7 +100,7 @@ internal class DPoPJwtBearerEvents
if (result.IsError)
{
context.Fail(result.ErrorDescription ?? result.Error ?? throw new Exception("No ErrorDescription or Error set."));
context.Fail(result.ErrorDescription ?? result.Error ?? throw new InvalidOperationException("No ErrorDescription or Error set."));
// we need to stash these values away, so they are available later when the Challenge method is called
if (!string.IsNullOrWhiteSpace(result.Error))
@ -202,19 +202,19 @@ internal class DPoPJwtBearerEvents
// For example:
// WWW-Authenticate: DPoP error="invalid_dpop_proof", error_description="Invalid 'iat' value."
var sb = new StringBuilder();
sb.Append(OidcConstants.AuthenticationSchemes.AuthorizationHeaderDPoP);
_ = sb.Append(OidcConstants.AuthenticationSchemes.AuthorizationHeaderDPoP);
if (context.HttpContext.Items.TryGetValue("DPoP-Error", out var dpopErrorObj) && dpopErrorObj is string error)
{
sb.Append(" error=\"");
sb.Append(error);
sb.Append('\"');
_ = sb.Append(" error=\"");
_ = sb.Append(error);
_ = sb.Append('\"');
if (context.HttpContext.Items.TryGetValue("DPoP-ErrorDescription", out var dpopErrorDescObj) && dpopErrorDescObj is string description)
{
sb.Append(", error_description=\"");
sb.Append(description);
sb.Append('\"');
_ = sb.Append(", error_description=\"");
_ = sb.Append(description);
_ = sb.Append('\"');
}
}

View file

@ -61,7 +61,7 @@ public sealed class DPoPOptions
/// <item>Signatures are allowed from RSA, PSA, or ECDSA algorithms with key sizes of 256, 384, or 512 bits.</item>
/// </list>
/// </remarks>
public TokenValidationParameters ProofTokenValidationParameters = new()
public TokenValidationParameters ProofTokenValidationParameters { get; set; } = new()
{
ValidateAudience = false,
ValidateIssuer = false,

View file

@ -178,7 +178,7 @@ internal class DPoPProofValidator : IDPoPProofValidator
}
catch (Exception ex)
{
Logger.LogDebug("Error parsing DPoP proof token: {error}", ex.Message);
Logger.LogDebug("Error parsing DPoP proof token: {Error}", ex.Message);
result.SetError("Malformed DPoP proof token.");
return;
}
@ -199,7 +199,7 @@ internal class DPoPProofValidator : IDPoPProofValidator
}
catch (Exception ex)
{
Logger.LogDebug("Error parsing DPoP jwk value: {error}", ex.Message);
Logger.LogDebug("Error parsing DPoP jwk value: {Error}", ex.Message);
result.SetError("Invalid 'jwk' value.");
return;
}
@ -234,13 +234,13 @@ internal class DPoPProofValidator : IDPoPProofValidator
}
catch (Exception ex)
{
Logger.LogDebug("Error parsing DPoP proof token: {error}", ex.Message);
Logger.LogDebug("Error parsing DPoP proof token: {Error}", ex.Message);
result.SetError("Invalid DPoP proof token.");
}
if (tokenValidationResult?.Exception != null)
{
Logger.LogDebug("Error validating DPoP proof token: {error}", tokenValidationResult.Exception.Message);
Logger.LogDebug("Error validating DPoP proof token: {Error}", tokenValidationResult.Exception.Message);
result.SetError("Invalid DPoP proof token.");
}
@ -337,7 +337,7 @@ internal class DPoPProofValidator : IDPoPProofValidator
}
}
private bool HtuValueIsValid(string requestedUri, string? htuValue)
private static bool HtuValueIsValid(string requestedUri, string? htuValue)
{
if (string.IsNullOrEmpty(requestedUri) || string.IsNullOrEmpty(htuValue))
{

View file

@ -20,18 +20,18 @@ public static class DPoPServiceCollectionExtensions
/// </summary>
public IServiceCollection ConfigureDPoPTokensForScheme(string scheme)
{
services.AddOptions<DPoPOptions>();
_ = services.AddOptions<DPoPOptions>();
services.AddSingleton<DPoPJwtBearerEvents>();
_ = services.AddSingleton<DPoPJwtBearerEvents>();
services.TryAddTransient<IDPoPProofValidator, DPoPProofValidator>();
services.TryAddTransient<IDPoPNonceValidator, DefaultDPoPNonceValidator>();
services.AddTransient<DPoPExpirationValidator>();
_ = services.AddTransient<DPoPExpirationValidator>();
services.TryAddTransient<IDPoPProofValidator, DPoPProofValidator>();
services.TryAddTransient<DPoPHybridCacheProvider>();
services.TryAddTransient<IReplayCache, ReplayCache>();
services.AddSingleton<IPostConfigureOptions<JwtBearerOptions>>(sp =>
_ = services.AddSingleton<IPostConfigureOptions<JwtBearerOptions>>(sp =>
{
var events = sp.GetRequiredService<DPoPJwtBearerEvents>();
return new ConfigureJwtBearerOptions(events)
@ -48,7 +48,7 @@ public static class DPoPServiceCollectionExtensions
/// </summary>
public IServiceCollection ConfigureDPoPTokensForScheme(string scheme, Action<DPoPOptions> configure)
{
services.Configure(scheme, configure);
_ = services.Configure(scheme, configure);
return services.ConfigureDPoPTokensForScheme(scheme);
}
}

View file

@ -1,6 +1,7 @@
// Copyright (c) Duende Software. All rights reserved.
// See LICENSE in the project root for license information.
using System.Globalization;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
@ -38,7 +39,7 @@ internal class DefaultDPoPNonceValidator : IDPoPNonceValidator
public string CreateNonce(DPoPProofValidationContext context)
{
var now = TimeProvider.GetUtcNow().ToUnixTimeSeconds();
return DataProtector.Protect(now.ToString());
return DataProtector.Protect(now.ToString(CultureInfo.InvariantCulture));
}
public NonceValidationResult ValidateNonce(DPoPProofValidationContext context, string? nonce)
@ -75,7 +76,7 @@ internal class DefaultDPoPNonceValidator : IDPoPNonceValidator
}
catch (Exception ex)
{
Logger.LogDebug("Error parsing DPoP 'nonce' value: {error}", ex.ToString());
Logger.LogDebug("Error parsing DPoP 'nonce' value: {Error}", ex.ToString());
}
time = 0;

View file

@ -1,6 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../src.props" />
<PropertyGroup>
@ -9,6 +10,11 @@
<MinVerTagPrefix>aaj-</MinVerTagPrefix>
<MinVerMinimumMajorMinor>0.2</MinVerMinimumMajorMinor>
<AssemblyName>Duende.$(MSBuildProjectName)</AssemblyName>
<!-- CA1848/CA1873: LoggerMessage refactor is a separate effort -->
<!-- CA1031: Intentional catch-all for malformed token parsing -->
<!-- CA5404: DPoP proofs intentionally skip audience/issuer/lifetime validation -->
<!-- CA1056: ExpectedUrl is used as string throughout the DPoP flow -->
<NoWarn>$(NoWarn);CA1848;CA1873;CA1031;CA5404;CA1056</NoWarn>
</PropertyGroup>
</Project>

View file

@ -0,0 +1,4 @@
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.targets', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../src.targets" />
</Project>

View file

@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
@ -7,7 +6,6 @@
<RootNameSpace>Duende.AspNetCore.Authentication.JwtBearer</RootNameSpace>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="AngleSharp" />
<PackageReference Include="Duende.AccessTokenManagement.OpenIdConnect" />
@ -16,13 +14,9 @@
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.AspNetCore.Mvc.Testing" />
<PackageReference Include="Microsoft.Extensions.Diagnostics.Testing" />
<PackageReference Include="MartinCostello.Logging.XUnit.v3" />
<PackageReference Include="Microsoft.Extensions.TimeProvider.Testing" />
<PackageReference Include="RichardSzalay.MockHttp" />
</ItemGroup>
<ItemGroup>
<ProjectReference
Include="..\..\src\AspNetCore.Authentication.JwtBearer\AspNetCore.Authentication.JwtBearer.csproj" />
<ProjectReference Include="..\..\src\AspNetCore.Authentication.JwtBearer\AspNetCore.Authentication.JwtBearer.csproj" />
</ItemGroup>
</Project>

View file

@ -174,18 +174,18 @@ public class DPoPIntegrationTests
// Verify that a nonce was issued in the response
result.Headers.TryGetValues("DPoP-Nonce", out var nonceValues).ShouldBeTrue();
var serverNonce = nonceValues?.FirstOrDefault();
serverNonce.ShouldNotBeNull();
_ = serverNonce.ShouldNotBeNull();
serverNonce.ShouldNotBeEmpty();
// Verify the WWW-Authenticate header contains the use_dpop_nonce error
var wwwAuthValues = result.Headers.GetValues(HeaderNames.WWWAuthenticate).FirstOrDefault();
wwwAuthValues.ShouldNotBeNull();
_ = wwwAuthValues.ShouldNotBeNull();
wwwAuthValues.ShouldContain("use_dpop_nonce");
// Make a new request with the server issued nonce
var proofWithNonce = await CreateProofToken(token, Jwk, HttpMethod.Get, new Uri("http://localhost/"),
nonce: DPoPNonce.Parse(serverNonce));
Api.HttpClient.DefaultRequestHeaders.Remove(OidcConstants.HttpHeaders.DPoP);
_ = Api.HttpClient.DefaultRequestHeaders.Remove(OidcConstants.HttpHeaders.DPoP);
Api.HttpClient.DefaultRequestHeaders.Add(OidcConstants.HttpHeaders.DPoP, proofWithNonce);
var secondResult = await Api.HttpClient.GetAsync("/");
secondResult.StatusCode.ShouldBe(HttpStatusCode.OK);
@ -202,7 +202,7 @@ public class DPoPIntegrationTests
};
Api.OnConfigureServices += services =>
{
services.AddSingleton<IDPoPNonceValidator>(customValidator);
_ = services.AddSingleton<IDPoPNonceValidator>(customValidator);
};
await Initialize();
@ -220,13 +220,13 @@ public class DPoPIntegrationTests
// Verify that a custom nonce was issued in the response
result.Headers.TryGetValues("DPoP-Nonce", out var nonceValues).ShouldBeTrue();
var serverNonce = nonceValues?.FirstOrDefault();
serverNonce.ShouldNotBeNull();
_ = serverNonce.ShouldNotBeNull();
serverNonce.ShouldStartWith("custom-nonce-");
// Make a new request with the custom server issued nonce
var proofWithNonce = await CreateProofToken(token, Jwk, HttpMethod.Get, new Uri("http://localhost/"),
nonce: DPoPNonce.Parse(serverNonce));
Api.HttpClient.DefaultRequestHeaders.Remove(OidcConstants.HttpHeaders.DPoP);
_ = Api.HttpClient.DefaultRequestHeaders.Remove(OidcConstants.HttpHeaders.DPoP);
Api.HttpClient.DefaultRequestHeaders.Add(OidcConstants.HttpHeaders.DPoP, proofWithNonce);
var secondResult = await Api.HttpClient.GetAsync("/");
secondResult.StatusCode.ShouldBe(HttpStatusCode.OK);
@ -238,7 +238,7 @@ public class DPoPIntegrationTests
var cache = new TestHybridCache();
Api.OnConfigureServices += services =>
{
services.AddKeyedSingleton<HybridCache>(ServiceProviderKeys.ProofTokenReplayHybridCache, cache);
_ = services.AddKeyedSingleton<HybridCache>(ServiceProviderKeys.ProofTokenReplayHybridCache, cache);
};
await Initialize();
@ -270,12 +270,12 @@ public class DPoPIntegrationTests
/// </summary>
private async Task<UserToken> LoginAndGetToken()
{
await App.LoginAsync("sub");
_ = await App.LoginAsync("sub");
var response = await App.BrowserClient.GetAsync(App.Url("/user_token"));
var token = await response.Content.ReadFromJsonAsync<UserToken>();
token.ShouldNotBeNull();
token.AccessToken.ToString().ShouldNotBeNull();
token.DPoPJsonWebKey.ShouldNotBeNull();
_ = token.ShouldNotBeNull();
_ = token.AccessToken.ToString().ShouldNotBeNull();
_ = token.DPoPJsonWebKey.ShouldNotBeNull();
return token;
}
@ -301,7 +301,7 @@ public class DPoPIntegrationTests
};
var proof = await dpopService.CreateProofTokenAsync(proofRequest);
proof.ShouldNotBeNull();
_ = proof.ShouldNotBeNull();
return proof.Value;
}
@ -329,10 +329,10 @@ public class DPoPIntegrationTests
var api = new ApiHost(IdentityServer, baseAddress);
api.OnConfigureServices += services =>
{
services.ConfigureDPoPTokensForScheme(ApiHost.AuthenticationScheme,
_ = services.ConfigureDPoPTokensForScheme(ApiHost.AuthenticationScheme,
opt => ApiOptions?.Invoke(opt));
services.AddKeyedHybridCache(ServiceProviderKeys.ProofTokenReplayHybridCache);
_ = services.AddKeyedHybridCache(ServiceProviderKeys.ProofTokenReplayHybridCache);
};
api.OnConfigure += app =>

View file

@ -10,7 +10,6 @@ using Duende.AspNetCore.Authentication.JwtBearer.TestFramework;
using Duende.IdentityModel;
using Microsoft.AspNetCore.DataProtection;
using Microsoft.Extensions.Logging.Testing;
using Microsoft.Extensions.Time.Testing;
using Microsoft.IdentityModel.JsonWebTokens;
using Microsoft.IdentityModel.Tokens;

View file

@ -12,7 +12,7 @@ public class FreshnessTests : DPoPProofValidatorTestBase
{
var nonce = DataProtector.Protect(IssuedAt.ToString());
NonceValidator.TryGetUnixTimeFromNonce(nonce, out var actual);
_ = NonceValidator.TryGetUnixTimeFromNonce(nonce, out var actual);
actual.ShouldBe(IssuedAt);
}
@ -22,7 +22,7 @@ public class FreshnessTests : DPoPProofValidatorTestBase
{
var nonce = DataProtector.Protect("garbage that isn't a long");
NonceValidator.TryGetUnixTimeFromNonce(nonce, out var actual);
_ = NonceValidator.TryGetUnixTimeFromNonce(nonce, out var actual);
actual.ShouldBe(0);
}

View file

@ -26,11 +26,11 @@ public class ApiHost : GenericHost
private void ConfigureServices(IServiceCollection services)
{
services.AddHybridCache();
services.AddRouting();
services.AddAuthorization();
_ = services.AddHybridCache();
_ = services.AddRouting();
_ = services.AddAuthorization();
services.AddAuthentication(AuthenticationScheme)
_ = services.AddAuthentication(AuthenticationScheme)
.AddJwtBearer(AuthenticationScheme, options =>
{
options.Authority = _identityServerHost.Url();
@ -42,7 +42,7 @@ public class ApiHost : GenericHost
private void Configure(IApplicationBuilder app)
{
app.Use(async (context, next) =>
_ = app.Use(async (context, next) =>
{
ApiInvoked.Invoke(context);
if (ApiStatusCodeToReturn != null)
@ -55,9 +55,9 @@ public class ApiHost : GenericHost
await next();
});
app.UseRouting();
_ = app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
_ = app.UseAuthentication();
_ = app.UseAuthorization();
}
}

View file

@ -40,10 +40,10 @@ public class AppHost : GenericHost
private void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddAuthorization();
_ = services.AddRouting();
_ = services.AddAuthorization();
services.AddAuthentication(options =>
_ = services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
@ -80,7 +80,7 @@ public class AppHost : GenericHost
if (IdentityServerHttpHandler != null)
{
// allow discovery document
IdentityServerHttpHandler.When("/.well-known/*")
_ = IdentityServerHttpHandler.When("/.well-known/*")
.Respond(identityServerHandler);
options.BackchannelHttpHandler = IdentityServerHttpHandler;
@ -93,8 +93,8 @@ public class AppHost : GenericHost
options.ProtocolValidator.RequireNonce = false;
});
services.AddDistributedMemoryCache();
services.AddOpenIdConnectAccessTokenManagement(opt =>
_ = services.AddDistributedMemoryCache();
_ = services.AddOpenIdConnectAccessTokenManagement(opt =>
{
_configureUserTokenManagementOptions?.Invoke(opt);
});
@ -103,13 +103,13 @@ public class AppHost : GenericHost
private void Configure(IApplicationBuilder app)
{
app.UseAuthentication();
app.UseRouting();
app.UseAuthorization();
_ = app.UseAuthentication();
_ = app.UseRouting();
_ = app.UseAuthorization();
app.UseEndpoints(endpoints =>
_ = app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/login", async context =>
_ = endpoints.MapGet("/login", async context =>
{
await context.ChallengeAsync(new AuthenticationProperties
{
@ -117,18 +117,18 @@ public class AppHost : GenericHost
});
});
endpoints.MapGet("/logout", async context =>
_ = endpoints.MapGet("/logout", async context =>
{
await context.SignOutAsync();
});
endpoints.MapGet("/user_token", async context =>
_ = endpoints.MapGet("/user_token", async context =>
{
var tokenResult = await context.GetUserAccessTokenAsync();
await context.Response.WriteAsJsonAsync(tokenResult.Token);
});
endpoints.MapGet("/user_token_with_resource/{resource}", async (string resource, HttpContext context) =>
_ = endpoints.MapGet("/user_token_with_resource/{resource}", async (string resource, HttpContext context) =>
{
var token = await context.GetUserAccessTokenAsync(new UserTokenRequestParameters
{
@ -137,7 +137,7 @@ public class AppHost : GenericHost
await context.Response.WriteAsJsonAsync(token);
});
endpoints.MapGet("/client_token", async context =>
_ = endpoints.MapGet("/client_token", async context =>
{
var token = await context.GetClientAccessTokenAsync();
await context.Response.WriteAsJsonAsync(token);

View file

@ -35,7 +35,7 @@ public class GenericHost
public TestServer Server
{
get => _server ?? throw new InvalidOperationException(
$"Attempt to use {nameof(Server)} before it was initialized. Did you forget to call ${Initialize}?");
$"Attempt to use {nameof(Server)} before it was initialized. Did you forget to call {nameof(Initialize)}?");
private set => _server = value;
}
@ -81,7 +81,7 @@ public class GenericHost
EnvironmentName = IsDevelopment ? "Development" : "Production",
ApplicationName = HostAssembly?.GetName()?.Name
});
builder.WebHost
_ = builder.WebHost
.UseTestServer();
ConfigureServices(builder.Services);
@ -101,15 +101,15 @@ public class GenericHost
private void ConfigureServices(IServiceCollection services)
{
// This adds log messages to the output of our tests when they fail.
services.AddLogging(options =>
_ = services.AddLogging(options =>
{
// If you need different log output to understand a test failure, configure it here
options.SetMinimumLevel(LogLevel.Error);
options.AddFilter("Duende", LogLevel.Information);
options.AddFilter("Duende.IdentityServer.License", LogLevel.Error);
options.AddFilter("Duende.IdentityServer.Startup", LogLevel.Error);
_ = options.SetMinimumLevel(LogLevel.Error);
_ = options.AddFilter("Duende", LogLevel.Information);
_ = options.AddFilter("Duende.IdentityServer.License", LogLevel.Error);
_ = options.AddFilter("Duende.IdentityServer.Startup", LogLevel.Error);
options.AddXUnit(_testOutputHelper);
_ = options.AddXUnit(_testOutputHelper);
});
OnConfigureServices(services);

View file

@ -39,10 +39,10 @@ public class IdentityServerHost : GenericHost
private void ConfigureServices(IServiceCollection services)
{
services.AddRouting();
services.AddAuthorization();
_ = services.AddRouting();
_ = services.AddAuthorization();
services.AddIdentityServer(options =>
_ = services.AddIdentityServer(options =>
{
options.EmitStaticAudienceClaim = true;
@ -58,19 +58,19 @@ public class IdentityServerHost : GenericHost
private void Configure(IApplicationBuilder app)
{
app.UseRouting();
_ = app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
_ = app.UseIdentityServer();
_ = app.UseAuthorization();
app.UseEndpoints(endpoints =>
_ = app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/account/login", context =>
_ = endpoints.MapGet("/account/login", context =>
{
return Task.CompletedTask;
});
endpoints.MapGet("/account/logout", async context =>
_ = endpoints.MapGet("/account/logout", async context =>
{
// signout as if the user were prompted
await context.SignOutAsync();

View file

@ -71,7 +71,7 @@ public class TestBrowserClient : HttpClient
{
LastResponse.StatusCode.ShouldBe((HttpStatusCode)302);
var location = LastResponse.Headers.Location!.ToString();
await GetAsync(location);
_ = await GetAsync(location);
}
// TODO - Finish conversion from CSQuery to AngleSharp (CSQuery is unmaintained)

View file

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../test.props" />
</Project>

View file

@ -2,7 +2,6 @@
"solution": {
"path": "..\\products.slnx",
"projects": [
".github\\workflow-gen\\workflow-gen.csproj",
"bff\\hosts\\Blazor\\PerComponent\\Hosts.Bff.Blazor.PerComponent.Client\\Hosts.Bff.Blazor.PerComponent.Client.csproj",
"bff\\hosts\\Blazor\\PerComponent\\Hosts.Bff.Blazor.PerComponent\\Hosts.Bff.Blazor.PerComponent.csproj",
"bff\\hosts\\Blazor\\WebAssembly\\Hosts.Bff.Blazor.WebAssembly.Client\\Hosts.Bff.Blazor.WebAssembly.Client.csproj",

View file

@ -1,8 +0,0 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<EnableRDI>False</EnableRDI>
<RdiConfigured>True</RdiConfigured>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>

28
bff/build.cs Normal file
View file

@ -0,0 +1,28 @@
#:project ../.github/build/BuildHelpers.csproj
using BuildHelpers;
using static Bullseye.Targets;
var repoRoot = Repo.FindRoot();
Targets.Shared(repoRoot, "bff/bff.slnf");
const string TestsBffTests = "tests-bff-tests";
const string TestsHostsTests = "tests-hosts-tests";
Targets.Test(TestsBffTests, "bff/test/Bff.Tests", repoRoot);
Targets.Test(TestsHostsTests, "bff/test/Hosts.Tests", repoRoot);
Target(SharedTargets.Default, [
SharedTargets.CheckSolutions,
SharedTargets.CheckUnusedPackages,
SharedTargets.CheckSortedRefs,
SharedTargets.VerifyFormatting,
SharedTargets.Clean,
SharedTargets.VerifyNoChanges,
SharedTargets.DotnetDevCerts,
TestsBffTests,
TestsHostsTests
]);
await RunTargetsAndExitAsync(args);

View file

@ -15,26 +15,26 @@ var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
// BFF setup for blazor
builder.Services.AddBff()
_ = builder.Services.AddBff()
.AddServerSideSessions()
.AddBlazorServer()
.AddRemoteApis();
builder.Services.AddUserAccessTokenHttpClient("callApi",
_ = builder.Services.AddUserAccessTokenHttpClient("callApi",
configureClient: client => client.BaseAddress = new Uri("https://localhost:5010/"));
// General blazor services
builder.Services.AddRazorComponents()
_ = builder.Services.AddRazorComponents()
.AddInteractiveServerComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddCascadingAuthenticationState();
_ = builder.Services.AddCascadingAuthenticationState();
// Service used by the sample to describe where code is running
builder.Services.AddScoped<IRenderModeContext, ServerRenderModeContext>();
_ = builder.Services.AddScoped<IRenderModeContext, ServerRenderModeContext>();
builder.Services.AddAuthentication(options =>
_ = builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
@ -72,7 +72,7 @@ builder.Services.AddAuthentication(options =>
var app = builder.Build();
app.UseHttpLogging();
_ = app.UseHttpLogging();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
@ -81,25 +81,25 @@ if (app.Environment.IsDevelopment())
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
_ = app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
_ = app.UseHsts();
}
app.UseHttpsRedirection();
_ = app.UseHttpsRedirection();
app.UseStaticFiles();
_ = app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseBff();
app.UseAuthorization();
app.UseAntiforgery();
_ = app.UseRouting();
_ = app.UseAuthentication();
_ = app.UseBff();
_ = app.UseAuthorization();
_ = app.UseAntiforgery();
app.MapRemoteBffApiEndpoint("/remote-apis/user-token", new Uri("https://localhost:5010"))
_ = app.MapRemoteBffApiEndpoint("/remote-apis/user-token", new Uri("https://localhost:5010"))
.WithAccessToken(RequiredTokenType.User);
app.MapRazorComponents<App>()
_ = app.MapRazorComponents<App>()
.AddInteractiveServerRenderMode()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(Hosts.Bff.Blazor.PerComponent.Client._Imports).Assembly);

View file

@ -11,12 +11,12 @@ var builder = WebApplication.CreateBuilder(args);
builder.AddServiceDefaults();
builder.Services.AddRazorComponents()
_ = builder.Services.AddRazorComponents()
.AddInteractiveWebAssemblyComponents();
builder.Services.AddBff();
_ = builder.Services.AddBff();
builder.Services.AddAuthentication(options =>
_ = builder.Services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
@ -48,10 +48,10 @@ builder.Services.AddAuthentication(options =>
options.Scope.Add("api");
options.Scope.Add("offline_access");
});
builder.Services.AddCascadingAuthenticationState();
builder.Services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
_ = builder.Services.AddCascadingAuthenticationState();
_ = builder.Services.AddScoped<AuthenticationStateProvider, ServerAuthenticationStateProvider>();
builder.Services.AddAuthorization();
_ = builder.Services.AddAuthorization();
var app = builder.Build();
@ -62,24 +62,24 @@ if (app.Environment.IsDevelopment())
}
else
{
app.UseExceptionHandler("/Error", createScopeForErrors: true);
_ = app.UseExceptionHandler("/Error", createScopeForErrors: true);
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
_ = app.UseHsts();
}
app.UseHttpLogging();
_ = app.UseHttpLogging();
app.UseHttpsRedirection();
_ = app.UseHttpsRedirection();
app.UseStaticFiles();
_ = app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseBff();
app.UseAuthorization();
app.UseAntiforgery();
_ = app.UseRouting();
_ = app.UseAuthentication();
_ = app.UseBff();
_ = app.UseAuthorization();
_ = app.UseAntiforgery();
app.MapRazorComponents<App>()
_ = app.MapRazorComponents<App>()
.AddInteractiveWebAssemblyRenderMode()
.AddAdditionalAssemblies(typeof(Hosts.Bff.Blazor.WebAssembly.Client._Imports).Assembly);

View file

@ -1,5 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<PropertyGroup>
<!-- This line is needed because:
@ -17,9 +18,8 @@
we do need this check, we're setting it here as well.
-->
<AnalysisMode>None</AnalysisMode>
<TargetFramework>net10.0</TargetFramework>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
</PropertyGroup>
<Import Project="../../hosts_and_clients.props" />
</Project>

View file

@ -5,6 +5,7 @@
<Nullable>enable</Nullable>
<IsAspireHost>true</IsAspireHost>
<UserSecretsId>616547e2-3a28-4c9d-8685-f4ac02581162</UserSecretsId>
</PropertyGroup>
<ItemGroup>

View file

@ -20,7 +20,7 @@ internal static class Extensions
var yarpBuilder = services.AddReverseProxy()
.AddBffExtensions();
yarpBuilder.LoadFromMemory(
_ = yarpBuilder.LoadFromMemory(
new[]
{
new RouteConfig()
@ -79,7 +79,7 @@ internal static class Extensions
});
// Add BFF services to DI - also add server-side session management
services.AddBff(options =>
_ = services.AddBff(options =>
{
var rsaKey = new RsaSecurityKey(RSA.Create(2048));
var jwkKey = JsonWebKeyConverter.ConvertFromSecurityKey(rsaKey);
@ -91,10 +91,10 @@ internal static class Extensions
.AddServerSideSessions();
// local APIs
services.AddControllers();
_ = services.AddControllers();
// cookie options
services.AddAuthentication(options =>
_ = services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
@ -136,7 +136,7 @@ internal static class Extensions
options.Scope.Add("offline_access");
});
services.AddUserAccessTokenHttpClient("api",
_ = services.AddUserAccessTokenHttpClient("api",
configureClient: client =>
{
client.BaseAddress = new Uri("https://localhost:5011/api");
@ -147,23 +147,23 @@ internal static class Extensions
public static WebApplication ConfigurePipeline(this WebApplication app)
{
app.UseHttpLogging();
app.UseDeveloperExceptionPage();
_ = app.UseHttpLogging();
_ = app.UseDeveloperExceptionPage();
app.UseDefaultFiles();
app.UseStaticFiles();
_ = app.UseDefaultFiles();
_ = app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
_ = app.UseAuthentication();
_ = app.UseRouting();
// adds antiforgery protection for local APIs
app.UseBff();
_ = app.UseBff();
// adds authorization for local and remote API endpoints
app.UseAuthorization();
_ = app.UseAuthorization();
// local APIs
app.MapControllers()
_ = app.MapControllers()
.RequireAuthorization()
.AsBffApiEndpoint();
@ -171,9 +171,9 @@ internal static class Extensions
app.MapBffManagementEndpoints();
// proxy endpoints using yarp
app.MapReverseProxy(proxyApp =>
_ = app.MapReverseProxy(proxyApp =>
{
proxyApp.UseAntiforgeryCheck();
_ = proxyApp.UseAntiforgeryCheck();
});
// proxy endpoints using BFF's simplified wrapper
@ -184,22 +184,22 @@ internal static class Extensions
private static void MapRemoteUrls(IEndpointRouteBuilder app)
{
// On this path, we use a client credentials token
app.MapRemoteBffApiEndpoint("/api/client-token", new Uri("https://localhost:5011"))
_ = app.MapRemoteBffApiEndpoint("/api/client-token", new Uri("https://localhost:5011"))
.WithAccessToken(RequiredTokenType.Client);
// On this path, we use a user token if logged in, and fall back to a client credentials token if not
app.MapRemoteBffApiEndpoint("/api/user-or-client-token", new Uri("https://localhost:5011"))
_ = app.MapRemoteBffApiEndpoint("/api/user-or-client-token", new Uri("https://localhost:5011"))
.WithAccessToken(RequiredTokenType.UserOrClient);
// On this path, we make anonymous requests
app.MapRemoteBffApiEndpoint("/api/anonymous", new Uri("https://localhost:5011"));
_ = app.MapRemoteBffApiEndpoint("/api/anonymous", new Uri("https://localhost:5011"));
// On this path, we use the client token only if the user is logged in
app.MapRemoteBffApiEndpoint("/api/optional-user-token", new Uri("https://localhost:5011"))
_ = app.MapRemoteBffApiEndpoint("/api/optional-user-token", new Uri("https://localhost:5011"))
.WithAccessToken(RequiredTokenType.UserOrNone);
// On this path, we require the user token
app.MapRemoteBffApiEndpoint("/api/user-token", new Uri("https://localhost:5011"))
_ = app.MapRemoteBffApiEndpoint("/api/user-token", new Uri("https://localhost:5011"))
.WithAccessToken();
}
}

View file

@ -17,12 +17,12 @@ internal static class Extensions
{
var services = builder.Services;
var configuration = builder.Configuration;
services.AddDataProtection()
_ = services.AddDataProtection()
.SetApplicationName("JS-EF-Sample");
// Add BFF services to DI - also add server-side session management
var cn = configuration.GetConnectionString("db");
services.AddBff(options =>
_ = services.AddBff(options =>
{
options.BackchannelLogoutAllUserSessions = true;
})
@ -30,15 +30,15 @@ internal static class Extensions
.AddEntityFrameworkServerSideSessions(options =>
{
//options.UseSqlServer(cn);
options.UseSqlite(cn, opt => opt.MigrationsAssembly(typeof(UserSessions).Assembly.FullName));
_ = options.UseSqlite(cn, opt => opt.MigrationsAssembly(typeof(UserSessions).Assembly.FullName));
})
.AddSessionCleanupBackgroundProcess();
// local APIs
services.AddControllers();
_ = services.AddControllers();
// cookie options
services.AddAuthentication(options =>
_ = services.AddAuthentication(options =>
{
options.DefaultScheme = "cookie";
options.DefaultChallengeScheme = "oidc";
@ -79,24 +79,24 @@ internal static class Extensions
public static WebApplication ConfigurePipeline(this WebApplication app)
{
app.UseHttpLogging();
app.UseDeveloperExceptionPage();
_ = app.UseHttpLogging();
_ = app.UseDeveloperExceptionPage();
app.UseDefaultFiles();
app.UseStaticFiles();
_ = app.UseDefaultFiles();
_ = app.UseStaticFiles();
app.UseAuthentication();
app.UseRouting();
_ = app.UseAuthentication();
_ = app.UseRouting();
// adds antiforgery protection for local APIs
app.UseBff();
_ = app.UseBff();
// adds authorization for local and remote API endpoints
app.UseAuthorization();
_ = app.UseAuthorization();
// local APIs
app.MapControllers()
_ = app.MapControllers()
.RequireAuthorization()
.AsBffApiEndpoint();
@ -104,7 +104,7 @@ internal static class Extensions
// all calls to /api/* will be forwarded to the remote API
// user or client access token will be attached in API call
// user access token will be managed automatically using the refresh token
app.MapRemoteBffApiEndpoint("/api", new Uri("https://localhost:5010"))
_ = app.MapRemoteBffApiEndpoint("/api", new Uri("https://localhost:5010"))
.WithAccessToken(RequiredTokenType.UserOrClient);
return app;

View file

@ -1,16 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<RootNamespace>Bff.EF</RootNamespace>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Authentication.OpenIdConnect" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\migrations\UserSessionDb\UserSessionDb.csproj" />
<ProjectReference Include="..\..\src\Bff.EntityFramework\Bff.EntityFramework.csproj" />
@ -19,5 +16,4 @@
<ItemGroup>
<ProjectReference Include="..\Hosts.ServiceDefaults\Hosts.ServiceDefaults.csproj" />
</ItemGroup>
</Project>

View file

@ -1,19 +1,15 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Duende.IdentityServer" />
<PackageReference Include="Duende.IdentityModel" />
<PackageReference Include="Duende.IdentityServer" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\..\bff\hosts\Hosts.ServiceDefaults\Hosts.ServiceDefaults.csproj" />
<ProjectReference Include="..\..\src\Bff.Yarp\Bff.Yarp.csproj" />
</ItemGroup>
</Project>

View file

@ -16,21 +16,21 @@ var builder = Host.CreateApplicationBuilder();
if (startupConfiguration.IsServiceEnabled("api"))
{
builder.Services.Configure<ApiSettings>(builder.Configuration);
builder.Services.AddHostedService<ApiHostedService>();
_ = builder.Services.Configure<ApiSettings>(builder.Configuration);
_ = builder.Services.AddHostedService<ApiHostedService>();
}
if (startupConfiguration.IsServiceEnabled("idsrv"))
{
builder.Services.Configure<IdentityServerSettings>(builder.Configuration);
builder.Services.AddHostedService<IdentityServerService>();
_ = builder.Services.Configure<IdentityServerSettings>(builder.Configuration);
_ = builder.Services.AddHostedService<IdentityServerService>();
}
if (startupConfiguration.IsServiceEnabled("bff"))
{
builder.Services.Configure<BffSettings>(builder.Configuration);
builder.Services.AddHostedService<SingleFrontendBffService>();
builder.Services.AddHostedService<MultiFrontendBffService>();
_ = builder.Services.Configure<BffSettings>(builder.Configuration);
_ = builder.Services.AddHostedService<SingleFrontendBffService>();
_ = builder.Services.AddHostedService<MultiFrontendBffService>();
}
// Add services to the container.

View file

@ -12,16 +12,16 @@ public class ApiHostedService(IOptions<ApiSettings> apiSettings) : BackgroundSer
protected override Task ExecuteAsync(Ct stoppingToken)
{
var builder = WebApplication.CreateBuilder();
builder.AddServiceDefaults();
_ = builder.AddServiceDefaults();
// Configure Kestrel to listen on the specified Uri
builder.WebHost.UseUrls(Settings.ApiUrl.ToString());
_ = builder.WebHost.UseUrls(Settings.ApiUrl.ToString());
var app = builder.Build();
app.UseRouting();
_ = app.UseRouting();
app.MapGet("/", () => "ok");
_ = app.MapGet("/", () => "ok");
return app.RunAsync(stoppingToken);
}

View file

@ -23,18 +23,18 @@ public abstract class BffService(string[] urlConfigKeys, IConfiguration config,
.ToArray();
var builder = WebApplication.CreateBuilder();
builder.AddServiceDefaults();
_ = builder.AddServiceDefaults();
// Configure Kestrel to listen on the specified Uri
builder.WebHost.UseUrls(urls);
_ = builder.WebHost.UseUrls(urls);
builder.Services.Configure<ForwardedHeadersOptions>(options =>
_ = builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownProxies.Clear();
options.KnownIPNetworks.Clear();
});
builder.Services.AddAuthorization();
_ = builder.Services.AddAuthorization();
ConfigureServices(builder.Services);
var bffBuilder = builder.Services.AddBff()
@ -44,35 +44,35 @@ public abstract class BffService(string[] urlConfigKeys, IConfiguration config,
// Build and run the web app
var app = builder.Build();
app.UseForwardedHeaders();
_ = app.UseForwardedHeaders();
app.UseHttpsRedirection();
_ = app.UseHttpsRedirection();
app.UseRouting();
_ = app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
_ = app.UseAuthentication();
_ = app.UseAuthorization();
app.UseBff();
_ = app.UseBff();
ConfigureApp(app);
app.MapGet("/local_anon", () => DateTime.Now.ToString("s"))
_ = app.MapGet("/local_anon", () => DateTime.Now.ToString("s"))
.AsBffApiEndpoint()
.AllowAnonymous();
app.MapGet("/local", () => DateTime.Now.ToString("s"))
_ = app.MapGet("/local", () => DateTime.Now.ToString("s"))
.RequireAuthorization()
.AsBffApiEndpoint();
app.MapRemoteBffApiEndpoint("/remote_anon", Settings.ApiUrl)
_ = app.MapRemoteBffApiEndpoint("/remote_anon", Settings.ApiUrl)
.WithAccessToken(RequiredTokenType.None);
app.MapRemoteBffApiEndpoint("/remote_user", Settings.ApiUrl)
_ = app.MapRemoteBffApiEndpoint("/remote_user", Settings.ApiUrl)
.WithAccessToken();
app.MapRemoteBffApiEndpoint("/remote_client", Settings.ApiUrl)
_ = app.MapRemoteBffApiEndpoint("/remote_client", Settings.ApiUrl)
.WithAccessToken(RequiredTokenType.Client);
// Todo: Make sure this is mapped implicitly

View file

@ -20,19 +20,19 @@ public class IdentityServerService(IOptions<IdentityServerSettings> settings, IC
protected override Task ExecuteAsync(Ct stoppingToken)
{
var builder = WebApplication.CreateBuilder();
builder.AddServiceDefaults();
_ = builder.AddServiceDefaults();
// Configure Kestrel to listen on the specified Uri
builder.WebHost.UseUrls(Settings.IdentityServerUrl);
_ = builder.WebHost.UseUrls(Settings.IdentityServerUrl);
builder.Services.Configure<ForwardedHeadersOptions>(options =>
_ = builder.Services.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.All;
options.KnownProxies.Clear();
options.KnownIPNetworks.Clear();
});
builder.Services.AddAuthorization();
builder.Services.AddHttpLogging();
_ = builder.Services.AddAuthorization();
_ = builder.Services.AddHttpLogging();
var isBuilder = builder.Services.AddIdentityServer(options =>
{
@ -61,8 +61,8 @@ public class IdentityServerService(IOptions<IdentityServerSettings> settings, IC
;
// in-memory, code config
isBuilder.AddInMemoryIdentityResources(Config.IdentityResources);
isBuilder.AddInMemoryApiScopes(Config.ApiScopes);
_ = isBuilder.AddInMemoryIdentityResources(Config.IdentityResources);
_ = isBuilder.AddInMemoryApiScopes(Config.ApiScopes);
var bffUrls = config.AsEnumerable()
.Where(x => x.Key.StartsWith("BFFURL"))
@ -77,7 +77,7 @@ public class IdentityServerService(IOptions<IdentityServerSettings> settings, IC
bffUrls.Add(bffUrl2 + "/path" + i);
}
isBuilder.AddInMemoryClients([new Client
_ = isBuilder.AddInMemoryClients([new Client
{
ClientId = "bff.perf",
ClientSecrets = [new Secret("secret".Sha256())],
@ -96,22 +96,22 @@ public class IdentityServerService(IOptions<IdentityServerSettings> settings, IC
AccessTokenLifetime = 15 // Force refresh
}]);
isBuilder.AddInMemoryApiResources(Config.ApiResources);
_ = isBuilder.AddInMemoryApiResources(Config.ApiResources);
var app = builder.Build();
app.UseForwardedHeaders();
_ = app.UseForwardedHeaders();
app.UseHttpLogging();
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
_ = app.UseHttpLogging();
_ = app.UseDeveloperExceptionPage();
_ = app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
_ = app.UseRouting();
_ = app.UseIdentityServer();
_ = app.UseAuthorization();
app.MapGet("/", () => "identity server");
_ = app.MapGet("/", () => "identity server");
app.MapGet("/account/login", async ctx =>
_ = app.MapGet("/account/login", async ctx =>
{
var props = new AuthenticationProperties();
await ctx.SignInAsync(new ClaimsPrincipal(new ClaimsIdentity(
@ -122,7 +122,7 @@ public class IdentityServerService(IOptions<IdentityServerSettings> settings, IC
"login", "name", "role")), props);
});
app.MapGet("/account/logout", async ctx =>
_ = app.MapGet("/account/logout", async ctx =>
{
// signout as if the user were prompted
await ctx.SignOutAsync();

View file

@ -16,7 +16,7 @@ public class MultiFrontendBffService(IConfiguration config, IOptions<BffSettings
public override void ConfigureBff(IBffServicesBuilder bff)
{
bff.ConfigureOpenIdConnect(o => DefaultOpenIdConfiguration.Apply(o, Settings))
_ = bff.ConfigureOpenIdConnect(o => DefaultOpenIdConfiguration.Apply(o, Settings))
.AddFrontends(new BffFrontend(BffFrontendName.Parse("default")))
// Note, in order for this to work, we'll need to inject this as config
@ -26,7 +26,7 @@ public class MultiFrontendBffService(IConfiguration config, IOptions<BffSettings
for (var i = 0; i < 100; i++)
{
bff.AddFrontends(
_ = bff.AddFrontends(
new BffFrontend(BffFrontendName.Parse("bff-with-path-" + i))
.MapToPath("/path" + i));
}
@ -35,4 +35,3 @@ public class MultiFrontendBffService(IConfiguration config, IOptions<BffSettings
public override void ConfigureApp(WebApplication app) => app.MapGet("/", (CurrentFrontendAccessor currentFrontendAccessor) => "multi - " + currentFrontendAccessor.Get().Name);
}

View file

@ -8,7 +8,7 @@ internal static class Extensions
{
public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
{
builder.Services.AddRazorPages();
_ = builder.Services.AddRazorPages();
var isBuilder = builder.Services.AddIdentityServer(options =>
{
@ -22,25 +22,25 @@ internal static class Extensions
.AddTestUsers(TestUsers.Users);
// in-memory, code config
isBuilder.AddInMemoryIdentityResources(Config.IdentityResources);
isBuilder.AddInMemoryApiScopes(Config.ApiScopes);
isBuilder.AddInMemoryClients(Config.Clients);
isBuilder.AddInMemoryApiResources(Config.ApiResources);
isBuilder.AddExtensionGrantValidator<TokenExchangeGrantValidator>();
_ = isBuilder.AddInMemoryIdentityResources(Config.IdentityResources);
_ = isBuilder.AddInMemoryApiScopes(Config.ApiScopes);
_ = isBuilder.AddInMemoryClients(Config.Clients);
_ = isBuilder.AddInMemoryApiResources(Config.ApiResources);
_ = isBuilder.AddExtensionGrantValidator<TokenExchangeGrantValidator>();
return builder.Build();
}
public static WebApplication ConfigurePipeline(this WebApplication app)
{
app.UseHttpLogging();
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
_ = app.UseHttpLogging();
_ = app.UseDeveloperExceptionPage();
_ = app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
app.MapRazorPages()
_ = app.UseRouting();
_ = app.UseIdentityServer();
_ = app.UseAuthorization();
_ = app.MapRazorPages()
.RequireAuthorization();
return app;

View file

@ -1,17 +1,13 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
<ImplicitUsings>true</ImplicitUsings>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Duende.IdentityServer" />
<PackageReference Include="Duende.IdentityModel" />
<PackageReference Include="Duende.IdentityServer" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Hosts.ServiceDefaults\Hosts.ServiceDefaults.csproj" />
</ItemGroup>
</Project>

View file

@ -123,7 +123,7 @@ public class Index : PageModel
if (grantedConsent != null)
{
// communicate outcome of consent back to identityserver
await _interaction.HandleRequestAsync(Input.UserCode, grantedConsent);
_ = await _interaction.HandleRequestAsync(Input.UserCode, grantedConsent);
// indicate that's it ok to redirect back to authorization endpoint
return RedirectToPage("/Device/Success");

View file

@ -75,7 +75,7 @@ public class Callback : PageModel
//
// remove the user id claim so we don't include it as an extra claim if/when we provision the user
var claims = externalUser.Claims.ToList();
claims.Remove(userIdClaim);
_ = claims.Remove(userIdClaim);
user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList());
}

View file

@ -23,25 +23,25 @@ public static class Extensions
{
public static TBuilder AddServiceDefaults<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
builder.ConfigureOpenTelemetry();
_ = builder.ConfigureOpenTelemetry();
builder.AddDefaultHealthChecks();
_ = builder.AddDefaultHealthChecks();
builder.Services.AddHttpLogging();
_ = builder.Services.AddHttpLogging();
builder.Services.AddServiceDiscovery();
_ = builder.Services.AddServiceDiscovery();
builder.Services.ConfigureHttpClientDefaults(http =>
_ = builder.Services.ConfigureHttpClientDefaults(http =>
{
// Turn on resilience by default
http.AddStandardResilienceHandler();
_ = http.AddStandardResilienceHandler();
// Turn on service discovery by default
http.AddServiceDiscovery();
_ = http.AddServiceDiscovery();
});
// Uncomment the following to restrict the allowed schemes for service discovery.
builder.Services.Configure<ServiceDiscoveryOptions>(options =>
_ = builder.Services.Configure<ServiceDiscoveryOptions>(options =>
{
options.AllowedSchemes = ["https"];
});
@ -51,23 +51,23 @@ public static class Extensions
public static TBuilder ConfigureOpenTelemetry<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
builder.Logging.AddOpenTelemetry(logging =>
_ = builder.Logging.AddOpenTelemetry(logging =>
{
logging.IncludeFormattedMessage = true;
logging.IncludeScopes = true;
});
builder.Services.AddOpenTelemetry()
_ = builder.Services.AddOpenTelemetry()
.WithMetrics(metrics =>
{
metrics.AddAspNetCoreInstrumentation()
_ = metrics.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation()
.AddRuntimeInstrumentation()
.AddMeter(BffMetrics.MeterName);
})
.WithTracing(tracing =>
{
tracing.AddSource(builder.Environment.ApplicationName)
_ = tracing.AddSource(builder.Environment.ApplicationName)
.AddAspNetCoreInstrumentation()
.AddHttpClientInstrumentation();
});
@ -81,7 +81,7 @@ public static class Extensions
if (useOtlpExporter)
{
builder.Services.AddOpenTelemetry().UseOtlpExporter();
_ = builder.Services.AddOpenTelemetry().UseOtlpExporter();
}
// Uncomment the following lines to enable the Azure Monitor exporter (requires the Azure.Monitor.OpenTelemetry.AspNetCore package)
@ -97,7 +97,7 @@ public static class Extensions
public static TBuilder AddDefaultHealthChecks<TBuilder>(this TBuilder builder) where TBuilder : IHostApplicationBuilder
{
// Add a default liveness check to ensure app is responsive
builder.Services.AddHealthChecks()
_ = builder.Services.AddHealthChecks()
.AddCheck("self", () => HealthCheckResult.Healthy(), ["live"]);
return builder;
@ -110,10 +110,10 @@ public static class Extensions
if (app.Environment.IsDevelopment())
{
// All health checks must pass for app to be considered ready to accept traffic after starting
app.MapHealthChecks("/health");
_ = app.MapHealthChecks("/health");
// Only health checks tagged with the "live" tag must pass for app to be considered alive
app.MapHealthChecks("/alive", new HealthCheckOptions
_ = app.MapHealthChecks("/alive", new HealthCheckOptions
{
Predicate = r => r.Tags.Contains("live")
});

View file

@ -12,9 +12,9 @@ internal static class Extensions
{
var services = builder.Services;
services.AddControllers();
_ = services.AddControllers();
services.AddAuthentication("token")
_ = services.AddAuthentication("token")
.AddJwtBearer("token", options =>
{
options.Authority = "https://localhost:5001";
@ -31,18 +31,18 @@ internal static class Extensions
});
// layers DPoP onto the "token" scheme above
services.ConfigureDPoPTokensForScheme("token");
_ = services.ConfigureDPoPTokensForScheme("token");
services.AddAuthorization(options =>
_ = services.AddAuthorization(options =>
{
options.AddPolicy("ApiCaller", policy =>
{
policy.RequireClaim("scope", "api");
_ = policy.RequireClaim("scope", "api");
});
options.AddPolicy("RequireInteractiveUser", policy =>
{
policy.RequireClaim("sub");
_ = policy.RequireClaim("sub");
});
});
return builder.Build();
@ -64,21 +64,20 @@ internal static class Extensions
// ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
// });
app.UseHttpLogging();
_ = app.UseHttpLogging();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
_ = app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
_ = app.UseRouting();
_ = app.UseAuthentication();
_ = app.UseAuthorization();
app.MapControllers()
_ = app.MapControllers()
.RequireAuthorization("ApiCaller");
return app;
}
}

View file

@ -1,13 +1,11 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFrameworks>net10.0</TargetFrameworks>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Duende.IdentityModel" />
<PackageReference Include="Duende.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Duende.IdentityModel" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Hosts.ServiceDefaults\Hosts.ServiceDefaults.csproj" />
</ItemGroup>

View file

@ -79,7 +79,7 @@ public class ToDoController : ControllerBase
return NotFound();
}
Data.Remove(item);
_ = Data.Remove(item);
_logger.LogInformation("Delete {id}", id);
return NoContent();

View file

@ -11,9 +11,9 @@ internal static class Extensions
public static WebApplication ConfigureServices(this WebApplicationBuilder builder)
{
var services = builder.Services;
services.AddControllers();
_ = services.AddControllers();
services.AddAuthentication("token")
_ = services.AddAuthentication("token")
.AddJwtBearer("token", options =>
{
options.Authority = "https://localhost:5001";
@ -30,16 +30,16 @@ internal static class Extensions
};
});
services.AddAuthorization(options =>
_ = services.AddAuthorization(options =>
{
options.AddPolicy("ApiCaller", policy =>
{
policy.RequireClaim("scope", "scope-for-isolated-api");
_ = policy.RequireClaim("scope", "scope-for-isolated-api");
});
options.AddPolicy("RequireInteractiveUser", policy =>
{
policy.RequireClaim("sub");
_ = policy.RequireClaim("sub");
});
});
return builder.Build();
@ -47,23 +47,23 @@ internal static class Extensions
public static WebApplication ConfigurePipeline(this WebApplication app)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
_ = app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
});
app.UseHttpLogging();
_ = app.UseHttpLogging();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
_ = app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
_ = app.UseRouting();
_ = app.UseAuthentication();
_ = app.UseAuthorization();
app.MapControllers()
_ = app.MapControllers()
.RequireAuthorization("ApiCaller");
return app;
}

View file

@ -79,7 +79,7 @@ public class ToDoController : ControllerBase
return NotFound();
}
Data.Remove(item);
_ = Data.Remove(item);
_logger.LogInformation("Delete {id}", id);
return NoContent();

View file

@ -12,9 +12,9 @@ internal static class Extensions
{
var services = builder.Services;
services.AddControllers();
_ = services.AddControllers();
services.AddAuthentication("token")
_ = services.AddAuthentication("token")
.AddJwtBearer("token", options =>
{
options.Authority = "https://localhost:5001";
@ -30,16 +30,16 @@ internal static class Extensions
};
});
services.AddAuthorization(options =>
_ = services.AddAuthorization(options =>
{
options.AddPolicy("ApiCaller", policy =>
{
policy.RequireClaim("scope", "api");
_ = policy.RequireClaim("scope", "api");
});
options.AddPolicy("RequireInteractiveUser", policy =>
{
policy.RequireClaim("sub");
_ = policy.RequireClaim("sub");
});
});
@ -48,33 +48,32 @@ internal static class Extensions
public static WebApplication ConfigurePipeline(this WebApplication app)
{
app.UseForwardedHeaders(new ForwardedHeadersOptions
_ = app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto | ForwardedHeaders.XForwardedHost,
});
app.UseHttpLogging();
_ = app.UseHttpLogging();
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
_ = app.UseDeveloperExceptionPage();
}
app.Map("/static", inner =>
_ = app.Map("/static", inner =>
{
inner.UseDefaultFiles();
inner.UseStaticFiles();
_ = inner.UseDefaultFiles();
_ = inner.UseStaticFiles();
});
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
_ = app.UseRouting();
_ = app.UseAuthentication();
_ = app.UseAuthorization();
app.MapControllers()
_ = app.MapControllers()
.RequireAuthorization("ApiCaller");
return app;
}
}

View file

@ -79,7 +79,7 @@ public class ToDoController : ControllerBase
return NotFound();
}
Data.Remove(item);
_ = Data.Remove(item);
_logger.LogInformation("Delete {id}", id);
return NoContent();

View file

@ -1,11 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<PropertyGroup>
<AnalysisMode>None</AnalysisMode>
<RootNamespace>$(AssemblyName)</RootNamespace>
<AssemblyName>Duende.$(MSBuildProjectName)</AssemblyName>
<TargetFramework>net10.0</TargetFramework>
<WarnOnPackingNonPackableProject>false</WarnOnPackingNonPackableProject>
</PropertyGroup>
<Import Project="../../hosts_and_clients.props" />
</Project>
</Project>

View file

@ -0,0 +1,2 @@
[*.cs]
dotnet_diagnostic.IDE0058.severity = none

View file

@ -25,6 +25,6 @@ public class Program
Host.CreateDefaultBuilder(args)
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
_ = webBuilder.UseStartup<Startup>();
});
}

View file

@ -16,10 +16,10 @@ public class Startup
{
var cn = Configuration.GetConnectionString("db");
services.AddDbContext<SessionDbContext>(options =>
_ = services.AddDbContext<SessionDbContext>(options =>
{
//options.UseSqlServer(cn, dbOpts => dbOpts.MigrationsAssembly(typeof(Startup).Assembly.FullName));
options.UseSqlite(cn, dbOpts => dbOpts.MigrationsAssembly(typeof(Startup).Assembly.FullName));
_ = options.UseSqlite(cn, dbOpts => dbOpts.MigrationsAssembly(typeof(Startup).Assembly.FullName));
});
}

View file

@ -1,20 +1,16 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" />
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Bff.EntityFramework\Bff.EntityFramework.csproj" />
</ItemGroup>
</Project>

View file

@ -116,10 +116,10 @@ public class BenchmarkConfig : ManualConfig
sizeUnit: SizeUnit.KB
));
AddJob(Job.LongRun);
AddColumnProvider(DefaultColumnProviders.Instance);
AddExporter(exporter);
AddLogger(new ConsoleLogger()); // Add a minimal logger back if you want basic running info
_ = AddJob(Job.LongRun);
_ = AddColumnProvider(DefaultColumnProviders.Instance);
_ = AddExporter(exporter);
_ = AddLogger(new ConsoleLogger()); // Add a minimal logger back if you want basic running info
}
}

View file

@ -1,5 +1,4 @@
<Project Sdk="Microsoft.NET.Sdk">
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<OutputType>Exe</OutputType>
@ -7,24 +6,20 @@
<Nullable>enable</Nullable>
<WarningsAsErrors>false</WarningsAsErrors>
</PropertyGroup>
<ItemGroup>
<FrameworkReference Include="Microsoft.AspNetCore.App" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Duende.IdentityServer" />
<PackageReference Include="Duende.IdentityModel" />
<PackageReference Include="BenchmarkDotNet" />
<PackageReference Include="Duende.IdentityModel" />
<PackageReference Include="Duende.IdentityServer" />
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" />
<PackageReference Include="Microsoft.AspNetCore.TestHost" />
<PackageReference Include="Serilog.Extensions.Logging" />
<PackageReference Include="Serilog.Sinks.File" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\Bff.Yarp\Bff.Yarp.csproj" />
<ProjectReference Include="..\..\src\Bff\Bff.csproj" />
</ItemGroup>
</Project>

View file

@ -12,7 +12,7 @@ public class ApiHost : Host
{
OnConfigureServices += services =>
{
services.AddAuthentication("token")
_ = services.AddAuthentication("token")
.AddJwtBearer("token", options =>
{
options.Authority = identityServerUri.ToString();
@ -23,17 +23,17 @@ public class ApiHost : Host
OnConfigure += app =>
{
app.Use(async (c, n) =>
_ = app.Use(async (c, n) =>
{
await n();
});
app.UseRouting();
_ = app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
_ = app.UseAuthentication();
_ = app.UseAuthorization();
app.MapGet("{**catch-all}", () => "ok");
_ = app.MapGet("{**catch-all}", () => "ok");
};

View file

@ -53,27 +53,27 @@ public class BffHost : Host
.AddRemoteApis();
if (!Internet.UseKestrel)
{
services.AddSingleton<IForwarderHttpClientFactory>(new SimulatedInternetYarpForwarderFactory(Internet));
_ = services.AddSingleton<IForwarderHttpClientFactory>(new SimulatedInternetYarpForwarderFactory(Internet));
}
OnConfigureBff(bff);
};
OnConfigure += app =>
{
app.UseRouting();
_ = app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
_ = app.UseAuthentication();
_ = app.UseAuthorization();
app.UseBff();
app.MapGet("/", () => "bff");
app.MapGet("/anon", () => "bff")
_ = app.UseBff();
_ = app.MapGet("/", () => "bff");
_ = app.MapGet("/anon", () => "bff")
.AllowAnonymous();
app.MapRemoteBffApiEndpoint("/allow_anon", apiUri);
app.MapRemoteBffApiEndpoint("/client_token", apiUri)
_ = app.MapRemoteBffApiEndpoint("/allow_anon", apiUri);
_ = app.MapRemoteBffApiEndpoint("/client_token", apiUri)
.WithAccessToken(RequiredTokenType.Client);
app.MapRemoteBffApiEndpoint("/user_token", apiUri)
_ = app.MapRemoteBffApiEndpoint("/user_token", apiUri)
.WithAccessToken(RequiredTokenType.User);
};
}

View file

@ -30,24 +30,24 @@ public abstract class Host : IAsyncDisposable
Internet = simulatedInternet;
_builder = WebApplication.CreateBuilder();
// Logs interfere with the benchmarks, so we clear them
_builder.Logging.ClearProviders();
_ = _builder.Logging.ClearProviders();
// Ensure dev certificate is used for SSL
if (Internet.UseKestrel)
{
_builder.WebHost.UseUrls("https://*:0");
_ = _builder.WebHost.UseUrls("https://*:0");
}
else
{
_builder.Logging.AddConsole();
_builder.WebHost
_ = _builder.Logging.AddConsole();
_ = _builder.WebHost
.UseTestServer();
}
_builder.Services.AddAuthentication();
_builder.Services.AddAuthorization();
_builder.Services.AddRouting();
_ = _builder.Services.AddAuthentication();
_ = _builder.Services.AddAuthorization();
_ = _builder.Services.AddRouting();
}
public T GetService<T>() where T : notnull => _app.Services.GetRequiredService<T>();

View file

@ -28,17 +28,17 @@ public class IdentityServerHost : Host
.AddInMemoryIdentityResources(IdentityResources)
.AddInMemoryApiScopes(ApiScopes);
identityServer.AddBackChannelLogoutHttpClient();
_ = identityServer.AddBackChannelLogoutHttpClient();
};
OnConfigure += app =>
{
app.UseRouting();
_ = app.UseRouting();
app.UseIdentityServer();
app.UseAuthorization();
_ = app.UseIdentityServer();
_ = app.UseAuthorization();
app.MapGet("/account/login", async ctx =>
_ = app.MapGet("/account/login", async ctx =>
{
await ctx.SignInAsync(UserToSignIn);
});

View file

@ -14,8 +14,8 @@ public class PlainYarpProxy : Host
{
OnConfigureServices += services =>
{
services.AddSingleton<IForwarderHttpClientFactory>(new SimulatedInternetYarpForwarderFactory(Internet));
services.AddReverseProxy()
_ = services.AddSingleton<IForwarderHttpClientFactory>(new SimulatedInternetYarpForwarderFactory(Internet));
_ = services.AddReverseProxy()
.LoadFromMemory(
[
new RouteConfig()
@ -42,8 +42,8 @@ public class PlainYarpProxy : Host
};
OnConfigure += app =>
{
app.UseRouting();
app.MapReverseProxy();
_ = app.UseRouting();
_ = app.MapReverseProxy();
};
}
}

View file

@ -22,7 +22,7 @@ internal class RoutingMessageHandler : HttpMessageHandler
{
var endpoint = new HostHandler(handler);
var host = $"{hostHeaderValue.Host}:{hostHeaderValue.Port}";
_hosts.TryAdd(host, endpoint);
_ = _hosts.TryAdd(host, endpoint);
}
protected override Task<HttpResponseMessage> SendAsync(

View file

@ -18,7 +18,7 @@ public class MultiFrontendBenchmarks : BenchmarkBase
_authenticatedBffClient = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
// Warm up the BFF Login
await _authenticatedBffClient.GetAsync("/bff/login")
_ = await _authenticatedBffClient.GetAsync("/bff/login")
.EnsureStatusCode();
_anonBffClient = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
@ -28,7 +28,7 @@ public class MultiFrontendBenchmarks : BenchmarkBase
public async Task MultiFrontend_login_to_default()
{
var client = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
await client.GetAsync("/bff/login")
_ = await client.GetAsync("/bff/login")
.EnsureStatusCode();
}
@ -36,7 +36,7 @@ public class MultiFrontendBenchmarks : BenchmarkBase
public async Task MultiFrontend_login_to_frontend_3()
{
var client = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
await client.GetAsync($"{GetPath(3)}/bff/login")
_ = await client.GetAsync($"{GetPath(3)}/bff/login")
.EnsureStatusCode();
}

View file

@ -25,7 +25,7 @@ public class MultiFrontendWarmedUpLoginBenchmarks : BenchmarkBase
{
var c = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
var path = GetPath(i);
await c.GetAsync(path + "/bff/login")
_ = await c.GetAsync(path + "/bff/login")
.EnsureStatusCode();
}
@ -39,10 +39,10 @@ public class MultiFrontendWarmedUpLoginBenchmarks : BenchmarkBase
var path = GetPath(_random.Next(0, PathsToTest - 1));
await client.GetAsync(path + "/bff/login")
_ = await client.GetAsync(path + "/bff/login")
.EnsureStatusCode();
await client.GetWithCSRF(path + "/user_token")
_ = await client.GetWithCSRF(path + "/user_token")
.EnsureStatusCode();
}

View file

@ -20,14 +20,14 @@ public class SingleFrontendBenchmarks : BenchmarkBase
_fixture = new SingleFrontendFixture();
_authenticatedBffClient = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
await _authenticatedBffClient.GetAsync("/bff/login")
_ = await _authenticatedBffClient.GetAsync("/bff/login")
.EnsureStatusCode();
_anonBffClient = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
_bffServerSideSessionsClient = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
await _bffServerSideSessionsClient.GetAsync("/bff/login")
_ = await _bffServerSideSessionsClient.GetAsync("/bff/login")
.EnsureStatusCode();
}
@ -56,7 +56,7 @@ public class SingleFrontendBenchmarks : BenchmarkBase
public async Task SingleFrontend_Login()
{
var client = _fixture.Internet.BuildHttpClient(_fixture.Bff.Url());
await client.GetAsync("/bff/login")
_ = await client.GetAsync("/bff/login")
.EnsureStatusCode();
}

View file

@ -21,7 +21,7 @@ public class CallAuthorizedLocalApi(Uri baseUri) : BaseScenario(baseUri.ToString
public override async Task Init(IScenarioInitContext c)
{
Client = TestClient.Create(baseUri);
await Client.TriggerLogin();
_ = await Client.TriggerLogin();
}
public override async Task<HttpResponseMessage> RunScenario(IScenarioContext context)

View file

@ -1,11 +1,17 @@
<?xml version="1.0" encoding="utf-8"?>
<Project>
<Import Project="$([MSBuild]::GetPathOfFileAbove('Directory.Build.props', '$(MSBuildThisFileDirectory)..\'))" />
<Import Project="../../src.props" />
<PropertyGroup>
<AnalysisMode>None</AnalysisMode>
<IsPackable>false</IsPackable>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
<!-- RS0026: Do not add multiple overloads with optional parameters -->
<NoWarn>$(NoWarn);RS0026</NoWarn>
<!-- RS0027: API with optional parameter(s) should have the most parameters amongst its public overloads -->
<NoWarn>$(NoWarn);RS0027</NoWarn>
</PropertyGroup>
</Project>

View file

@ -1,23 +1,18 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFramework>net10.0</TargetFramework>
<Nullable>enable</Nullable>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
<AssemblyName>Duende.BFF.Blazor.Client</AssemblyName>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
<PackageReference Include="Microsoft.Extensions.Http" />
<PackageReference Include="Duende.Private.Licensing" />
<PackageReference Include="Microsoft.AspNetCore.Components.Authorization" />
<PackageReference Include="Microsoft.AspNetCore.Components.WebAssembly" />
<PackageReference Include="Microsoft.Extensions.Http" />
</ItemGroup>
<ItemGroup>
<InternalsVisibleTo Include="Duende.Bff.Tests" />
<InternalsVisibleTo Include="DynamicProxyGenAssembly2" />
</ItemGroup>
</Project>

View file

@ -30,7 +30,7 @@ internal class BffClientAuthenticationStateProvider : AuthenticationStateProvide
ILogger<BffClientAuthenticationStateProvider> logger)
{
_fetchUserService = fetchUserService;
persistentUserService.GetPersistedUser(out var user);
_ = persistentUserService.GetPersistedUser(out var user);
_user = user;
_timer = timeProvider.CreateTimer(TimerCallback,
null,
@ -48,7 +48,7 @@ internal class BffClientAuthenticationStateProvider : AuthenticationStateProvide
}
finally
{
_semaphore.Release();
_ = _semaphore.Release();
}
}
@ -99,7 +99,7 @@ internal class BffClientAuthenticationStateProvider : AuthenticationStateProvide
}
finally
{
_semaphore.Release();
_ = _semaphore.Release();
}
}

View file

@ -21,10 +21,10 @@ public static class ServiceCollectionExtensions
{
if (configureAction != null)
{
services.Configure(configureAction);
_ = services.Configure(configureAction);
}
services
_ = services
.AddAuthorizationCore()
// Most services for wasm are singletons, because DI scope doesn't exist in wasm
.AddSingleton<PersistentUserService>()

View file

@ -17,9 +17,9 @@ public static class
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.AddActivatedSingleton<ServerSideSessionChecker>();
_ = builder.Services.AddActivatedSingleton<ServerSideSessionChecker>();
builder.Services
_ = builder.Services
.AddOpenIdConnectAccessTokenManagement()
.AddBlazorServerAccessTokenManagement<ServerSideTokenStore>()
.AddSingleton<IClaimsTransformation, AddServerManagementClaimsTransform>()
@ -28,7 +28,7 @@ public static class
if (configureOptions != null)
{
builder.Services.Configure(configureOptions);
_ = builder.Services.Configure(configureOptions);
}
return builder;

View file

@ -68,7 +68,7 @@ internal sealed class BffServerAuthenticationStateProvider : RevalidatingServerA
AuthenticationStateChanged += OnAuthenticationStateChanged;
_subscription = _state.RegisterOnPersisting(OnPersistingAsync, RenderMode.InteractiveWebAssembly);
licenseValidator.CheckLicense();
_ = licenseValidator.CheckLicense();
}
private void OnAuthenticationStateChanged(Task<AuthenticationState> task) => _authenticationStateTask = task;

View file

@ -46,7 +46,7 @@ public static class BffBuilderExtensions
where T : IBffServicesBuilder
{
ArgumentNullException.ThrowIfNull(bffBuilder);
bffBuilder.Services.AddDbContext<TContext>(action);
_ = bffBuilder.Services.AddDbContext<TContext>(action);
return bffBuilder.AddEntityFrameworkServerSideSessionsServices<TContext, T>();
}
@ -61,7 +61,7 @@ public static class BffBuilderExtensions
where T : IBffServicesBuilder
{
ArgumentNullException.ThrowIfNull(bffBuilder);
bffBuilder.Services.AddDbContext<TContext>(action);
_ = bffBuilder.Services.AddDbContext<TContext>(action);
return bffBuilder.AddEntityFrameworkServerSideSessionsServices<TContext, T>();
}
@ -76,9 +76,9 @@ public static class BffBuilderExtensions
where T : IBffServicesBuilder
{
ArgumentNullException.ThrowIfNull(bffBuilder);
bffBuilder.Services.AddTransient<IUserSessionStoreCleanup, UserSessionStore>();
bffBuilder.Services.AddTransient<ISessionDbContext>(svcs => svcs.GetRequiredService<TContext>());
bffBuilder.AddServerSideSessions<UserSessionStore>();
_ = bffBuilder.Services.AddTransient<IUserSessionStoreCleanup, UserSessionStore>();
_ = bffBuilder.Services.AddTransient<ISessionDbContext>(svcs => svcs.GetRequiredService<TContext>());
_ = bffBuilder.AddServerSideSessions<UserSessionStore>();
return bffBuilder;
}
@ -90,7 +90,7 @@ public static class BffBuilderExtensions
where T : IBffServicesBuilder
{
ArgumentNullException.ThrowIfNull(bffBuilder);
bffBuilder.Services.Configure(action);
_ = bffBuilder.Services.Configure(action);
return bffBuilder;
}
@ -98,8 +98,8 @@ public static class BffBuilderExtensions
where T : IBffServicesBuilder
{
ArgumentNullException.ThrowIfNull(bffBuilder);
bffBuilder.Services.AddSingleton<SessionCleanupHost>();
bffBuilder.Services.AddSingleton<IHostedService>(sp => sp.GetRequiredService<SessionCleanupHost>());
_ = bffBuilder.Services.AddSingleton<SessionCleanupHost>();
_ = bffBuilder.Services.AddSingleton<IHostedService>(sp => sp.GetRequiredService<SessionCleanupHost>());
return bffBuilder;
}

View file

@ -34,11 +34,11 @@ internal sealed class UserSessionStore(
var item = new UserSessionEntity();
session.CopyTo(item);
sessionDbContext.UserSessions.Add(item);
_ = sessionDbContext.UserSessions.Add(item);
try
{
await sessionDbContext.SaveChangesAsync(ct);
_ = await sessionDbContext.SaveChangesAsync(ct);
}
catch (DbUpdateException ex)
{
@ -84,10 +84,10 @@ internal sealed class UserSessionStore(
logger.DeletingUserSession(LogLevel.Debug, item.SubjectId, item.SessionId);
sessionDbContext.UserSessions.Remove(item);
_ = sessionDbContext.UserSessions.Remove(item);
try
{
await sessionDbContext.SaveChangesAsync(ct);
_ = await sessionDbContext.SaveChangesAsync(ct);
}
catch (DbUpdateConcurrencyException ex)
{
@ -135,7 +135,7 @@ internal sealed class UserSessionStore(
try
{
await sessionDbContext.SaveChangesAsync(ct);
_ = await sessionDbContext.SaveChangesAsync(ct);
}
catch (DbUpdateConcurrencyException ex)
{
@ -231,7 +231,7 @@ internal sealed class UserSessionStore(
logger.UpdatingUserSession(LogLevel.Debug, item.SubjectId, item.SessionId);
session.CopyTo(item);
await sessionDbContext.SaveChangesAsync(ct);
_ = await sessionDbContext.SaveChangesAsync(ct);
}
/// <inheritdoc/>
@ -263,7 +263,7 @@ internal sealed class UserSessionStore(
removed += found;
try
{
await sessionDbContext.SaveChangesAsync(ct);
_ = await sessionDbContext.SaveChangesAsync(ct);
}
catch (DbUpdateConcurrencyException ex)
{

View file

@ -30,24 +30,24 @@ public static class ModelBuilderExtensions
ArgumentNullException.ThrowIfNull(storeOptions);
if (!string.IsNullOrWhiteSpace(storeOptions.DefaultSchema))
{
modelBuilder.HasDefaultSchema(storeOptions.DefaultSchema);
_ = modelBuilder.HasDefaultSchema(storeOptions.DefaultSchema);
}
modelBuilder.Entity<UserSessionEntity>(entity =>
_ = modelBuilder.Entity<UserSessionEntity>(entity =>
{
entity.ToTable(storeOptions.UserSessions);
_ = entity.ToTable(storeOptions.UserSessions);
entity.HasKey(x => x.Id);
_ = entity.HasKey(x => x.Id);
entity.Property(x => x.PartitionKey).HasConversion<PartitionKeyConverter>().HasMaxLength(200);
entity.Property(x => x.Key).HasConversion<UserKeyConverter>().IsRequired().HasMaxLength(200);
entity.Property(x => x.SubjectId).IsRequired().HasMaxLength(200);
entity.Property(x => x.Ticket).IsRequired();
_ = entity.Property(x => x.PartitionKey).HasConversion<PartitionKeyConverter>().HasMaxLength(200);
_ = entity.Property(x => x.Key).HasConversion<UserKeyConverter>().IsRequired().HasMaxLength(200);
_ = entity.Property(x => x.SubjectId).IsRequired().HasMaxLength(200);
_ = entity.Property(x => x.Ticket).IsRequired();
entity.HasIndex(x => new { ApplicationName = x.PartitionKey, x.Key }).IsUnique();
entity.HasIndex(x => new { ApplicationName = x.PartitionKey, x.SubjectId, x.SessionId }).IsUnique();
entity.HasIndex(x => new { ApplicationName = x.PartitionKey, x.SessionId }).IsUnique();
entity.HasIndex(x => x.Expires);
_ = entity.HasIndex(x => new { ApplicationName = x.PartitionKey, x.Key }).IsUnique();
_ = entity.HasIndex(x => new { ApplicationName = x.PartitionKey, x.SubjectId, x.SessionId }).IsUnique();
_ = entity.HasIndex(x => new { ApplicationName = x.PartitionKey, x.SessionId }).IsUnique();
_ = entity.HasIndex(x => x.Expires);
});
}

View file

@ -38,21 +38,21 @@ public static class BffBuilderExtensions
// As a workaround, we're registering the config as a singleton
// then loading the singleton when the config reloads.
services.AddKeyedSingleton(ServiceProviderKeys.ProxyConfigurationKey, config);
_ = services.AddKeyedSingleton(ServiceProviderKeys.ProxyConfigurationKey, config);
});
builder.Services.Configure<BffOptions>(opt =>
_ = builder.Services.Configure<BffOptions>(opt =>
{
opt.MiddlewareLoaders.Add(app =>
{
app.UseBffRemoteRoutes();
_ = app.UseBffRemoteRoutes();
});
});
builder.Services.AddHttpForwarder();
builder.Services.AddSingleton<RemoteRouteHandler>();
_ = builder.Services.AddHttpForwarder();
_ = builder.Services.AddSingleton<RemoteRouteHandler>();
builder.Services.AddSingleton<IBffPluginLoader, ProxyBffPluginLoader>();
_ = builder.Services.AddSingleton<IBffPluginLoader, ProxyBffPluginLoader>();
return builder;
}
@ -65,7 +65,7 @@ public static class BffBuilderExtensions
var yarpBuilder = builder.Services.AddReverseProxy()
.AddBffExtensions();
yarpBuilder.LoadFromMemory(routes, clusters);
_ = yarpBuilder.LoadFromMemory(routes, clusters);
return yarpBuilder;
}
@ -77,7 +77,7 @@ public static class BffBuilderExtensions
var yarpBuilder = builder.Services.AddReverseProxy()
.AddBffExtensions();
yarpBuilder.LoadFromConfig(config);
_ = yarpBuilder.LoadFromConfig(config);
return yarpBuilder;
}

View file

@ -18,8 +18,8 @@ public static class DefaultBffYarpTransformerBuilders
public static readonly BffYarpTransformBuilder DirectProxyWithAccessToken =
(pathMatch, context) =>
{
context.AddRequestHeaderRemove("Cookie");
context.AddPathRemovePrefix(pathMatch);
context.AddBffAccessToken(pathMatch);
_ = context.AddRequestHeaderRemove("Cookie");
_ = context.AddPathRemovePrefix(pathMatch);
_ = context.AddBffAccessToken(pathMatch);
};
}

View file

@ -171,7 +171,7 @@ internal class AccessTokenRequestTransform(
});
if (proofToken != null)
{
context.ProxyRequest.Headers.Remove(OidcConstants.HttpHeaders.DPoP);
_ = context.ProxyRequest.Headers.Remove(OidcConstants.HttpHeaders.DPoP);
context.ProxyRequest.Headers.Add(OidcConstants.HttpHeaders.DPoP, proofToken.Value);
context.ProxyRequest.Headers.Authorization =
new AuthenticationHeaderValue(OidcConstants.AuthenticationSchemes.AuthorizationHeaderDPoP, token.AccessToken);

View file

@ -45,12 +45,12 @@ internal class AccessTokenTransformProvider(IOptions<BffOptions> options, ILogge
var values = new HashSet<string>();
if (!string.IsNullOrEmpty(routeValue))
{
values.Add(routeValue);
_ = values.Add(routeValue);
}
if (!string.IsNullOrEmpty(clusterValue))
{
values.Add(clusterValue);
_ = values.Add(clusterValue);
}
if (values.Count > 1)
@ -78,7 +78,7 @@ internal class AccessTokenTransformProvider(IOptions<BffOptions> options, ILogge
return;
}
transformBuildContext.AddRequestTransform(async transformContext =>
_ = transformBuildContext.AddRequestTransform(async transformContext =>
{
transformContext.HttpContext.CheckForBffMiddleware(_options);

View file

@ -103,7 +103,7 @@ internal class RemoteRouteHandler : IDisposable
var destinationPrefix = remoteApi.TargetUri.ToString();
await _httpForwarder.SendAsync(context, destinationPrefix, _invoker, requestConfig,
_ = await _httpForwarder.SendAsync(context, destinationPrefix, _invoker, requestConfig,
httpTransformer, ct);
return true;

View file

@ -63,7 +63,7 @@ public static class ProxyConfigExtensions
metadata = new();
}
metadata.TryAdd(key, value);
_ = metadata.TryAdd(key, value);
return config with { Metadata = metadata };
}
@ -89,7 +89,7 @@ public static class ProxyConfigExtensions
metadata = new();
}
metadata.TryAdd(Constants.Yarp.TokenTypeMetadata, requiredTokenType.ToString());
_ = metadata.TryAdd(Constants.Yarp.TokenTypeMetadata, requiredTokenType.ToString());
return config with { Metadata = metadata };
}

View file

@ -18,7 +18,7 @@ public static class ReverseProxyBuilderExtensions
/// <returns></returns>
public static IReverseProxyBuilder AddBffExtensions(this IReverseProxyBuilder builder)
{
builder.AddTransforms<AccessTokenTransformProvider>();
_ = builder.AddTransforms<AccessTokenTransformProvider>();
return builder;
}

View file

@ -50,7 +50,7 @@ public readonly record struct DPoPProofKey : IStronglyTypedValue<DPoPProofKey>
message = string.Empty;
try
{
JsonWebKey.Create(value);
_ = JsonWebKey.Create(value);
return true;
}
catch (JsonException e)

View file

@ -4,9 +4,6 @@
<TargetFramework>net10.0</TargetFramework>
<AssemblyName>Duende.BFF</AssemblyName>
<Description>Backend for frontend (BFF) host for ASP.NET Core</Description>
<!-- Related to https://github.com/dotnet/sdk/issues/50676-->
<!-- <EmitCompilerGeneratedFiles>true</EmitCompilerGeneratedFiles>-->
<!-- <CompilerGeneratedFilesOutputPath>Otel/Generated</CompilerGeneratedFilesOutputPath>-->
</PropertyGroup>
<ItemGroup>

View file

@ -49,9 +49,9 @@ public static class BffApplicationBuilderExtensions
public static IApplicationBuilder UseBffPreProcessing(this IApplicationBuilder app)
{
ArgumentNullException.ThrowIfNull(app);
app.UseBffFrontendSelection();
app.UseBffPathMapping();
app.UseBffOpenIdCallbacks();
_ = app.UseBffFrontendSelection();
_ = app.UseBffPathMapping();
_ = app.UseBffOpenIdCallbacks();
return app;
}
@ -72,7 +72,7 @@ public static class BffApplicationBuilderExtensions
{
loader(app);
}
app.UseEndpoints(endpoints =>
_ = app.UseEndpoints(endpoints =>
{
// Mapping the management endpoints.
endpoints.MapBffManagementLoginEndpoint();
@ -84,7 +84,7 @@ public static class BffApplicationBuilderExtensions
endpoints.MapBffManagementBackchannelEndpoint();
endpoints.MapBffDiagnosticsEndpoint();
});
app.UseBffStaticFileProxying();
_ = app.UseBffStaticFileProxying();
return app;
}

View file

@ -34,103 +34,103 @@ public static class BffBuilderExtensions
public static T ConfigureOpenIdConnect<T>(this T builder, Action<OpenIdConnectOptions> oidc)
where T : IBffBuilder
{
builder.Services.Configure<BffOptions>(bffOptions => bffOptions.ConfigureOpenIdConnectDefaults += oidc);
_ = builder.Services.Configure<BffOptions>(bffOptions => bffOptions.ConfigureOpenIdConnectDefaults += oidc);
return builder;
}
public static T ConfigureCookies<T>(this T builder, Action<CookieAuthenticationOptions> oidc)
where T : IBffBuilder
{
builder.Services.Configure<BffOptions>(bffOptions => bffOptions.ConfigureCookieDefaults += oidc);
_ = builder.Services.Configure<BffOptions>(bffOptions => bffOptions.ConfigureCookieDefaults += oidc);
return builder;
}
internal static T AddBaseBffServices<T>(this T builder) where T : IBffServicesBuilder
{
builder.Services.AddSingleton<GetLicenseKey>(sp =>
_ = builder.Services.AddSingleton<GetLicenseKey>(sp =>
() => sp.GetRequiredService<IOptions<BffOptions>>().Value.LicenseKey);
builder.Services.AddSingleton<License>(sp =>
_ = builder.Services.AddSingleton<License>(sp =>
{
var accessor = sp.GetRequiredService<LicenseAccessor>();
return accessor.Current;
});
builder.Services.AddSingleton<LicenseAccessor>();
_ = builder.Services.AddSingleton<LicenseAccessor>();
builder.Services.TryAddSingleton<LicenseValidator>();
builder.Services.AddDistributedMemoryCache();
_ = builder.Services.AddDistributedMemoryCache();
// IMPORTANT: The BffConfigureOpenIdConnectOptions MUST be called before calling
// AddOpenIdConnectAccessTokenManagement because both configure the same options
// The AddOpenIdConnectAccessTokenManagement adds OR wraps the BackchannelHttpHandler
// to add DPoP support. However, our code can also add a backchannel handler.
builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, BffConfigureOpenIdConnectOptions>();
builder.Services.AddOpenIdConnectAccessTokenManagement();
_ = builder.Services.AddSingleton<IConfigureOptions<OpenIdConnectOptions>, BffConfigureOpenIdConnectOptions>();
_ = builder.Services.AddOpenIdConnectAccessTokenManagement();
builder.Services
_ = builder.Services
.AddSingleton<IConfigureOptions<UserTokenManagementOptions>, ConfigureUserTokenManagementOptions>();
builder.Services.AddTransient<IReturnUrlValidator, LocalUrlReturnUrlValidator>();
_ = builder.Services.AddTransient<IReturnUrlValidator, LocalUrlReturnUrlValidator>();
builder.Services.TryAddSingleton<IAccessTokenRetriever, DefaultAccessTokenRetriever>();
// management endpoints
builder.Services.AddTransient<ILoginEndpoint, DefaultLoginEndpoint>();
_ = builder.Services.AddTransient<ILoginEndpoint, DefaultLoginEndpoint>();
#pragma warning disable CS0618 // Type or member is obsolete
builder.Services.AddTransient<ISilentLoginEndpoint, DefaultSilentLoginEndpoint>();
_ = builder.Services.AddTransient<ISilentLoginEndpoint, DefaultSilentLoginEndpoint>();
#pragma warning restore CS0618 // Type or member is obsolete
builder.Services.AddTransient<ISilentLoginCallbackEndpoint, DefaultSilentLoginCallbackEndpoint>();
builder.Services.AddTransient<ILogoutEndpoint, DefaultLogoutEndpoint>();
builder.Services.AddTransient<IUserEndpoint, DefaultUserEndpoint>();
builder.Services.AddTransient<IBackchannelLogoutEndpoint, DefaultBackchannelLogoutEndpoint>();
builder.Services.AddTransient<IDiagnosticsEndpoint, DefaultDiagnosticsEndpoint>();
_ = builder.Services.AddTransient<ISilentLoginCallbackEndpoint, DefaultSilentLoginCallbackEndpoint>();
_ = builder.Services.AddTransient<ILogoutEndpoint, DefaultLogoutEndpoint>();
_ = builder.Services.AddTransient<IUserEndpoint, DefaultUserEndpoint>();
_ = builder.Services.AddTransient<IBackchannelLogoutEndpoint, DefaultBackchannelLogoutEndpoint>();
_ = builder.Services.AddTransient<IDiagnosticsEndpoint, DefaultDiagnosticsEndpoint>();
// session management
builder.Services.TryAddTransient<ISessionRevocationService, NopSessionRevocationService>();
// cookie configuration
builder.Services
_ = builder.Services
.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>, PostConfigureSlidingExpirationCheck>();
builder.Services
_ = builder.Services
.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>,
PostConfigureApplicationCookieRevokeRefreshToken>();
builder.Services.AddSingleton<ActiveCookieAuthenticationScheme>();
builder.Services.AddSingleton<ActiveOpenIdConnectAuthenticationScheme>();
_ = builder.Services.AddSingleton<ActiveCookieAuthenticationScheme>();
_ = builder.Services.AddSingleton<ActiveOpenIdConnectAuthenticationScheme>();
builder.Services
_ = builder.Services
.AddSingleton<IPostConfigureOptions<OpenIdConnectOptions>, PostConfigureOidcOptionsForSilentLogin>();
builder.Services.AddSingleton<TrialModeAuthenticatedSessionTracker>();
builder.Services
_ = builder.Services.AddSingleton<TrialModeAuthenticatedSessionTracker>();
_ = builder.Services
.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>,
PostConfigureApplicationCookieTrialModeCheck>();
AddBffMetrics(builder);
// wrap ASP.NET Core
builder.Services.AddAuthentication();
_ = builder.Services.AddAuthentication();
builder.Services.AddTransientDecorator<IAuthenticationService, BffAuthenticationService>();
// Make sure the session partitioning is registered. There are a few codepaths that require this injected
// even if you are not using session management.
builder.Services.AddSingleton<BuildUserSessionPartitionKey>(sp =>
_ = builder.Services.AddSingleton<BuildUserSessionPartitionKey>(sp =>
sp.GetRequiredService<UserSessionPartitionKeyBuilder>().BuildPartitionKey);
builder.Services.AddSingleton<UserSessionPartitionKeyBuilder>();
_ = builder.Services.AddSingleton<UserSessionPartitionKeyBuilder>();
return builder;
}
internal static void AddBffMetrics<T>(T builder) where T : IBffBuilder =>
builder.Services.AddSingleton<BffMetrics>();
_ = builder.Services.AddSingleton<BffMetrics>();
internal static T AddDiagnostics<T>(this T builder)
where T : IBffServicesBuilder
{
builder.Services.AddSingleton<IDiagnosticEntry, BasicServerInfoDiagnosticEntry>();
builder.Services.AddSingleton<IDiagnosticEntry, AssemblyInfoDiagnosticEntry>();
builder.Services.AddSingleton<IDiagnosticEntry, FrontendCountDiagnosticEntry>();
builder.Services.AddSingleton<DiagnosticSummary>();
builder.Services.AddSingleton(serviceProvider => new DiagnosticDataService(
_ = builder.Services.AddSingleton<IDiagnosticEntry, BasicServerInfoDiagnosticEntry>();
_ = builder.Services.AddSingleton<IDiagnosticEntry, AssemblyInfoDiagnosticEntry>();
_ = builder.Services.AddSingleton<IDiagnosticEntry, FrontendCountDiagnosticEntry>();
_ = builder.Services.AddSingleton<DiagnosticSummary>();
_ = builder.Services.AddSingleton(serviceProvider => new DiagnosticDataService(
serviceProvider.GetRequiredService<TimeProvider>().GetUtcNow().UtcDateTime,
serviceProvider.GetServices<IDiagnosticEntry>()));
builder.Services.AddHostedService<DiagnosticHostedService>();
_ = builder.Services.AddHostedService<DiagnosticHostedService>();
return builder;
}
@ -138,31 +138,31 @@ public static class BffBuilderExtensions
internal static T AddDynamicFrontends<T>(this T builder)
where T : IBffServicesBuilder
{
builder.Services.AddHybridCache();
_ = builder.Services.AddHybridCache();
builder.Services.AddHostedService<BffCacheClearingHostedService>();
_ = builder.Services.AddHostedService<BffCacheClearingHostedService>();
builder.Services.AddTransient<IStartupFilter, ConfigureBffStartupFilter>();
_ = builder.Services.AddTransient<IStartupFilter, ConfigureBffStartupFilter>();
// Register the frontend collection, which will be used to store and retrieve frontends
builder.Services.AddSingleton<FrontendCollection>();
_ = builder.Services.AddSingleton<FrontendCollection>();
// Add a public accessible interface to the frontend collection, so our users can access it
builder.Services.AddSingleton<IFrontendCollection>((sp) => sp.GetRequiredService<FrontendCollection>());
_ = builder.Services.AddSingleton<IFrontendCollection>((sp) => sp.GetRequiredService<FrontendCollection>());
builder.Services.AddTransient<CurrentFrontendAccessor>();
builder.Services.AddSingleton<FrontendSelector>();
_ = builder.Services.AddTransient<CurrentFrontendAccessor>();
_ = builder.Services.AddSingleton<FrontendSelector>();
// Add a scheme provider that will inject authentication schemes that are needed for the BFF
builder.Services.AddTransient<IAuthenticationSchemeProvider, BffAuthenticationSchemeProvider>();
_ = builder.Services.AddTransient<IAuthenticationSchemeProvider, BffAuthenticationSchemeProvider>();
// Configure the AspNet Core Authentication settings if no
// .AddAuthentication().AddCookie().AddOpenIdConnect() was added
builder.Services
_ = builder.Services
.AddSingleton<IPostConfigureOptions<AuthenticationOptions>, BffConfigureAuthenticationOptions>();
builder.Services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, BffConfigureCookieOptions>();
_ = builder.Services.AddSingleton<IConfigureOptions<CookieAuthenticationOptions>, BffConfigureCookieOptions>();
builder.Services.AddHttpContextAccessor();
_ = builder.Services.AddHttpContextAccessor();
// Add 'default' configure methods that would have been added by
// .AddAuthentication().AddCookie().AddOpenIdConnect()
@ -186,7 +186,7 @@ public static class BffBuilderExtensions
{
var indexHtmlClientBuilder = services.AddHttpClient(Constants.HttpClientNames.StaticAssetsClientName);
services.Configure<HttpClientFactoryOptions>(indexHtmlClientBuilder.Name, options =>
_ = services.Configure<HttpClientFactoryOptions>(indexHtmlClientBuilder.Name, options =>
{
options.HttpMessageHandlerBuilderActions.Add(httpMessageHandlerBuilder =>
{
@ -215,16 +215,16 @@ public static class BffBuilderExtensions
internal static void AddServerSideSessionsSupportingServices(this IServiceCollection services)
{
services.AddSingleton<BuildUserSessionPartitionKey>(sp =>
_ = services.AddSingleton<BuildUserSessionPartitionKey>(sp =>
sp.GetRequiredService<UserSessionPartitionKeyBuilder>().BuildPartitionKey);
services.AddSingleton<UserSessionPartitionKeyBuilder>();
_ = services.AddSingleton<UserSessionPartitionKeyBuilder>();
services.AddSingleton<UserSessionPartitionKeyBuilder>();
services
_ = services.AddSingleton<UserSessionPartitionKeyBuilder>();
_ = services
.AddSingleton<IPostConfigureOptions<CookieAuthenticationOptions>,
PostConfigureApplicationCookieTicketStore>();
services.AddTransient<IServerTicketStore, ServerSideTicketStore>();
services.AddTransient<ISessionRevocationService, SessionRevocationService>();
_ = services.AddTransient<IServerTicketStore, ServerSideTicketStore>();
_ = services.AddTransient<ISessionRevocationService, SessionRevocationService>();
// only add if not already in DI
}
@ -237,8 +237,8 @@ public static class BffBuilderExtensions
where T : class, IUserSessionStore
{
ArgumentNullException.ThrowIfNull(builder);
builder.Services.AddTransient<IUserSessionStore, T>();
builder.AddServerSideSessions();
_ = builder.Services.AddTransient<IUserSessionStore, T>();
_ = builder.AddServerSideSessions();
return builder;
}

View file

@ -71,7 +71,7 @@ public static class BffEndpointRouteBuilderExtensions
return;
}
endpoints.Map(options.LoginPath.Value!, ProcessWith<ILoginEndpoint>)
_ = endpoints.Map(options.LoginPath.Value!, ProcessWith<ILoginEndpoint>)
.WithMetadata(new BffUiEndpointAttribute())
.AllowAnonymous();
}
@ -91,7 +91,7 @@ public static class BffEndpointRouteBuilderExtensions
if (!endpoints.AlreadyMappedManagementEndpoint(options.SilentLoginPath))
{
endpoints.MapGet(options.SilentLoginPath.Value!, ProcessWith<ISilentLoginEndpoint>)
_ = endpoints.MapGet(options.SilentLoginPath.Value!, ProcessWith<ISilentLoginEndpoint>)
.WithName("SilentLogin")
.WithMetadata(new BffUiEndpointAttribute())
.AllowAnonymous();
@ -99,7 +99,7 @@ public static class BffEndpointRouteBuilderExtensions
if (!endpoints.AlreadyMappedManagementEndpoint(options.SilentLoginCallbackPath))
{
endpoints.MapGet(options.SilentLoginCallbackPath.Value!, ProcessWith<ISilentLoginCallbackEndpoint>)
_ = endpoints.MapGet(options.SilentLoginCallbackPath.Value!, ProcessWith<ISilentLoginCallbackEndpoint>)
.WithMetadata(new BffUiEndpointAttribute())
.AllowAnonymous();
}
@ -121,7 +121,7 @@ public static class BffEndpointRouteBuilderExtensions
return;
}
endpoints.MapGet(options.LogoutPath.Value!, ProcessWith<ILogoutEndpoint>)
_ = endpoints.MapGet(options.LogoutPath.Value!, ProcessWith<ILogoutEndpoint>)
.WithName("Logout")
.WithMetadata(new BffUiEndpointAttribute())
.AllowAnonymous();
@ -143,7 +143,7 @@ public static class BffEndpointRouteBuilderExtensions
return;
}
endpoints.MapGet(options.UserPath.Value!, ProcessWith<IUserEndpoint>)
_ = endpoints.MapGet(options.UserPath.Value!, ProcessWith<IUserEndpoint>)
.AllowAnonymous()
.AsBffApiEndpoint();
}
@ -164,7 +164,7 @@ public static class BffEndpointRouteBuilderExtensions
return;
}
endpoints.MapPost(options.BackChannelLogoutPath.Value!, ProcessWith<IBackchannelLogoutEndpoint>)
_ = endpoints.MapPost(options.BackChannelLogoutPath.Value!, ProcessWith<IBackchannelLogoutEndpoint>)
.AllowAnonymous();
}
@ -184,7 +184,7 @@ public static class BffEndpointRouteBuilderExtensions
return;
}
endpoints.MapGet(options.DiagnosticsPath.Value!, ProcessWith<IDiagnosticsEndpoint>)
_ = endpoints.MapGet(options.DiagnosticsPath.Value!, ProcessWith<IDiagnosticsEndpoint>)
.AllowAnonymous();
}
@ -201,6 +201,6 @@ public static class BffEndpointRouteBuilderExtensions
internal static void CheckLicense(this IServiceProvider serviceProvider)
{
var license = serviceProvider.GetRequiredService<LicenseValidator>();
license.CheckLicense();
_ = license.CheckLicense();
}
}

Some files were not shown because too many files have changed in this diff Show more