diff --git a/appveyor.yml b/.appveyor.yml similarity index 50% rename from appveyor.yml rename to .appveyor.yml index 4f85bae46..d45bd5a1f 100644 --- a/appveyor.yml +++ b/.appveyor.yml @@ -1,19 +1,17 @@ -init: - - git config --global core.autocrlf true +init: +- git config --global core.autocrlf true branches: only: - - master - - release - - dev - - /^(.*\/)?ci-.*$/ - - /^rel\/.*/ + - master + - /^release\/.*$/ + - /^(.*\/)?ci-.*$/ build_script: - - ps: .\build.ps1 +- ps: .\run.ps1 default-build clone_depth: 1 environment: global: DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true DOTNET_CLI_TELEMETRY_OPTOUT: 1 -test: off -deploy: off +test: 'off' +deploy: 'off' os: Visual Studio 2017 diff --git a/.gitignore b/.gitignore index bac5b7505..3d7e16e84 100644 --- a/.gitignore +++ b/.gitignore @@ -30,4 +30,4 @@ project.lock.json /.vs/ .vscode/ global.json -korebuild-lock.txt +BenchmarkDotNet.Artifacts/ diff --git a/.travis.yml b/.travis.yml index 6c59666f3..ab3980055 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,25 +3,25 @@ sudo: false dist: trusty env: global: - - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true - - DOTNET_CLI_TELEMETRY_OPTOUT: 1 + - DOTNET_SKIP_FIRST_TIME_EXPERIENCE: true + - DOTNET_CLI_TELEMETRY_OPTOUT: 1 mono: none os: - - linux - - osx +- linux +- osx osx_image: xcode8.2 addons: apt: packages: - - libunwind8 + - libunwind8 branches: only: - - master - - release - - dev - - /^(.*\/)?ci-.*$/ - - /^rel\/.*/ + - master + - /^release\/.*$/ + - /^(.*\/)?ci-.*$/ before_install: - - if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib /usr/local/lib/; fi +- if test "$TRAVIS_OS_NAME" == "osx"; then brew update; brew install openssl; ln -s + /usr/local/opt/openssl/lib/libcrypto.1.0.0.dylib /usr/local/lib/; ln -s /usr/local/opt/openssl/lib/libssl.1.0.0.dylib + /usr/local/lib/; fi script: - - ./build.sh +- ./build.sh diff --git a/.vsts-pipelines/builds/ci-internal.yml b/.vsts-pipelines/builds/ci-internal.yml new file mode 100644 index 000000000..c2c5336fd --- /dev/null +++ b/.vsts-pipelines/builds/ci-internal.yml @@ -0,0 +1,13 @@ +trigger: +- master +- release/* + +resources: + repositories: + - repository: buildtools + type: git + name: aspnet-BuildTools + ref: refs/heads/master + +phases: +- template: .vsts-pipelines/templates/project-ci.yml@buildtools diff --git a/.vsts-pipelines/builds/ci-public.yml b/.vsts-pipelines/builds/ci-public.yml new file mode 100644 index 000000000..507c89b02 --- /dev/null +++ b/.vsts-pipelines/builds/ci-public.yml @@ -0,0 +1,15 @@ +trigger: +- master +- release/* + +# See https://github.com/aspnet/BuildTools +resources: + repositories: + - repository: buildtools + type: github + endpoint: DotNet-Bot GitHub Connection + name: aspnet/BuildTools + ref: refs/heads/master + +phases: +- template: .vsts-pipelines/templates/project-ci.yml@buildtools diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 64ff041d5..eac4268e4 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ Contributing ====== -Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/dev/CONTRIBUTING.md) in the Home repo. +Information on contributing to this repo is in the [Contributing Guide](https://github.com/aspnet/Home/blob/master/CONTRIBUTING.md) in the Home repo. diff --git a/Directory.Build.props b/Directory.Build.props new file mode 100644 index 000000000..eb9d9535e --- /dev/null +++ b/Directory.Build.props @@ -0,0 +1,19 @@ + + + + + + + + + Microsoft ASP.NET Core + https://github.com/aspnet/Security + git + $(MSBuildThisFileDirectory) + $(MSBuildThisFileDirectory)build\Key.snk + true + true + + diff --git a/Directory.Build.targets b/Directory.Build.targets new file mode 100644 index 000000000..7e3f8df92 --- /dev/null +++ b/Directory.Build.targets @@ -0,0 +1,6 @@ + + + $(MicrosoftNETCoreAppPackageVersion) + $(NETStandardLibrary20PackageVersion) + + diff --git a/LICENSE.txt b/LICENSE.txt index 7b2956ece..b3b180cd5 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,14 +1,201 @@ -Copyright (c) .NET Foundation and Contributors + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ -All rights reserved. + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION -Licensed under the Apache License, Version 2.0 (the "License"); you may not use -this file except in compliance with the License. You may obtain a copy of the -License at + 1. Definitions. - http://www.apache.org/licenses/LICENSE-2.0 + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. -Unless required by applicable law or agreed to in writing, software distributed -under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR -CONDITIONS OF ANY KIND, either express or implied. See the License for the -specific language governing permissions and limitations under the License. + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + + APPENDIX: How to apply the Apache License to your work. + + To apply the Apache License to your work, attach the following + boilerplate notice, with the fields enclosed by brackets "[]" + replaced with your own identifying information. (Don't include + the brackets!) The text should be enclosed in the appropriate + comment syntax for the file format. We also recommend that a + file or class name and description of purpose be included on the + same "printed page" as the copyright notice for easier + identification within third-party archives. + + Copyright (c) .NET Foundation and Contributors + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. diff --git a/NuGet.config b/NuGet.config index 4e8a1f6de..e32bddfd5 100644 --- a/NuGet.config +++ b/NuGet.config @@ -2,8 +2,6 @@ - - - + diff --git a/README.md b/README.md index e8e64c293..66c2aa45e 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,7 @@ -ASP.NET Security -======== +ASP.NET Security [Archived] +=========================== -AppVeyor: [![AppVeyor](https://ci.appveyor.com/api/projects/status/fujhh8n956v5ohfd/branch/dev?svg=true)](https://ci.appveyor.com/project/aspnetci/Security/branch/dev) - -Travis: [![Travis](https://travis-ci.org/aspnet/Security.svg?branch=dev)](https://travis-ci.org/aspnet/Security) +**This GitHub project has been archived.** Ongoing development on this project can be found in . Contains the security and authorization middlewares for ASP.NET Core. @@ -14,4 +12,4 @@ A list of community projects related to authentication and security for ASP.NET ASP.NET Security will not include Basic Authentication middleware due to its potential insecurity and performance problems. If you host under IIS you can enable it via IIS configuration. -This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [Home](https://github.com/aspnet/home) repo. +This project is part of ASP.NET Core. You can find samples, documentation and getting started instructions for ASP.NET Core at the [AspNetCore](https://github.com/aspnet/AspNetCore) repo. diff --git a/Security.sln b/Security.sln index 2e5ee56d0..240100e8c 100644 --- a/Security.sln +++ b/Security.sln @@ -1,14 +1,20 @@ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 -VisualStudioVersion = 15.0.26621.2 -MinimumVisualStudioVersion = 10.0.40219.1 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 15.0.26730.03 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{4D2B6A51-2F9F-44F5-8131-EA5CAC053652}" + ProjectSection(SolutionItems) = preProject + src\Directory.Build.props = src\Directory.Build.props + EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "samples", "samples", "{F8C0AA27-F3FB-4286-8E4C-47EF86B539FF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CookieSample", "samples\CookieSample\CookieSample.csproj", "{558C2C2A-AED8-49DE-BB60-D5F8AE06C714}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{7BF11F3A-60B6-4796-B504-579C67FFBA34}" + ProjectSection(SolutionItems) = preProject + test\Directory.Build.props = test\Directory.Build.props + EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SocialSample", "samples\SocialSample\SocialSample.csproj", "{8C73D216-332D-41D8-BFD0-45BC4BC36552}" EndProject @@ -56,15 +62,27 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Chunki EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{86BD08B1-F978-4F58-9982-2A017807F01C}" ProjectSection(SolutionItems) = preProject - build\common.props = build\common.props build\dependencies.props = build\dependencies.props + Directory.Build.props = Directory.Build.props + Directory.Build.targets = Directory.Build.targets build\Key.snk = build\Key.snk NuGet.config = NuGet.config build\repo.props = build\repo.props + build\sources.props = build\sources.props EndProjectSection EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authorization.Policy", "src\Microsoft.AspNetCore.Authorization.Policy\Microsoft.AspNetCore.Authorization.Policy.csproj", "{58194599-F07D-47A3-9DF2-E21A22C5EF9E}" EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "CookiePolicySample", "samples\CookiePolicySample\CookiePolicySample.csproj", "{24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Authentication.WsFederation", "src\Microsoft.AspNetCore.Authentication.WsFederation\Microsoft.AspNetCore.Authentication.WsFederation.csproj", "{B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WsFedSample", "samples\WsFedSample\WsFedSample.csproj", "{5EC2E398-E46A-430D-8E4B-E91C8FC3E800}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "benchmarks", "benchmarks", "{55052FE3-F8C2-4E6C-97B0-C02ED1DBEA62}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Microsoft.AspNetCore.Security.Performance", "benchmarks\Microsoft.AspNetCore.Security.Performance\Microsoft.AspNetCore.Security.Performance.csproj", "{556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -455,6 +473,70 @@ Global {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|x64.Build.0 = Release|Any CPU {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|x86.ActiveCfg = Release|Any CPU {58194599-F07D-47A3-9DF2-E21A22C5EF9E}.Release|x86.Build.0 = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|x64.ActiveCfg = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|x64.Build.0 = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|x86.ActiveCfg = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Debug|x86.Build.0 = Debug|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|Any CPU.Build.0 = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|x64.ActiveCfg = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|x64.Build.0 = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|x86.ActiveCfg = Release|Any CPU + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E}.Release|x86.Build.0 = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|x64.ActiveCfg = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|x64.Build.0 = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|x86.ActiveCfg = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Debug|x86.Build.0 = Debug|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|Any CPU.Build.0 = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|x64.ActiveCfg = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|x64.Build.0 = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|x86.ActiveCfg = Release|Any CPU + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29}.Release|x86.Build.0 = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|x64.ActiveCfg = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|x64.Build.0 = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|x86.ActiveCfg = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Debug|x86.Build.0 = Debug|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|Any CPU.Build.0 = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|x64.ActiveCfg = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|x64.Build.0 = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|x86.ActiveCfg = Release|Any CPU + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800}.Release|x86.Build.0 = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x64.ActiveCfg = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x64.Build.0 = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x86.ActiveCfg = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Debug|x86.Build.0 = Debug|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Any CPU.Build.0 = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|Mixed Platforms.Build.0 = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x64.ActiveCfg = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x64.Build.0 = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x86.ActiveCfg = Release|Any CPU + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -484,6 +566,10 @@ Global {3A7AD414-EBDE-4F92-B307-4E8F19B6117E} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF} {51563775-C659-4907-9BAF-9995BAB87D01} = {7BF11F3A-60B6-4796-B504-579C67FFBA34} {58194599-F07D-47A3-9DF2-E21A22C5EF9E} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652} + {24A28F5D-E5A9-4CA8-B0D2-924A1F8BE14E} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF} + {B1FC6AAF-9BF2-4CDA-84A2-AA8BF7603F29} = {4D2B6A51-2F9F-44F5-8131-EA5CAC053652} + {5EC2E398-E46A-430D-8E4B-E91C8FC3E800} = {F8C0AA27-F3FB-4286-8E4C-47EF86B539FF} + {556C4FAA-F4B1-4EA9-8921-CB1DF7D94C2A} = {55052FE3-F8C2-4E6C-97B0-C02ED1DBEA62} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {ABF8089E-43D0-4010-84A7-7A9DCFE49357} diff --git a/benchmarks/Microsoft.AspNetCore.Security.Performance/AuthorizationMiddlewareBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Security.Performance/AuthorizationMiddlewareBenchmark.cs new file mode 100644 index 000000000..90cb135de --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Security.Performance/AuthorizationMiddlewareBenchmark.cs @@ -0,0 +1,53 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Security +{ + public class AuthorizationMiddlewareBenchmark + { + private AuthorizationMiddleware _authorizationMiddleware; + private DefaultHttpContext _httpContextNoEndpoint; + private DefaultHttpContext _httpContextHasEndpoint; + + [GlobalSetup] + public void Setup() + { + var policyProvider = new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions())); + _authorizationMiddleware = new AuthorizationMiddleware((context) => Task.CompletedTask, policyProvider); + + _httpContextNoEndpoint = new DefaultHttpContext(); + + var feature = new EndpointFeature + { + Endpoint = new Endpoint((context) => Task.CompletedTask, EndpointMetadataCollection.Empty, "Test endpoint") + }; + _httpContextHasEndpoint = new DefaultHttpContext(); + _httpContextHasEndpoint.Features.Set(feature); + } + + [Benchmark] + public Task Invoke_NoEndpoint_NoAuthorization() + { + return _authorizationMiddleware.Invoke(_httpContextNoEndpoint); + } + + [Benchmark] + public Task Invoke_HasEndpoint_NoAuthorization() + { + return _authorizationMiddleware.Invoke(_httpContextHasEndpoint); + } + + private class EndpointFeature : IEndpointFeature + { + public Endpoint Endpoint { get; set; } + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Security.Performance/AuthorizationPolicyBenchmark.cs b/benchmarks/Microsoft.AspNetCore.Security.Performance/AuthorizationPolicyBenchmark.cs new file mode 100644 index 000000000..86afdcd5a --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Security.Performance/AuthorizationPolicyBenchmark.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; +using BenchmarkDotNet.Attributes; +using Microsoft.AspNetCore.Authorization; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Security +{ + public class AuthorizationPolicyBenchmark + { + private DefaultAuthorizationPolicyProvider _policyProvider; + + [GlobalSetup] + public void Setup() + { + _policyProvider = new DefaultAuthorizationPolicyProvider(Options.Create(new AuthorizationOptions())); + } + + [Benchmark] + public Task CombineAsync() + { + return AuthorizationPolicy.CombineAsync(_policyProvider, Array.Empty()); + } + } +} diff --git a/benchmarks/Microsoft.AspNetCore.Security.Performance/Microsoft.AspNetCore.Security.Performance.csproj b/benchmarks/Microsoft.AspNetCore.Security.Performance/Microsoft.AspNetCore.Security.Performance.csproj new file mode 100644 index 000000000..2f16dd669 --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Security.Performance/Microsoft.AspNetCore.Security.Performance.csproj @@ -0,0 +1,24 @@ + + + + netcoreapp3.0 + Exe + true + true + false + Microsoft.AspNetCore.Security + + + + + + + + + + + + + + + diff --git a/benchmarks/Microsoft.AspNetCore.Security.Performance/Properties/AssemblyInfo.cs b/benchmarks/Microsoft.AspNetCore.Security.Performance/Properties/AssemblyInfo.cs new file mode 100644 index 000000000..cfb0b19c2 --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Security.Performance/Properties/AssemblyInfo.cs @@ -0,0 +1 @@ +[assembly: BenchmarkDotNet.Attributes.AspNetCoreBenchmark] \ No newline at end of file diff --git a/benchmarks/Microsoft.AspNetCore.Security.Performance/readme.md b/benchmarks/Microsoft.AspNetCore.Security.Performance/readme.md new file mode 100644 index 000000000..cbe60261b --- /dev/null +++ b/benchmarks/Microsoft.AspNetCore.Security.Performance/readme.md @@ -0,0 +1,16 @@ +Compile the solution in Release mode (so binaries are available in release) + +To run a specific benchmark add it as parameter. +``` +dotnet run -c Release --framework +``` + +To run all benchmarks use '*' as the name. +``` +dotnet run -c Release --framework * +``` + +If you run without any parameters, you'll be offered the list of all benchmarks and get to choose. +``` +dotnet run -c Release --framework +``` \ No newline at end of file diff --git a/build.cmd b/build.cmd index b6c8d2486..c0050bda1 100644 --- a/build.cmd +++ b/build.cmd @@ -1,2 +1,2 @@ @ECHO OFF -PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0build.ps1' %*; exit $LASTEXITCODE" +PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' default-build %*; exit $LASTEXITCODE" diff --git a/build.sh b/build.sh index 11cdbe550..98a4b2276 100755 --- a/build.sh +++ b/build.sh @@ -1,199 +1,8 @@ #!/usr/bin/env bash set -euo pipefail - -# -# variables -# - -RESET="\033[0m" -RED="\033[0;31m" -MAGENTA="\033[0;95m" DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" -[ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet" -config_file="$DIR/version.xml" -verbose=false -update=false -repo_path="$DIR" -channel='' -tools_source='' - -# -# Functions -# -__usage() { - echo "Usage: $(basename "${BASH_SOURCE[0]}") [options] [[--] ...]" - echo "" - echo "Arguments:" - echo " ... Arguments passed to MSBuild. Variable number of arguments allowed." - echo "" - echo "Options:" - echo " --verbose Show verbose output." - echo " -c|--channel The channel of KoreBuild to download. Overrides the value from the config file.." - echo " --config-file TThe path to the configuration file that stores values. Defaults to version.xml." - echo " -d|--dotnet-home The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet." - echo " --path The directory to build. Defaults to the directory containing the script." - echo " -s|--tools-source The base url where build tools can be downloaded. Overrides the value from the config file." - echo " -u|--update Update to the latest KoreBuild even if the lock file is present." - echo "" - echo "Description:" - echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be." - echo " When the lockfile is not present, KoreBuild will create one using latest available version from \$channel." - - if [[ "${1:-}" != '--no-exit' ]]; then - exit 2 - fi -} - -get_korebuild() { - local version - local lock_file="$repo_path/korebuild-lock.txt" - if [ ! -f "$lock_file" ] || [ "$update" = true ]; then - __get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file" - fi - version="$(grep 'version:*' -m 1 "$lock_file")" - if [[ "$version" == '' ]]; then - __error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'" - return 1 - fi - version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" - local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version" - - { - if [ ! -d "$korebuild_path" ]; then - mkdir -p "$korebuild_path" - local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip" - tmpfile="$(mktemp)" - echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}" - if __get_remote_file "$remote_path" "$tmpfile"; then - unzip -q -d "$korebuild_path" "$tmpfile" - fi - rm "$tmpfile" || true - fi - - source "$korebuild_path/KoreBuild.sh" - } || { - if [ -d "$korebuild_path" ]; then - echo "Cleaning up after failed installation" - rm -rf "$korebuild_path" || true - fi - return 1 - } -} - -__error() { - echo -e "${RED}$*${RESET}" 1>&2 -} - -__machine_has() { - hash "$1" > /dev/null 2>&1 - return $? -} - -__get_remote_file() { - local remote_path=$1 - local local_path=$2 - - if [[ "$remote_path" != 'http'* ]]; then - cp "$remote_path" "$local_path" - return 0 - fi - - local failed=false - if __machine_has wget; then - wget --tries 10 --quiet -O "$local_path" "$remote_path" || failed=true - else - failed=true - fi - - if [ "$failed" = true ] && __machine_has curl; then - failed=false - curl --retry 10 -sSL -f --create-dirs -o "$local_path" "$remote_path" || failed=true - fi - - if [ "$failed" = true ]; then - __error "Download failed: $remote_path" 1>&2 - return 1 - fi -} - -__read_dom () { local IFS=\> ; read -r -d \< ENTITY CONTENT ;} - -# -# main -# - -while [[ $# -gt 0 ]]; do - case $1 in - -\?|-h|--help) - __usage --no-exit - exit 0 - ;; - -c|--channel|-Channel) - shift - channel="${1:-}" - [ -z "$channel" ] && __usage - ;; - --config-file|-ConfigFile) - shift - config_file="${1:-}" - [ -z "$config_file" ] && __usage - ;; - -d|--dotnet-home|-DotNetHome) - shift - DOTNET_HOME="${1:-}" - [ -z "$DOTNET_HOME" ] && __usage - ;; - --path|-Path) - shift - repo_path="${1:-}" - [ -z "$repo_path" ] && __usage - ;; - -s|--tools-source|-ToolsSource) - shift - tools_source="${1:-}" - [ -z "$tools_source" ] && __usage - ;; - -u|--update|-Update) - update=true - ;; - --verbose|-Verbose) - verbose=true - ;; - --) - shift - break - ;; - *) - break - ;; - esac - shift -done - -if ! __machine_has unzip; then - __error 'Missing required command: unzip' - exit 1 -fi - -if ! __machine_has curl && ! __machine_has wget; then - __error 'Missing required command. Either wget or curl is required.' - exit 1 -fi - -if [ -f "$config_file" ]; then - comment=false - while __read_dom; do - if [ "$comment" = true ]; then [[ $CONTENT == *'-->'* ]] && comment=false ; continue; fi - if [[ $ENTITY == '!--'* ]]; then comment=true; continue; fi - if [ -z "$channel" ] && [[ $ENTITY == "KoreBuildChannel" ]]; then channel=$CONTENT; fi - if [ -z "$tools_source" ] && [[ $ENTITY == "KoreBuildToolsSource" ]]; then tools_source=$CONTENT; fi - done < "$config_file" -fi - -[ -z "$channel" ] && channel='dev' -[ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools' -get_korebuild -install_tools "$tools_source" "$DOTNET_HOME" -invoke_repository_build "$repo_path" "$@" +# Call "sync" between "chmod" and execution to prevent "text file busy" error in Docker (aufs) +chmod +x "$DIR/run.sh"; sync +"$DIR/run.sh" default-build "$@" diff --git a/build/common.props b/build/common.props deleted file mode 100644 index ea940214e..000000000 --- a/build/common.props +++ /dev/null @@ -1,20 +0,0 @@ - - - - - - Microsoft ASP.NET Core - https://github.com/aspnet/Security - git - $(MSBuildThisFileDirectory)Key.snk - true - true - $(VersionSuffix)-$(BuildNumber) - true - - - - - - - diff --git a/build/dependencies.props b/build/dependencies.props index a97cae9f6..163a3d400 100644 --- a/build/dependencies.props +++ b/build/dependencies.props @@ -1,16 +1,56 @@ - + - 2.1.0-* - 4.4.0-* - 3.14.1 - 2.1.4 - 2.1.1-* - 10.0.1 - 2.0.0-* - 2.0.0-* - 3.0.1 - 2.0.0-* - 15.3.0-* - 2.3.0-beta2-* + $(MSBuildAllProjects);$(MSBuildThisFileFullPath) + + 0.10.13 + 3.0.0-build-20181114.5 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-preview-181113-11 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-alpha1-10742 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.0.0-preview-181113-11 + 3.19.8 + 5.3.0 + 5.3.0 + 3.0.0-preview1-26907-05 + 15.6.1 + 3.0.1 + 3.0.1 + 3.0.1 + 4.10.0 + 2.0.3 + 11.0.2 + 5.3.0 + 0.10.0 + 2.3.1 + 2.4.0 + + + + diff --git a/build/repo.props b/build/repo.props index d4bab3eeb..4402da1d0 100644 --- a/build/repo.props +++ b/build/repo.props @@ -1,5 +1,13 @@ + + + + + Internal.AspNetCore.Universe.Lineup + https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json + + - + diff --git a/build/sources.props b/build/sources.props new file mode 100644 index 000000000..9215df975 --- /dev/null +++ b/build/sources.props @@ -0,0 +1,17 @@ + + + + + $(DotNetRestoreSources) + + $(RestoreSources); + https://dotnet.myget.org/F/dotnet-core/api/v3/index.json; + https://dotnet.myget.org/F/aspnetcore-dev/api/v3/index.json; + https://dotnet.myget.org/F/aspnetcore-tools/api/v3/index.json; + + + $(RestoreSources); + https://api.nuget.org/v3/index.json; + + + diff --git a/korebuild-lock.txt b/korebuild-lock.txt new file mode 100644 index 000000000..73613543d --- /dev/null +++ b/korebuild-lock.txt @@ -0,0 +1,2 @@ +version:3.0.0-build-20181114.5 +commithash:880e9a204d4ee4a18dfd83c9fb05a192a28bca60 diff --git a/korebuild.json b/korebuild.json new file mode 100644 index 000000000..8a276a7f3 --- /dev/null +++ b/korebuild.json @@ -0,0 +1,4 @@ +{ + "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", + "channel": "master" +} diff --git a/run.cmd b/run.cmd new file mode 100644 index 000000000..d52d5c7e6 --- /dev/null +++ b/run.cmd @@ -0,0 +1,2 @@ +@ECHO OFF +PowerShell -NoProfile -NoLogo -ExecutionPolicy unrestricted -Command "[System.Threading.Thread]::CurrentThread.CurrentCulture = ''; [System.Threading.Thread]::CurrentThread.CurrentUICulture = '';& '%~dp0run.ps1' %*; exit $LASTEXITCODE" diff --git a/build.ps1 b/run.ps1 similarity index 60% rename from build.ps1 rename to run.ps1 index d5eb4d5cf..34604c717 100644 --- a/build.ps1 +++ b/run.ps1 @@ -3,10 +3,13 @@ <# .SYNOPSIS -Build this repository +Executes KoreBuild commands. .DESCRIPTION -Downloads korebuild if required. Then builds the repository. +Downloads korebuild if required. Then executes the KoreBuild command. To see available commands, execute with `-Command help`. + +.PARAMETER Command +The KoreBuild command to run. .PARAMETER Path The folder to build. Defaults to the folder containing this script. @@ -23,32 +26,42 @@ The base url where build tools can be downloaded. Overrides the value from the c .PARAMETER Update Updates KoreBuild to the latest version even if a lock file is present. +.PARAMETER Reinstall +Re-installs KoreBuild + .PARAMETER ConfigFile -The path to the configuration file that stores values. Defaults to version.xml. +The path to the configuration file that stores values. Defaults to korebuild.json. + +.PARAMETER ToolsSourceSuffix +The Suffix to append to the end of the ToolsSource. Useful for query strings in blob stores. -.PARAMETER MSBuildArgs -Arguments to be passed to MSBuild +.PARAMETER CI +Sets up CI specific settings and variables. + +.PARAMETER Arguments +Arguments to be passed to the command .NOTES This function will create a file $PSScriptRoot/korebuild-lock.txt. This lock file can be committed to source, but does not have to be. When the lockfile is not present, KoreBuild will create one using latest available version from $Channel. -The $ConfigFile is expected to be an XML file. It is optional, and the configuration values in it are optional as well. +The $ConfigFile is expected to be an JSON file. It is optional, and the configuration values in it are optional as well. Any options set +in the file are overridden by command line parameters. .EXAMPLE Example config file: -```xml - - - - dev - https://aspnetcore.blob.core.windows.net/buildtools - - +```json +{ + "$schema": "https://raw.githubusercontent.com/aspnet/BuildTools/master/tools/korebuild.schema.json", + "channel": "master", + "toolsSource": "https://aspnetcore.blob.core.windows.net/buildtools" +} ``` #> [CmdletBinding(PositionalBinding = $false)] param( + [Parameter(Mandatory = $true, Position = 0)] + [string]$Command, [string]$Path = $PSScriptRoot, [Alias('c')] [string]$Channel, @@ -58,9 +71,12 @@ param( [string]$ToolsSource, [Alias('u')] [switch]$Update, - [string]$ConfigFile = (Join-Path $PSScriptRoot 'version.xml'), + [switch]$Reinstall, + [string]$ToolsSourceSuffix, + [string]$ConfigFile = $null, + [switch]$CI, [Parameter(ValueFromRemainingArguments = $true)] - [string[]]$MSBuildArgs + [string[]]$Arguments ) Set-StrictMode -Version 2 @@ -75,7 +91,7 @@ function Get-KoreBuild { $lockFile = Join-Path $Path 'korebuild-lock.txt' if (!(Test-Path $lockFile) -or $Update) { - Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile + Get-RemoteFile "$ToolsSource/korebuild/channels/$Channel/latest.txt" $lockFile $ToolsSourceSuffix } $version = Get-Content $lockFile | Where-Object { $_ -like 'version:*' } | Select-Object -first 1 @@ -85,6 +101,10 @@ function Get-KoreBuild { $version = $version.TrimStart('version:').Trim() $korebuildPath = Join-Paths $DotNetHome ('buildtools', 'korebuild', $version) + if ($Reinstall -and (Test-Path $korebuildPath)) { + Remove-Item -Force -Recurse $korebuildPath + } + if (!(Test-Path $korebuildPath)) { Write-Host -ForegroundColor Magenta "Downloading KoreBuild $version" New-Item -ItemType Directory -Path $korebuildPath | Out-Null @@ -92,10 +112,10 @@ function Get-KoreBuild { try { $tmpfile = Join-Path ([IO.Path]::GetTempPath()) "KoreBuild-$([guid]::NewGuid()).zip" - Get-RemoteFile $remotePath $tmpfile - if (Get-Command -Name 'Expand-Archive' -ErrorAction Ignore) { + Get-RemoteFile $remotePath $tmpfile $ToolsSourceSuffix + if (Get-Command -Name 'Microsoft.PowerShell.Archive\Expand-Archive' -ErrorAction Ignore) { # Use built-in commands where possible as they are cross-plat compatible - Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath + Microsoft.PowerShell.Archive\Expand-Archive -Path $tmpfile -DestinationPath $korebuildPath } else { # Fallback to old approach for old installations of PowerShell @@ -120,7 +140,7 @@ function Join-Paths([string]$path, [string[]]$childPaths) { return $path } -function Get-RemoteFile([string]$RemotePath, [string]$LocalPath) { +function Get-RemoteFile([string]$RemotePath, [string]$LocalPath, [string]$RemoteSuffix) { if ($RemotePath -notlike 'http*') { Copy-Item $RemotePath $LocalPath return @@ -130,7 +150,7 @@ function Get-RemoteFile([string]$RemotePath, [string]$LocalPath) { while ($retries -gt 0) { $retries -= 1 try { - Invoke-WebRequest -UseBasicParsing -Uri $RemotePath -OutFile $LocalPath + Invoke-WebRequest -UseBasicParsing -Uri $($RemotePath + $RemoteSuffix) -OutFile $LocalPath return } catch { @@ -147,10 +167,22 @@ function Get-RemoteFile([string]$RemotePath, [string]$LocalPath) { # Load configuration or set defaults +$Path = Resolve-Path $Path +if (!$ConfigFile) { $ConfigFile = Join-Path $Path 'korebuild.json' } + if (Test-Path $ConfigFile) { - [xml] $config = Get-Content $ConfigFile - if (!($Channel)) { [string] $Channel = Select-Xml -Xml $config -XPath '/Project/PropertyGroup/KoreBuildChannel' } - if (!($ToolsSource)) { [string] $ToolsSource = Select-Xml -Xml $config -XPath '/Project/PropertyGroup/KoreBuildToolsSource' } + try { + $config = Get-Content -Raw -Encoding UTF8 -Path $ConfigFile | ConvertFrom-Json + if ($config) { + if (!($Channel) -and (Get-Member -Name 'channel' -InputObject $config)) { [string] $Channel = $config.channel } + if (!($ToolsSource) -and (Get-Member -Name 'toolsSource' -InputObject $config)) { [string] $ToolsSource = $config.toolsSource} + } + } + catch { + Write-Host -ForegroundColor Red $Error[0] + Write-Error "$ConfigFile contains invalid JSON." + exit 1 + } } if (!$DotNetHome) { @@ -160,7 +192,7 @@ if (!$DotNetHome) { else { Join-Path $PSScriptRoot '.dotnet'} } -if (!$Channel) { $Channel = 'dev' } +if (!$Channel) { $Channel = 'master' } if (!$ToolsSource) { $ToolsSource = 'https://aspnetcore.blob.core.windows.net/buildtools' } # Execute @@ -169,8 +201,8 @@ $korebuildPath = Get-KoreBuild Import-Module -Force -Scope Local (Join-Path $korebuildPath 'KoreBuild.psd1') try { - Install-Tools $ToolsSource $DotNetHome - Invoke-RepositoryBuild $Path @MSBuildArgs + Set-KoreBuildSettings -ToolsSource $ToolsSource -DotNetHome $DotNetHome -RepoPath $Path -ConfigFile $ConfigFile -CI:$CI + Invoke-KoreBuildCommand $Command @Arguments } finally { Remove-Module 'KoreBuild' -ErrorAction Ignore diff --git a/run.sh b/run.sh new file mode 100755 index 000000000..4c1fed564 --- /dev/null +++ b/run.sh @@ -0,0 +1,256 @@ +#!/usr/bin/env bash + +set -euo pipefail + +# +# variables +# + +RESET="\033[0m" +RED="\033[0;31m" +YELLOW="\033[0;33m" +MAGENTA="\033[0;95m" +DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )" +[ -z "${DOTNET_HOME:-}" ] && DOTNET_HOME="$HOME/.dotnet" +verbose=false +update=false +reinstall=false +repo_path="$DIR" +channel='' +tools_source='' +tools_source_suffix='' +ci=false + +# +# Functions +# +__usage() { + echo "Usage: $(basename "${BASH_SOURCE[0]}") command [options] [[--] ...]" + echo "" + echo "Arguments:" + echo " command The command to be run." + echo " ... Arguments passed to the command. Variable number of arguments allowed." + echo "" + echo "Options:" + echo " --verbose Show verbose output." + echo " -c|--channel The channel of KoreBuild to download. Overrides the value from the config file.." + echo " --config-file The path to the configuration file that stores values. Defaults to korebuild.json." + echo " -d|--dotnet-home The directory where .NET Core tools will be stored. Defaults to '\$DOTNET_HOME' or '\$HOME/.dotnet." + echo " --path The directory to build. Defaults to the directory containing the script." + echo " -s|--tools-source|-ToolsSource The base url where build tools can be downloaded. Overrides the value from the config file." + echo " --tools-source-suffix|-ToolsSourceSuffix The suffix to append to tools-source. Useful for query strings." + echo " -u|--update Update to the latest KoreBuild even if the lock file is present." + echo " --reinstall Reinstall KoreBuild." + echo " --ci Apply CI specific settings and environment variables." + echo "" + echo "Description:" + echo " This function will create a file \$DIR/korebuild-lock.txt. This lock file can be committed to source, but does not have to be." + echo " When the lockfile is not present, KoreBuild will create one using latest available version from \$channel." + + if [[ "${1:-}" != '--no-exit' ]]; then + exit 2 + fi +} + +get_korebuild() { + local version + local lock_file="$repo_path/korebuild-lock.txt" + if [ ! -f "$lock_file" ] || [ "$update" = true ]; then + __get_remote_file "$tools_source/korebuild/channels/$channel/latest.txt" "$lock_file" "$tools_source_suffix" + fi + version="$(grep 'version:*' -m 1 "$lock_file")" + if [[ "$version" == '' ]]; then + __error "Failed to parse version from $lock_file. Expected a line that begins with 'version:'" + return 1 + fi + version="$(echo "${version#version:}" | sed -e 's/^[[:space:]]*//' -e 's/[[:space:]]*$//')" + local korebuild_path="$DOTNET_HOME/buildtools/korebuild/$version" + + if [ "$reinstall" = true ] && [ -d "$korebuild_path" ]; then + rm -rf "$korebuild_path" + fi + + { + if [ ! -d "$korebuild_path" ]; then + mkdir -p "$korebuild_path" + local remote_path="$tools_source/korebuild/artifacts/$version/korebuild.$version.zip" + tmpfile="$(mktemp)" + echo -e "${MAGENTA}Downloading KoreBuild ${version}${RESET}" + if __get_remote_file "$remote_path" "$tmpfile" "$tools_source_suffix"; then + unzip -q -d "$korebuild_path" "$tmpfile" + fi + rm "$tmpfile" || true + fi + + source "$korebuild_path/KoreBuild.sh" + } || { + if [ -d "$korebuild_path" ]; then + echo "Cleaning up after failed installation" + rm -rf "$korebuild_path" || true + fi + return 1 + } +} + +__error() { + echo -e "${RED}error: $*${RESET}" 1>&2 +} + +__warn() { + echo -e "${YELLOW}warning: $*${RESET}" +} + +__machine_has() { + hash "$1" > /dev/null 2>&1 + return $? +} + +__get_remote_file() { + local remote_path=$1 + local local_path=$2 + local remote_path_suffix=$3 + + if [[ "$remote_path" != 'http'* ]]; then + cp "$remote_path" "$local_path" + return 0 + fi + + local failed=false + if __machine_has wget; then + wget --tries 10 --quiet -O "$local_path" "${remote_path}${remote_path_suffix}" || failed=true + else + failed=true + fi + + if [ "$failed" = true ] && __machine_has curl; then + failed=false + curl --retry 10 -sSL -f --create-dirs -o "$local_path" "${remote_path}${remote_path_suffix}" || failed=true + fi + + if [ "$failed" = true ]; then + __error "Download failed: $remote_path" 1>&2 + return 1 + fi +} + +# +# main +# + +command="${1:-}" +shift + +while [[ $# -gt 0 ]]; do + case $1 in + -\?|-h|--help) + __usage --no-exit + exit 0 + ;; + -c|--channel|-Channel) + shift + channel="${1:-}" + [ -z "$channel" ] && __usage + ;; + --config-file|-ConfigFile) + shift + config_file="${1:-}" + [ -z "$config_file" ] && __usage + if [ ! -f "$config_file" ]; then + __error "Invalid value for --config-file. $config_file does not exist." + exit 1 + fi + ;; + -d|--dotnet-home|-DotNetHome) + shift + DOTNET_HOME="${1:-}" + [ -z "$DOTNET_HOME" ] && __usage + ;; + --path|-Path) + shift + repo_path="${1:-}" + [ -z "$repo_path" ] && __usage + ;; + -s|--tools-source|-ToolsSource) + shift + tools_source="${1:-}" + [ -z "$tools_source" ] && __usage + ;; + --tools-source-suffix|-ToolsSourceSuffix) + shift + tools_source_suffix="${1:-}" + [ -z "$tools_source_suffix" ] && __usage + ;; + -u|--update|-Update) + update=true + ;; + --reinstall|-[Rr]einstall) + reinstall=true + ;; + --ci|-[Cc][Ii]) + ci=true + ;; + --verbose|-Verbose) + verbose=true + ;; + --) + shift + break + ;; + *) + break + ;; + esac + shift +done + +if ! __machine_has unzip; then + __error 'Missing required command: unzip' + exit 1 +fi + +if ! __machine_has curl && ! __machine_has wget; then + __error 'Missing required command. Either wget or curl is required.' + exit 1 +fi + +[ -z "${config_file:-}" ] && config_file="$repo_path/korebuild.json" +if [ -f "$config_file" ]; then + if __machine_has jq ; then + if jq '.' "$config_file" >/dev/null ; then + config_channel="$(jq -r 'select(.channel!=null) | .channel' "$config_file")" + config_tools_source="$(jq -r 'select(.toolsSource!=null) | .toolsSource' "$config_file")" + else + __error "$config_file contains invalid JSON." + exit 1 + fi + elif __machine_has python ; then + if python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then + config_channel="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" + config_tools_source="$(python -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" + else + __error "$config_file contains invalid JSON." + exit 1 + fi + elif __machine_has python3 ; then + if python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'))" >/dev/null ; then + config_channel="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['channel'] if 'channel' in obj else '')")" + config_tools_source="$(python3 -c "import json,codecs;obj=json.load(codecs.open('$config_file', 'r', 'utf-8-sig'));print(obj['toolsSource'] if 'toolsSource' in obj else '')")" + else + __error "$config_file contains invalid JSON." + exit 1 + fi + else + __error 'Missing required command: jq or python. Could not parse the JSON file.' + exit 1 + fi + + [ ! -z "${config_channel:-}" ] && channel="$config_channel" + [ ! -z "${config_tools_source:-}" ] && tools_source="$config_tools_source" +fi + +[ -z "$channel" ] && channel='master' +[ -z "$tools_source" ] && tools_source='https://aspnetcore.blob.core.windows.net/buildtools' + +get_korebuild +set_korebuildsettings "$tools_source" "$DOTNET_HOME" "$repo_path" "$config_file" "$ci" +invoke_korebuild_command "$command" "$@" diff --git a/samples/CookiePolicySample/CookiePolicySample.csproj b/samples/CookiePolicySample/CookiePolicySample.csproj new file mode 100644 index 000000000..010edb018 --- /dev/null +++ b/samples/CookiePolicySample/CookiePolicySample.csproj @@ -0,0 +1,18 @@ + + + + netcoreapp3.0 + + + + + + + + + + + + + + diff --git a/samples/CookiePolicySample/Program.cs b/samples/CookiePolicySample/Program.cs new file mode 100644 index 000000000..3fc09a3db --- /dev/null +++ b/samples/CookiePolicySample/Program.cs @@ -0,0 +1,26 @@ +using System.IO; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Logging; + +namespace CookiePolicySample +{ + public static class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .ConfigureLogging(factory => + { + factory.AddConsole(); + factory.AddFilter("Microsoft", LogLevel.Trace); + }) + .UseKestrel() + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + } +} diff --git a/samples/CookiePolicySample/Properties/launchSettings.json b/samples/CookiePolicySample/Properties/launchSettings.json new file mode 100644 index 000000000..38ca6fc37 --- /dev/null +++ b/samples/CookiePolicySample/Properties/launchSettings.json @@ -0,0 +1,27 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:1788/", + "sslPort": 0 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "CookieSample": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "http://localhost:12345", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/samples/CookiePolicySample/Startup.cs b/samples/CookiePolicySample/Startup.cs new file mode 100644 index 000000000..4dcc6d4dc --- /dev/null +++ b/samples/CookiePolicySample/Startup.cs @@ -0,0 +1,127 @@ +using System; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; + +namespace CookiePolicySample +{ + public class Startup + { + public void ConfigureServices(IServiceCollection services) + { + services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) + .AddCookie(); + services.Configure(options => + { + options.CheckConsentNeeded = context => context.Request.PathBase.Equals("/NeedsConsent"); + + options.OnAppendCookie = context => { }; + }); + } + + public void Configure(IApplicationBuilder app) + { + app.UseCookiePolicy(); + app.UseAuthentication(); + + app.Map("/NeedsConsent", NestedApp); + app.Map("/NeedsNoConsent", NestedApp); + NestedApp(app); + } + + private void NestedApp(IApplicationBuilder app) + { + app.Run(async context => + { + var path = context.Request.Path; + switch (path) + { + case "/Login": + var user = new ClaimsPrincipal(new ClaimsIdentity(new[] { new Claim(ClaimTypes.Name, "bob") }, + CookieAuthenticationDefaults.AuthenticationScheme)); + await context.SignInAsync(user); + break; + case "/Logout": + await context.SignOutAsync(); + break; + case "/CreateTempCookie": + context.Response.Cookies.Append("Temp", "1"); + break; + case "/RemoveTempCookie": + context.Response.Cookies.Delete("Temp"); + break; + case "/CreateEssentialCookie": + context.Response.Cookies.Append("EssentialCookie", "2", + new CookieOptions() { IsEssential = true }); + break; + case "/RemoveEssentialCookie": + context.Response.Cookies.Delete("EssentialCookie"); + break; + case "/GrantConsent": + context.Features.Get().GrantConsent(); + break; + case "/WithdrawConsent": + context.Features.Get().WithdrawConsent(); + break; + } + + // TODO: Debug log when cookie is suppressed + + await HomePage(context); + }); + } + + private async Task HomePage(HttpContext context) + { + var response = context.Response; + var cookies = context.Request.Cookies; + response.ContentType = "text/html"; + await response.WriteAsync("\r\n"); + + await response.WriteAsync($"Home
\r\n"); + await response.WriteAsync($"Login
\r\n"); + await response.WriteAsync($"Logout
\r\n"); + await response.WriteAsync($"Create Temp Cookie
\r\n"); + await response.WriteAsync($"Remove Temp Cookie
\r\n"); + await response.WriteAsync($"Create Essential Cookie
\r\n"); + await response.WriteAsync($"Remove Essential Cookie
\r\n"); + await response.WriteAsync($"Grant Consent
\r\n"); + await response.WriteAsync($"Withdraw Consent
\r\n"); + await response.WriteAsync("
\r\n"); + await response.WriteAsync($"Needs Consent
\r\n"); + await response.WriteAsync($"Needs No Consent
\r\n"); + await response.WriteAsync("
\r\n"); + + var feature = context.Features.Get(); + await response.WriteAsync($"Consent:
\r\n"); + await response.WriteAsync($" - IsNeeded: {feature.IsConsentNeeded}
\r\n"); + await response.WriteAsync($" - Has: {feature.HasConsent}
\r\n"); + await response.WriteAsync($" - Can Track: {feature.CanTrack}
\r\n"); + await response.WriteAsync("
\r\n"); + + await response.WriteAsync($"{cookies.Count} Request Cookies:
\r\n"); + foreach (var cookie in cookies) + { + await response.WriteAsync($" - {cookie.Key} = {cookie.Value}
\r\n"); + } + await response.WriteAsync("
\r\n"); + + var responseCookies = response.Headers[HeaderNames.SetCookie]; + await response.WriteAsync($"{responseCookies.Count} Response Cookies:
\r\n"); + foreach (var cookie in responseCookies) + { + await response.WriteAsync($" - {cookie}
\r\n"); + } + + await response.WriteAsync(""); + } + } +} diff --git a/samples/CookieSample/CookieSample.csproj b/samples/CookieSample/CookieSample.csproj index 64438de77..bc92d68a2 100644 --- a/samples/CookieSample/CookieSample.csproj +++ b/samples/CookieSample/CookieSample.csproj @@ -1,9 +1,7 @@ - - - net461;netcoreapp2.0 + netcoreapp3.0 @@ -11,12 +9,12 @@ - - - - - - + + + + + + diff --git a/samples/CookieSessionSample/CookieSessionSample.csproj b/samples/CookieSessionSample/CookieSessionSample.csproj index cb07f4385..6230c98a5 100644 --- a/samples/CookieSessionSample/CookieSessionSample.csproj +++ b/samples/CookieSessionSample/CookieSessionSample.csproj @@ -1,9 +1,7 @@ - - - net461;netcoreapp2.0 + netcoreapp3.0 @@ -11,12 +9,12 @@ - - - - - - + + + + + + diff --git a/samples/JwtBearerSample/JwtBearerSample.csproj b/samples/JwtBearerSample/JwtBearerSample.csproj index fe2a5fa9d..58bf5e2d9 100644 --- a/samples/JwtBearerSample/JwtBearerSample.csproj +++ b/samples/JwtBearerSample/JwtBearerSample.csproj @@ -1,9 +1,7 @@ - - - net461;netcoreapp2.0 + netcoreapp3.0 aspnet5-JwtBearerSample-20151210102827 @@ -12,12 +10,12 @@ - - - - - - + + + + + + diff --git a/samples/JwtBearerSample/Startup.cs b/samples/JwtBearerSample/Startup.cs index 6f2c5e2ec..8c4a63cad 100644 --- a/samples/JwtBearerSample/Startup.cs +++ b/samples/JwtBearerSample/Startup.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.IO; +using System.Runtime.ExceptionServices; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.AspNetCore.Builder; @@ -43,33 +44,13 @@ public Startup(IHostingEnvironment env) // For more information on how to configure your application, visit http://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { - // This can be removed after https://github.com/aspnet/IISIntegration/issues/371 - services.AddAuthentication(options => - { - options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; - options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; - }).AddJwtBearer(o => - { - // You also need to update /wwwroot/app/scripts/app.js - o.Authority = Configuration["jwt:authority"]; - o.Audience = Configuration["jwt:audience"]; - o.Events = new JwtBearerEvents() + services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) + .AddJwtBearer(o => { - OnAuthenticationFailed = c => - { - c.NoResult(); - - c.Response.StatusCode = 500; - c.Response.ContentType = "text/plain"; - if (Environment.IsDevelopment()) - { - // Debug only, in production do not share exceptions with the remote host. - return c.Response.WriteAsync(c.Exception.ToString()); - } - return c.Response.WriteAsync("An error occurred processing your authentication."); - } - }; - }); + // You also need to update /wwwroot/app/scripts/app.js + o.Authority = Configuration["oidc:authority"]; + o.Audience = Configuration["oidc:clientid"]; + }); } // This method gets called by the runtime. Use this method to configure the HTTP request pipeline. @@ -86,13 +67,16 @@ public void Configure(IApplicationBuilder app) app.Use(async (context, next) => { // Use this if there are multiple authentication schemes - // var user = await context.Authentication.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); - - var user = context.User; // We can do this because of there's only a single authentication scheme - if (user?.Identity?.IsAuthenticated ?? false) + var authResult = await context.AuthenticateAsync(JwtBearerDefaults.AuthenticationScheme); + if (authResult.Succeeded && authResult.Principal.Identity.IsAuthenticated) { await next(); } + else if (authResult.Failure != null) + { + // Rethrow, let the exception page handle it. + ExceptionDispatchInfo.Capture(authResult.Failure).Throw(); + } else { await context.ChallengeAsync(); diff --git a/samples/JwtBearerSample/wwwroot/index.html b/samples/JwtBearerSample/wwwroot/index.html index f71dccb69..4715ebd42 100644 --- a/samples/JwtBearerSample/wwwroot/index.html +++ b/samples/JwtBearerSample/wwwroot/index.html @@ -64,5 +64,6 @@

Todo List

+ diff --git a/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj b/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj index cd8b6976f..61754b60b 100644 --- a/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj +++ b/samples/OpenIdConnect.AzureAdSample/OpenIdConnect.AzureAdSample.csproj @@ -1,9 +1,7 @@ - - - + - net461;netcoreapp2.0 + netcoreapp3.0 aspnet5-OpenIdConnectSample-20151210110318 @@ -13,13 +11,13 @@ - - - - - - - + + + + + + + diff --git a/samples/OpenIdConnectSample/OpenIdConnectSample.csproj b/samples/OpenIdConnectSample/OpenIdConnectSample.csproj index b3d875d47..137ee47ba 100644 --- a/samples/OpenIdConnectSample/OpenIdConnectSample.csproj +++ b/samples/OpenIdConnectSample/OpenIdConnectSample.csproj @@ -1,9 +1,7 @@ - - - + - net461;netcoreapp2.0 + netcoreapp3.0 aspnet5-OpenIdConnectSample-20151210110318 @@ -17,15 +15,15 @@ - - - - - - - - - + + + + + + + + + diff --git a/samples/OpenIdConnectSample/Startup.cs b/samples/OpenIdConnectSample/Startup.cs index c05bc8b52..a2b5a9a5c 100644 --- a/samples/OpenIdConnectSample/Startup.cs +++ b/samples/OpenIdConnectSample/Startup.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Globalization; +using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net.Http; using System.Text.Encodings.Web; @@ -44,6 +45,8 @@ public Startup(IHostingEnvironment env) public void ConfigureServices(IServiceCollection services) { + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); + services.AddAuthentication(sharedOptions => { sharedOptions.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme; @@ -56,9 +59,14 @@ public void ConfigureServices(IServiceCollection services) o.ClientId = Configuration["oidc:clientid"]; o.ClientSecret = Configuration["oidc:clientsecret"]; // for code flow o.Authority = Configuration["oidc:authority"]; + o.ResponseType = OpenIdConnectResponseType.CodeIdToken; o.SaveTokens = true; o.GetClaimsFromUserInfoEndpoint = true; + o.AccessDeniedPath = "/access-denied-from-remote"; + + o.ClaimActions.MapAllExcept("aud", "iss", "iat", "nbf", "exp", "aio", "c_hash", "uti", "nonce"); + o.Events = new OpenIdConnectEvents() { OnAuthenticationFailed = c => @@ -119,6 +127,16 @@ await WriteHtmlAsync(response, async res => return; } + if (context.Request.Path.Equals("/access-denied-from-remote")) + { + await WriteHtmlAsync(response, async res => + { + await res.WriteAsync($"

Access Denied error received from the remote authorization server

"); + await res.WriteAsync("Home"); + }); + return; + } + if (context.Request.Path.Equals("/Account/AccessDenied")) { await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); @@ -218,11 +236,28 @@ await WriteHtmlAsync(response, async res => return; } + if (context.Request.Path.Equals("/login-challenge")) + { + // Challenge the user authentication, and force a login prompt by overwriting the + // "prompt". This could be used for example to require the user to re-enter their + // credentials at the authentication provider, to add an extra confirmation layer. + await context.ChallengeAsync(OpenIdConnectDefaults.AuthenticationScheme, new OpenIdConnectChallengeProperties() + { + Prompt = "login", + + // it is also possible to specify different scopes, e.g. + // Scope = new string[] { "openid", "profile", "other" } + }); + + return; + } + await WriteHtmlAsync(response, async res => { await res.WriteAsync($"

Hello Authenticated User {HtmlEncode(user.Identity.Name)}

"); await res.WriteAsync("Refresh tokens"); await res.WriteAsync("Restricted"); + await res.WriteAsync("Login challenge"); await res.WriteAsync("Sign Out"); await res.WriteAsync("Sign Out Remote"); diff --git a/samples/SocialSample/SocialSample.csproj b/samples/SocialSample/SocialSample.csproj index d35796f75..e1e2262b0 100644 --- a/samples/SocialSample/SocialSample.csproj +++ b/samples/SocialSample/SocialSample.csproj @@ -1,9 +1,7 @@ - - - + - net461;netcoreapp2.0 + netcoreapp3.0 aspnet5-SocialSample-20151210111056 @@ -24,15 +22,15 @@
- - - - - - - - - + + + + + + + + +
diff --git a/samples/SocialSample/Startup.cs b/samples/SocialSample/Startup.cs index 36a53b38a..b5032072b 100644 --- a/samples/SocialSample/Startup.cs +++ b/samples/SocialSample/Startup.cs @@ -59,6 +59,7 @@ public void ConfigureServices(IServiceCollection services) .AddCookie(o => o.LoginPath = new PathString("/login")) // You must first create an app with Facebook and add its ID and Secret to your user-secrets. // https://developers.facebook.com/apps/ + // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login .AddFacebook(o => { o.AppId = Configuration["facebook:appid"]; @@ -67,9 +68,15 @@ public void ConfigureServices(IServiceCollection services) o.Fields.Add("name"); o.Fields.Add("email"); o.SaveTokens = true; + o.Events = new OAuthEvents() + { + OnRemoteFailure = HandleOnRemoteFailure + }; }) // You must first create an app with Google and add its ID and Secret to your user-secrets. // https://console.developers.google.com/project + // https://developers.google.com/identity/protocols/OAuth2WebServer + // https://developers.google.com/+/web/people/ .AddOAuth("Google-AccessToken", "Google AccessToken only", o => { o.ClientId = Configuration["google:clientid"]; @@ -81,9 +88,15 @@ public void ConfigureServices(IServiceCollection services) o.Scope.Add("profile"); o.Scope.Add("email"); o.SaveTokens = true; + o.Events = new OAuthEvents() + { + OnRemoteFailure = HandleOnRemoteFailure + }; }) // You must first create an app with Google and add its ID and Secret to your user-secrets. // https://console.developers.google.com/project + // https://developers.google.com/identity/protocols/OAuth2WebServer + // https://developers.google.com/+/web/people/ .AddGoogle(o => { o.ClientId = Configuration["google:clientid"]; @@ -93,18 +106,14 @@ public void ConfigureServices(IServiceCollection services) o.SaveTokens = true; o.Events = new OAuthEvents() { - OnRemoteFailure = ctx => - { - ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message)); - ctx.HandleResponse(); - return Task.FromResult(0); - } + OnRemoteFailure = HandleOnRemoteFailure }; o.ClaimActions.MapJsonSubKey("urn:google:image", "image", "url"); o.ClaimActions.Remove(ClaimTypes.GivenName); }) // You must first create an app with Twitter and add its key and Secret to your user-secrets. // https://apps.twitter.com/ + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/access_token .AddTwitter(o => { o.ConsumerKey = Configuration["twitter:consumerkey"]; @@ -116,12 +125,7 @@ public void ConfigureServices(IServiceCollection services) o.ClaimActions.MapJsonKey("urn:twitter:profilepicture", "profile_image_url", ClaimTypes.Uri); o.Events = new TwitterEvents() { - OnRemoteFailure = ctx => - { - ctx.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(ctx.Failure.Message)); - ctx.HandleResponse(); - return Task.FromResult(0); - } + OnRemoteFailure = HandleOnRemoteFailure }; }) /* Azure AD app model v2 has restrictions that prevent the use of plain HTTP for redirect URLs. @@ -139,6 +143,10 @@ public void ConfigureServices(IServiceCollection services) o.TokenEndpoint = MicrosoftAccountDefaults.TokenEndpoint; o.Scope.Add("https://graph.microsoft.com/user.read"); o.SaveTokens = true; + o.Events = new OAuthEvents() + { + OnRemoteFailure = HandleOnRemoteFailure + }; }) // You must first create an app with Microsoft Account and add its ID and Secret to your user-secrets. // https://azure.microsoft.com/en-us/documentation/articles/active-directory-v2-app-registration/ @@ -148,6 +156,10 @@ public void ConfigureServices(IServiceCollection services) o.ClientSecret = Configuration["microsoftaccount:clientsecret"]; o.SaveTokens = true; o.Scope.Add("offline_access"); + o.Events = new OAuthEvents() + { + OnRemoteFailure = HandleOnRemoteFailure + }; }) // You must first create an app with GitHub and add its ID and Secret to your user-secrets. // https://github.com/settings/applications/ @@ -159,6 +171,10 @@ public void ConfigureServices(IServiceCollection services) o.AuthorizationEndpoint = "https://github.com/login/oauth/authorize"; o.TokenEndpoint = "https://github.com/login/oauth/access_token"; o.SaveTokens = true; + o.Events = new OAuthEvents() + { + OnRemoteFailure = HandleOnRemoteFailure + }; }) // You must first create an app with GitHub and add its ID and Secret to your user-secrets. // https://github.com/settings/applications/ @@ -180,6 +196,7 @@ public void ConfigureServices(IServiceCollection services) o.ClaimActions.MapJsonKey("urn:github:url", "url"); o.Events = new OAuthEvents { + OnRemoteFailure = HandleOnRemoteFailure, OnCreatingTicket = async context => { // Get the GitHub user @@ -198,6 +215,30 @@ public void ConfigureServices(IServiceCollection services) }); } + private async Task HandleOnRemoteFailure(RemoteFailureContext context) + { + context.Response.StatusCode = 500; + context.Response.ContentType = "text/html"; + await context.Response.WriteAsync(""); + await context.Response.WriteAsync("A remote failure has occurred: " + UrlEncoder.Default.Encode(context.Failure.Message) + "
"); + + if (context.Properties != null) + { + await context.Response.WriteAsync("Properties:
"); + foreach (var pair in context.Properties.Items) + { + await context.Response.WriteAsync($"-{ UrlEncoder.Default.Encode(pair.Key)}={ UrlEncoder.Default.Encode(pair.Value)}
"); + } + } + + await context.Response.WriteAsync("Home"); + await context.Response.WriteAsync(""); + + // context.Response.Redirect("/error?FailureMessage=" + UrlEncoder.Default.Encode(context.Failure.Message)); + + context.HandleResponse(); + } + public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); @@ -415,7 +456,7 @@ public void Configure(IApplicationBuilder app) } await response.WriteAsync("Tokens:
"); - + await response.WriteAsync("Access Token: " + await context.GetTokenAsync("access_token") + "
"); await response.WriteAsync("Refresh Token: " + await context.GetTokenAsync("refresh_token") + "
"); await response.WriteAsync("Token Type: " + await context.GetTokenAsync("token_type") + "
"); @@ -426,19 +467,19 @@ public void Configure(IApplicationBuilder app) }); } - private async Task GetOAuthOptionsAsync(HttpContext context, string currentAuthType) + private Task GetOAuthOptionsAsync(HttpContext context, string currentAuthType) { if (string.Equals(GoogleDefaults.AuthenticationScheme, currentAuthType)) { - return context.RequestServices.GetRequiredService>().Get(currentAuthType); + return Task.FromResult(context.RequestServices.GetRequiredService>().Get(currentAuthType)); } else if (string.Equals(MicrosoftAccountDefaults.AuthenticationScheme, currentAuthType)) { - return context.RequestServices.GetRequiredService>().Get(currentAuthType); + return Task.FromResult(context.RequestServices.GetRequiredService>().Get(currentAuthType)); } else if (string.Equals(FacebookDefaults.AuthenticationScheme, currentAuthType)) { - return context.RequestServices.GetRequiredService>().Get(currentAuthType); + return Task.FromResult(context.RequestServices.GetRequiredService>().Get(currentAuthType)); } throw new NotImplementedException(currentAuthType); diff --git a/samples/WsFedSample/Program.cs b/samples/WsFedSample/Program.cs new file mode 100644 index 000000000..40e1945c6 --- /dev/null +++ b/samples/WsFedSample/Program.cs @@ -0,0 +1,64 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Reflection; +using System.Security.Cryptography.X509Certificates; +using System.Threading.Tasks; +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.FileProviders; +using Microsoft.Extensions.Logging; + +namespace WsFedSample +{ + public class Program + { + public static void Main(string[] args) + { + var host = new WebHostBuilder() + .ConfigureLogging(factory => + { + factory.AddConsole(); + factory.AddDebug(); + factory.AddFilter("Console", level => level >= LogLevel.Information); + factory.AddFilter("Debug", level => level >= LogLevel.Information); + }) + .UseKestrel(options => + { + options.Listen(IPAddress.Loopback, 44307, listenOptions => + { + // Configure SSL + var serverCertificate = LoadCertificate(); + listenOptions.UseHttps(serverCertificate); + }); + }) + .UseContentRoot(Directory.GetCurrentDirectory()) + .UseIISIntegration() + .UseStartup() + .Build(); + + host.Run(); + } + + private static X509Certificate2 LoadCertificate() + { + var assembly = typeof(Startup).GetTypeInfo().Assembly; + var embeddedFileProvider = new EmbeddedFileProvider(assembly, "WsFedSample"); + var certificateFileInfo = embeddedFileProvider.GetFileInfo("compiler/resources/cert.pfx"); + using (var certificateStream = certificateFileInfo.CreateReadStream()) + { + byte[] certificatePayload; + using (var memoryStream = new MemoryStream()) + { + certificateStream.CopyTo(memoryStream); + certificatePayload = memoryStream.ToArray(); + } + + return new X509Certificate2(certificatePayload, "testPassword"); + } + } + } +} diff --git a/samples/WsFedSample/Properties/launchSettings.json b/samples/WsFedSample/Properties/launchSettings.json new file mode 100644 index 000000000..bdf80e248 --- /dev/null +++ b/samples/WsFedSample/Properties/launchSettings.json @@ -0,0 +1,28 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "https://localhost:44307/", + "sslPort": 44318 + } + }, + "profiles": { + "IIS Express": { + "commandName": "IISExpress", + "launchBrowser": true, + "launchUrl": "https://localhost:44307/", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + }, + "WsFedSample": { + "commandName": "Project", + "launchBrowser": true, + "applicationUrl": "https://localhost:44307/", + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + } + } + } +} \ No newline at end of file diff --git a/samples/WsFedSample/Startup.cs b/samples/WsFedSample/Startup.cs new file mode 100644 index 000000000..0fc32769e --- /dev/null +++ b/samples/WsFedSample/Startup.cs @@ -0,0 +1,168 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Authentication.WsFederation; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; + +namespace WsFedSample +{ + public class Startup + { + public Startup(IConfiguration configuration) + { + Configuration = configuration; + } + + public IConfiguration Configuration { get; } + + // This method gets called by the runtime. Use this method to add services to the container. + public void ConfigureServices(IServiceCollection services) + { + services.AddAuthentication(sharedOptions => + { + sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme; + }) + .AddWsFederation(options => + { + options.Wtrealm = "https://Tratcheroutlook.onmicrosoft.com/WsFedSample"; + options.MetadataAddress = "https://login.windows.net/cdc690f9-b6b8-4023-813a-bae7143d1f87/FederationMetadata/2007-06/FederationMetadata.xml"; + // options.CallbackPath = "/"; + // options.SkipUnrecognizedRequests = true; + }) + .AddCookie(); + } + + public void Configure(IApplicationBuilder app) + { + app.UseDeveloperExceptionPage(); + app.UseAuthentication(); + + app.Run(async context => + { + if (context.Request.Path.Equals("/signedout")) + { + await WriteHtmlAsync(context.Response, async res => + { + await res.WriteAsync($"

You have been signed out.

"); + await res.WriteAsync("Sign In"); + }); + return; + } + + if (context.Request.Path.Equals("/signout")) + { + await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + await WriteHtmlAsync(context.Response, async res => + { + await context.Response.WriteAsync($"

Signed out {HtmlEncode(context.User.Identity.Name)}

"); + await context.Response.WriteAsync("Sign In"); + }); + return; + } + + if (context.Request.Path.Equals("/signout-remote")) + { + // Redirects + await context.SignOutAsync(CookieAuthenticationDefaults.AuthenticationScheme); + await context.SignOutAsync(WsFederationDefaults.AuthenticationScheme, new AuthenticationProperties() + { + RedirectUri = "/signedout" + }); + return; + } + + if (context.Request.Path.Equals("/Account/AccessDenied")) + { + await WriteHtmlAsync(context.Response, async res => + { + await context.Response.WriteAsync($"

Access Denied for user {HtmlEncode(context.User.Identity.Name)} to resource '{HtmlEncode(context.Request.Query["ReturnUrl"])}'

"); + await context.Response.WriteAsync("Sign Out"); + }); + return; + } + + // DefaultAuthenticateScheme causes User to be set + var user = context.User; + + // This is what [Authorize] calls + // var user = await context.AuthenticateAsync(); + + // This is what [Authorize(ActiveAuthenticationSchemes = WsFederationDefaults.AuthenticationScheme)] calls + // var user = await context.AuthenticateAsync(WsFederationDefaults.AuthenticationScheme); + + // Not authenticated + if (user == null || !user.Identities.Any(identity => identity.IsAuthenticated)) + { + // This is what [Authorize] calls + await context.ChallengeAsync(); + + // This is what [Authorize(ActiveAuthenticationSchemes = WsFederationDefaults.AuthenticationScheme)] calls + // await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme); + + return; + } + + // Authenticated, but not authorized + if (context.Request.Path.Equals("/restricted") && !user.Identities.Any(identity => identity.HasClaim("special", "true"))) + { + await context.ForbidAsync(); + return; + } + + await WriteHtmlAsync(context.Response, async response => + { + await response.WriteAsync($"

Hello Authenticated User {HtmlEncode(user.Identity.Name)}

"); + await response.WriteAsync("Restricted"); + await response.WriteAsync("Sign Out"); + await response.WriteAsync("Sign Out Remote"); + + await response.WriteAsync("

Claims:

"); + await WriteTableHeader(response, new string[] { "Claim Type", "Value" }, context.User.Claims.Select(c => new string[] { c.Type, c.Value })); + }); + }); + } + + private static async Task WriteHtmlAsync(HttpResponse response, Func writeContent) + { + var bootstrap = ""; + + response.ContentType = "text/html"; + await response.WriteAsync($"{bootstrap}
"); + await writeContent(response); + await response.WriteAsync("
"); + } + + private static async Task WriteTableHeader(HttpResponse response, IEnumerable columns, IEnumerable> data) + { + await response.WriteAsync(""); + await response.WriteAsync(""); + foreach (var column in columns) + { + await response.WriteAsync($""); + } + await response.WriteAsync(""); + foreach (var row in data) + { + await response.WriteAsync(""); + foreach (var column in row) + { + await response.WriteAsync($""); + } + await response.WriteAsync(""); + } + await response.WriteAsync("
{HtmlEncode(column)}
{HtmlEncode(column)}
"); + } + + private static string HtmlEncode(string content) => + string.IsNullOrEmpty(content) ? string.Empty : HtmlEncoder.Default.Encode(content); + } +} diff --git a/samples/WsFedSample/WsFedSample.csproj b/samples/WsFedSample/WsFedSample.csproj new file mode 100644 index 000000000..e1191ac81 --- /dev/null +++ b/samples/WsFedSample/WsFedSample.csproj @@ -0,0 +1,27 @@ + + + + netcoreapp3.0 + + + + + + + + + + + + + + + + + + + + + + + diff --git a/samples/WsFedSample/compiler/resources/cert.pfx b/samples/WsFedSample/compiler/resources/cert.pfx new file mode 100644 index 000000000..7118908c2 Binary files /dev/null and b/samples/WsFedSample/compiler/resources/cert.pfx differ diff --git a/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs b/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs index 9b602383c..42cc4e2f0 100644 --- a/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs +++ b/shared/Microsoft.AspNetCore.ChunkingCookieManager.Sources/ChunkingCookieManager.cs @@ -44,7 +44,6 @@ public ChunkingCookieManager() // See http://browsercookielimits.x64.me/. // Leave at least 40 in case CookiePolicy tries to add 'secure', 'samesite=strict' and/or 'httponly'. ChunkSize = DefaultChunkSize; - ThrowForPartialCookies = true; } /// @@ -286,6 +285,7 @@ public void DeleteCookie(HttpContext context, string key, CookieOptions options) Path = options.Path, Domain = options.Domain, SameSite = options.SameSite, + IsEssential = options.IsEssential, Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), }); @@ -300,6 +300,7 @@ public void DeleteCookie(HttpContext context, string key, CookieOptions options) Path = options.Path, Domain = options.Domain, SameSite = options.SameSite, + IsEssential = options.IsEssential, Expires = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc), }); } diff --git a/src/Directory.Build.props b/src/Directory.Build.props new file mode 100644 index 000000000..1e0980f66 --- /dev/null +++ b/src/Directory.Build.props @@ -0,0 +1,7 @@ + + + + + + + diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs index 996c334a7..ff6bb3cbc 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationHandler.cs @@ -14,13 +14,10 @@ namespace Microsoft.AspNetCore.Authentication.Cookies { - public class CookieAuthenticationHandler : - AuthenticationHandler, - IAuthenticationSignInHandler, - IAuthenticationSignOutHandler + public class CookieAuthenticationHandler : SignInAuthenticationHandler { private const string HeaderValueNoCache = "no-cache"; - private const string HeaderValueMinusOne = "-1"; + private const string HeaderValueEpocDate = "Thu, 01 Jan 1970 00:00:00 GMT"; private const string SessionIdClaim = "Microsoft.AspNetCore.Authentication.Cookies-SessionId"; private bool _shouldRefresh; @@ -31,6 +28,7 @@ public class CookieAuthenticationHandler : private DateTimeOffset? _refreshExpiresUtc; private string _sessionKey; private Task _readCookieTask; + private AuthenticationTicket _refreshTicket; public CookieAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) @@ -87,7 +85,7 @@ private void CheckForRefresh(AuthenticationTicket ticket) } } - private void RequestRefresh(AuthenticationTicket ticket) + private void RequestRefresh(AuthenticationTicket ticket, ClaimsPrincipal replacedPrincipal = null) { var issuedUtc = ticket.Properties.IssuedUtc; var expiresUtc = ticket.Properties.ExpiresUtc; @@ -99,9 +97,28 @@ private void RequestRefresh(AuthenticationTicket ticket) _refreshIssuedUtc = currentUtc; var timeSpan = expiresUtc.Value.Subtract(issuedUtc.Value); _refreshExpiresUtc = currentUtc.Add(timeSpan); + _refreshTicket = CloneTicket(ticket, replacedPrincipal); } } + private AuthenticationTicket CloneTicket(AuthenticationTicket ticket, ClaimsPrincipal replacedPrincipal) + { + var principal = replacedPrincipal ?? ticket.Principal; + var newPrincipal = new ClaimsPrincipal(); + foreach (var identity in principal.Identities) + { + newPrincipal.AddIdentity(identity.Clone()); + } + + var newProperties = new AuthenticationProperties(); + foreach (var item in ticket.Properties.Items) + { + newProperties.Items[item.Key] = item.Value; + } + + return new AuthenticationTicket(newPrincipal, newProperties, ticket.AuthenticationScheme); + } + private async Task ReadCookieTicket() { var cookie = Options.CookieManager.GetRequestCookie(Context, Options.Cookie.Name); @@ -132,7 +149,6 @@ private async Task ReadCookieTicket() } var currentUtc = Clock.UtcNow; - var issuedUtc = ticket.Properties.IssuedUtc; var expiresUtc = ticket.Properties.ExpiresUtc; if (expiresUtc != null && expiresUtc.Value < currentUtc) @@ -168,7 +184,7 @@ protected override async Task HandleAuthenticateAsync() if (context.ShouldRenew) { - RequestRefresh(result.Ticket); + RequestRefresh(result.Ticket, context.Principal); } return AuthenticateResult.Success(new AuthenticationTicket(context.Principal, context.Properties, Scheme.Name)); @@ -191,7 +207,7 @@ protected virtual async Task FinishResponseAsync() return; } - var ticket = (await HandleAuthenticateOnceSafeAsync())?.Ticket; + var ticket = _refreshTicket; if (ticket != null) { var properties = ticket.Properties; @@ -234,7 +250,7 @@ protected virtual async Task FinishResponseAsync() } } - public async virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + protected async override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { if (user == null) { @@ -246,7 +262,7 @@ public async virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProper _signInCalled = true; // Process the request cookie to initialize members like _sessionKey. - var result = await EnsureCookieTicket(); + await EnsureCookieTicket(); var cookieOptions = BuildCookieOptions(); var signInContext = new CookieSigningInContext( @@ -321,14 +337,14 @@ public async virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProper Logger.SignedIn(Scheme.Name); } - public async virtual Task SignOutAsync(AuthenticationProperties properties) + protected async override Task HandleSignOutAsync(AuthenticationProperties properties) { properties = properties ?? new AuthenticationProperties(); _signOutCalled = true; // Process the request cookie to initialize members like _sessionKey. - var ticket = await EnsureCookieTicket(); + await EnsureCookieTicket(); var cookieOptions = BuildCookieOptions(); if (Options.SessionStore != null && _sessionKey != null) { @@ -360,7 +376,7 @@ private async Task ApplyHeaders(bool shouldRedirectToReturnUrl, AuthenticationPr { Response.Headers[HeaderNames.CacheControl] = HeaderValueNoCache; Response.Headers[HeaderNames.Pragma] = HeaderValueNoCache; - Response.Headers[HeaderNames.Expires] = HeaderValueMinusOne; + Response.Headers[HeaderNames.Expires] = HeaderValueEpocDate; if (shouldRedirectToReturnUrl && Response.StatusCode == 200) { @@ -406,7 +422,7 @@ protected override async Task HandleForbiddenAsync(AuthenticationProperties prop var returnUrl = properties.RedirectUri; if (string.IsNullOrEmpty(returnUrl)) { - returnUrl = OriginalPathBase + Request.Path + Request.QueryString; + returnUrl = OriginalPathBase + OriginalPath + Request.QueryString; } var accessDeniedUri = Options.AccessDeniedPath + QueryString.Create(Options.ReturnUrlParameter, returnUrl); var redirectContext = new RedirectContext(Context, Scheme, Options, properties, BuildRedirectUri(accessDeniedUri)); @@ -418,7 +434,7 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop var redirectUri = properties.RedirectUri; if (string.IsNullOrEmpty(redirectUri)) { - redirectUri = OriginalPathBase + Request.Path + Request.QueryString; + redirectUri = OriginalPathBase + OriginalPath + Request.QueryString; } var loginUri = Options.LoginPath + QueryString.Create(Options.ReturnUrlParameter, redirectUri); diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs index 0f2dbd312..78156caec 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/CookieAuthenticationOptions.cs @@ -21,6 +21,7 @@ public class CookieAuthenticationOptions : AuthenticationSchemeOptions SameSite = SameSiteMode.Lax, HttpOnly = true, SecurePolicy = CookieSecurePolicy.SameAsRequest, + IsEssential = true, }; /// @@ -80,11 +81,10 @@ public CookieBuilder Cookie public bool SlidingExpiration { get; set; } /// - /// The LoginPath property informs the handler that it should change an outgoing 401 Unauthorized status - /// code into a 302 redirection onto the given login path. The current url which generated the 401 is added - /// to the LoginPath as a query string parameter named by the ReturnUrlParameter. Once a request to the - /// LoginPath grants a new SignIn identity, the ReturnUrlParameter value is used to redirect the browser back - /// to the url which caused the original unauthorized status code. + /// The LoginPath property is used by the handler for the redirection target when handling ChallengeAsync. + /// The current url which is added to the LoginPath as a query string parameter named by the ReturnUrlParameter. + /// Once a request to the LoginPath grants a new SignIn identity, the ReturnUrlParameter value is used to redirect + /// the browser back to the original url. /// public PathString LoginPath { get; set; } @@ -94,16 +94,14 @@ public CookieBuilder Cookie public PathString LogoutPath { get; set; } /// - /// The AccessDeniedPath property informs the handler that it should change an outgoing 403 Forbidden status - /// code into a 302 redirection onto the given path. + /// The AccessDeniedPath property is used by the handler for the redirection target when handling ForbidAsync. /// public PathString AccessDeniedPath { get; set; } /// /// The ReturnUrlParameter determines the name of the query string parameter which is appended by the handler - /// when a 401 Unauthorized status code is changed to a 302 redirect onto the login path. This is also the query - /// string parameter looked for when a request arrives on the login path or logout path, in order to return to the - /// original url after the action is performed. + /// when during a Challenge. This is also the query string parameter looked for when a request arrives on the + /// login path or logout path, in order to return to the original url after the action is performed. /// public string ReturnUrlParameter { get; set; } @@ -149,68 +147,5 @@ public CookieBuilder Cookie /// /// public TimeSpan ExpireTimeSpan { get; set; } - - #region Obsolete API - /// - /// - /// This property is obsolete and will be removed in a future version. The recommended alternative is on . - /// - /// - /// Determines the cookie name used to persist the identity. The default value is ".AspNetCore.Cookies". - /// This value should be changed if you change the name of the AuthenticationScheme, especially if your - /// system uses the cookie authentication handler multiple times. - /// - /// - [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Name) + ".")] - public string CookieName { get => Cookie.Name; set => Cookie.Name = value; } - - /// - /// - /// This property is obsolete and will be removed in a future version. The recommended alternative is on . - /// - /// - /// Determines the domain used to create the cookie. Is not provided by default. - /// - /// - [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Domain) + ".")] - public string CookieDomain { get => Cookie.Domain; set => Cookie.Domain = value; } - - /// - /// - /// This property is obsolete and will be removed in a future version. The recommended alternative is on . - /// - /// - /// Determines the path used to create the cookie. The default value is "/" for highest browser compatibility. - /// - /// - [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.Path) + ".")] - public string CookiePath { get => Cookie.Path; set => Cookie.Path = value; } - - /// - /// - /// This property is obsolete and will be removed in a future version. The recommended alternative is on . - /// - /// - /// Determines if the browser should allow the cookie to be accessed by client-side javascript. The - /// default is true, which means the cookie will only be passed to http requests and is not made available - /// to script on the page. - /// - /// - [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.HttpOnly) + ".")] - public bool CookieHttpOnly { get => Cookie.HttpOnly; set => Cookie.HttpOnly = value; } - - /// - /// - /// This property is obsolete and will be removed in a future version. The recommended alternative is on . - /// - /// - /// Determines if the cookie should only be transmitted on HTTPS request. The default is to limit the cookie - /// to HTTPS requests if the page which is doing the SignIn is also HTTPS. If you have an HTTPS sign in page - /// and portions of your site are HTTP you may need to change this value. - /// - /// - [Obsolete("This property is obsolete and will be removed in a future version. The recommended alternative is " + nameof(Cookie) + "." + nameof(CookieBuilder.SecurePolicy) + ".")] - public CookieSecurePolicy CookieSecure { get => Cookie.SecurePolicy; set => Cookie.SecurePolicy = value; } - #endregion } } diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj b/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj index 712aa8177..093f60a3d 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/Microsoft.AspNetCore.Authentication.Cookies.csproj @@ -1,10 +1,8 @@  - - ASP.NET Core middleware that enables an application to use cookie based authentication. - netstandard2.0 + netcoreapp3.0 $(DefineConstants);SECURITY $(NoWarn);CS1591 true diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.Cookies/baseline.netcore.json index 56e48d3fe..b218669b7 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/baseline.netcore.json @@ -1,8 +1,8 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Cookies, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Cookies, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Builder.CookieAppBuilderExtensions", + "Name": "Microsoft.Extensions.DependencyInjection.CookieExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -12,14 +12,14 @@ "Members": [ { "Kind": "Method", - "Name": "UseCookieAuthentication", + "Name": "AddCookie", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, "Extension": true, "Visibility": "Public", @@ -27,323 +27,394 @@ }, { "Kind": "Method", - "Name": "UseCookieAuthentication", + "Name": "AddCookie", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" }, { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + "Name": "authenticationScheme", + "Type": "System.String" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.AuthenticationOptions", - "ImplementedInterfaces": [ - "Microsoft.Extensions.Options.IOptions" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_CookieName", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_CookieName", + "Name": "AddCookie", "Parameters": [ { - "Name": "value", - "Type": "System.String" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_CookieDomain", - "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_CookieDomain", + "Name": "AddCookie", "Parameters": [ { - "Name": "value", + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_CookiePath", - "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_CookiePath", + "Name": "AddCookie", "Parameters": [ { - "Name": "value", + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_CookieHttpOnly", - "Parameters": [], - "ReturnType": "System.Boolean", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.CookieAppBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "set_CookieHttpOnly", + "Name": "UseCookieAuthentication", "Parameters": [ { - "Name": "value", - "Type": "System.Boolean" + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_CookieSecure", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.CookieSecurePolicy", + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_CookieSecure", + "Name": "UseCookieAuthentication", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.CookieSecurePolicy" + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions" } ], - "ReturnType": "System.Void", + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ { - "Kind": "Method", - "Name": "get_DataProtectionProvider", + "Kind": "Field", + "Name": "CookiePrefix", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider", + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "set_DataProtectionProvider", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" - } - ], - "ReturnType": "System.Void", + "Kind": "Field", + "Name": "LoginPath", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_ExpireTimeSpan", + "Kind": "Field", + "Name": "LogoutPath", "Parameters": [], - "ReturnType": "System.TimeSpan", + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "set_ExpireTimeSpan", - "Parameters": [ - { - "Name": "value", - "Type": "System.TimeSpan" - } - ], - "ReturnType": "System.Void", + "Kind": "Field", + "Name": "AccessDeniedPath", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_SlidingExpiration", + "Kind": "Field", + "Name": "ReturnUrlParameter", "Parameters": [], - "ReturnType": "System.Boolean", + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "set_SlidingExpiration", - "Parameters": [ - { - "Name": "value", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Void", + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, "Visibility": "Public", - "GenericParameter": [] - }, + "GenericParameter": [], + "Constant": true, + "Literal": "\"Cookies\"" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.SignInAuthenticationHandler", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "get_LoginPath", + "Name": "get_Events", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Visibility": "Public", + "ReturnType": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_LoginPath", + "Name": "set_Events", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Http.PathString" + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents" } ], "ReturnType": "System.Void", - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_LogoutPath", + "Name": "InitializeHandlerAsync", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_LogoutPath", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.PathString" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", + "Name": "CreateEventsAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_AccessDeniedPath", + "Name": "HandleAuthenticateAsync", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_AccessDeniedPath", + "Name": "FinishResponseAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleSignInAsync", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.PathString" + "Name": "user", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_ReturnUrlParameter", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", + "Name": "HandleSignOutAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ReturnUrlParameter", + "Name": "HandleForbiddenAsync", "Parameters": [ { - "Name": "value", - "Type": "System.String" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Events", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", - "Visibility": "Public", + "Name": "HandleChallengeAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "set_Events", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents" + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], - "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "get_TicketDataFormat", + "Name": "get_Cookie", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "ReturnType": "Microsoft.AspNetCore.Http.CookieBuilder", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_TicketDataFormat", + "Name": "set_Cookie", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISecureDataFormat" + "Type": "Microsoft.AspNetCore.Http.CookieBuilder" } ], "ReturnType": "System.Void", @@ -352,19 +423,19 @@ }, { "Kind": "Method", - "Name": "get_SystemClock", + "Name": "get_DataProtectionProvider", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISystemClock", + "ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SystemClock", + "Name": "set_DataProtectionProvider", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" } ], "ReturnType": "System.Void", @@ -373,19 +444,19 @@ }, { "Kind": "Method", - "Name": "get_CookieManager", + "Name": "get_SlidingExpiration", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", + "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_CookieManager", + "Name": "set_SlidingExpiration", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager" + "Type": "System.Boolean" } ], "ReturnType": "System.Void", @@ -394,58 +465,40 @@ }, { "Kind": "Method", - "Name": "get_SessionStore", + "Name": "get_LoginPath", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.Cookies.ITicketStore", + "ReturnType": "Microsoft.AspNetCore.Http.PathString", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SessionStore", + "Name": "set_LoginPath", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.ITicketStore" + "Type": "Microsoft.AspNetCore.Http.PathString" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager" - ], - "Members": [ { "Kind": "Method", - "Name": "get_ChunkSize", + "Name": "get_LogoutPath", "Parameters": [], - "ReturnType": "System.Nullable", + "ReturnType": "Microsoft.AspNetCore.Http.PathString", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ChunkSize", + "Name": "set_LogoutPath", "Parameters": [ { "Name": "value", - "Type": "System.Nullable" + "Type": "Microsoft.AspNetCore.Http.PathString" } ], "ReturnType": "System.Void", @@ -454,19 +507,19 @@ }, { "Kind": "Method", - "Name": "get_ThrowForPartialCookies", + "Name": "get_AccessDeniedPath", "Parameters": [], - "ReturnType": "System.Boolean", + "ReturnType": "Microsoft.AspNetCore.Http.PathString", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ThrowForPartialCookies", + "Name": "set_AccessDeniedPath", "Parameters": [ { "Name": "value", - "Type": "System.Boolean" + "Type": "Microsoft.AspNetCore.Http.PathString" } ], "ReturnType": "System.Void", @@ -475,367 +528,239 @@ }, { "Kind": "Method", - "Name": "GetRequestCookie", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "key", - "Type": "System.String" - } - ], + "Name": "get_ReturnUrlParameter", + "Parameters": [], "ReturnType": "System.String", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "AppendResponseCookie", + "Name": "set_ReturnUrlParameter", "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "key", - "Type": "System.String" - }, { "Name": "value", "Type": "System.String" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Http.CookieOptions" } ], "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "DeleteCookie", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Events", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "key", - "Type": "System.String" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Http.CookieOptions" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents" } ], "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationDefaults", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Field", - "Name": "CookiePrefix", + "Kind": "Method", + "Name": "get_TicketDataFormat", "Parameters": [], - "ReturnType": "System.String", - "Static": true, - "ReadOnly": true, + "ReturnType": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "LoginPath", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Static": true, - "ReadOnly": true, + "Kind": "Method", + "Name": "set_TicketDataFormat", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.ISecureDataFormat" + } + ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "LogoutPath", + "Kind": "Method", + "Name": "get_CookieManager", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Static": true, - "ReadOnly": true, + "ReturnType": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "AccessDeniedPath", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Static": true, - "ReadOnly": true, + "Kind": "Method", + "Name": "set_CookieManager", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager" + } + ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "ReturnUrlParameter", + "Kind": "Method", + "Name": "get_SessionStore", "Parameters": [], - "ReturnType": "System.String", - "Static": true, - "ReadOnly": true, + "ReturnType": "Microsoft.AspNetCore.Authentication.Cookies.ITicketStore", "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Field", - "Name": "AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", - "GenericParameter": [], - "Constant": true, - "Literal": "\"Cookies\"" - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "ImplementedInterfaces": [], - "Members": [ { "Kind": "Method", - "Name": "CreateHandler", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", - "Virtual": true, - "Override": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", + "Name": "set_SessionStore", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" - }, - { - "Name": "dataProtectionProvider", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" - }, - { - "Name": "loggerFactory", - "Type": "Microsoft.Extensions.Logging.ILoggerFactory" - }, - { - "Name": "urlEncoder", - "Type": "System.Text.Encodings.Web.UrlEncoder" - }, - { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.ITicketStore" } ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "GetRequestCookie", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "key", - "Type": "System.String" - } - ], - "ReturnType": "System.String", + "Name": "get_ExpireTimeSpan", + "Parameters": [], + "ReturnType": "System.TimeSpan", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "AppendResponseCookie", + "Name": "set_ExpireTimeSpan", "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "key", - "Type": "System.String" - }, { "Name": "value", - "Type": "System.String" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Http.CookieOptions" + "Type": "System.TimeSpan" } ], "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "DeleteCookie", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "key", - "Type": "System.String" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Http.CookieOptions" - } - ], - "ReturnType": "System.Void", + "Name": "get_CookieName", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.ITicketStore", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "StoreAsync", + "Name": "set_CookieName", "Parameters": [ { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + "Name": "value", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RenewAsync", + "Name": "get_CookieDomain", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CookieDomain", "Parameters": [ { - "Name": "key", + "Name": "value", "Type": "System.String" - }, - { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RetrieveAsync", + "Name": "get_CookiePath", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CookiePath", "Parameters": [ { - "Name": "key", + "Name": "value", "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RemoveAsync", + "Name": "get_CookieHttpOnly", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CookieHttpOnly", "Parameters": [ { - "Name": "key", - "Type": "System.String" + "Name": "value", + "Type": "System.Boolean" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "get_Options", + "Name": "get_CookieSecure", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions", + "ReturnType": "Microsoft.AspNetCore.Http.CookieSecurePolicy", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "set_CookieSecure", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.CookieSecurePolicy" } ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], "Visibility": "Public", "GenericParameter": [] } @@ -846,9 +771,7 @@ "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents" - ], + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", @@ -938,7 +861,7 @@ "Kind": "Method", "Name": "get_OnRedirectToLogin", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.Func, System.Threading.Tasks.Task>", "Visibility": "Public", "GenericParameter": [] }, @@ -948,7 +871,7 @@ "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "System.Func, System.Threading.Tasks.Task>" } ], "ReturnType": "System.Void", @@ -959,7 +882,7 @@ "Kind": "Method", "Name": "get_OnRedirectToAccessDenied", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.Func, System.Threading.Tasks.Task>", "Visibility": "Public", "GenericParameter": [] }, @@ -969,7 +892,7 @@ "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "System.Func, System.Threading.Tasks.Task>" } ], "ReturnType": "System.Void", @@ -980,7 +903,7 @@ "Kind": "Method", "Name": "get_OnRedirectToLogout", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.Func, System.Threading.Tasks.Task>", "Visibility": "Public", "GenericParameter": [] }, @@ -990,7 +913,7 @@ "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "System.Func, System.Threading.Tasks.Task>" } ], "ReturnType": "System.Void", @@ -1001,7 +924,7 @@ "Kind": "Method", "Name": "get_OnRedirectToReturnUrl", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.Func, System.Threading.Tasks.Task>", "Visibility": "Public", "GenericParameter": [] }, @@ -1011,7 +934,7 @@ "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "System.Func, System.Threading.Tasks.Task>" } ], "ReturnType": "System.Void", @@ -1029,7 +952,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1044,7 +966,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1059,7 +980,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1074,7 +994,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1084,12 +1003,11 @@ "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Type": "Microsoft.AspNetCore.Authentication.RedirectContext" } ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1099,12 +1017,11 @@ "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Type": "Microsoft.AspNetCore.Authentication.RedirectContext" } ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1114,12 +1031,11 @@ "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Type": "Microsoft.AspNetCore.Authentication.RedirectContext" } ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1129,12 +1045,11 @@ "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Type": "Microsoft.AspNetCore.Authentication.RedirectContext" } ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1148,100 +1063,13 @@ ], "GenericParameters": [] }, - { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_RedirectUri", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_RedirectUri", - "Parameters": [ - { - "Name": "value", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" - }, - { - "Name": "redirectUri", - "Type": "System.String" - }, - { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, { "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieSignedInContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", + "BaseType": "Microsoft.AspNetCore.Authentication.PrincipalContext", "ImplementedInterfaces": [], "Members": [ - { - "Kind": "Method", - "Name": "get_AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Principal", - "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsPrincipal", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, { "Kind": "Constructor", "Name": ".ctor", @@ -1251,12 +1079,8 @@ "Type": "Microsoft.AspNetCore.Http.HttpContext" }, { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" - }, - { - "Name": "authenticationScheme", - "Type": "System.String" + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" }, { "Name": "principal", @@ -1264,7 +1088,11 @@ }, { "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions" } ], "Visibility": "Public", @@ -1277,59 +1105,9 @@ "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieSigningInContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", + "BaseType": "Microsoft.AspNetCore.Authentication.PrincipalContext", "ImplementedInterfaces": [], "Members": [ - { - "Kind": "Method", - "Name": "get_AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Principal", - "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsPrincipal", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Principal", - "Parameters": [ - { - "Name": "value", - "Type": "System.Security.Claims.ClaimsPrincipal" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Properties", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, { "Kind": "Method", "Name": "get_CookieOptions", @@ -1360,12 +1138,12 @@ "Type": "Microsoft.AspNetCore.Http.HttpContext" }, { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" }, { - "Name": "authenticationScheme", - "Type": "System.String" + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions" }, { "Name": "principal", @@ -1373,7 +1151,7 @@ }, { "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" }, { "Name": "cookieOptions", @@ -1390,7 +1168,7 @@ "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieSigningOutContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", + "BaseType": "Microsoft.AspNetCore.Authentication.PropertiesContext", "ImplementedInterfaces": [], "Members": [ { @@ -1414,27 +1192,6 @@ "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Properties", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, { "Kind": "Constructor", "Name": ".ctor", @@ -1443,13 +1200,17 @@ "Name": "context", "Type": "Microsoft.AspNetCore.Http.HttpContext" }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions" }, { "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" }, { "Name": "cookieOptions", @@ -1465,26 +1226,10 @@ { "Name": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext", "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Principal", - "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsPrincipal", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.PrincipalContext", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", "Name": "get_ShouldRenew", @@ -1536,12 +1281,16 @@ "Type": "Microsoft.AspNetCore.Http.HttpContext" }, { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.CookieAuthenticationOptions" + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions" + }, + { + "Name": "ticket", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], "Visibility": "Public", @@ -1551,7 +1300,7 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", + "Name": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", "Visibility": "Public", "Kind": "Interface", "Abstract": true, @@ -1559,47 +1308,97 @@ "Members": [ { "Kind": "Method", - "Name": "ValidatePrincipal", + "Name": "GetRequestCookie", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "key", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SigningIn", + "Name": "AppendResponseCookie", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieSigningInContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Http.CookieOptions" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SignedIn", + "Name": "DeleteCookie", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieSignedInContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Http.CookieOptions" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Cookies.ITicketStore", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "StoreAsync", + "Parameters": [ + { + "Name": "ticket", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + } + ], + "ReturnType": "System.Threading.Tasks.Task", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RedirectToLogout", + "Name": "RenewAsync", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Name": "key", + "Type": "System.String" + }, + { + "Name": "ticket", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], "ReturnType": "System.Threading.Tasks.Task", @@ -1607,55 +1406,216 @@ }, { "Kind": "Method", - "Name": "RedirectToLogin", + "Name": "RetrieveAsync", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Name": "key", + "Type": "System.String" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoveAsync", + "Parameters": [ + { + "Name": "key", + "Type": "System.String" } ], "ReturnType": "System.Threading.Tasks.Task", "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Cookies.PostConfigureCookieAuthenticationOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Options.IPostConfigureOptions" + ], + "Members": [ + { + "Kind": "Method", + "Name": "PostConfigure", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Options.IPostConfigureOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "dataProtection", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Cookies.ChunkingCookieManager", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_ChunkSize", + "Parameters": [], + "ReturnType": "System.Nullable", + "Visibility": "Public", + "GenericParameter": [] }, { "Kind": "Method", - "Name": "RedirectToReturnUrl", + "Name": "set_ChunkSize", + "Parameters": [ + { + "Name": "value", + "Type": "System.Nullable" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ThrowForPartialCookies", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ThrowForPartialCookies", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GetRequestCookie", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "key", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.String", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RedirectToAccessDenied", + "Name": "AppendResponseCookie", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "value", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Http.CookieOptions" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SigningOut", + "Name": "DeleteCookie", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Cookies.CookieSigningOutContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "key", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Http.CookieOptions" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Cookies.ICookieManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "DefaultChunkSize", + "Parameters": [], + "ReturnType": "System.Int32", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "4050" } ], "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Cookies/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.Cookies/breakingchanges.netcore.json index 91781d2dd..7a1ab7962 100644 --- a/src/Microsoft.AspNetCore.Authentication.Cookies/breakingchanges.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.Cookies/breakingchanges.netcore.json @@ -1,47 +1,52 @@ [ { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext : Microsoft.AspNetCore.Authentication.BaseContext", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public Microsoft.AspNetCore.Http.CookieSecurePolicy get_CookieSecure()", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationEvents : Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.Boolean get_CookieHttpOnly()", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationMiddleware : Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.String get_CookieDomain()", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Builder.CookieAuthenticationOptions : Microsoft.AspNetCore.Builder.AuthenticationOptions, Microsoft.Extensions.Options.IOptions", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.String get_CookieName()", "Kind": "Removal" }, { - "TypeId": "public interface Microsoft.AspNetCore.Authentication.Cookies.ICookieAuthenticationEvents", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.String get_CookiePath()", "Kind": "Removal" }, { - "TypeId": "public static class Microsoft.AspNetCore.Builder.CookieAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseCookieAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.CookieAuthenticationOptions options)", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.Void set_CookieDomain(System.String value)", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieRedirectContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.Void set_CookieHttpOnly(System.Boolean value)", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSignedInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.Void set_CookieName(System.String value)", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningInContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.Void set_CookiePath(System.String value)", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieSigningOutContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieValidatePrincipalContext : Microsoft.AspNetCore.Authentication.Cookies.BaseCookieContext", + "TypeId": "public class Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationOptions : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", + "MemberId": "public System.Void set_CookieSecure(Microsoft.AspNetCore.Http.CookieSecurePolicy value)", "Kind": "Removal" } ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs index 6143a4f23..ff9bb40bf 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookDefaults.cs @@ -9,10 +9,11 @@ public static class FacebookDefaults public static readonly string DisplayName = "Facebook"; - public static readonly string AuthorizationEndpoint = "https://www.facebook.com/v2.6/dialog/oauth"; + // https://developers.facebook.com/docs/facebook-login/manually-build-a-login-flow#login + public static readonly string AuthorizationEndpoint = "https://www.facebook.com/v3.1/dialog/oauth"; - public static readonly string TokenEndpoint = "https://graph.facebook.com/v2.6/oauth/access_token"; + public static readonly string TokenEndpoint = "https://graph.facebook.com/v3.1/oauth/access_token"; - public static readonly string UserInformationEndpoint = "https://graph.facebook.com/v2.6/me"; + public static readonly string UserInformationEndpoint = "https://graph.facebook.com/v3.1/me"; } } diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs index 9004fc09c..eb4251143 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookHandler.cs @@ -1,6 +1,7 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using System.Collections.Generic; using System.Globalization; using System.Net.Http; using System.Security.Claims; @@ -16,7 +17,7 @@ namespace Microsoft.AspNetCore.Authentication.Facebook { - internal class FacebookHandler : OAuthHandler + public class FacebookHandler : OAuthHandler { public FacebookHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) @@ -37,7 +38,7 @@ protected override async Task CreateTicketAsync(ClaimsIden var response = await Backchannel.GetAsync(endpoint, Context.RequestAborted); if (!response.IsSuccessStatusCode) { - throw new HttpRequestException($"Failed to retrieve Facebook user information ({response.StatusCode}) Please check if the authentication information is correct and the corresponding Facebook Graph API is enabled."); + throw new HttpRequestException($"An error occurred when retrieving Facebook user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Facebook Graph API is enabled."); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); @@ -65,12 +66,15 @@ private string GenerateAppSecretProof(string accessToken) } } - protected override string FormatScope() + protected override string FormatScope(IEnumerable scopes) { // Facebook deviates from the OAuth spec here. They require comma separated instead of space separated. // https://developers.facebook.com/docs/reference/dialogs/oauth // http://tools.ietf.org/html/rfc6749#section-3.3 - return string.Join(",", Options.Scope); + return string.Join(",", scopes); } + + protected override string FormatScope() + => base.FormatScope(); } } diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs index 7010bb20a..c2078a017 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/FacebookOptions.cs @@ -26,7 +26,6 @@ public FacebookOptions() AuthorizationEndpoint = FacebookDefaults.AuthorizationEndpoint; TokenEndpoint = FacebookDefaults.TokenEndpoint; UserInformationEndpoint = FacebookDefaults.UserInformationEndpoint; - Scope.Add("public_profile"); Scope.Add("email"); Fields.Add("name"); Fields.Add("email"); diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj b/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj index a9ec57199..f57fe26e0 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/Microsoft.AspNetCore.Authentication.Facebook.csproj @@ -1,10 +1,8 @@  - - ASP.NET Core middleware that enables an application to support Facebook's OAuth 2.0 authentication workflow. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.Facebook/Properties/Resources.Designer.cs index a3a2f2874..655da24a3 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/Properties/Resources.Designer.cs @@ -15,16 +15,14 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_OptionMustBeProvided { - get { return GetString("Exception_OptionMustBeProvided"); } + get => GetString("Exception_OptionMustBeProvided"); } /// /// The '{0}' option must be provided. /// internal static string FormatException_OptionMustBeProvided(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.Facebook/baseline.netcore.json index 1e070fc7f..5d95efca6 100644 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.Facebook/baseline.netcore.json @@ -1,61 +1,8 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Facebook, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Facebook, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Authentication.Facebook.FacebookDefaults", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Field", - "Name": "AuthorizationEndpoint", - "Parameters": [], - "ReturnType": "System.String", - "Static": true, - "ReadOnly": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Field", - "Name": "TokenEndpoint", - "Parameters": [], - "ReturnType": "System.String", - "Static": true, - "ReadOnly": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Field", - "Name": "UserInformationEndpoint", - "Parameters": [], - "ReturnType": "System.String", - "Static": true, - "ReadOnly": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Field", - "Name": "AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", - "GenericParameter": [], - "Constant": true, - "Literal": "\"Facebook\"" - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Facebook.FacebookHelper", + "Name": "Microsoft.Extensions.DependencyInjection.FacebookAuthenticationOptionsExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -65,215 +12,204 @@ "Members": [ { "Kind": "Method", - "Name": "GetId", + "Name": "AddFacebook", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" } ], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetAgeRangeMin", + "Name": "AddFacebook", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetAgeRangeMax", - "Parameters": [ + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetBirthday", + "Name": "AddFacebook", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetEmail", - "Parameters": [ + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GetFirstName", - "Parameters": [ + "Name": "authenticationScheme", + "Type": "System.String" + }, { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetGender", + "Name": "AddFacebook", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Facebook.FacebookDefaults", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ { - "Kind": "Method", - "Name": "GetLastName", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "DisplayName", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetLink", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "AuthorizationEndpoint", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetLocation", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "TokenEndpoint", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetLocale", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "UserInformationEndpoint", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetMiddleName", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], "ReturnType": "System.String", "Static": true, "Visibility": "Public", - "GenericParameter": [] - }, + "GenericParameter": [], + "Constant": true, + "Literal": "\"Facebook\"" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Facebook.FacebookHandler", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "GetName", + "Name": "CreateTicketAsync", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "tokens", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse" } ], - "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetTimeZone", + "Name": "FormatScope", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "scopes", + "Type": "System.Collections.Generic.IEnumerable" } ], "ReturnType": "System.String", - "Static": true, - "Visibility": "Public", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Facebook.FacebookMiddleware", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "CreateHandler", + "Name": "FormatScope", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "ReturnType": "System.String", "Virtual": true, "Override": true, "Visibility": "Protected", @@ -284,15 +220,11 @@ "Name": ".ctor", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" - }, - { - "Name": "dataProtectionProvider", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" }, { - "Name": "loggerFactory", + "Name": "logger", "Type": "Microsoft.Extensions.Logging.ILoggerFactory" }, { @@ -300,12 +232,8 @@ "Type": "System.Text.Encodings.Web.UrlEncoder" }, { - "Name": "sharedOptions", - "Type": "Microsoft.Extensions.Options.IOptions" - }, - { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], "Visibility": "Public", @@ -315,58 +243,22 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.FacebookAppBuilderExtensions", + "Name": "Microsoft.AspNetCore.Authentication.Facebook.FacebookOptions", "Visibility": "Public", "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "UseFacebookAuthentication", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, + "Name": "Validate", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Method", - "Name": "UseFacebookAuthentication", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.FacebookOptions" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Builder.FacebookOptions", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.OAuthOptions", - "ImplementedInterfaces": [], - "Members": [ { "Kind": "Method", "Name": "get_AppId", @@ -447,7 +339,52 @@ } ], "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.FacebookAppBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseFacebookAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UseFacebookAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Facebook.FacebookOptions" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Facebook/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.Facebook/breakingchanges.netcore.json deleted file mode 100644 index f64e6b834..000000000 --- a/src/Microsoft.AspNetCore.Authentication.Facebook/breakingchanges.netcore.json +++ /dev/null @@ -1,19 +0,0 @@ - [ - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Facebook.FacebookMiddleware : Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.FacebookOptions : Microsoft.AspNetCore.Builder.OAuthOptions", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.Facebook.FacebookHelper", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.FacebookAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseFacebookAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.FacebookOptions options)", - "Kind": "Removal" - } - ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleChallengeProperties.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleChallengeProperties.cs new file mode 100644 index 000000000..714df4565 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleChallengeProperties.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using Microsoft.AspNetCore.Authentication.OAuth; + +namespace Microsoft.AspNetCore.Authentication.Google +{ + public class GoogleChallengeProperties : OAuthChallengeProperties + { + /// + /// The parameter key for the "access_type" argument being used for a challenge request. + /// + public static readonly string AccessTypeKey = "access_type"; + + /// + /// The parameter key for the "approval_prompt" argument being used for a challenge request. + /// + public static readonly string ApprovalPromptKey = "approval_prompt"; + + /// + /// The parameter key for the "include_granted_scopes" argument being used for a challenge request. + /// + public static readonly string IncludeGrantedScopesKey = "include_granted_scopes"; + + /// + /// The parameter key for the "login_hint" argument being used for a challenge request. + /// + public static readonly string LoginHintKey = "login_hint"; + + /// + /// The parameter key for the "prompt" argument being used for a challenge request. + /// + public static readonly string PromptParameterKey = "prompt"; + + public GoogleChallengeProperties() + { } + + public GoogleChallengeProperties(IDictionary items) + : base(items) + { } + + public GoogleChallengeProperties(IDictionary items, IDictionary parameters) + : base(items, parameters) + { } + + /// + /// The "access_type" parameter value being used for a challenge request. + /// + public string AccessType + { + get => GetParameter(AccessTypeKey); + set => SetParameter(AccessTypeKey, value); + } + + /// + /// The "approval_prompt" parameter value being used for a challenge request. + /// + public string ApprovalPrompt + { + get => GetParameter(ApprovalPromptKey); + set => SetParameter(ApprovalPromptKey, value); + } + + /// + /// The "include_granted_scopes" parameter value being used for a challenge request. + /// + public bool? IncludeGrantedScopes + { + get => GetParameter(IncludeGrantedScopesKey); + set => SetParameter(IncludeGrantedScopesKey, value); + } + + /// + /// The "login_hint" parameter value being used for a challenge request. + /// + public string LoginHint + { + get => GetParameter(LoginHintKey); + set => SetParameter(LoginHintKey, value); + } + + /// + /// The "prompt" parameter value being used for a challenge request. + /// + public string Prompt + { + get => GetParameter(PromptParameterKey); + set => SetParameter(PromptParameterKey, value); + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs index e4bd66615..26b3b8f01 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleDefaults.cs @@ -12,10 +12,12 @@ public static class GoogleDefaults public static readonly string DisplayName = "Google"; - public static readonly string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/auth"; + // https://developers.google.com/identity/protocols/OAuth2WebServer + public static readonly string AuthorizationEndpoint = "https://accounts.google.com/o/oauth2/v2/auth"; public static readonly string TokenEndpoint = "https://www.googleapis.com/oauth2/v4/token"; + // https://developers.google.com/+/web/people/ public static readonly string UserInformationEndpoint = "https://www.googleapis.com/plus/v1/people/me"; } } diff --git a/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs b/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs index 7a2e1a2d1..88d48d446 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/GoogleHandler.cs @@ -16,7 +16,7 @@ namespace Microsoft.AspNetCore.Authentication.Google { - internal class GoogleHandler : OAuthHandler + public class GoogleHandler : OAuthHandler { public GoogleHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) @@ -34,7 +34,7 @@ protected override async Task CreateTicketAsync( var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { - throw new HttpRequestException($"An error occurred when retrieving user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Google+ API is enabled."); + throw new HttpRequestException($"An error occurred when retrieving Google user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Google+ API is enabled."); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); @@ -57,12 +57,12 @@ protected override string BuildChallengeUrl(AuthenticationProperties properties, queryStrings.Add("client_id", Options.ClientId); queryStrings.Add("redirect_uri", redirectUri); - AddQueryString(queryStrings, properties, "scope", FormatScope()); - AddQueryString(queryStrings, properties, "access_type", Options.AccessType); - AddQueryString(queryStrings, properties, "approval_prompt"); - AddQueryString(queryStrings, properties, "prompt"); - AddQueryString(queryStrings, properties, "login_hint"); - AddQueryString(queryStrings, properties, "include_granted_scopes"); + AddQueryString(queryStrings, properties, GoogleChallengeProperties.ScopeKey, FormatScope, Options.Scope); + AddQueryString(queryStrings, properties, GoogleChallengeProperties.AccessTypeKey, Options.AccessType); + AddQueryString(queryStrings, properties, GoogleChallengeProperties.ApprovalPromptKey); + AddQueryString(queryStrings, properties, GoogleChallengeProperties.PromptParameterKey); + AddQueryString(queryStrings, properties, GoogleChallengeProperties.LoginHintKey); + AddQueryString(queryStrings, properties, GoogleChallengeProperties.IncludeGrantedScopesKey, v => v?.ToString().ToLower(), (bool?)null); var state = Options.StateDataFormat.Protect(properties); queryStrings.Add("state", state); @@ -71,29 +71,38 @@ protected override string BuildChallengeUrl(AuthenticationProperties properties, return authorizationEndpoint; } - private static void AddQueryString( + private void AddQueryString( IDictionary queryStrings, AuthenticationProperties properties, string name, - string defaultValue = null) + Func formatter, + T defaultValue) { - string value; - if (!properties.Items.TryGetValue(name, out value)) + string value = null; + var parameterValue = properties.GetParameter(name); + if (parameterValue != null) { - value = defaultValue; + value = formatter(parameterValue); } - else + else if (!properties.Items.TryGetValue(name, out value)) { - // Remove the parameter from AuthenticationProperties so it won't be serialized to state parameter - properties.Items.Remove(name); + value = formatter(defaultValue); } - if (value == null) + // Remove the parameter from AuthenticationProperties so it won't be serialized into the state + properties.Items.Remove(name); + + if (value != null) { - return; + queryStrings[name] = value; } - - queryStrings[name] = value; } + + private void AddQueryString( + IDictionary queryStrings, + AuthenticationProperties properties, + string name, + string defaultValue = null) + => AddQueryString(queryStrings, properties, name, x => x, defaultValue); } } diff --git a/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj b/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj index 805de682f..eba5b98ad 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Google/Microsoft.AspNetCore.Authentication.Google.csproj @@ -1,10 +1,8 @@  - - ASP.NET Core contains middleware to support Google's OpenId and OAuth 2.0 authentication workflows. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.Google/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.Google/Properties/Resources.Designer.cs index 690c5a280..03448b408 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication.Google/Properties/Resources.Designer.cs @@ -15,32 +15,28 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_OptionMustBeProvided { - get { return GetString("Exception_OptionMustBeProvided"); } + get => GetString("Exception_OptionMustBeProvided"); } /// /// The '{0}' option must be provided. /// internal static string FormatException_OptionMustBeProvided(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string Exception_ValidatorHandlerMismatch { - get { return GetString("Exception_ValidatorHandlerMismatch"); } + get => GetString("Exception_ValidatorHandlerMismatch"); } /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string FormatException_ValidatorHandlerMismatch() - { - return GetString("Exception_ValidatorHandlerMismatch"); - } + => GetString("Exception_ValidatorHandlerMismatch"); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authentication.Google/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.Google/baseline.netcore.json index 647633afa..0a623b3b8 100644 --- a/src/Microsoft.AspNetCore.Authentication.Google/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.Google/baseline.netcore.json @@ -1,8 +1,8 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Google, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Google, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleDefaults", + "Name": "Microsoft.Extensions.DependencyInjection.GoogleExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -11,139 +11,286 @@ "ImplementedInterfaces": [], "Members": [ { - "Kind": "Field", - "Name": "AuthorizationEndpoint", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddGoogle", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "TokenEndpoint", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddGoogle", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "UserInformationEndpoint", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddGoogle", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddGoogle", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", - "GenericParameter": [], - "Constant": true, - "Literal": "\"Google\"" + "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleHelper", + "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleChallengeProperties", "Visibility": "Public", "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthChallengeProperties", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "GetId", + "Name": "get_AccessType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AccessType", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "value", + "Type": "System.String" } ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ApprovalPrompt", + "Parameters": [], "ReturnType": "System.String", - "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetName", + "Name": "set_ApprovalPrompt", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "value", + "Type": "System.String" } ], - "ReturnType": "System.String", - "Static": true, + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IncludeGrantedScopes", + "Parameters": [], + "ReturnType": "System.Nullable", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetGivenName", + "Name": "set_IncludeGrantedScopes", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "value", + "Type": "System.Nullable" } ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_LoginHint", + "Parameters": [], "ReturnType": "System.String", - "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetFamilyName", + "Name": "set_LoginHint", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "value", + "Type": "System.String" } ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Prompt", + "Parameters": [], "ReturnType": "System.String", - "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetProfile", + "Name": "set_Prompt", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "value", + "Type": "System.String" } ], - "ReturnType": "System.String", - "Static": true, + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetEmail", + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "items", + "Type": "System.Collections.Generic.IDictionary" } ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "items", + "Type": "System.Collections.Generic.IDictionary" + }, + { + "Name": "parameters", + "Type": "System.Collections.Generic.IDictionary" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "AccessTypeKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "ApprovalPromptKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "IncludeGrantedScopesKey", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "LoginHintKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "PromptParameterKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] } @@ -151,36 +298,127 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleMiddleware", + "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleDefaults", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware", + "Abstract": true, + "Static": true, + "Sealed": true, "ImplementedInterfaces": [], "Members": [ { - "Kind": "Method", - "Name": "CreateHandler", + "Kind": "Field", + "Name": "DisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "AuthorizationEndpoint", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "TokenEndpoint", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "UserInformationEndpoint", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"Google\"" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleHandler", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CreateTicketAsync", + "Parameters": [ + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "tokens", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse" + } + ], + "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, "Override": true, "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "BuildChallengeUrl", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" }, { - "Name": "dataProtectionProvider", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + "Name": "redirectUri", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" }, { - "Name": "loggerFactory", + "Name": "logger", "Type": "Microsoft.Extensions.Logging.ILoggerFactory" }, { @@ -188,12 +426,8 @@ "Type": "System.Text.Encodings.Web.UrlEncoder" }, { - "Name": "sharedOptions", - "Type": "Microsoft.Extensions.Options.IOptions" - }, - { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], "Visibility": "Public", @@ -203,7 +437,7 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.GoogleAppBuilderExtensions", + "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleHelper", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -213,35 +447,15 @@ "Members": [ { "Kind": "Method", - "Name": "UseGoogleAuthentication", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "UseGoogleAuthentication", + "Name": "GetEmail", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.GoogleOptions" + "Name": "user", + "Type": "Newtonsoft.Json.Linq.JObject" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "ReturnType": "System.String", "Static": true, - "Extension": true, "Visibility": "Public", "GenericParameter": [] } @@ -249,10 +463,10 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.GoogleOptions", + "Name": "Microsoft.AspNetCore.Authentication.Google.GoogleOptions", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.OAuthOptions", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions", "ImplementedInterfaces": [], "Members": [ { @@ -285,7 +499,52 @@ } ], "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.GoogleAppBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseGoogleAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UseGoogleAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Google.GoogleOptions" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Google/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.Google/breakingchanges.netcore.json deleted file mode 100644 index db3d1fcf0..000000000 --- a/src/Microsoft.AspNetCore.Authentication.Google/breakingchanges.netcore.json +++ /dev/null @@ -1,40 +0,0 @@ - [ - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Google.GoogleMiddleware : Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.GoogleOptions : Microsoft.AspNetCore.Builder.OAuthOptions", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.GoogleAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseGoogleAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.GoogleOptions options)", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.Google.GoogleHelper", - "MemberId": "public static System.String GetFamilyName(Newtonsoft.Json.Linq.JObject user)", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.Google.GoogleHelper", - "MemberId": "public static System.String GetGivenName(Newtonsoft.Json.Linq.JObject user)", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.Google.GoogleHelper", - "MemberId": "public static System.String GetId(Newtonsoft.Json.Linq.JObject user)", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.Google.GoogleHelper", - "MemberId": "public static System.String GetName(Newtonsoft.Json.Linq.JObject user)", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.Google.GoogleHelper", - "MemberId": "public static System.String GetProfile(Newtonsoft.Json.Linq.JObject user)", - "Kind": "Removal" - } - ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs index 9cf73182b..4f03ccd69 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/JwtBearerHandler.cs @@ -18,11 +18,11 @@ namespace Microsoft.AspNetCore.Authentication.JwtBearer { - internal class JwtBearerHandler : AuthenticationHandler + public class JwtBearerHandler : AuthenticationHandler { private OpenIdConnectConfiguration _configuration; - public JwtBearerHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, IDataProtectionProvider dataProtection, ISystemClock clock) + public JwtBearerHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) { } @@ -32,8 +32,8 @@ public JwtBearerHandler(IOptionsMonitor options, ILoggerFactor /// protected new JwtBearerEvents Events { - get { return (JwtBearerEvents)base.Events; } - set { base.Events = value; } + get => (JwtBearerEvents)base.Events; + set => base.Events = value; } protected override Task CreateEventsAsync() => Task.FromResult(new JwtBearerEvents()); @@ -110,7 +110,7 @@ protected override async Task HandleAuthenticateAsync() } catch (Exception ex) { - Logger.TokenValidationFailed(token, ex); + Logger.TokenValidationFailed(ex); // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event. if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null @@ -267,9 +267,8 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop private static string CreateErrorDescription(Exception authFailure) { IEnumerable exceptions; - if (authFailure is AggregateException) + if (authFailure is AggregateException agEx) { - var agEx = authFailure as AggregateException; exceptions = agEx.InnerExceptions; } else @@ -283,37 +282,32 @@ private static string CreateErrorDescription(Exception authFailure) { // Order sensitive, some of these exceptions derive from others // and we want to display the most specific message possible. - if (ex is SecurityTokenInvalidAudienceException) + switch (ex) { - messages.Add("The audience is invalid"); - } - else if (ex is SecurityTokenInvalidIssuerException) - { - messages.Add("The issuer is invalid"); - } - else if (ex is SecurityTokenNoExpirationException) - { - messages.Add("The token has no expiration"); - } - else if (ex is SecurityTokenInvalidLifetimeException) - { - messages.Add("The token lifetime is invalid"); - } - else if (ex is SecurityTokenNotYetValidException) - { - messages.Add("The token is not valid yet"); - } - else if (ex is SecurityTokenExpiredException) - { - messages.Add("The token is expired"); - } - else if (ex is SecurityTokenSignatureKeyNotFoundException) - { - messages.Add("The signature key was not found"); - } - else if (ex is SecurityTokenInvalidSignatureException) - { - messages.Add("The signature is invalid"); + case SecurityTokenInvalidAudienceException _: + messages.Add("The audience is invalid"); + break; + case SecurityTokenInvalidIssuerException _: + messages.Add("The issuer is invalid"); + break; + case SecurityTokenNoExpirationException _: + messages.Add("The token has no expiration"); + break; + case SecurityTokenInvalidLifetimeException _: + messages.Add("The token lifetime is invalid"); + break; + case SecurityTokenNotYetValidException _: + messages.Add("The token is not valid yet"); + break; + case SecurityTokenExpiredException _: + messages.Add("The token is expired"); + break; + case SecurityTokenSignatureKeyNotFoundException _: + messages.Add("The signature key was not found"); + break; + case SecurityTokenInvalidSignatureException _: + messages.Add("The signature is invalid"); + break; } } diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/LoggingExtensions.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/LoggingExtensions.cs index 008190f51..5c6ca088a 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/LoggingExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/LoggingExtensions.cs @@ -7,16 +7,16 @@ namespace Microsoft.Extensions.Logging { internal static class LoggingExtensions { - private static Action _tokenValidationFailed; + private static Action _tokenValidationFailed; private static Action _tokenValidationSucceeded; private static Action _errorProcessingMessage; static LoggingExtensions() { - _tokenValidationFailed = LoggerMessage.Define( + _tokenValidationFailed = LoggerMessage.Define( eventId: 1, logLevel: LogLevel.Information, - formatString: "Failed to validate the token {Token}."); + formatString: "Failed to validate the token."); _tokenValidationSucceeded = LoggerMessage.Define( eventId: 2, logLevel: LogLevel.Information, @@ -27,19 +27,13 @@ static LoggingExtensions() formatString: "Exception occurred while processing message."); } - public static void TokenValidationFailed(this ILogger logger, string token, Exception ex) - { - _tokenValidationFailed(logger, token, ex); - } + public static void TokenValidationFailed(this ILogger logger, Exception ex) + => _tokenValidationFailed(logger, ex); public static void TokenValidationSucceeded(this ILogger logger) - { - _tokenValidationSucceeded(logger, null); - } + => _tokenValidationSucceeded(logger, null); public static void ErrorProcessingMessage(this ILogger logger, Exception ex) - { - _errorProcessingMessage(logger, ex); - } + => _errorProcessingMessage(logger, ex); } } diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj b/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj index fe0bda164..8a26c58f3 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/Microsoft.AspNetCore.Authentication.JwtBearer.csproj @@ -1,14 +1,19 @@ - - + + ASP.NET Core middleware that enables an application to receive an OpenID Connect bearer token. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security + - + + + + + diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.JwtBearer/Properties/Resources.Designer.cs index ef1d784f2..e95b8e061 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/Properties/Resources.Designer.cs @@ -15,32 +15,28 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_OptionMustBeProvided { - get { return GetString("Exception_OptionMustBeProvided"); } + get => GetString("Exception_OptionMustBeProvided"); } /// /// The '{0}' option must be provided. /// internal static string FormatException_OptionMustBeProvided(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string Exception_ValidatorHandlerMismatch { - get { return GetString("Exception_ValidatorHandlerMismatch"); } + get => GetString("Exception_ValidatorHandlerMismatch"); } /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string FormatException_ValidatorHandlerMismatch() - { - return GetString("Exception_ValidatorHandlerMismatch"); - } + => GetString("Exception_ValidatorHandlerMismatch"); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.JwtBearer/baseline.netcore.json index 37e18e53e..d3839022b 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/baseline.netcore.json @@ -1,8 +1,8 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.JwtBearer, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.JwtBearer, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults", + "Name": "Microsoft.Extensions.DependencyInjection.JwtBearerExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -11,104 +11,86 @@ "ImplementedInterfaces": [], "Members": [ { - "Kind": "Field", - "Name": "AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddJwtBearer", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", - "GenericParameter": [], - "Constant": true, - "Literal": "\"Bearer\"" - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "CreateHandler", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", - "Virtual": true, - "Override": true, - "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "AddJwtBearer", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" - }, - { - "Name": "loggerFactory", - "Type": "Microsoft.Extensions.Logging.ILoggerFactory" - }, - { - "Name": "encoder", - "Type": "System.Text.Encodings.Web.UrlEncoder" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" }, { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "configureOptions", + "Type": "System.Action" } ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Exception", - "Parameters": [], - "ReturnType": "System.Exception", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Exception", + "Name": "AddJwtBearer", "Parameters": [ { - "Name": "value", - "Type": "System.Exception" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "AddJwtBearer", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" }, { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.JwtBearerOptions" + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] } @@ -116,33 +98,45 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", + "Name": "Microsoft.AspNetCore.Builder.JwtBearerAppBuilderExtensions", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseControlContext", + "Abstract": true, + "Static": true, + "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Options", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.JwtBearerOptions", + "Name": "UseJwtBearerAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "UseJwtBearerAuthentication", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.JwtBearerOptions" + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions" } ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] } @@ -150,58 +144,51 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", + "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext", "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.ResultContext", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "AuthenticationFailed", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", + "Name": "get_Exception", + "Parameters": [], + "ReturnType": "System.Exception", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "MessageReceived", + "Name": "set_Exception", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.MessageReceivedContext" + "Name": "value", + "Type": "System.Exception" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "TokenValidated", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Challenge", - "Parameters": [ + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext" + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions" } ], - "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", "GenericParameter": [] } ], @@ -211,17 +198,9 @@ "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", + "BaseType": "Microsoft.AspNetCore.Authentication.PropertiesContext", "ImplementedInterfaces": [], "Members": [ - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, { "Kind": "Method", "Name": "get_AuthenticateFailure", @@ -306,6 +285,22 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_Handled", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleResponse", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Constructor", "Name": ".ctor", @@ -314,13 +309,17 @@ "Name": "context", "Type": "Microsoft.AspNetCore.Http.HttpContext" }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.JwtBearerOptions" + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions" }, { "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "Visibility": "Public", @@ -333,9 +332,7 @@ "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents" - ], + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", @@ -432,7 +429,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -447,7 +443,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -462,7 +457,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -477,7 +471,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -495,7 +488,7 @@ "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.MessageReceivedContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", + "BaseType": "Microsoft.AspNetCore.Authentication.ResultContext", "ImplementedInterfaces": [], "Members": [ { @@ -527,9 +520,13 @@ "Name": "context", "Type": "Microsoft.AspNetCore.Http.HttpContext" }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.JwtBearerOptions" + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions" } ], "Visibility": "Public", @@ -542,7 +539,7 @@ "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", + "BaseType": "Microsoft.AspNetCore.Authentication.ResultContext", "ImplementedInterfaces": [], "Members": [ { @@ -574,9 +571,13 @@ "Name": "context", "Type": "Microsoft.AspNetCore.Http.HttpContext" }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.JwtBearerOptions" + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions" } ], "Visibility": "Public", @@ -586,45 +587,116 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.JwtBearerAppBuilderExtensions", + "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerDefaults", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"Bearer\"" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "UseJwtBearerAuthentication", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Events", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, - "Visibility": "Public", + "ReturnType": "System.Void", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "UseJwtBearerAuthentication", + "Name": "CreateEventsAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleAuthenticateAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleChallengeAsync", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - }, + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.JwtBearerOptions" + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "dataProtection", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, "Visibility": "Public", "GenericParameter": [] } @@ -632,10 +704,10 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.JwtBearerOptions", + "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.AuthenticationOptions", + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", "ImplementedInterfaces": [], "Members": [ { @@ -747,7 +819,7 @@ "Kind": "Method", "Name": "get_Events", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", + "ReturnType": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -757,7 +829,7 @@ "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents" + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents" } ], "ReturnType": "System.Void", @@ -869,27 +941,6 @@ "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Method", - "Name": "get_SystemClock", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISystemClock", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_SystemClock", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, { "Kind": "Method", "Name": "get_SecurityTokenValidators", @@ -970,7 +1021,44 @@ } ], "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerPostConfigureOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Options.IPostConfigureOptions" + ], + "Members": [ + { + "Kind": "Method", + "Name": "PostConfigure", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerOptions" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Options.IPostConfigureOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.JwtBearer/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.JwtBearer/breakingchanges.netcore.json index 223ff401b..d17003a80 100644 --- a/src/Microsoft.AspNetCore.Authentication.JwtBearer/breakingchanges.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.JwtBearer/breakingchanges.netcore.json @@ -1,43 +1,7 @@ [ { - "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerEvents : Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerMiddleware : Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.JwtBearerOptions : Microsoft.AspNetCore.Builder.AuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public interface Microsoft.AspNetCore.Authentication.JwtBearer.IJwtBearerEvents", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.JwtBearerAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseJwtBearerAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.JwtBearerOptions options)", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.AuthenticationFailedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext : Microsoft.AspNetCore.Authentication.BaseControlContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerChallengeContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.MessageReceivedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.TokenValidatedContext : Microsoft.AspNetCore.Authentication.JwtBearer.BaseJwtBearerContext", + "TypeId": "public class Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler : Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "MemberId": "public .ctor(Microsoft.Extensions.Options.IOptionsMonitor options, Microsoft.Extensions.Logging.ILoggerFactory logger, System.Text.Encodings.Web.UrlEncoder encoder, Microsoft.AspNetCore.DataProtection.IDataProtectionProvider dataProtection, Microsoft.AspNetCore.Authentication.ISystemClock clock)", "Kind": "Removal" } ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj index 9437b5727..1841157c9 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Microsoft.AspNetCore.Authentication.MicrosoftAccount.csproj @@ -1,10 +1,8 @@  - - ASP.NET Core middleware that enables an application to support the Microsoft Account authentication workflow. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs index 1b0859c5b..0421fa14b 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountDefaults.cs @@ -9,6 +9,7 @@ public static class MicrosoftAccountDefaults public static readonly string DisplayName = "Microsoft"; + // https://developer.microsoft.com/en-us/graph/docs/concepts/auth_v2_user public static readonly string AuthorizationEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/authorize"; public static readonly string TokenEndpoint = "https://login.microsoftonline.com/common/oauth2/v2.0/token"; diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs index 8204bf07b..bba547277 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/MicrosoftAccountHandler.cs @@ -13,7 +13,7 @@ namespace Microsoft.AspNetCore.Authentication.MicrosoftAccount { - internal class MicrosoftAccountHandler : OAuthHandler + public class MicrosoftAccountHandler : OAuthHandler { public MicrosoftAccountHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) @@ -27,7 +27,7 @@ protected override async Task CreateTicketAsync(ClaimsIden var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { - throw new HttpRequestException($"Failed to retrived Microsoft user information ({response.StatusCode}) Please check if the authentication information is correct and the corresponding Microsoft Account API is enabled."); + throw new HttpRequestException($"An error occurred when retrieving Microsoft user information ({response.StatusCode}). Please check if the authentication information is correct and the corresponding Microsoft Account API is enabled."); } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Properties/Resources.Designer.cs index 618d143ee..7ef5acecb 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/Properties/Resources.Designer.cs @@ -15,48 +15,42 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_MissingId { - get { return GetString("Exception_MissingId"); } + get => GetString("Exception_MissingId"); } /// /// The user does not have an id. /// internal static string FormatException_MissingId() - { - return GetString("Exception_MissingId"); - } + => GetString("Exception_MissingId"); /// /// The '{0}' option must be provided. /// internal static string Exception_OptionMustBeProvided { - get { return GetString("Exception_OptionMustBeProvided"); } + get => GetString("Exception_OptionMustBeProvided"); } /// /// The '{0}' option must be provided. /// internal static string FormatException_OptionMustBeProvided(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string Exception_ValidatorHandlerMismatch { - get { return GetString("Exception_ValidatorHandlerMismatch"); } + get => GetString("Exception_ValidatorHandlerMismatch"); } /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string FormatException_ValidatorHandlerMismatch() - { - return GetString("Exception_ValidatorHandlerMismatch"); - } + => GetString("Exception_ValidatorHandlerMismatch"); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/baseline.netcore.json index 06b3cd0d5..877e9035a 100644 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/baseline.netcore.json @@ -1,8 +1,8 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.MicrosoftAccount, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.MicrosoftAccount, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountDefaults", + "Name": "Microsoft.Extensions.DependencyInjection.MicrosoftAccountExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -11,51 +11,94 @@ "ImplementedInterfaces": [], "Members": [ { - "Kind": "Field", - "Name": "AuthorizationEndpoint", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddMicrosoftAccount", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "TokenEndpoint", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddMicrosoftAccount", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "UserInformationEndpoint", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddMicrosoftAccount", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddMicrosoftAccount", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", - "GenericParameter": [], - "Constant": true, - "Literal": "\"Microsoft\"" + "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountHelper", + "Name": "Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountDefaults", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -64,90 +107,84 @@ "ImplementedInterfaces": [], "Members": [ { - "Kind": "Method", - "Name": "GetId", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "DisplayName", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetDisplayName", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "AuthorizationEndpoint", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetGivenName", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "TokenEndpoint", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetSurname", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "UserInformationEndpoint", + "Parameters": [], "ReturnType": "System.String", "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "GetEmail", - "Parameters": [ - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], "ReturnType": "System.String", "Static": true, "Visibility": "Public", - "GenericParameter": [] + "GenericParameter": [], + "Constant": true, + "Literal": "\"Microsoft\"" } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountMiddleware", + "Name": "Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountHandler", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "CreateHandler", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "Name": "CreateTicketAsync", + "Parameters": [ + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "tokens", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse" + } + ], + "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, "Override": true, "Visibility": "Protected", @@ -158,15 +195,11 @@ "Name": ".ctor", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" - }, - { - "Name": "dataProtectionProvider", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" }, { - "Name": "loggerFactory", + "Name": "logger", "Type": "Microsoft.Extensions.Logging.ILoggerFactory" }, { @@ -174,12 +207,8 @@ "Type": "System.Text.Encodings.Web.UrlEncoder" }, { - "Name": "sharedOptions", - "Type": "Microsoft.Extensions.Options.IOptions" - }, - { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], "Visibility": "Public", @@ -188,6 +217,23 @@ ], "GenericParameters": [] }, + { + "Name": "Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountOptions", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, { "Name": "Microsoft.AspNetCore.Builder.MicrosoftAccountAppBuilderExtensions", "Visibility": "Public", @@ -222,7 +268,7 @@ }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.MicrosoftAccountOptions" + "Type": "Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountOptions" } ], "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", @@ -233,24 +279,6 @@ } ], "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Builder.MicrosoftAccountOptions", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.OAuthOptions", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/breakingchanges.netcore.json deleted file mode 100644 index 66f2a77b4..000000000 --- a/src/Microsoft.AspNetCore.Authentication.MicrosoftAccount/breakingchanges.netcore.json +++ /dev/null @@ -1,19 +0,0 @@ - [ - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountMiddleware : Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.MicrosoftAccountOptions : Microsoft.AspNetCore.Builder.OAuthOptions", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.MicrosoftAccount.MicrosoftAccountHelper", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.MicrosoftAccountAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseMicrosoftAccountAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.MicrosoftAccountOptions options)", - "Kind": "Removal" - } - ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimAction.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimAction.cs index 965ca5fdb..78b63bb38 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimAction.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimAction.cs @@ -31,10 +31,10 @@ public ClaimAction(string claimType, string valueType) public string ValueType { get; } /// - /// Exhamine the given userData json, determine if the requisite data is present, and optionally add it + /// Examine the given userData json, determine if the requisite data is present, and optionally add it /// as a new Claim on the ClaimsIdentity. /// - /// The source data to exhamine. This value may be null. + /// The source data to examine. This value may be null. /// The identity to add Claims to. /// The value to use for Claim.Issuer when creating a Claim. public abstract void Run(JObject userData, ClaimsIdentity identity, string issuer); diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimActionCollectionMapExtensions.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimActionCollectionMapExtensions.cs index f3fee6a22..5a178957a 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimActionCollectionMapExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/ClaimActionCollectionMapExtensions.cs @@ -87,6 +87,27 @@ public static void MapCustomJson(this ClaimActionCollection collection, string c collection.Add(new CustomJsonClaimAction(claimType, valueType, resolver)); } + /// + /// Clears any current ClaimsActions and maps all values from the json user data as claims, excluding duplicates. + /// + /// + public static void MapAll(this ClaimActionCollection collection) + { + collection.Clear(); + collection.Add(new MapAllClaimsAction()); + } + + /// + /// Clears any current ClaimsActions and maps all values from the json user data as claims, excluding the specified types. + /// + /// + /// + public static void MapAllExcept(this ClaimActionCollection collection, params string[] exclusions) + { + collection.MapAll(); + collection.DeleteClaims(exclusions); + } + /// /// Delete all claims from the given ClaimsIdentity with the given ClaimType. /// @@ -96,5 +117,23 @@ public static void DeleteClaim(this ClaimActionCollection collection, string cla { collection.Add(new DeleteClaimAction(claimType)); } + + /// + /// Delete all claims from the ClaimsIdentity with the given claimTypes. + /// + /// + /// + public static void DeleteClaims(this ClaimActionCollection collection, params string[] claimTypes) + { + if (claimTypes == null) + { + throw new ArgumentNullException(nameof(claimTypes)); + } + + foreach (var claimType in claimTypes) + { + collection.Add(new DeleteClaimAction(claimType)); + } + } } } diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/JsonKeyClaimAction.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/JsonKeyClaimAction.cs index e628904de..ccd1a965d 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/JsonKeyClaimAction.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/JsonKeyClaimAction.cs @@ -32,7 +32,22 @@ public JsonKeyClaimAction(string claimType, string valueType, string jsonKey) /// public override void Run(JObject userData, ClaimsIdentity identity, string issuer) { - var value = userData?.Value(JsonKey); + var value = userData?[JsonKey]; + if (value is JValue) + { + AddClaim(value?.ToString(), identity, issuer); + } + else if (value is JArray) + { + foreach (var v in value) + { + AddClaim(v?.ToString(), identity, issuer); + } + } + } + + private void AddClaim(string value, ClaimsIdentity identity, string issuer) + { if (!string.IsNullOrEmpty(value)) { identity.AddClaim(new Claim(ClaimType, value, ValueType, issuer)); diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/MapAllClaimsAction.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/MapAllClaimsAction.cs new file mode 100644 index 000000000..b3bf5d99f --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Claims/MapAllClaimsAction.cs @@ -0,0 +1,42 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNetCore.Authentication.OAuth.Claims +{ + /// + /// A ClaimAction that selects all top level values from the json user data and adds them as Claims. + /// This excludes duplicate sets of names and values. + /// + public class MapAllClaimsAction : ClaimAction + { + public MapAllClaimsAction() : base("All", ClaimValueTypes.String) + { + } + + public override void Run(JObject userData, ClaimsIdentity identity, string issuer) + { + if (userData == null) + { + return; + } + foreach (var pair in userData) + { + var claimValue = userData.TryGetValue(pair.Key, out var value) ? value.ToString() : null; + + // Avoid adding a claim if there's a duplicate name and value. This often happens in OIDC when claims are + // retrieved both from the id_token and from the user-info endpoint. + var duplicate = identity.FindFirst(c => string.Equals(c.Type, pair.Key, StringComparison.OrdinalIgnoreCase) + && string.Equals(c.Value, claimValue, StringComparison.Ordinal)) != null; + + if (!duplicate) + { + identity.AddClaim(new Claim(pair.Key, claimValue, ClaimValueTypes.String, issuer)); + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj b/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj index a15a75ee6..97d4a9703 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Microsoft.AspNetCore.Authentication.OAuth.csproj @@ -1,10 +1,8 @@ - - - + ASP.NET Core middleware that enables an application to support any standard OAuth 2.0 authentication workflow. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security @@ -12,7 +10,10 @@ - + + + + diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthChallengeProperties.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthChallengeProperties.cs new file mode 100644 index 000000000..fc768a8ac --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthChallengeProperties.cs @@ -0,0 +1,41 @@ +using System.Collections.Generic; + +namespace Microsoft.AspNetCore.Authentication.OAuth +{ + public class OAuthChallengeProperties : AuthenticationProperties + { + /// + /// The parameter key for the "scope" argument being used for a challenge request. + /// + public static readonly string ScopeKey = "scope"; + + public OAuthChallengeProperties() + { } + + public OAuthChallengeProperties(IDictionary items) + : base(items) + { } + + public OAuthChallengeProperties(IDictionary items, IDictionary parameters) + : base(items, parameters) + { } + + /// + /// The "scope" parameter value being used for a challenge request. + /// + public ICollection Scope + { + get => GetParameter>(ScopeKey); + set => SetParameter(ScopeKey, value); + } + + /// + /// Set the "scope" parameter value. + /// + /// List of scopes. + public virtual void SetScope(params string[] scopes) + { + Scope = scopes; + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs index 007d7dbef..05ba4df68 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthHandler.cs @@ -44,12 +44,35 @@ public OAuthHandler(IOptionsMonitor options, ILoggerFactory logger, Ur protected override async Task HandleRemoteAuthenticateAsync() { - AuthenticationProperties properties = null; var query = Request.Query; + var state = query["state"]; + var properties = Options.StateDataFormat.Unprotect(state); + + if (properties == null) + { + return HandleRequestResult.Fail("The oauth state was missing or invalid."); + } + + // OAuth2 10.12 CSRF + if (!ValidateCorrelationId(properties)) + { + return HandleRequestResult.Fail("Correlation failed.", properties); + } + var error = query["error"]; if (!StringValues.IsNullOrEmpty(error)) { + // Note: access_denied errors are special protocol errors indicating the user didn't + // approve the authorization demand requested by the remote authorization server. + // Since it's a frequent scenario (that is not caused by incorrect configuration), + // denied errors are handled differently using HandleAccessDeniedErrorAsync(). + // Visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information. + if (StringValues.Equals(error, "access_denied")) + { + return await HandleAccessDeniedErrorAsync(properties); + } + var failureMessage = new StringBuilder(); failureMessage.Append(error); var errorDescription = query["error_description"]; @@ -63,39 +86,26 @@ protected override async Task HandleRemoteAuthenticateAsync failureMessage.Append(";Uri=").Append(errorUri); } - return HandleRequestResult.Fail(failureMessage.ToString()); + return HandleRequestResult.Fail(failureMessage.ToString(), properties); } var code = query["code"]; - var state = query["state"]; - - properties = Options.StateDataFormat.Unprotect(state); - if (properties == null) - { - return HandleRequestResult.Fail("The oauth state was missing or invalid."); - } - - // OAuth2 10.12 CSRF - if (!ValidateCorrelationId(properties)) - { - return HandleRequestResult.Fail("Correlation failed."); - } if (StringValues.IsNullOrEmpty(code)) { - return HandleRequestResult.Fail("Code was not found."); + return HandleRequestResult.Fail("Code was not found.", properties); } var tokens = await ExchangeCodeAsync(code, BuildRedirectUri(Options.CallbackPath)); if (tokens.Error != null) { - return HandleRequestResult.Fail(tokens.Error); + return HandleRequestResult.Fail(tokens.Error, properties); } if (string.IsNullOrEmpty(tokens.AccessToken)) { - return HandleRequestResult.Fail("Failed to retrieve access token."); + return HandleRequestResult.Fail("Failed to retrieve access token.", properties); } var identity = new ClaimsIdentity(ClaimsIssuer); @@ -141,7 +151,7 @@ protected override async Task HandleRemoteAuthenticateAsync } else { - return HandleRequestResult.Fail("Failed to retrieve user information from remote server."); + return HandleRequestResult.Fail("Failed to retrieve user information from remote server.", properties); } } @@ -194,7 +204,7 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop { if (string.IsNullOrEmpty(properties.RedirectUri)) { - properties.RedirectUri = CurrentUri; + properties.RedirectUri = OriginalPathBase + OriginalPath + Request.QueryString; } // OAuth2 10.12 CSRF @@ -209,7 +219,8 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop protected virtual string BuildChallengeUrl(AuthenticationProperties properties, string redirectUri) { - var scope = FormatScope(); + var scopeParameter = properties.GetParameter>(OAuthChallengeProperties.ScopeKey); + var scope = scopeParameter != null ? FormatScope(scopeParameter) : FormatScope(); var state = Options.StateDataFormat.Protect(properties); var parameters = new Dictionary @@ -223,10 +234,20 @@ protected virtual string BuildChallengeUrl(AuthenticationProperties properties, return QueryHelpers.AddQueryString(Options.AuthorizationEndpoint, parameters); } + /// + /// Format a list of OAuth scopes. + /// + /// List of scopes. + /// Formatted scopes. + protected virtual string FormatScope(IEnumerable scopes) + => string.Join(" ", scopes); // OAuth2 3.3 space separated + + /// + /// Format the property. + /// + /// Formatted scopes. + /// Subclasses should rather override . protected virtual string FormatScope() - { - // OAuth2 3.3 space separated - return string.Join(" ", Options.Scope); - } + => FormatScope(Options.Scope); } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs index 3c71f055f..4eacbd0b3 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/OAuthOptions.cs @@ -3,11 +3,9 @@ using System; using System.Collections.Generic; -using Microsoft.AspNetCore.Authentication; -using Microsoft.AspNetCore.Authentication.OAuth; -using Microsoft.AspNetCore.Authentication.OAuth.Claims; -using Microsoft.AspNetCore.Http.Authentication; using System.Globalization; +using Microsoft.AspNetCore.Authentication.OAuth.Claims; +using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Authentication.OAuth { diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.OAuth/Properties/Resources.Designer.cs index 00c7b848b..5a38ade0b 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/Properties/Resources.Designer.cs @@ -15,32 +15,28 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_OptionMustBeProvided { - get { return GetString("Exception_OptionMustBeProvided"); } + get => GetString("Exception_OptionMustBeProvided"); } /// /// The '{0}' option must be provided. /// internal static string FormatException_OptionMustBeProvided(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string Exception_ValidatorHandlerMismatch { - get { return GetString("Exception_ValidatorHandlerMismatch"); } + get => GetString("Exception_ValidatorHandlerMismatch"); } /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string FormatException_ValidatorHandlerMismatch() - { - return GetString("Exception_ValidatorHandlerMismatch"); - } + => GetString("Exception_ValidatorHandlerMismatch"); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.OAuth/baseline.netcore.json index d485aedb1..9c2394704 100644 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.OAuth/baseline.netcore.json @@ -1,6 +1,870 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.OAuth, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.OAuth, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.OAuthExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddOAuth", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddOAuth", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddOAuth", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler" + ] + } + ] + }, + { + "Kind": "Method", + "Name": "AddOAuth", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler" + ] + } + ] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.DependencyInjection.OAuthPostConfigureOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Options.IPostConfigureOptions" + ], + "Members": [ + { + "Kind": "Method", + "Name": "PostConfigure", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "T0" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Options.IPostConfigureOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "dataProtection", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Builder.OAuthAppBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseOAuthAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "UseOAuthAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.ClaimActionCollectionMapExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "MapJsonKey", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "jsonKey", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapJsonKey", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "jsonKey", + "Type": "System.String" + }, + { + "Name": "valueType", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapJsonSubKey", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "jsonKey", + "Type": "System.String" + }, + { + "Name": "subKey", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapJsonSubKey", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "jsonKey", + "Type": "System.String" + }, + { + "Name": "subKey", + "Type": "System.String" + }, + { + "Name": "valueType", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapCustomJson", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "resolver", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapCustomJson", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "valueType", + "Type": "System.String" + }, + { + "Name": "resolver", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapAll", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MapAllExcept", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "exclusions", + "Type": "System.String[]", + "IsParams": true + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteClaim", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "DeleteClaims", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimTypes", + "Type": "System.String[]", + "IsParams": true + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthCreatingTicketContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.ResultContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_User", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Linq.JObject", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TokenResponse", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AccessToken", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TokenType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RefreshToken", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ExpiresIn", + "Parameters": [], + "ReturnType": "System.Nullable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Backchannel", + "Parameters": [], + "ReturnType": "System.Net.Http.HttpClient", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Identity", + "Parameters": [], + "ReturnType": "System.Security.Claims.ClaimsIdentity", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RunClaimActions", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RunClaimActions", + "Parameters": [ + { + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions" + }, + { + "Name": "backchannel", + "Type": "System.Net.Http.HttpClient" + }, + { + "Name": "tokens", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions" + }, + { + "Name": "backchannel", + "Type": "System.Net.Http.HttpClient" + }, + { + "Name": "tokens", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse" + }, + { + "Name": "user", + "Type": "Newtonsoft.Json.Linq.JObject" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_OnCreatingTicket", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnCreatingTicket", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnRedirectToAuthorizationEndpoint", + "Parameters": [], + "ReturnType": "System.Func, System.Threading.Tasks.Task>", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnRedirectToAuthorizationEndpoint", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func, System.Threading.Tasks.Task>" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreatingTicket", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthCreatingTicketContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RedirectToAuthorizationEndpoint", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.RedirectContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthChallengeProperties", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Scope", + "Parameters": [], + "ReturnType": "System.Collections.Generic.ICollection", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Scope", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.ICollection" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SetScope", + "Parameters": [ + { + "Name": "scopes", + "Type": "System.String[]", + "IsParams": true + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "items", + "Type": "System.Collections.Generic.IDictionary" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "items", + "Type": "System.Collections.Generic.IDictionary" + }, + { + "Name": "parameters", + "Type": "System.Collections.Generic.IDictionary" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "ScopeKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthDefaults", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "DisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, { "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler", "Visibility": "Public", @@ -16,11 +880,42 @@ "Visibility": "Protected", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Events", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateEventsAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, { "Kind": "Method", "Name": "HandleRemoteAuthenticateAsync", "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, "Override": true, "Visibility": "Protected", @@ -54,7 +949,7 @@ }, { "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" }, { "Name": "tokens", @@ -68,14 +963,14 @@ }, { "Kind": "Method", - "Name": "HandleUnauthorizedAsync", + "Name": "HandleChallengeAsync", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, "Override": true, "Visibility": "Protected", @@ -87,7 +982,7 @@ "Parameters": [ { "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" }, { "Name": "redirectUri", @@ -102,57 +997,23 @@ { "Kind": "Method", "Name": "FormatScope", - "Parameters": [], - "ReturnType": "System.String", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", "Parameters": [ { - "Name": "backchannel", - "Type": "System.Net.Http.HttpClient" + "Name": "scopes", + "Type": "System.Collections.Generic.IEnumerable" } ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "TOptions", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [ - "Microsoft.AspNetCore.Builder.OAuthOptions" - ] - } - ] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Backchannel", - "Parameters": [], - "ReturnType": "System.Net.Http.HttpClient", + "ReturnType": "System.String", + "Virtual": true, "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "CreateHandler", + "Name": "FormatScope", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "ReturnType": "System.String", "Virtual": true, - "Override": true, "Visibility": "Protected", "GenericParameter": [] }, @@ -161,15 +1022,11 @@ "Name": ".ctor", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" - }, - { - "Name": "dataProtectionProvider", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" }, { - "Name": "loggerFactory", + "Name": "logger", "Type": "Microsoft.Extensions.Logging.ILoggerFactory" }, { @@ -177,12 +1034,8 @@ "Type": "System.Text.Encodings.Web.UrlEncoder" }, { - "Name": "sharedOptions", - "Type": "Microsoft.Extensions.Options.IOptions" - }, - { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], "Visibility": "Public", @@ -195,60 +1048,64 @@ "ParameterPosition": 0, "New": true, "BaseTypeOrInterfaces": [ - "Microsoft.AspNetCore.Builder.OAuthOptions" + "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions" ] } ] }, { - "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", + "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthOptions", "Visibility": "Public", "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "Success", - "Parameters": [ - { - "Name": "response", - "Type": "Newtonsoft.Json.Linq.JObject" - } - ], - "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", - "Static": true, + "Name": "Validate", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ClientId", + "Parameters": [], + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Failed", + "Name": "set_ClientId", "Parameters": [ { - "Name": "error", - "Type": "System.Exception" + "Name": "value", + "Type": "System.String" } ], - "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", - "Static": true, + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Response", + "Name": "get_ClientSecret", "Parameters": [], - "ReturnType": "Newtonsoft.Json.Linq.JObject", + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Response", + "Name": "set_ClientSecret", "Parameters": [ { "Name": "value", - "Type": "Newtonsoft.Json.Linq.JObject" + "Type": "System.String" } ], "ReturnType": "System.Void", @@ -257,7 +1114,7 @@ }, { "Kind": "Method", - "Name": "get_AccessToken", + "Name": "get_AuthorizationEndpoint", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -265,7 +1122,7 @@ }, { "Kind": "Method", - "Name": "set_AccessToken", + "Name": "set_AuthorizationEndpoint", "Parameters": [ { "Name": "value", @@ -278,7 +1135,7 @@ }, { "Kind": "Method", - "Name": "get_TokenType", + "Name": "get_TokenEndpoint", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -286,7 +1143,7 @@ }, { "Kind": "Method", - "Name": "set_TokenType", + "Name": "set_TokenEndpoint", "Parameters": [ { "Name": "value", @@ -299,7 +1156,7 @@ }, { "Kind": "Method", - "Name": "get_RefreshToken", + "Name": "get_UserInformationEndpoint", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -307,7 +1164,7 @@ }, { "Kind": "Method", - "Name": "set_RefreshToken", + "Name": "set_UserInformationEndpoint", "Parameters": [ { "Name": "value", @@ -320,19 +1177,19 @@ }, { "Kind": "Method", - "Name": "get_ExpiresIn", + "Name": "get_Events", "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ExpiresIn", + "Name": "set_Events", "Parameters": [ { "Name": "value", - "Type": "System.String" + "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents" } ], "ReturnType": "System.Void", @@ -341,82 +1198,88 @@ }, { "Kind": "Method", - "Name": "get_Error", + "Name": "get_ClaimActions", "Parameters": [], - "ReturnType": "System.Exception", + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Error", + "Name": "get_Scope", + "Parameters": [], + "ReturnType": "System.Collections.Generic.ICollection", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_StateDataFormat", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_StateDataFormat", "Parameters": [ { "Name": "value", - "Type": "System.Exception" + "Type": "Microsoft.AspNetCore.Authentication.ISecureDataFormat" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents", + "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents" - ], + "Kind": "Class", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "CreatingTicket", + "Name": "Success", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthCreatingTicketContext" + "Name": "response", + "Type": "Newtonsoft.Json.Linq.JObject" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", + "Static": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RedirectToAuthorizationEndpoint", + "Name": "Failed", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext" + "Name": "error", + "Type": "System.Exception" } ], - "ReturnType": "System.Threading.Tasks.Task", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthCreatingTicketContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Options", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.OAuthOptions", + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", + "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_User", + "Name": "get_Response", "Parameters": [], "ReturnType": "Newtonsoft.Json.Linq.JObject", "Visibility": "Public", @@ -424,9 +1287,14 @@ }, { "Kind": "Method", - "Name": "get_TokenResponse", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse", + "Name": "set_Response", + "Parameters": [ + { + "Name": "value", + "Type": "Newtonsoft.Json.Linq.JObject" + } + ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, @@ -440,15 +1308,20 @@ }, { "Kind": "Method", - "Name": "get_TokenType", - "Parameters": [], - "ReturnType": "System.String", + "Name": "set_AccessToken", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_RefreshToken", + "Name": "get_TokenType", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -456,35 +1329,32 @@ }, { "Kind": "Method", - "Name": "get_ExpiresIn", - "Parameters": [], - "ReturnType": "System.Nullable", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Backchannel", - "Parameters": [], - "ReturnType": "System.Net.Http.HttpClient", + "Name": "set_TokenType", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Ticket", + "Name": "get_RefreshToken", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Ticket", + "Name": "set_RefreshToken", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + "Type": "System.String" } ], "ReturnType": "System.Void", @@ -493,69 +1363,43 @@ }, { "Kind": "Method", - "Name": "get_Identity", + "Name": "get_ExpiresIn", "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsIdentity", + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "set_ExpiresIn", "Parameters": [ { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" - }, - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OAuthOptions" - }, - { - "Name": "backchannel", - "Type": "System.Net.Http.HttpClient" - }, - { - "Name": "tokens", - "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse" + "Name": "value", + "Type": "System.String" } ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "get_Error", + "Parameters": [], + "ReturnType": "System.Exception", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Error", "Parameters": [ { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" - }, - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OAuthOptions" - }, - { - "Name": "backchannel", - "Type": "System.Net.Http.HttpClient" - }, - { - "Name": "tokens", - "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthTokenResponse" - }, - { - "Name": "user", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "value", + "Type": "System.Exception" } ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] } @@ -563,50 +1407,93 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents", + "Name": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimAction", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents" - ], + "Abstract": true, + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_OnCreatingTicket", + "Name": "get_ClaimType", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnCreatingTicket", + "Name": "get_ValueType", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Run", "Parameters": [ { - "Name": "value", - "Type": "System.Func" + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "issuer", + "Type": "System.String" } ], "ReturnType": "System.Void", + "Virtual": true, + "Abstract": true, "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "valueType", + "Type": "System.String" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "System.Collections.Generic.IEnumerable" + ], + "Members": [ { "Kind": "Method", - "Name": "get_OnRedirectToAuthorizationEndpoint", + "Name": "Clear", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnRedirectToAuthorizationEndpoint", + "Name": "Remove", "Parameters": [ { - "Name": "value", - "Type": "System.Func" + "Name": "claimType", + "Type": "System.String" } ], "ReturnType": "System.Void", @@ -615,31 +1502,25 @@ }, { "Kind": "Method", - "Name": "CreatingTicket", + "Name": "Add", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthCreatingTicketContext" + "Name": "action", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimAction" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RedirectToAuthorizationEndpoint", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", + "Name": "GetEnumerator", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerator", + "Sealed": true, "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents", + "ImplementedInterface": "System.Collections.Generic.IEnumerable", "Visibility": "Public", "GenericParameter": [] }, @@ -654,33 +1535,40 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext", + "Name": "Microsoft.AspNetCore.Authentication.OAuth.Claims.CustomJsonClaimAction", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimAction", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Options", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.OAuthOptions", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_RedirectUri", + "Name": "get_Resolver", "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "Name": "Run", + "Parameters": [ + { + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "issuer", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, "Visibility": "Public", "GenericParameter": [] }, @@ -689,20 +1577,16 @@ "Name": ".ctor", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OAuthOptions" + "Name": "claimType", + "Type": "System.String" }, { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Name": "valueType", + "Type": "System.String" }, { - "Name": "redirectUri", - "Type": "System.String" + "Name": "resolver", + "Type": "System.Func" } ], "Visibility": "Public", @@ -712,45 +1596,44 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.OAuthAppBuilderExtensions", + "Name": "Microsoft.AspNetCore.Authentication.OAuth.Claims.DeleteClaimAction", "Visibility": "Public", "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimAction", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "UseOAuthAuthentication", + "Name": "Run", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "issuer", + "Type": "System.String" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "UseOAuthAuthentication", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OAuthOptions" + "Name": "claimType", + "Type": "System.String" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, "Visibility": "Public", "GenericParameter": [] } @@ -758,15 +1641,15 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.OAuthOptions", + "Name": "Microsoft.AspNetCore.Authentication.OAuth.Claims.JsonKeyClaimAction", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimAction", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_ClientId", + "Name": "get_JsonKey", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -774,83 +1657,60 @@ }, { "Kind": "Method", - "Name": "set_ClientId", + "Name": "Run", "Parameters": [ { - "Name": "value", + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "issuer", "Type": "System.String" } ], "ReturnType": "System.Void", + "Virtual": true, + "Override": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_ClientSecret", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_ClientSecret", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "value", + "Name": "claimType", "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_AuthorizationEndpoint", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_AuthorizationEndpoint", - "Parameters": [ + }, { - "Name": "value", + "Name": "valueType", "Type": "System.String" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_TokenEndpoint", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_TokenEndpoint", - "Parameters": [ + }, { - "Name": "value", + "Name": "jsonKey", "Type": "System.String" } ], - "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OAuth.Claims.JsonSubKeyClaimAction", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.JsonKeyClaimAction", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "get_UserInformationEndpoint", + "Name": "get_SubKey", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -858,85 +1718,81 @@ }, { "Kind": "Method", - "Name": "set_UserInformationEndpoint", + "Name": "Run", "Parameters": [ { - "Name": "value", + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "issuer", "Type": "System.String" } ], "ReturnType": "System.Void", + "Virtual": true, + "Override": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_Events", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Events", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Scope", - "Parameters": [], - "ReturnType": "System.Collections.Generic.ICollection", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_StateDataFormat", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_StateDataFormat", - "Parameters": [ + "Name": "claimType", + "Type": "System.String" + }, { - "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISecureDataFormat" + "Name": "valueType", + "Type": "System.String" + }, + { + "Name": "jsonKey", + "Type": "System.String" + }, + { + "Name": "subKey", + "Type": "System.String" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_SystemClock", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISystemClock", "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OAuth.Claims.MapAllClaimsAction", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimAction", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "set_SystemClock", + "Name": "Run", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "issuer", + "Type": "System.String" } ], "ReturnType": "System.Void", + "Virtual": true, + "Override": true, "Visibility": "Public", "GenericParameter": [] }, @@ -950,6 +1806,5 @@ ], "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.OAuth/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.OAuth/breakingchanges.netcore.json deleted file mode 100644 index 3124d7fe7..000000000 --- a/src/Microsoft.AspNetCore.Authentication.OAuth/breakingchanges.netcore.json +++ /dev/null @@ -1,35 +0,0 @@ - [ - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthCreatingTicketContext : Microsoft.AspNetCore.Authentication.BaseContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthEvents : Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents, Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthHandler : Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler where T0 : Microsoft.AspNetCore.Builder.OAuthOptions", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthMiddleware : Microsoft.AspNetCore.Authentication.AuthenticationMiddleware where T0 : Microsoft.AspNetCore.Builder.OAuthOptions, new()", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.OAuthOptions : Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public interface Microsoft.AspNetCore.Authentication.OAuth.IOAuthEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.OAuthAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseOAuthAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.OAuthOptions options)", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OAuth.OAuthRedirectToAuthorizationContext : Microsoft.AspNetCore.Authentication.BaseContext", - "Kind": "Removal" - } - ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Events/OpenIdConnectEvents.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Events/OpenIdConnectEvents.cs index d8467be8d..2a48d250b 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Events/OpenIdConnectEvents.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Events/OpenIdConnectEvents.cs @@ -38,6 +38,11 @@ public class OpenIdConnectEvents : RemoteAuthenticationEvents /// public Func OnRedirectToIdentityProviderForSignOut { get; set; } = context => Task.CompletedTask; + /// + /// Invoked before redirecting to the at the end of a remote sign-out flow. + /// + public Func OnSignedOutCallbackRedirect { get; set; } = context => Task.CompletedTask; + /// /// Invoked when a request is received on the RemoteSignOutPath. /// @@ -68,6 +73,8 @@ public class OpenIdConnectEvents : RemoteAuthenticationEvents public virtual Task RedirectToIdentityProviderForSignOut(RedirectContext context) => OnRedirectToIdentityProviderForSignOut(context); + public virtual Task SignedOutCallbackRedirect(RemoteSignOutContext context) => OnSignedOutCallbackRedirect(context); + public virtual Task RemoteSignOut(RemoteSignOutContext context) => OnRemoteSignOut(context); public virtual Task TokenResponseReceived(TokenResponseReceivedContext context) => OnTokenResponseReceived(context); diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/LoggingExtensions.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/LoggingExtensions.cs index 458cfd73e..224af87b6 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/LoggingExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/LoggingExtensions.cs @@ -9,6 +9,8 @@ internal static class LoggingExtensions { private static Action _redirectToIdentityProviderForSignOutHandledResponse; private static Action _redirectToIdentityProviderHandledResponse; + private static Action _signoutCallbackRedirectHandledResponse; + private static Action _signoutCallbackRedirectSkipped; private static Action _updatingConfiguration; private static Action _receivedIdToken; private static Action _redeemingCodeForTokens; @@ -248,6 +250,14 @@ static LoggingExtensions() eventId: 49, logLevel: LogLevel.Information, formatString: "AuthenticationScheme: {AuthenticationScheme} signed out."); + _signoutCallbackRedirectHandledResponse = LoggerMessage.Define( + eventId: 50, + logLevel: LogLevel.Debug, + formatString: "RedirectToSignedOutRedirectUri.HandledResponse"); + _signoutCallbackRedirectSkipped = LoggerMessage.Define( + eventId: 51, + logLevel: LogLevel.Debug, + formatString: "RedirectToSignedOutRedirectUri.Skipped"); } public static void UpdatingConfiguration(this ILogger logger) @@ -345,6 +355,16 @@ public static void RedirectToIdentityProviderHandledResponse(this ILogger logger _redirectToIdentityProviderHandledResponse(logger, null); } + public static void SignoutCallbackRedirectHandledResponse(this ILogger logger) + { + _signoutCallbackRedirectHandledResponse(logger, null); + } + + public static void SignoutCallbackRedirectSkipped(this ILogger logger) + { + _signoutCallbackRedirectSkipped(logger, null); + } + public static void UserInformationReceivedHandledResponse(this ILogger logger) { _userInformationReceivedHandledResponse(logger, null); diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj index ae8ffd8d5..65ca6b842 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Microsoft.AspNetCore.Authentication.OpenIdConnect.csproj @@ -1,14 +1,19 @@ - - + + ASP.NET Core middleware that enables an application to support the OpenID Connect authentication workflow. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security + - + + + + + diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectChallengeProperties.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectChallengeProperties.cs new file mode 100644 index 000000000..0ced488de --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectChallengeProperties.cs @@ -0,0 +1,49 @@ +using System; +using System.Collections.Generic; +using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.IdentityModel.Protocols.OpenIdConnect; + +namespace Microsoft.AspNetCore.Authentication.OpenIdConnect +{ + public class OpenIdConnectChallengeProperties : OAuthChallengeProperties + { + /// + /// The parameter key for the "max_age" argument being used for a challenge request. + /// + public static readonly string MaxAgeKey = OpenIdConnectParameterNames.MaxAge; + + /// + /// The parameter key for the "prompt" argument being used for a challenge request. + /// + public static readonly string PromptKey = OpenIdConnectParameterNames.Prompt; + + public OpenIdConnectChallengeProperties() + { } + + public OpenIdConnectChallengeProperties(IDictionary items) + : base(items) + { } + + public OpenIdConnectChallengeProperties(IDictionary items, IDictionary parameters) + : base(items, parameters) + { } + + /// + /// The "max_age" parameter value being used for a challenge request. + /// + public TimeSpan? MaxAge + { + get => GetParameter(MaxAgeKey); + set => SetParameter(MaxAgeKey, value); + } + + /// + /// The "prompt" parameter value being used for a challenge request. + /// + public string Prompt + { + get => GetParameter(PromptKey); + set => SetParameter(PromptKey, value); + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs index 65e1e1951..8b0b72428 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectHandler.cs @@ -16,7 +16,6 @@ using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; -using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; using Microsoft.Net.Http.Headers; @@ -30,22 +29,8 @@ namespace Microsoft.AspNetCore.Authentication.OpenIdConnect public class OpenIdConnectHandler : RemoteAuthenticationHandler, IAuthenticationSignOutHandler { private const string NonceProperty = "N"; - private const string UriSchemeDelimiter = "://"; - - private const string InputTagFormat = @""; - private const string HtmlFormFormat = @" - - - Please wait while you're being redirected to the identity provider - - -
- {1} - -
- - -"; + + private const string HeaderValueEpocDate = "Thu, 01 Jan 1970 00:00:00 GMT"; private static readonly RandomNumberGenerator CryptoRandom = RandomNumberGenerator.Create(); @@ -122,6 +107,10 @@ protected virtual async Task HandleRemoteSignOutAsync() Logger.RemoteSignOutSkipped(); return false; } + if (remoteSignOutContext.Result.Failure != null) + { + throw new InvalidOperationException("An error was returned from the RemoteSignOut event.", remoteSignOutContext.Result.Failure); + } } if (message == null) @@ -166,6 +155,13 @@ protected virtual async Task HandleRemoteSignOutAsync() /// A task executing the sign out procedure public async virtual Task SignOutAsync(AuthenticationProperties properties) { + var target = ResolveTarget(Options.ForwardSignOut); + if (target != null) + { + await Context.SignOutAsync(target, properties); + return; + } + properties = properties ?? new AuthenticationProperties(); Logger.EnteringOpenIdAuthenticationHandlerHandleSignOutAsync(GetType().FullName); @@ -190,7 +186,7 @@ public async virtual Task SignOutAsync(AuthenticationProperties properties) properties.RedirectUri = BuildRedirectUriIfRelative(Options.SignedOutRedirectUri); if (string.IsNullOrWhiteSpace(properties.RedirectUri)) { - properties.RedirectUri = CurrentUri; + properties.RedirectUri = OriginalPathBase + OriginalPath + Request.QueryString; } } Logger.PostSignOutRedirect(properties.RedirectUri); @@ -236,19 +232,7 @@ public async virtual Task SignOutAsync(AuthenticationProperties properties) } else if (Options.AuthenticationMethod == OpenIdConnectRedirectBehavior.FormPost) { - var inputs = new StringBuilder(); - foreach (var parameter in message.Parameters) - { - var name = HtmlEncoder.Encode(parameter.Key); - var value = HtmlEncoder.Encode(parameter.Value); - - var input = string.Format(CultureInfo.InvariantCulture, InputTagFormat, name, value); - inputs.AppendLine(input); - } - - var issuer = HtmlEncoder.Encode(message.IssuerAddress); - - var content = string.Format(CultureInfo.InvariantCulture, HtmlFormFormat, issuer, inputs); + var content = message.BuildFormPost(); var buffer = Encoding.UTF8.GetBytes(content); Response.ContentLength = buffer.Length; @@ -257,7 +241,7 @@ public async virtual Task SignOutAsync(AuthenticationProperties properties) // Emit Cache-Control=no-cache to prevent client caching. Response.Headers[HeaderNames.CacheControl] = "no-cache"; Response.Headers[HeaderNames.Pragma] = "no-cache"; - Response.Headers[HeaderNames.Expires] = "-1"; + Response.Headers[HeaderNames.Expires] = HeaderValueEpocDate; await Response.Body.WriteAsync(buffer, 0, buffer.Length); } @@ -273,18 +257,46 @@ public async virtual Task SignOutAsync(AuthenticationProperties properties) /// Response to the callback from OpenId provider after session ended. /// /// A task executing the callback procedure - protected virtual Task HandleSignOutCallbackAsync() + protected async virtual Task HandleSignOutCallbackAsync() { - if (Request.Query.TryGetValue(OpenIdConnectParameterNames.State, out StringValues protectedState)) + var message = new OpenIdConnectMessage(Request.Query.Select(pair => new KeyValuePair(pair.Key, pair.Value))); + AuthenticationProperties properties = null; + if (!string.IsNullOrEmpty(message.State)) + { + properties = Options.StateDataFormat.Unprotect(message.State); + } + + var signOut = new RemoteSignOutContext(Context, Scheme, Options, message) { - var properties = Options.StateDataFormat.Unprotect(protectedState); - if (!string.IsNullOrEmpty(properties?.RedirectUri)) + Properties = properties, + }; + + await Events.SignedOutCallbackRedirect(signOut); + if (signOut.Result != null) + { + if (signOut.Result.Handled) + { + Logger.SignoutCallbackRedirectHandledResponse(); + return true; + } + if (signOut.Result.Skipped) + { + Logger.SignoutCallbackRedirectSkipped(); + return false; + } + if (signOut.Result.Failure != null) { - Response.Redirect(properties.RedirectUri); + throw new InvalidOperationException("An error was returned from the SignedOutCallbackRedirect event.", signOut.Result.Failure); } } - return Task.FromResult(true); + properties = signOut.Properties; + if (!string.IsNullOrEmpty(properties?.RedirectUri)) + { + Response.Redirect(properties.RedirectUri); + } + + return true; } /// @@ -300,7 +312,7 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop // 2. CurrentUri if RedirectUri is not set) if (string.IsNullOrEmpty(properties.RedirectUri)) { - properties.RedirectUri = CurrentUri; + properties.RedirectUri = OriginalPathBase + OriginalPath + Request.QueryString; } Logger.PostAuthenticationLocalRedirect(properties.RedirectUri); @@ -317,9 +329,19 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop RedirectUri = BuildRedirectUri(Options.CallbackPath), Resource = Options.Resource, ResponseType = Options.ResponseType, - Scope = string.Join(" ", Options.Scope) + Prompt = properties.GetParameter(OpenIdConnectParameterNames.Prompt) ?? Options.Prompt, + Scope = string.Join(" ", properties.GetParameter>(OpenIdConnectParameterNames.Scope) ?? Options.Scope), }; + // Add the 'max_age' parameter to the authentication request if MaxAge is not null. + // See http://openid.net/specs/openid-connect-core-1_0.html#AuthRequest + var maxAge = properties.GetParameter(OpenIdConnectParameterNames.MaxAge) ?? Options.MaxAge; + if (maxAge.HasValue) + { + message.MaxAge = Convert.ToInt64(Math.Floor((maxAge.Value).TotalSeconds)) + .ToString(CultureInfo.InvariantCulture); + } + // Omitting the response_mode parameter when it already corresponds to the default // response_mode used for the specified response_type is recommended by the specifications. // See http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#ResponseModes @@ -380,19 +402,7 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop } else if (Options.AuthenticationMethod == OpenIdConnectRedirectBehavior.FormPost) { - var inputs = new StringBuilder(); - foreach (var parameter in message.Parameters) - { - var name = HtmlEncoder.Encode(parameter.Key); - var value = HtmlEncoder.Encode(parameter.Value); - - var input = string.Format(CultureInfo.InvariantCulture, InputTagFormat, name, value); - inputs.AppendLine(input); - } - - var issuer = HtmlEncoder.Encode(message.IssuerAddress); - - var content = string.Format(CultureInfo.InvariantCulture, HtmlFormFormat, issuer, inputs); + var content = message.BuildFormPost(); var buffer = Encoding.UTF8.GetBytes(content); Response.ContentLength = buffer.Length; @@ -401,7 +411,7 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop // Emit Cache-Control=no-cache to prevent client caching. Response.Headers[HeaderNames.CacheControl] = "no-cache"; Response.Headers[HeaderNames.Pragma] = "no-cache"; - Response.Headers[HeaderNames.Expires] = "-1"; + Response.Headers[HeaderNames.Expires] = HeaderValueEpocDate; await Response.Body.WriteAsync(buffer, 0, buffer.Length); return; @@ -459,13 +469,10 @@ protected override async Task HandleRemoteAuthenticateAsync return HandleRequestResult.Fail("No message."); } + AuthenticationProperties properties = null; try { - AuthenticationProperties properties = null; - if (!string.IsNullOrEmpty(authorizationResponse.State)) - { - properties = Options.StateDataFormat.Unprotect(authorizationResponse.State); - } + properties = ReadPropertiesAndClearState(authorizationResponse); var messageReceivedContext = await RunMessageReceivedEventAsync(authorizationResponse, properties); if (messageReceivedContext.Result != null) @@ -489,8 +496,7 @@ protected override async Task HandleRemoteAuthenticateAsync return HandleRequestResult.Fail(Resources.MessageStateIsNullOrEmpty); } - // if state exists and we failed to 'unprotect' this is not a message we should process. - properties = Options.StateDataFormat.Unprotect(authorizationResponse.State); + properties = ReadPropertiesAndClearState(authorizationResponse); } if (properties == null) @@ -501,21 +507,30 @@ protected override async Task HandleRemoteAuthenticateAsync // Not for us? return HandleRequestResult.SkipHandler(); } + + // if state exists and we failed to 'unprotect' this is not a message we should process. return HandleRequestResult.Fail(Resources.MessageStateIsInvalid); } - properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out string userstate); - authorizationResponse.State = userstate; - if (!ValidateCorrelationId(properties)) { - return HandleRequestResult.Fail("Correlation failed."); + return HandleRequestResult.Fail("Correlation failed.", properties); } // if any of the error fields are set, throw error null if (!string.IsNullOrEmpty(authorizationResponse.Error)) { - return HandleRequestResult.Fail(CreateOpenIdConnectProtocolException(authorizationResponse, response: null)); + // Note: access_denied errors are special protocol errors indicating the user didn't + // approve the authorization demand requested by the remote authorization server. + // Since it's a frequent scenario (that is not caused by incorrect configuration), + // denied errors are handled differently using HandleAccessDeniedErrorAsync(). + // Visit https://tools.ietf.org/html/rfc6749#section-4.1.2.1 for more information. + if (string.Equals(authorizationResponse.Error, "access_denied", StringComparison.Ordinal)) + { + return await HandleAccessDeniedErrorAsync(properties); + } + + return HandleRequestResult.Fail(CreateOpenIdConnectProtocolException(authorizationResponse, response: null), properties); } if (_configuration == null && Options.ConfigurationManager != null) @@ -603,8 +618,7 @@ protected override async Task HandleRemoteAuthenticateAsync // At least a cursory validation is required on the new IdToken, even if we've already validated the one from the authorization response. // And we'll want to validate the new JWT in ValidateTokenResponse. - JwtSecurityToken tokenEndpointJwt; - var tokenEndpointUser = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out tokenEndpointJwt); + var tokenEndpointUser = ValidateToken(tokenEndpointResponse.IdToken, properties, validationParameters, out var tokenEndpointJwt); // Avoid reading & deleting the nonce cookie, running the event, etc, if it was already done as part of the authorization response validation. if (user == null) @@ -690,8 +704,25 @@ protected override async Task HandleRemoteAuthenticateAsync return authenticationFailedContext.Result; } - return HandleRequestResult.Fail(exception); + return HandleRequestResult.Fail(exception, properties); + } + } + + private AuthenticationProperties ReadPropertiesAndClearState(OpenIdConnectMessage message) + { + AuthenticationProperties properties = null; + if (!string.IsNullOrEmpty(message.State)) + { + properties = Options.StateDataFormat.Unprotect(message.State); + + if (properties != null) + { + // If properties can be decoded from state, clear the message state. + properties.Items.TryGetValue(OpenIdConnectDefaults.UserstatePropertiesKey, out var userstate); + message.State = userstate; + } } + return properties; } private void PopulateSessionProperties(OpenIdConnectMessage message, AuthenticationProperties properties) @@ -763,7 +794,7 @@ protected virtual async Task RedeemAuthorizationCodeAsync( /// The authentication properties. /// which is used to determine if the remote authentication was successful. protected virtual async Task GetUserInformationAsync( - OpenIdConnectMessage message, JwtSecurityToken jwt, + OpenIdConnectMessage message, JwtSecurityToken jwt, ClaimsPrincipal principal, AuthenticationProperties properties) { var userInfoEndpoint = _configuration?.UserInfoEndpoint; @@ -798,7 +829,7 @@ protected virtual async Task GetUserInformationAsync( } else { - return HandleRequestResult.Fail("Unknown response type: " + contentType.MediaType); + return HandleRequestResult.Fail("Unknown response type: " + contentType.MediaType, properties); } var userInformationReceivedContext = await RunUserInformationReceivedEventAsync(principal, properties, message, user); diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs index a8545e35a..cbf6e8eab 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/OpenIdConnectOptions.cs @@ -74,6 +74,7 @@ public OpenIdConnectOptions() HttpOnly = true, SameSite = SameSiteMode.None, SecurePolicy = CookieSecurePolicy.SameAsRequest, + IsEssential = true, }; } @@ -84,6 +85,11 @@ public override void Validate() { base.Validate(); + if (MaxAge.HasValue && MaxAge.Value < TimeSpan.Zero) + { + throw new ArgumentOutOfRangeException(nameof(MaxAge), MaxAge.Value, "The value must not be a negative TimeSpan."); + } + if (string.IsNullOrEmpty(ClientId)) { throw new ArgumentException("Options.ClientId must be provided", nameof(ClientId)); @@ -159,6 +165,13 @@ public override void Validate() set => base.Events = value; } + /// + /// Gets or sets the 'max_age'. If set the 'max_age' parameter will be sent with the authentication request. If the identity + /// provider has not actively authenticated the user within the length of time specified, the user will be prompted to + /// re-authenticate. By default no max_age is specified. + /// + public TimeSpan? MaxAge { get; set; } + /// /// Gets or sets the that is used to ensure that the 'id_token' received /// is valid per: http://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation @@ -209,6 +222,11 @@ public override void Validate() /// public string ResponseType { get; set; } = OpenIdConnectResponseType.IdToken; + /// + /// Gets or sets the 'prompt'. + /// + public string Prompt { get; set; } + /// /// Gets the list of permissions to request. /// diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Properties/Resources.Designer.cs index 65a9273a0..753373ece 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/Properties/Resources.Designer.cs @@ -15,96 +15,84 @@ private static readonly ResourceManager _resourceManager /// internal static string MessageStateIsNullOrEmpty { - get { return GetString("MessageStateIsNullOrEmpty"); } + get => GetString("MessageStateIsNullOrEmpty"); } /// /// OpenIdConnectAuthenticationHandler: message.State is null or empty. /// internal static string FormatMessageStateIsNullOrEmpty() - { - return GetString("MessageStateIsNullOrEmpty"); - } + => GetString("MessageStateIsNullOrEmpty"); /// /// Unable to unprotect the message.State. /// internal static string MessageStateIsInvalid { - get { return GetString("MessageStateIsInvalid"); } + get => GetString("MessageStateIsInvalid"); } /// /// Unable to unprotect the message.State. /// internal static string FormatMessageStateIsInvalid() - { - return GetString("MessageStateIsInvalid"); - } + => GetString("MessageStateIsInvalid"); /// /// Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'. /// internal static string MessageContainsError { - get { return GetString("MessageContainsError"); } + get => GetString("MessageContainsError"); } /// /// Message contains error: '{0}', error_description: '{1}', error_uri: '{2}'. /// internal static string FormatMessageContainsError(object p0, object p1, object p2) - { - return string.Format(CultureInfo.CurrentCulture, GetString("MessageContainsError"), p0, p1, p2); - } + => string.Format(CultureInfo.CurrentCulture, GetString("MessageContainsError"), p0, p1, p2); /// /// The Validated Security Token must be of type JwtSecurityToken, but instead its type is: '{0}'. /// internal static string ValidatedSecurityTokenNotJwt { - get { return GetString("ValidatedSecurityTokenNotJwt"); } + get => GetString("ValidatedSecurityTokenNotJwt"); } /// /// The Validated Security Token must be of type JwtSecurityToken, but instead its type is: '{0}'. /// internal static string FormatValidatedSecurityTokenNotJwt(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("ValidatedSecurityTokenNotJwt"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("ValidatedSecurityTokenNotJwt"), p0); /// /// Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: '{0}'." /// internal static string UnableToValidateToken { - get { return GetString("UnableToValidateToken"); } + get => GetString("UnableToValidateToken"); } /// /// Unable to validate the 'id_token', no suitable ISecurityTokenValidator was found for: '{0}'." /// internal static string FormatUnableToValidateToken(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("UnableToValidateToken"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("UnableToValidateToken"), p0); /// /// Cannot process the message. Both id_token and code are missing. /// internal static string IdTokenCodeMissing { - get { return GetString("IdTokenCodeMissing"); } + get => GetString("IdTokenCodeMissing"); } /// /// Cannot process the message. Both id_token and code are missing. /// internal static string FormatIdTokenCodeMissing() - { - return GetString("IdTokenCodeMissing"); - } + => GetString("IdTokenCodeMissing"); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/baseline.netcore.json index 64cb79487..d5c10d18d 100644 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/baseline.netcore.json @@ -1,8 +1,8 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.OpenIdConnect, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.OpenIdConnect, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectDefaults", + "Name": "Microsoft.Extensions.DependencyInjection.OpenIdConnectExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -11,274 +11,436 @@ "ImplementedInterfaces": [], "Members": [ { - "Kind": "Field", - "Name": "AuthenticationPropertiesKey", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddOpenIdConnect", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "Caption", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddOpenIdConnect", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "CookieNoncePrefix", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddOpenIdConnect", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "RedirectUriForCodePropertiesKey", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddOpenIdConnect", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.OpenIdConnectAppBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ { - "Kind": "Field", - "Name": "UserstatePropertiesKey", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "UseOpenIdConnectAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", "Static": true, - "ReadOnly": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "UseOpenIdConnectAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", - "GenericParameter": [], - "Constant": true, - "Literal": "\"OpenIdConnect\"" + "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler", + "Name": "Microsoft.AspNetCore.Authentication.ClaimActionCollectionUniqueExtensions", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", + "Abstract": true, + "Static": true, + "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Backchannel", - "Parameters": [], - "ReturnType": "System.Net.Http.HttpClient", - "Visibility": "Protected", + "Name": "MapUniqueJsonKey", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "jsonKey", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_HtmlEncoder", + "Name": "MapUniqueJsonKey", + "Parameters": [ + { + "Name": "collection", + "Type": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection" + }, + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "jsonKey", + "Type": "System.String" + }, + { + "Name": "valueType", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthenticationFailedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", "Parameters": [], - "ReturnType": "System.Text.Encodings.Web.HtmlEncoder", - "Visibility": "Protected", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleRequestAsync", - "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Override": true, + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + } + ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleRemoteSignOutAsync", + "Name": "get_Exception", "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "System.Exception", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleSignOutAsync", + "Name": "set_Exception", "Parameters": [ { - "Name": "signout", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignOutContext" + "Name": "value", + "Type": "System.Exception" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Override": true, - "Visibility": "Protected", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "HandleUnauthorizedAsync", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Override": true, - "Visibility": "Protected", + "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "HandleRemoteAuthenticateAsync", + "Name": "get_ProtocolMessage", "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Override": true, - "Visibility": "Protected", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RedeemAuthorizationCodeAsync", + "Name": "set_ProtocolMessage", "Parameters": [ { - "Name": "tokenEndpointRequest", + "Name": "value", "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetUserInformationAsync", + "Name": "get_JwtSecurityToken", + "Parameters": [], + "ReturnType": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_JwtSecurityToken", "Parameters": [ { - "Name": "message", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" - }, - { - "Name": "jwt", + "Name": "value", "Type": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken" - }, - { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "get_TokenEndpointRequest", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TokenEndpointRequest", "Parameters": [ { - "Name": "backchannel", - "Type": "System.Net.Http.HttpClient" - }, - { - "Name": "htmlEncoder", - "Type": "System.Text.Encodings.Web.HtmlEncoder" + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectMiddleware", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", "Name": "get_Backchannel", "Parameters": [], "ReturnType": "System.Net.Http.HttpClient", - "Visibility": "Protected", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_HtmlEncoder", + "Name": "get_TokenEndpointResponse", "Parameters": [], - "ReturnType": "System.Text.Encodings.Web.HtmlEncoder", - "Visibility": "Protected", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "CreateHandler", + "Name": "set_TokenEndpointResponse", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_HandledCodeRedemption", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", - "Virtual": true, - "Override": true, - "Visibility": "Protected", + "ReturnType": "System.Boolean", + "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "HandleCodeRedemption", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleCodeRedemption", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" - }, - { - "Name": "dataProtectionProvider", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + "Name": "accessToken", + "Type": "System.String" }, { - "Name": "loggerFactory", - "Type": "Microsoft.Extensions.Logging.ILoggerFactory" - }, + "Name": "idToken", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleCodeRedemption", + "Parameters": [ { - "Name": "encoder", - "Type": "System.Text.Encodings.Web.UrlEncoder" - }, + "Name": "tokenEndpointResponse", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ { - "Name": "services", - "Type": "System.IServiceProvider" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" }, { - "Name": "sharedOptions", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" }, { "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" }, { - "Name": "htmlEncoder", - "Type": "System.Text.Encodings.Web.HtmlEncoder" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "Visibility": "Public", @@ -288,51 +450,48 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectRedirectBehavior", + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext", "Visibility": "Public", - "Kind": "Enumeration", - "Sealed": true, + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", "ImplementedInterfaces": [], "Members": [ { - "Kind": "Field", - "Name": "RedirectGet", + "Kind": "Method", + "Name": "get_ProtocolMessage", "Parameters": [], - "GenericParameter": [], - "Literal": "0" + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "Visibility": "Public", + "GenericParameter": [] }, { - "Kind": "Field", - "Name": "FormPost", - "Parameters": [], - "GenericParameter": [], - "Literal": "1" - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthenticationFailedContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "ImplementedInterfaces": [], - "Members": [ + "Kind": "Method", + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Method", - "Name": "get_Exception", + "Name": "get_Token", "Parameters": [], - "ReturnType": "System.Exception", + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Exception", + "Name": "set_Token", "Parameters": [ { "Name": "value", - "Type": "System.Exception" + "Type": "System.String" } ], "ReturnType": "System.Void", @@ -347,9 +506,17 @@ "Name": "context", "Type": "Microsoft.AspNetCore.Http.HttpContext" }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "Visibility": "Public", @@ -359,27 +526,27 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext", + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Properties", + "Name": "get_OnAuthenticationFailed", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Properties", + "Name": "set_OnAuthenticationFailed", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "System.Func" } ], "ReturnType": "System.Void", @@ -388,19 +555,19 @@ }, { "Kind": "Method", - "Name": "get_JwtSecurityToken", + "Name": "get_OnAuthorizationCodeReceived", "Parameters": [], - "ReturnType": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_JwtSecurityToken", + "Name": "set_OnAuthorizationCodeReceived", "Parameters": [ { "Name": "value", - "Type": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken" + "Type": "System.Func" } ], "ReturnType": "System.Void", @@ -409,19 +576,19 @@ }, { "Kind": "Method", - "Name": "get_TokenEndpointRequest", + "Name": "get_OnMessageReceived", "Parameters": [], - "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_TokenEndpointRequest", + "Name": "set_OnMessageReceived", "Parameters": [ { "Name": "value", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + "Type": "System.Func" } ], "ReturnType": "System.Void", @@ -430,27 +597,40 @@ }, { "Kind": "Method", - "Name": "get_Backchannel", + "Name": "get_OnRedirectToIdentityProvider", "Parameters": [], - "ReturnType": "System.Net.Http.HttpClient", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_TokenEndpointResponse", + "Name": "set_OnRedirectToIdentityProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnRedirectToIdentityProviderForSignOut", "Parameters": [], - "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_TokenEndpointResponse", + "Name": "set_OnRedirectToIdentityProviderForSignOut", "Parameters": [ { "Name": "value", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + "Type": "System.Func" } ], "ReturnType": "System.Void", @@ -459,44 +639,40 @@ }, { "Kind": "Method", - "Name": "get_HandledCodeRedemption", + "Name": "get_OnSignedOutCallbackRedirect", "Parameters": [], - "ReturnType": "System.Boolean", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleCodeRedemption", - "Parameters": [], + "Name": "set_OnSignedOutCallbackRedirect", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleCodeRedemption", - "Parameters": [ - { - "Name": "accessToken", - "Type": "System.String" - }, - { - "Name": "idToken", - "Type": "System.String" - } - ], - "ReturnType": "System.Void", + "Name": "get_OnRemoteSignOut", + "Parameters": [], + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleCodeRedemption", + "Name": "set_OnRemoteSignOut", "Parameters": [ { - "Name": "tokenEndpointResponse", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + "Name": "value", + "Type": "System.Func" } ], "ReturnType": "System.Void", @@ -504,54 +680,41 @@ "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" - } - ], + "Kind": "Method", + "Name": "get_OnTokenResponseReceived", + "Parameters": [], + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseControlContext", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "get_Options", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions", + "Name": "set_OnTokenResponseReceived", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_ProtocolMessage", + "Name": "get_OnTokenValidated", "Parameters": [], - "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "ReturnType": "System.Func", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ProtocolMessage", + "Name": "set_OnTokenValidated", "Parameters": [ { "Name": "value", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + "Type": "System.Func" } ], "ReturnType": "System.Void", @@ -559,33 +722,26 @@ "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "get_OnUserInformationReceived", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnUserInformationReceived", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" + "Name": "value", + "Type": "System.Func" } ], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents" - ], - "Members": [ + }, { "Kind": "Method", "Name": "AuthenticationFailed", @@ -596,6 +752,8 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -608,6 +766,8 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -620,6 +780,8 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -632,6 +794,8 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -644,6 +808,22 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignedOutCallbackRedirect", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RemoteSignOutContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -656,6 +836,8 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -668,6 +850,8 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -680,6 +864,8 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -692,33 +878,42 @@ } ], "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext", + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", + "BaseType": "Microsoft.AspNetCore.Authentication.PropertiesContext", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Token", + "Name": "get_ProtocolMessage", "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Token", + "Name": "set_ProtocolMessage", "Parameters": [ { "Name": "value", - "Type": "System.String" + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "ReturnType": "System.Void", @@ -727,19 +922,69 @@ }, { "Kind": "Method", - "Name": "get_Properties", + "Name": "get_Handled", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleResponse", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RemoteSignOutContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Properties", + "Name": "set_ProtocolMessage", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "ReturnType": "System.Void", @@ -754,9 +999,17 @@ "Name": "context", "Type": "Microsoft.AspNetCore.Http.HttpContext" }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + }, + { + "Name": "message", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "Visibility": "Public", @@ -766,29 +1019,27 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents", + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenResponseReceivedContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents" - ], + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_OnAuthenticationFailed", + "Name": "get_ProtocolMessage", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnAuthenticationFailed", + "Name": "set_ProtocolMessage", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "ReturnType": "System.Void", @@ -797,19 +1048,19 @@ }, { "Kind": "Method", - "Name": "get_OnAuthorizationCodeReceived", + "Name": "get_TokenEndpointResponse", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnAuthorizationCodeReceived", + "Name": "set_TokenEndpointResponse", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "ReturnType": "System.Void", @@ -817,41 +1068,58 @@ "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_OnMessageReceived", - "Parameters": [], - "ReturnType": "System.Func", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_OnMessageReceived", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "value", - "Type": "System.Func" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + }, + { + "Name": "user", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "get_OnRedirectToIdentityProvider", + "Name": "get_ProtocolMessage", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnRedirectToIdentityProvider", + "Name": "set_ProtocolMessage", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "ReturnType": "System.Void", @@ -860,19 +1128,19 @@ }, { "Kind": "Method", - "Name": "get_OnRedirectToIdentityProviderForSignOut", + "Name": "get_SecurityToken", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnRedirectToIdentityProviderForSignOut", + "Name": "set_SecurityToken", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken" } ], "ReturnType": "System.Void", @@ -881,19 +1149,19 @@ }, { "Kind": "Method", - "Name": "get_OnRemoteSignOut", + "Name": "get_TokenEndpointResponse", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnRemoteSignOut", + "Name": "set_TokenEndpointResponse", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "ReturnType": "System.Void", @@ -902,19 +1170,19 @@ }, { "Kind": "Method", - "Name": "get_OnTokenResponseReceived", + "Name": "get_Nonce", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnTokenResponseReceived", + "Name": "set_Nonce", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "System.String" } ], "ReturnType": "System.Void", @@ -922,41 +1190,58 @@ "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_OnTokenValidated", - "Parameters": [], - "ReturnType": "System.Func", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_OnTokenValidated", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "value", - "Type": "System.Func" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + }, + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.UserInformationReceivedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "get_OnUserInformationReceived", + "Name": "get_ProtocolMessage", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnUserInformationReceived", + "Name": "set_ProtocolMessage", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" } ], "ReturnType": "System.Void", @@ -965,136 +1250,102 @@ }, { "Kind": "Method", - "Name": "AuthenticationFailed", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthenticationFailedContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", + "Name": "get_User", + "Parameters": [], + "ReturnType": "Newtonsoft.Json.Linq.JObject", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "AuthorizationCodeReceived", + "Name": "set_User", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext" + "Name": "value", + "Type": "Newtonsoft.Json.Linq.JObject" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "MessageReceived", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "RedirectToIdentityProvider", - "Parameters": [ + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "RedirectToIdentityProviderForSignOut", - "Parameters": [ + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext" + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + }, + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectChallengeProperties", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.OAuthChallengeProperties", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "RemoteSignOut", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RemoteSignOutContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", + "Name": "get_MaxAge", + "Parameters": [], + "ReturnType": "System.Nullable", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "TokenResponseReceived", + "Name": "set_MaxAge", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenResponseReceivedContext" + "Name": "value", + "Type": "System.Nullable" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "TokenValidated", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", + "Name": "get_Prompt", + "Parameters": [], + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "UserInformationReceived", + "Name": "set_Prompt", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.UserInformationReceivedContext" + "Name": "value", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, @@ -1104,273 +1355,294 @@ "Parameters": [], "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" - }, - { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Name": "items", + "Type": "System.Collections.Generic.IDictionary" } ], "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.RemoteSignOutContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" + "Name": "items", + "Type": "System.Collections.Generic.IDictionary" }, { - "Name": "message", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + "Name": "parameters", + "Type": "System.Collections.Generic.IDictionary" } ], "Visibility": "Public", "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "MaxAgeKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "PromptKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenResponseReceivedContext", + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectDefaults", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", + "Abstract": true, + "Static": true, + "Sealed": true, "ImplementedInterfaces": [], "Members": [ { - "Kind": "Method", - "Name": "get_Properties", + "Kind": "Field", + "Name": "AuthenticationPropertiesKey", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_TokenEndpointResponse", + "Kind": "Field", + "Name": "DisplayName", "Parameters": [], - "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "set_TokenEndpointResponse", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" - } - ], - "ReturnType": "System.Void", + "Kind": "Field", + "Name": "CookieNoncePrefix", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" - }, - { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - } - ], + "Kind": "Field", + "Name": "RedirectUriForCodePropertiesKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "UserstatePropertiesKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, "Visibility": "Public", "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"OpenIdConnect\"" } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext", + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "ImplementedInterfaces": [], + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IAuthenticationSignOutHandler" + ], "Members": [ { "Kind": "Method", - "Name": "get_Properties", + "Name": "HandleRequestAsync", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationRequestHandler", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Properties", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", + "Name": "get_Backchannel", + "Parameters": [], + "ReturnType": "System.Net.Http.HttpClient", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_SecurityToken", + "Name": "get_HtmlEncoder", "Parameters": [], - "ReturnType": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken", - "Visibility": "Public", + "ReturnType": "System.Text.Encodings.Web.HtmlEncoder", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SecurityToken", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Events", "Parameters": [ { "Name": "value", - "Type": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken" + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents" } ], "ReturnType": "System.Void", - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_TokenEndpointResponse", + "Name": "CreateEventsAsync", "Parameters": [], - "ReturnType": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_TokenEndpointResponse", + "Name": "HandleRemoteSignOutAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignOutAsync", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationSignOutHandler", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Nonce", + "Name": "HandleSignOutCallbackAsync", "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Nonce", + "Name": "HandleChallengeAsync", "Parameters": [ { - "Name": "value", - "Type": "System.String" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" - } - ], - "Visibility": "Public", + "Kind": "Method", + "Name": "HandleRemoteAuthenticateAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.UserInformationReceivedContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "get_User", - "Parameters": [], - "ReturnType": "Newtonsoft.Json.Linq.JObject", - "Visibility": "Public", + "Name": "RedeemAuthorizationCodeAsync", + "Parameters": [ + { + "Name": "tokenEndpointRequest", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_User", + "Name": "GetUserInformationAsync", "Parameters": [ { - "Name": "value", - "Type": "Newtonsoft.Json.Linq.JObject" + "Name": "message", + "Type": "Microsoft.IdentityModel.Protocols.OpenIdConnect.OpenIdConnectMessage" + }, + { + "Name": "jwt", + "Type": "System.IdentityModel.Tokens.Jwt.JwtSecurityToken" + }, + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { @@ -1378,12 +1650,24 @@ "Name": ".ctor", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" }, { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "htmlEncoder", + "Type": "System.Text.Encodings.Web.HtmlEncoder" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], "Visibility": "Public", @@ -1393,58 +1677,22 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.OpenIdConnectAppBuilderExtensions", + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions", "Visibility": "Public", "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "UseOpenIdConnectAuthentication", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, + "Name": "Validate", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Method", - "Name": "UseOpenIdConnectAuthentication", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Builder.OpenIdConnectOptions", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", - "ImplementedInterfaces": [], - "Members": [ { "Kind": "Method", "Name": "get_Authority", @@ -1571,6 +1819,14 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_ClaimActions", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Method", "Name": "get_RequireHttpsMetadata", @@ -1617,7 +1873,7 @@ "Kind": "Method", "Name": "get_Events", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", + "ReturnType": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -1627,7 +1883,28 @@ "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents" + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_MaxAge", + "Parameters": [], + "ReturnType": "System.Nullable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MaxAge", + "Parameters": [ + { + "Name": "value", + "Type": "System.Nullable" } ], "ReturnType": "System.Void", @@ -1657,7 +1934,28 @@ }, { "Kind": "Method", - "Name": "get_PostLogoutRedirectUri", + "Name": "get_SignedOutCallbackPath", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SignedOutCallbackPath", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.PathString" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SignedOutRedirectUri", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -1665,7 +1963,7 @@ }, { "Kind": "Method", - "Name": "set_PostLogoutRedirectUri", + "Name": "set_SignedOutRedirectUri", "Parameters": [ { "Name": "value", @@ -1781,6 +2079,27 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_Prompt", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Prompt", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Method", "Name": "get_Scope", @@ -1835,7 +2154,7 @@ "Kind": "Method", "Name": "get_StateDataFormat", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "ReturnType": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", "Visibility": "Public", "GenericParameter": [] }, @@ -1845,7 +2164,7 @@ "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISecureDataFormat" + "Type": "Microsoft.AspNetCore.Authentication.ISecureDataFormat" } ], "ReturnType": "System.Void", @@ -1959,19 +2278,40 @@ }, { "Kind": "Method", - "Name": "get_SystemClock", + "Name": "get_DisableTelemetry", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISystemClock", + "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SystemClock", + "Name": "set_DisableTelemetry", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_NonceCookie", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.CookieBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_NonceCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.CookieBuilder" } ], "ReturnType": "System.Void", @@ -1984,13 +2324,121 @@ "Parameters": [], "Visibility": "Public", "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectPostConfigureOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Options.IPostConfigureOptions" + ], + "Members": [ + { + "Kind": "Method", + "Name": "PostConfigure", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectOptions" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Options.IPostConfigureOptions", + "Visibility": "Public", + "GenericParameter": [] }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { - "Name": "authenticationScheme", + "Name": "dataProtection", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectRedirectBehavior", + "Visibility": "Public", + "Kind": "Enumeration", + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "RedirectGet", + "Parameters": [], + "GenericParameter": [], + "Literal": "0" + }, + { + "Kind": "Field", + "Name": "FormPost", + "Parameters": [], + "GenericParameter": [], + "Literal": "1" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.OpenIdConnect.Claims.UniqueJsonKeyClaimAction", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.JsonKeyClaimAction", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Run", + "Parameters": [ + { + "Name": "userData", + "Type": "Newtonsoft.Json.Linq.JObject" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "issuer", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "claimType", + "Type": "System.String" + }, + { + "Name": "valueType", + "Type": "System.String" + }, + { + "Name": "jsonKey", "Type": "System.String" } ], @@ -2000,6 +2448,5 @@ ], "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/breakingchanges.netcore.json deleted file mode 100644 index 931a7b079..000000000 --- a/src/Microsoft.AspNetCore.Authentication.OpenIdConnect/breakingchanges.netcore.json +++ /dev/null @@ -1,68 +0,0 @@ - [ - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectEvents : Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents, Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler : Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectMiddleware : Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.OpenIdConnectOptions : Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public interface Microsoft.AspNetCore.Authentication.OpenIdConnect.IOpenIdConnectEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.OpenIdConnectAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseOpenIdConnectAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.OpenIdConnectOptions options)", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthenticationFailedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.AuthorizationCodeReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext : Microsoft.AspNetCore.Authentication.BaseControlContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.MessageReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.RedirectContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.RemoteSignOutContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenResponseReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.TokenValidatedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.OpenIdConnect.UserInformationReceivedContext : Microsoft.AspNetCore.Authentication.OpenIdConnect.BaseOpenIdConnectContext", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectDefaults", - "MemberId": "public static readonly System.String Caption", - "Kind": "Removal" - } - ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj b/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj index d9c5cd0b3..4f2aae616 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/Microsoft.AspNetCore.Authentication.Twitter.csproj @@ -1,10 +1,8 @@  - - ASP.NET Core middleware that enables an application to support Twitter's OAuth 1.0 authentication workflow. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/Properties/Resources.Designer.cs index d60c2fc73..2eabfff29 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/Properties/Resources.Designer.cs @@ -15,32 +15,28 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_OptionMustBeProvided { - get { return GetString("Exception_OptionMustBeProvided"); } + get => GetString("Exception_OptionMustBeProvided"); } /// /// The '{0}' option must be provided. /// internal static string FormatException_OptionMustBeProvided(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string Exception_ValidatorHandlerMismatch { - get { return GetString("Exception_ValidatorHandlerMismatch"); } + get => GetString("Exception_ValidatorHandlerMismatch"); } /// /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. /// internal static string FormatException_ValidatorHandlerMismatch() - { - return GetString("Exception_ValidatorHandlerMismatch"); - } + => GetString("Exception_ValidatorHandlerMismatch"); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs index a39a3f036..bdab80e59 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterDefaults.cs @@ -8,5 +8,14 @@ public static class TwitterDefaults public const string AuthenticationScheme = "Twitter"; public static readonly string DisplayName = "Twitter"; + + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/request_token + internal const string RequestTokenEndpoint = "https://api.twitter.com/oauth/request_token"; + + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/authenticate + internal const string AuthenticationEndpoint = "https://api.twitter.com/oauth/authenticate?oauth_token="; + + // https://developer.twitter.com/en/docs/basics/authentication/api-reference/access_token + internal const string AccessTokenEndpoint = "https://api.twitter.com/oauth/access_token"; } } diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs index 1e1dd08d8..cc8591ed3 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterHandler.cs @@ -19,12 +19,9 @@ namespace Microsoft.AspNetCore.Authentication.Twitter { - internal class TwitterHandler : RemoteAuthenticationHandler + public class TwitterHandler : RemoteAuthenticationHandler { private static readonly DateTime Epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc); - private const string RequestTokenEndpoint = "https://api.twitter.com/oauth/request_token"; - private const string AuthenticationEndpoint = "https://api.twitter.com/oauth/authenticate?oauth_token="; - private const string AccessTokenEndpoint = "https://api.twitter.com/oauth/access_token"; private HttpClient Backchannel => Options.Backchannel; @@ -46,7 +43,6 @@ public TwitterHandler(IOptionsMonitor options, ILoggerFactory lo protected override async Task HandleRemoteAuthenticateAsync() { - AuthenticationProperties properties = null; var query = Request.Query; var protectedRequestToken = Request.Cookies[Options.StateCookie.Name]; @@ -57,25 +53,33 @@ protected override async Task HandleRemoteAuthenticateAsync return HandleRequestResult.Fail("Invalid state cookie."); } - properties = requestToken.Properties; + var properties = requestToken.Properties; - // REVIEW: see which of these are really errors + var denied = query["denied"]; + if (!StringValues.IsNullOrEmpty(denied)) + { + // Note: denied errors are special protocol errors indicating the user didn't + // approve the authorization demand requested by the remote authorization server. + // Since it's a frequent scenario (that is not caused by incorrect configuration), + // denied errors are handled differently using HandleAccessDeniedErrorAsync(). + return await HandleAccessDeniedErrorAsync(properties); + } var returnedToken = query["oauth_token"]; if (StringValues.IsNullOrEmpty(returnedToken)) { - return HandleRequestResult.Fail("Missing oauth_token"); + return HandleRequestResult.Fail("Missing oauth_token", properties); } if (!string.Equals(returnedToken, requestToken.Token, StringComparison.Ordinal)) { - return HandleRequestResult.Fail("Unmatched token"); + return HandleRequestResult.Fail("Unmatched token", properties); } var oauthVerifier = query["oauth_verifier"]; if (StringValues.IsNullOrEmpty(oauthVerifier)) { - return HandleRequestResult.Fail("Missing or blank oauth_verifier"); + return HandleRequestResult.Fail("Missing or blank oauth_verifier", properties); } var cookieOptions = Options.StateCookie.Build(Context, Clock.UtcNow); @@ -128,12 +132,12 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop { if (string.IsNullOrEmpty(properties.RedirectUri)) { - properties.RedirectUri = CurrentUri; + properties.RedirectUri = OriginalPathBase + OriginalPath + Request.QueryString; } // If CallbackConfirmed is false, this will throw var requestToken = await ObtainRequestTokenAsync(BuildRedirectUri(Options.CallbackPath), properties); - var twitterAuthenticationEndpoint = AuthenticationEndpoint + requestToken.Token; + var twitterAuthenticationEndpoint = TwitterDefaults.AuthenticationEndpoint + requestToken.Token; var cookieOptions = Options.StateCookie.Build(Context, Clock.UtcNow); @@ -143,53 +147,92 @@ protected override async Task HandleChallengeAsync(AuthenticationProperties prop await Events.RedirectToAuthorizationEndpoint(redirectContext); } - private async Task ObtainRequestTokenAsync(string callBackUri, AuthenticationProperties properties) + private async Task ExecuteRequestAsync(string url, HttpMethod httpMethod, RequestToken accessToken = null, Dictionary extraOAuthPairs = null, Dictionary queryParameters = null, Dictionary formData = null) { - Logger.ObtainRequestToken(); - - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary + var authorizationParts = new SortedDictionary(extraOAuthPairs ?? new Dictionary()) { - { "oauth_callback", callBackUri }, { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, + { "oauth_nonce", Guid.NewGuid().ToString("N") }, { "oauth_signature_method", "HMAC-SHA1" }, { "oauth_timestamp", GenerateTimeStamp() }, { "oauth_version", "1.0" } }; + if (accessToken != null) + { + authorizationParts.Add("oauth_token", accessToken.Token); + } + + var signatureParts = new SortedDictionary(authorizationParts); + if (queryParameters != null) + { + foreach (var queryParameter in queryParameters) + { + signatureParts.Add(queryParameter.Key, queryParameter.Value); + } + } + if (formData != null) + { + foreach (var formItem in formData) + { + signatureParts.Add(formItem.Key, formItem.Value); + } + } + var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) + foreach (var signaturePart in signatureParts) { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); + parameterBuilder.AppendFormat("{0}={1}&", Uri.EscapeDataString(signaturePart.Key), Uri.EscapeDataString(signaturePart.Value)); } parameterBuilder.Length--; var parameterString = parameterBuilder.ToString(); var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Post.Method); + canonicalizedRequestBuilder.Append(httpMethod.Method); canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(RequestTokenEndpoint)); + canonicalizedRequestBuilder.Append(Uri.EscapeDataString(url)); canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); + canonicalizedRequestBuilder.Append(Uri.EscapeDataString(parameterString)); - var signature = ComputeSignature(Options.ConsumerSecret, null, canonicalizedRequestBuilder.ToString()); + var signature = ComputeSignature(Options.ConsumerSecret, accessToken?.TokenSecret, canonicalizedRequestBuilder.ToString()); authorizationParts.Add("oauth_signature", signature); + var queryString = ""; + if (queryParameters != null) + { + var queryStringBuilder = new StringBuilder("?"); + foreach (var queryParam in queryParameters) + { + queryStringBuilder.AppendFormat("{0}={1}&", queryParam.Key, queryParam.Value); + } + queryStringBuilder.Length--; + queryString = queryStringBuilder.ToString(); + } + var authorizationHeaderBuilder = new StringBuilder(); authorizationHeaderBuilder.Append("OAuth "); foreach (var authorizationPart in authorizationParts) { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); + authorizationHeaderBuilder.AppendFormat("{0}=\"{1}\",", authorizationPart.Key, Uri.EscapeDataString(authorizationPart.Value)); } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; + authorizationHeaderBuilder.Length--; - var request = new HttpRequestMessage(HttpMethod.Post, RequestTokenEndpoint); + var request = new HttpRequestMessage(httpMethod, url + queryString); request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); - var response = await Backchannel.SendAsync(request, Context.RequestAborted); + if (formData != null) + { + request.Content = new FormUrlEncodedContent(formData); + } + + return await Backchannel.SendAsync(request, Context.RequestAborted); + } + + private async Task ObtainRequestTokenAsync(string callBackUri, AuthenticationProperties properties) + { + Logger.ObtainRequestToken(); + + var response = await ExecuteRequestAsync(TwitterDefaults.RequestTokenEndpoint, HttpMethod.Post, extraOAuthPairs: new Dictionary() { { "oauth_callback", callBackUri } }); response.EnsureSuccessStatusCode(); var responseText = await response.Content.ReadAsStringAsync(); @@ -208,58 +251,8 @@ private async Task ObtainAccessTokenAsync(RequestToken token, strin Logger.ObtainAccessToken(); - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary - { - { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, - { "oauth_signature_method", "HMAC-SHA1" }, - { "oauth_token", token.Token }, - { "oauth_timestamp", GenerateTimeStamp() }, - { "oauth_verifier", verifier }, - { "oauth_version", "1.0" }, - }; - - var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) - { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); - } - parameterBuilder.Length--; - var parameterString = parameterBuilder.ToString(); - - var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Post.Method); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(AccessTokenEndpoint)); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); - - var signature = ComputeSignature(Options.ConsumerSecret, token.TokenSecret, canonicalizedRequestBuilder.ToString()); - authorizationParts.Add("oauth_signature", signature); - authorizationParts.Remove("oauth_verifier"); - - var authorizationHeaderBuilder = new StringBuilder(); - authorizationHeaderBuilder.Append("OAuth "); - foreach (var authorizationPart in authorizationParts) - { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); - } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; - - var request = new HttpRequestMessage(HttpMethod.Post, AccessTokenEndpoint); - request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); - - var formPairs = new Dictionary() - { - { "oauth_verifier", verifier }, - }; - - request.Content = new FormUrlEncodedContent(formPairs); - - var response = await Backchannel.SendAsync(request, Context.RequestAborted); + var formPost = new Dictionary { { "oauth_verifier", verifier } }; + var response = await ExecuteRequestAsync(TwitterDefaults.AccessTokenEndpoint, HttpMethod.Post, token, formData: formPost); if (!response.IsSuccessStatusCode) { @@ -284,53 +277,8 @@ private async Task RetrieveUserDetailsAsync(AccessToken accessToken, Cl { Logger.RetrieveUserDetails(); - var nonce = Guid.NewGuid().ToString("N"); - - var authorizationParts = new SortedDictionary - { - { "oauth_consumer_key", Options.ConsumerKey }, - { "oauth_nonce", nonce }, - { "oauth_signature_method", "HMAC-SHA1" }, - { "oauth_timestamp", GenerateTimeStamp() }, - { "oauth_token", accessToken.Token }, - { "oauth_version", "1.0" } - }; - - var parameterBuilder = new StringBuilder(); - foreach (var authorizationKey in authorizationParts) - { - parameterBuilder.AppendFormat("{0}={1}&", UrlEncoder.Encode(authorizationKey.Key), UrlEncoder.Encode(authorizationKey.Value)); - } - parameterBuilder.Length--; - var parameterString = parameterBuilder.ToString(); - - var resource_url = "https://api.twitter.com/1.1/account/verify_credentials.json"; - var resource_query = "include_email=true"; - var canonicalizedRequestBuilder = new StringBuilder(); - canonicalizedRequestBuilder.Append(HttpMethod.Get.Method); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(resource_url)); - canonicalizedRequestBuilder.Append("&"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(resource_query)); - canonicalizedRequestBuilder.Append("%26"); - canonicalizedRequestBuilder.Append(UrlEncoder.Encode(parameterString)); - - var signature = ComputeSignature(Options.ConsumerSecret, accessToken.TokenSecret, canonicalizedRequestBuilder.ToString()); - authorizationParts.Add("oauth_signature", signature); - - var authorizationHeaderBuilder = new StringBuilder(); - authorizationHeaderBuilder.Append("OAuth "); - foreach (var authorizationPart in authorizationParts) - { - authorizationHeaderBuilder.AppendFormat( - "{0}=\"{1}\", ", authorizationPart.Key, UrlEncoder.Encode(authorizationPart.Value)); - } - authorizationHeaderBuilder.Length = authorizationHeaderBuilder.Length - 2; - - var request = new HttpRequestMessage(HttpMethod.Get, resource_url + "?include_email=true"); - request.Headers.Add("Authorization", authorizationHeaderBuilder.ToString()); + var response = await ExecuteRequestAsync("https://api.twitter.com/1.1/account/verify_credentials.json", HttpMethod.Get, accessToken, queryParameters: new Dictionary() { { "include_email", "true" } }); - var response = await Backchannel.SendAsync(request, Context.RequestAborted); if (!response.IsSuccessStatusCode) { Logger.LogError("Email request failed with a status code of " + response.StatusCode); @@ -356,8 +304,8 @@ private string ComputeSignature(string consumerSecret, string tokenSecret, strin algorithm.Key = Encoding.ASCII.GetBytes( string.Format(CultureInfo.InvariantCulture, "{0}&{1}", - UrlEncoder.Encode(consumerSecret), - string.IsNullOrEmpty(tokenSecret) ? string.Empty : UrlEncoder.Encode(tokenSecret))); + Uri.EscapeDataString(consumerSecret), + string.IsNullOrEmpty(tokenSecret) ? string.Empty : Uri.EscapeDataString(tokenSecret))); var hash = algorithm.ComputeHash(Encoding.ASCII.GetBytes(signatureData)); return Convert.ToBase64String(hash); } diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterOptions.cs b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterOptions.cs index 86919d092..269516ae1 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/TwitterOptions.cs @@ -2,8 +2,8 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Security.Claims; using System.Globalization; +using System.Security.Claims; using Microsoft.AspNetCore.Authentication.OAuth.Claims; using Microsoft.AspNetCore.Http; @@ -35,6 +35,7 @@ public TwitterOptions() SecurePolicy = CookieSecurePolicy.SameAsRequest, HttpOnly = true, SameSite = SameSiteMode.Lax, + IsEssential = true, }; } diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.Twitter/baseline.netcore.json index c35232a31..03ee64562 100644 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication.Twitter/baseline.netcore.json @@ -1,8 +1,8 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Twitter, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.Twitter, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterDefaults", + "Name": "Microsoft.Extensions.DependencyInjection.TwitterExtensions", "Visibility": "Public", "Kind": "Class", "Abstract": true, @@ -11,99 +11,86 @@ "ImplementedInterfaces": [], "Members": [ { - "Kind": "Field", - "Name": "AuthenticationScheme", - "Parameters": [], - "ReturnType": "System.String", + "Kind": "Method", + "Name": "AddTwitter", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, + "Extension": true, "Visibility": "Public", - "GenericParameter": [], - "Constant": true, - "Literal": "\"Twitter\"" - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterMiddleware", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "CreateHandler", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", - "Virtual": true, - "Override": true, - "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "AddTwitter", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" - }, - { - "Name": "dataProtectionProvider", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" }, { - "Name": "loggerFactory", - "Type": "Microsoft.Extensions.Logging.ILoggerFactory" - }, + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddTwitter", + "Parameters": [ { - "Name": "encoder", - "Type": "System.Text.Encodings.Web.UrlEncoder" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" }, { - "Name": "sharedOptions", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "authenticationScheme", + "Type": "System.String" }, { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Name": "configureOptions", + "Type": "System.Action" } ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Options", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.TwitterOptions", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "AddTwitter", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" }, { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.TwitterOptions" + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, "Visibility": "Public", "GenericParameter": [] } @@ -111,36 +98,46 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents", + "Name": "Microsoft.AspNetCore.Builder.TwitterAppBuilderExtensions", "Visibility": "Public", - "Kind": "Interface", + "Kind": "Class", "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents" - ], + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "CreatingTicket", + "Name": "UseTwitterAuthentication", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterCreatingTicketContext" + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RedirectToAuthorizationEndpoint", + "Name": "UseTwitterAuthentication", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterRedirectToAuthorizationEndpointContext" + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterOptions" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", "GenericParameter": [] } ], @@ -150,7 +147,7 @@ "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterCreatingTicketContext", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext", + "BaseType": "Microsoft.AspNetCore.Authentication.ResultContext", "ImplementedInterfaces": [], "Members": [ { @@ -193,48 +190,6 @@ "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Method", - "Name": "get_Principal", - "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsPrincipal", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Principal", - "Parameters": [ - { - "Name": "value", - "Type": "System.Security.Claims.ClaimsPrincipal" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Properties", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, { "Kind": "Constructor", "Name": ".ctor", @@ -243,9 +198,21 @@ "Name": "context", "Type": "Microsoft.AspNetCore.Http.HttpContext" }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.TwitterOptions" + "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterOptions" + }, + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" }, { "Name": "userId", @@ -279,9 +246,7 @@ "Visibility": "Public", "Kind": "Class", "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents" - ], + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", @@ -308,7 +273,7 @@ "Kind": "Method", "Name": "get_OnRedirectToAuthorizationEndpoint", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "System.Func, System.Threading.Tasks.Task>", "Visibility": "Public", "GenericParameter": [] }, @@ -318,7 +283,7 @@ "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "System.Func, System.Threading.Tasks.Task>" } ], "ReturnType": "System.Void", @@ -336,7 +301,6 @@ ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -346,12 +310,11 @@ "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterRedirectToAuthorizationEndpointContext" + "Type": "Microsoft.AspNetCore.Authentication.RedirectContext" } ], "ReturnType": "System.Threading.Tasks.Task", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -365,56 +328,6 @@ ], "GenericParameters": [] }, - { - "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterRedirectToAuthorizationEndpointContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_RedirectUri", - "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.TwitterOptions" - }, - { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - }, - { - "Name": "redirectUri", - "Type": "System.String" - } - ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, { "Name": "Microsoft.AspNetCore.Authentication.Twitter.AccessToken", "Visibility": "Public", @@ -547,7 +460,7 @@ "Kind": "Method", "Name": "get_Properties", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", "Visibility": "Public", "GenericParameter": [] }, @@ -557,7 +470,7 @@ "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "ReturnType": "System.Void", @@ -655,45 +568,148 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.TwitterAppBuilderExtensions", + "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterDefaults", "Visibility": "Public", "Kind": "Class", "Abstract": true, "Static": true, "Sealed": true, "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "DisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"Twitter\"" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterHandler", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "UseTwitterAuthentication", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.Twitter.TwitterEvents", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Events", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterEvents" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, - "Visibility": "Public", + "ReturnType": "System.Void", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "UseTwitterAuthentication", + "Name": "CreateEventsAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleRemoteAuthenticateAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateTicketAsync", "Parameters": [ { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "token", + "Type": "Microsoft.AspNetCore.Authentication.Twitter.AccessToken" }, + { + "Name": "user", + "Type": "Newtonsoft.Json.Linq.JObject" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleChallengeAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.TwitterOptions" + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, "Visibility": "Public", "GenericParameter": [] } @@ -701,10 +717,10 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.TwitterOptions", + "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterOptions", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions", "ImplementedInterfaces": [], "Members": [ { @@ -770,6 +786,14 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_ClaimActions", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.OAuth.Claims.ClaimActionCollection", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Method", "Name": "get_StateDataFormat", @@ -795,7 +819,7 @@ "Kind": "Method", "Name": "get_Events", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents", + "ReturnType": "Microsoft.AspNetCore.Authentication.Twitter.TwitterEvents", "Visibility": "Public", "GenericParameter": [] }, @@ -805,7 +829,7 @@ "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents" + "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterEvents" } ], "ReturnType": "System.Void", @@ -814,25 +838,35 @@ }, { "Kind": "Method", - "Name": "get_SystemClock", + "Name": "get_StateCookie", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.ISystemClock", + "ReturnType": "Microsoft.AspNetCore.Http.CookieBuilder", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SystemClock", + "Name": "set_StateCookie", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + "Type": "Microsoft.AspNetCore.Http.CookieBuilder" } ], "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "Validate", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Constructor", "Name": ".ctor", @@ -842,7 +876,49 @@ } ], "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.Twitter.TwitterPostConfigureOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Options.IPostConfigureOptions" + ], + "Members": [ + { + "Kind": "Method", + "Name": "PostConfigure", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.Twitter.TwitterOptions" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Options.IPostConfigureOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "dataProtection", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.Twitter/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication.Twitter/breakingchanges.netcore.json deleted file mode 100644 index 6b8440911..000000000 --- a/src/Microsoft.AspNetCore.Authentication.Twitter/breakingchanges.netcore.json +++ /dev/null @@ -1,45 +0,0 @@ - [ - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext : Microsoft.AspNetCore.Authentication.BaseContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterEvents : Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents, Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterMiddleware : Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.TwitterOptions : Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public interface Microsoft.AspNetCore.Authentication.Twitter.ITwitterEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.RequestToken", - "MemberId": "public Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties get_Properties()", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.RequestToken", - "MemberId": "public System.Void set_Properties(Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties value)", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.TwitterAppBuilderExtensions", - "MemberId": "public static Microsoft.AspNetCore.Builder.IApplicationBuilder UseTwitterAuthentication(this Microsoft.AspNetCore.Builder.IApplicationBuilder app, Microsoft.AspNetCore.Builder.TwitterOptions options)", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterCreatingTicketContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.Twitter.TwitterRedirectToAuthorizationEndpointContext : Microsoft.AspNetCore.Authentication.Twitter.BaseTwitterContext", - "Kind": "Removal" - } - ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/AuthenticationFailedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/AuthenticationFailedContext.cs new file mode 100644 index 000000000..f643fad97 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/AuthenticationFailedContext.cs @@ -0,0 +1,35 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Protocols.WsFederation; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// The context object used in for . + /// + public class AuthenticationFailedContext : RemoteAuthenticationContext + { + /// + /// Creates a new context object + /// + /// + /// + /// + public AuthenticationFailedContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options) + : base(context, scheme, options, new AuthenticationProperties()) + { } + + /// + /// The from the request, if any. + /// + public WsFederationMessage ProtocolMessage { get; set; } + + /// + /// The that triggered this event. + /// + public Exception Exception { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/MessageReceivedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/MessageReceivedContext.cs new file mode 100644 index 000000000..4028fa5e3 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/MessageReceivedContext.cs @@ -0,0 +1,33 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Protocols.WsFederation; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// The context object used for . + /// + public class MessageReceivedContext : RemoteAuthenticationContext + { + /// + /// Creates a new context object. + /// + /// + /// + /// + /// + public MessageReceivedContext( + HttpContext context, + AuthenticationScheme scheme, + WsFederationOptions options, + AuthenticationProperties properties) + : base(context, scheme, options, properties) { } + + /// + /// The received on this request. + /// + public WsFederationMessage ProtocolMessage { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RedirectContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RedirectContext.cs new file mode 100644 index 000000000..654037d0a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RedirectContext.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Protocols.WsFederation; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// When a user configures the to be notified prior to redirecting to an IdentityProvider + /// an instance of is passed to the 'RedirectToAuthenticationEndpoint' or 'RedirectToEndSessionEndpoint' events. + /// + public class RedirectContext : PropertiesContext + { + /// + /// Creates a new context object. + /// + /// + /// + /// + /// + public RedirectContext( + HttpContext context, + AuthenticationScheme scheme, + WsFederationOptions options, + AuthenticationProperties properties) + : base(context, scheme, options, properties) { } + + /// + /// The used to compose the redirect. + /// + public WsFederationMessage ProtocolMessage { get; set; } + + /// + /// If true, will skip any default logic for this redirect. + /// + public bool Handled { get; private set; } + + /// + /// Skips any default logic for this redirect. + /// + public void HandleResponse() => Handled = true; + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RemoteSignoutContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RemoteSignoutContext.cs new file mode 100644 index 000000000..8aec24a64 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/RemoteSignoutContext.cs @@ -0,0 +1,30 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Protocols.WsFederation; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// An event context for RemoteSignOut. + /// + public class RemoteSignOutContext : RemoteAuthenticationContext + { + /// + /// + /// + /// + /// + /// + /// + public RemoteSignOutContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options, WsFederationMessage message) + : base(context, scheme, options, new AuthenticationProperties()) + => ProtocolMessage = message; + + /// + /// The signout message. + /// + public WsFederationMessage ProtocolMessage { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenReceivedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenReceivedContext.cs new file mode 100644 index 000000000..311f41515 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenReceivedContext.cs @@ -0,0 +1,28 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Claims; +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Protocols.WsFederation; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// This Context can be used to be informed when an 'AuthorizationCode' is redeemed for tokens at the token endpoint. + /// + public class SecurityTokenReceivedContext : RemoteAuthenticationContext + { + /// + /// Creates a + /// + public SecurityTokenReceivedContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options, AuthenticationProperties properties) + : base(context, scheme, options, properties) + { + } + + /// + /// The received on this request. + /// + public WsFederationMessage ProtocolMessage { get; set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenValidatedContext.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenValidatedContext.cs new file mode 100644 index 000000000..1f32014b6 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/SecurityTokenValidatedContext.cs @@ -0,0 +1,34 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Protocols.WsFederation; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// The context object used for . + /// + public class SecurityTokenValidatedContext : RemoteAuthenticationContext + { + /// + /// Creates a + /// + public SecurityTokenValidatedContext(HttpContext context, AuthenticationScheme scheme, WsFederationOptions options, ClaimsPrincipal principal, AuthenticationProperties properties) + : base(context, scheme, options, properties) + => Principal = principal; + + /// + /// The received on this request. + /// + public WsFederationMessage ProtocolMessage { get; set; } + + /// + /// The that was validated. + /// + public SecurityToken SecurityToken { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/WsFederationEvents.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/WsFederationEvents.cs new file mode 100644 index 000000000..55c3936f9 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Events/WsFederationEvents.cs @@ -0,0 +1,74 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Threading.Tasks; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// Specifies events which the invokes to enable developer control over the authentication process. /> + /// + public class WsFederationEvents : RemoteAuthenticationEvents + { + /// + /// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed. + /// + public Func OnAuthenticationFailed { get; set; } = context => Task.CompletedTask; + + /// + /// Invoked when a protocol message is first received. + /// + public Func OnMessageReceived { get; set; } = context => Task.CompletedTask; + + /// + /// Invoked to manipulate redirects to the identity provider for SignIn, SignOut, or Challenge. + /// + public Func OnRedirectToIdentityProvider { get; set; } = context => Task.CompletedTask; + + /// + /// Invoked when a wsignoutcleanup request is received at the RemoteSignOutPath endpoint. + /// + public Func OnRemoteSignOut { get; set; } = context => Task.CompletedTask; + + /// + /// Invoked with the security token that has been extracted from the protocol message. + /// + public Func OnSecurityTokenReceived { get; set; } = context => Task.CompletedTask; + + /// + /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated. + /// + public Func OnSecurityTokenValidated { get; set; } = context => Task.CompletedTask; + + /// + /// Invoked if exceptions are thrown during request processing. The exceptions will be re-thrown after this event unless suppressed. + /// + public virtual Task AuthenticationFailed(AuthenticationFailedContext context) => OnAuthenticationFailed(context); + + /// + /// Invoked when a protocol message is first received. + /// + public virtual Task MessageReceived(MessageReceivedContext context) => OnMessageReceived(context); + + /// + /// Invoked to manipulate redirects to the identity provider for SignIn, SignOut, or Challenge. + /// + public virtual Task RedirectToIdentityProvider(RedirectContext context) => OnRedirectToIdentityProvider(context); + + /// + /// Invoked when a wsignoutcleanup request is received at the RemoteSignOutPath endpoint. + /// + public virtual Task RemoteSignOut(RemoteSignOutContext context) => OnRemoteSignOut(context); + + /// + /// Invoked with the security token that has been extracted from the protocol message. + /// + public virtual Task SecurityTokenReceived(SecurityTokenReceivedContext context) => OnSecurityTokenReceived(context); + + /// + /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated. + /// + public virtual Task SecurityTokenValidated(SecurityTokenValidatedContext context) => OnSecurityTokenValidated(context); + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/LoggingExtensions.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/LoggingExtensions.cs new file mode 100644 index 000000000..e28b7e15b --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/LoggingExtensions.cs @@ -0,0 +1,85 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Logging +{ + internal static class LoggingExtensions + { + private static Action _signInWithoutWresult; + private static Action _signInWithoutToken; + private static Action _exceptionProcessingMessage; + private static Action _malformedRedirectUri; + private static Action _remoteSignOutHandledResponse; + private static Action _remoteSignOutSkipped; + private static Action _remoteSignOut; + + static LoggingExtensions() + { + _signInWithoutWresult = LoggerMessage.Define( + eventId: 1, + logLevel: LogLevel.Debug, + formatString: "Received a sign-in message without a WResult."); + _signInWithoutToken = LoggerMessage.Define( + eventId: 2, + logLevel: LogLevel.Debug, + formatString: "Received a sign-in message without a token."); + _exceptionProcessingMessage = LoggerMessage.Define( + eventId: 3, + logLevel: LogLevel.Error, + formatString: "Exception occurred while processing message."); + _malformedRedirectUri = LoggerMessage.Define( + eventId: 4, + logLevel: LogLevel.Warning, + formatString: "The sign-out redirect URI '{0}' is malformed."); + _remoteSignOutHandledResponse = LoggerMessage.Define( + eventId: 5, + logLevel: LogLevel.Debug, + formatString: "RemoteSignOutContext.HandledResponse"); + _remoteSignOutSkipped = LoggerMessage.Define( + eventId: 6, + logLevel: LogLevel.Debug, + formatString: "RemoteSignOutContext.Skipped"); + _remoteSignOut = LoggerMessage.Define( + eventId: 7, + logLevel: LogLevel.Information, + formatString: "Remote signout request processed."); + } + + public static void SignInWithoutWresult(this ILogger logger) + { + _signInWithoutWresult(logger, null); + } + + public static void SignInWithoutToken(this ILogger logger) + { + _signInWithoutToken(logger, null); + } + + public static void ExceptionProcessingMessage(this ILogger logger, Exception ex) + { + _exceptionProcessingMessage(logger, ex); + } + + public static void MalformedRedirectUri(this ILogger logger, string uri) + { + _malformedRedirectUri(logger, uri, null); + } + + public static void RemoteSignOutHandledResponse(this ILogger logger) + { + _remoteSignOutHandledResponse(logger, null); + } + + public static void RemoteSignOutSkipped(this ILogger logger) + { + _remoteSignOutSkipped(logger, null); + } + + public static void RemoteSignOut(this ILogger logger) + { + _remoteSignOut(logger, null); + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Microsoft.AspNetCore.Authentication.WsFederation.csproj b/src/Microsoft.AspNetCore.Authentication.WsFederation/Microsoft.AspNetCore.Authentication.WsFederation.csproj new file mode 100644 index 000000000..ae4d52aff --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Microsoft.AspNetCore.Authentication.WsFederation.csproj @@ -0,0 +1,17 @@ + + + + ASP.NET Core middleware that enables an application to support the WsFederation authentication workflow. + netcoreapp3.0 + true + aspnetcore;authentication;security + + + + + + + + + + diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/Properties/Resources.Designer.cs new file mode 100644 index 000000000..564e826a7 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Properties/Resources.Designer.cs @@ -0,0 +1,114 @@ +// +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + using System.Globalization; + using System.Reflection; + using System.Resources; + + internal static class Resources + { + private static readonly ResourceManager _resourceManager + = new ResourceManager("Microsoft.AspNetCore.Authentication.WsFederation.Resources", typeof(Resources).GetTypeInfo().Assembly); + + /// + /// The service descriptor is missing. + /// + internal static string Exception_MissingDescriptor + { + get => GetString("Exception_MissingDescriptor"); + } + + /// + /// The service descriptor is missing. + /// + internal static string FormatException_MissingDescriptor() + => GetString("Exception_MissingDescriptor"); + + /// + /// No token validator was found for the given token. + /// + internal static string Exception_NoTokenValidatorFound + { + get => GetString("Exception_NoTokenValidatorFound"); + } + + /// + /// No token validator was found for the given token. + /// + internal static string FormatException_NoTokenValidatorFound() + => GetString("Exception_NoTokenValidatorFound"); + + /// + /// The '{0}' option must be provided. + /// + internal static string Exception_OptionMustBeProvided + { + get => GetString("Exception_OptionMustBeProvided"); + } + + /// + /// The '{0}' option must be provided. + /// + internal static string FormatException_OptionMustBeProvided(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); + + /// + /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. + /// + internal static string Exception_ValidatorHandlerMismatch + { + get => GetString("Exception_ValidatorHandlerMismatch"); + } + + /// + /// An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. + /// + internal static string FormatException_ValidatorHandlerMismatch() + => GetString("Exception_ValidatorHandlerMismatch"); + + /// + /// The sign in message does not contain a required token. + /// + internal static string SignInMessageTokenIsMissing + { + get => GetString("SignInMessageTokenIsMissing"); + } + + /// + /// The sign in message does not contain a required token. + /// + internal static string FormatSignInMessageTokenIsMissing() + => GetString("SignInMessageTokenIsMissing"); + + /// + /// The sign in message does not contain a required wresult. + /// + internal static string SignInMessageWresultIsMissing + { + get => GetString("SignInMessageWresultIsMissing"); + } + + /// + /// The sign in message does not contain a required wresult. + /// + internal static string FormatSignInMessageWresultIsMissing() + => GetString("SignInMessageWresultIsMissing"); + + private static string GetString(string name, params string[] formatterNames) + { + var value = _resourceManager.GetString(name); + + System.Diagnostics.Debug.Assert(value != null); + + if (formatterNames != null) + { + for (var i = 0; i < formatterNames.Length; i++) + { + value = value.Replace("{" + formatterNames[i] + "}", "{" + i + "}"); + } + } + + return value; + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/Resources.resx b/src/Microsoft.AspNetCore.Authentication.WsFederation/Resources.resx new file mode 100644 index 000000000..e2edafb67 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/Resources.resx @@ -0,0 +1,138 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + The service descriptor is missing. + + + No token validator was found for the given token. + + + The '{0}' option must be provided. + + + An ICertificateValidator cannot be specified at the same time as an HttpMessageHandler unless it is a WebRequestHandler. + + + The sign in message does not contain a required token. + + + The sign in message does not contain a required wresult. + + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationDefaults.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationDefaults.cs new file mode 100644 index 000000000..3b97d995b --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationDefaults.cs @@ -0,0 +1,26 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// Default values related to WsFederation authentication handler + /// + public static class WsFederationDefaults + { + /// + /// The default authentication type used when registering the WsFederationHandler. + /// + public const string AuthenticationScheme = "WsFederation"; + + /// + /// The default display name used when registering the WsFederationHandler. + /// + public const string DisplayName = "WsFederation"; + + /// + /// Constant used to identify userstate inside AuthenticationProperties that have been serialized in the 'wctx' parameter. + /// + public static readonly string UserstatePropertiesKey = "WsFederation.Userstate"; + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationExtensions.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationExtensions.cs new file mode 100644 index 000000000..47091d58d --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationExtensions.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authentication.WsFederation; +using Microsoft.Extensions.DependencyInjection.Extensions; +using Microsoft.Extensions.Options; + +namespace Microsoft.Extensions.DependencyInjection +{ + /// + /// Extensions for registering the . + /// + public static class WsFederationExtensions + { + /// + /// Registers the using the default authentication scheme, display name, and options. + /// + /// + /// + public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder) + => builder.AddWsFederation(WsFederationDefaults.AuthenticationScheme, _ => { }); + + /// + /// Registers the using the default authentication scheme, display name, and the given options configuration. + /// + /// + /// A delegate that configures the . + /// + public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder, Action configureOptions) + => builder.AddWsFederation(WsFederationDefaults.AuthenticationScheme, configureOptions); + + /// + /// Registers the using the given authentication scheme, default display name, and the given options configuration. + /// + /// + /// + /// A delegate that configures the . + /// + public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) + => builder.AddWsFederation(authenticationScheme, WsFederationDefaults.DisplayName, configureOptions); + + /// + /// Registers the using the given authentication scheme, display name, and options configuration. + /// + /// + /// + /// + /// A delegate that configures the . + /// + public static AuthenticationBuilder AddWsFederation(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) + { + builder.Services.TryAddEnumerable(ServiceDescriptor.Singleton, WsFederationPostConfigureOptions>()); + return builder.AddRemoteScheme(authenticationScheme, displayName, configureOptions); + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationHandler.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationHandler.cs new file mode 100644 index 000000000..c4bb8fff7 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationHandler.cs @@ -0,0 +1,425 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Protocols.WsFederation; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// A per-request authentication handler for the WsFederation. + /// + public class WsFederationHandler : RemoteAuthenticationHandler, IAuthenticationSignOutHandler + { + private const string CorrelationProperty = ".xsrf"; + private WsFederationConfiguration _configuration; + + /// + /// Creates a new WsFederationAuthenticationHandler + /// + /// + /// + /// + /// + public WsFederationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) + : base(options, logger, encoder, clock) + { + } + + /// + /// The handler calls methods on the events which give the application control at certain points where processing is occurring. + /// If it is not provided a default instance is supplied which does nothing when the methods are called. + /// + protected new WsFederationEvents Events + { + get { return (WsFederationEvents)base.Events; } + set { base.Events = value; } + } + + /// + /// Creates a new instance of the events instance. + /// + /// A new instance of the events instance. + protected override Task CreateEventsAsync() => Task.FromResult(new WsFederationEvents()); + + /// + /// Overridden to handle remote signout requests + /// + /// + public override Task HandleRequestAsync() + { + // RemoteSignOutPath and CallbackPath may be the same, fall through if the message doesn't match. + if (Options.RemoteSignOutPath.HasValue && Options.RemoteSignOutPath == Request.Path && HttpMethods.IsGet(Request.Method) + && string.Equals(Request.Query[WsFederationConstants.WsFederationParameterNames.Wa], + WsFederationConstants.WsFederationActions.SignOutCleanup, StringComparison.OrdinalIgnoreCase)) + { + // We've received a remote sign-out request + return HandleRemoteSignOutAsync(); + } + + return base.HandleRequestAsync(); + } + + /// + /// Handles Challenge + /// + /// + protected override async Task HandleChallengeAsync(AuthenticationProperties properties) + { + if (_configuration == null) + { + _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); + } + + // Save the original challenge URI so we can redirect back to it when we're done. + if (string.IsNullOrEmpty(properties.RedirectUri)) + { + properties.RedirectUri = OriginalPathBase + OriginalPath + Request.QueryString; + } + + var wsFederationMessage = new WsFederationMessage() + { + IssuerAddress = _configuration.TokenEndpoint ?? string.Empty, + Wtrealm = Options.Wtrealm, + Wa = WsFederationConstants.WsFederationActions.SignIn, + }; + + if (!string.IsNullOrEmpty(Options.Wreply)) + { + wsFederationMessage.Wreply = Options.Wreply; + } + else + { + wsFederationMessage.Wreply = BuildRedirectUri(Options.CallbackPath); + } + + GenerateCorrelationId(properties); + + var redirectContext = new RedirectContext(Context, Scheme, Options, properties) + { + ProtocolMessage = wsFederationMessage + }; + await Events.RedirectToIdentityProvider(redirectContext); + + if (redirectContext.Handled) + { + return; + } + + wsFederationMessage = redirectContext.ProtocolMessage; + + if (!string.IsNullOrEmpty(wsFederationMessage.Wctx)) + { + properties.Items[WsFederationDefaults.UserstatePropertiesKey] = wsFederationMessage.Wctx; + } + + wsFederationMessage.Wctx = Uri.EscapeDataString(Options.StateDataFormat.Protect(properties)); + + var redirectUri = wsFederationMessage.CreateSignInUrl(); + if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute)) + { + Logger.MalformedRedirectUri(redirectUri); + } + Response.Redirect(redirectUri); + } + + /// + /// Invoked to process incoming authentication messages. + /// + /// + protected override async Task HandleRemoteAuthenticateAsync() + { + WsFederationMessage wsFederationMessage = null; + AuthenticationProperties properties = null; + + // assumption: if the ContentType is "application/x-www-form-urlencoded" it should be safe to read as it is small. + if (HttpMethods.IsPost(Request.Method) + && !string.IsNullOrEmpty(Request.ContentType) + // May have media/type; charset=utf-8, allow partial match. + && Request.ContentType.StartsWith("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase) + && Request.Body.CanRead) + { + var form = await Request.ReadFormAsync(); + + wsFederationMessage = new WsFederationMessage(form.Select(pair => new KeyValuePair(pair.Key, pair.Value))); + } + + if (wsFederationMessage == null || !wsFederationMessage.IsSignInMessage) + { + if (Options.SkipUnrecognizedRequests) + { + // Not for us? + return HandleRequestResult.SkipHandler(); + } + + return HandleRequestResult.Fail("No message."); + } + + try + { + // Retrieve our cached redirect uri + var state = wsFederationMessage.Wctx; + // WsFed allows for uninitiated logins, state may be missing. See AllowUnsolicitedLogins. + properties = Options.StateDataFormat.Unprotect(state); + + if (properties == null) + { + if (!Options.AllowUnsolicitedLogins) + { + return HandleRequestResult.Fail("Unsolicited logins are not allowed."); + } + } + else + { + // Extract the user state from properties and reset. + properties.Items.TryGetValue(WsFederationDefaults.UserstatePropertiesKey, out var userState); + wsFederationMessage.Wctx = userState; + } + + var messageReceivedContext = new MessageReceivedContext(Context, Scheme, Options, properties) + { + ProtocolMessage = wsFederationMessage + }; + await Events.MessageReceived(messageReceivedContext); + if (messageReceivedContext.Result != null) + { + return messageReceivedContext.Result; + } + wsFederationMessage = messageReceivedContext.ProtocolMessage; + properties = messageReceivedContext.Properties; // Provides a new instance if not set. + + // If state did flow from the challenge then validate it. See AllowUnsolicitedLogins above. + if (properties.Items.TryGetValue(CorrelationProperty, out string correlationId) + && !ValidateCorrelationId(properties)) + { + return HandleRequestResult.Fail("Correlation failed.", properties); + } + + if (wsFederationMessage.Wresult == null) + { + Logger.SignInWithoutWresult(); + return HandleRequestResult.Fail(Resources.SignInMessageWresultIsMissing, properties); + } + + var token = wsFederationMessage.GetToken(); + if (string.IsNullOrEmpty(token)) + { + Logger.SignInWithoutToken(); + return HandleRequestResult.Fail(Resources.SignInMessageTokenIsMissing, properties); + } + + var securityTokenReceivedContext = new SecurityTokenReceivedContext(Context, Scheme, Options, properties) + { + ProtocolMessage = wsFederationMessage + }; + await Events.SecurityTokenReceived(securityTokenReceivedContext); + if (securityTokenReceivedContext.Result != null) + { + return securityTokenReceivedContext.Result; + } + wsFederationMessage = securityTokenReceivedContext.ProtocolMessage; + properties = messageReceivedContext.Properties; + + if (_configuration == null) + { + _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); + } + + // Copy and augment to avoid cross request race conditions for updated configurations. + var tvp = Options.TokenValidationParameters.Clone(); + var issuers = new[] { _configuration.Issuer }; + tvp.ValidIssuers = (tvp.ValidIssuers == null ? issuers : tvp.ValidIssuers.Concat(issuers)); + tvp.IssuerSigningKeys = (tvp.IssuerSigningKeys == null ? _configuration.SigningKeys : tvp.IssuerSigningKeys.Concat(_configuration.SigningKeys)); + + ClaimsPrincipal principal = null; + SecurityToken parsedToken = null; + foreach (var validator in Options.SecurityTokenHandlers) + { + if (validator.CanReadToken(token)) + { + principal = validator.ValidateToken(token, tvp, out parsedToken); + break; + } + } + + if (principal == null) + { + throw new SecurityTokenException(Resources.Exception_NoTokenValidatorFound); + } + + if (Options.UseTokenLifetime && parsedToken != null) + { + // Override any session persistence to match the token lifetime. + var issued = parsedToken.ValidFrom; + if (issued != DateTime.MinValue) + { + properties.IssuedUtc = issued.ToUniversalTime(); + } + var expires = parsedToken.ValidTo; + if (expires != DateTime.MinValue) + { + properties.ExpiresUtc = expires.ToUniversalTime(); + } + properties.AllowRefresh = false; + } + + var securityTokenValidatedContext = new SecurityTokenValidatedContext(Context, Scheme, Options, principal, properties) + { + ProtocolMessage = wsFederationMessage, + SecurityToken = parsedToken, + }; + + await Events.SecurityTokenValidated(securityTokenValidatedContext); + if (securityTokenValidatedContext.Result != null) + { + return securityTokenValidatedContext.Result; + } + + // Flow possible changes + principal = securityTokenValidatedContext.Principal; + properties = securityTokenValidatedContext.Properties; + + return HandleRequestResult.Success(new AuthenticationTicket(principal, properties, Scheme.Name)); + } + catch (Exception exception) + { + Logger.ExceptionProcessingMessage(exception); + + // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the notification. + if (Options.RefreshOnIssuerKeyNotFound && exception.GetType().Equals(typeof(SecurityTokenSignatureKeyNotFoundException))) + { + Options.ConfigurationManager.RequestRefresh(); + } + + var authenticationFailedContext = new AuthenticationFailedContext(Context, Scheme, Options) + { + ProtocolMessage = wsFederationMessage, + Exception = exception + }; + await Events.AuthenticationFailed(authenticationFailedContext); + if (authenticationFailedContext.Result != null) + { + return authenticationFailedContext.Result; + } + + return HandleRequestResult.Fail(exception, properties); + } + } + + /// + /// Handles Signout + /// + /// + public async virtual Task SignOutAsync(AuthenticationProperties properties) + { + var target = ResolveTarget(Options.ForwardSignOut); + if (target != null) + { + await Context.SignOutAsync(target, properties); + return; + } + + if (_configuration == null) + { + _configuration = await Options.ConfigurationManager.GetConfigurationAsync(Context.RequestAborted); + } + + var wsFederationMessage = new WsFederationMessage() + { + IssuerAddress = _configuration.TokenEndpoint ?? string.Empty, + Wtrealm = Options.Wtrealm, + Wa = WsFederationConstants.WsFederationActions.SignOut, + }; + + // Set Wreply in order: + // 1. properties.Redirect + // 2. Options.SignOutWreply + // 3. Options.Wreply + if (properties != null && !string.IsNullOrEmpty(properties.RedirectUri)) + { + wsFederationMessage.Wreply = BuildRedirectUriIfRelative(properties.RedirectUri); + } + else if (!string.IsNullOrEmpty(Options.SignOutWreply)) + { + wsFederationMessage.Wreply = BuildRedirectUriIfRelative(Options.SignOutWreply); + } + else if (!string.IsNullOrEmpty(Options.Wreply)) + { + wsFederationMessage.Wreply = BuildRedirectUriIfRelative(Options.Wreply); + } + + var redirectContext = new RedirectContext(Context, Scheme, Options, properties) + { + ProtocolMessage = wsFederationMessage + }; + await Events.RedirectToIdentityProvider(redirectContext); + + if (!redirectContext.Handled) + { + var redirectUri = redirectContext.ProtocolMessage.CreateSignOutUrl(); + if (!Uri.IsWellFormedUriString(redirectUri, UriKind.Absolute)) + { + Logger.MalformedRedirectUri(redirectUri); + } + Response.Redirect(redirectUri); + } + } + + /// + /// Handles wsignoutcleanup1.0 messages sent to the RemoteSignOutPath + /// + /// + protected virtual async Task HandleRemoteSignOutAsync() + { + var message = new WsFederationMessage(Request.Query.Select(pair => new KeyValuePair(pair.Key, pair.Value))); + var remoteSignOutContext = new RemoteSignOutContext(Context, Scheme, Options, message); + await Events.RemoteSignOut(remoteSignOutContext); + + if (remoteSignOutContext.Result != null) + { + if (remoteSignOutContext.Result.Handled) + { + Logger.RemoteSignOutHandledResponse(); + return true; + } + if (remoteSignOutContext.Result.Skipped) + { + Logger.RemoteSignOutSkipped(); + return false; + } + } + + Logger.RemoteSignOut(); + + await Context.SignOutAsync(Options.SignOutScheme); + return true; + } + + /// + /// Build a redirect path if the given path is a relative path. + /// + private string BuildRedirectUriIfRelative(string uri) + { + if (string.IsNullOrEmpty(uri)) + { + return uri; + } + + if (!uri.StartsWith("/", StringComparison.Ordinal)) + { + return uri; + } + + return BuildRedirectUri(uri); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationOptions.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationOptions.cs new file mode 100644 index 000000000..4e0612677 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationOptions.cs @@ -0,0 +1,180 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.IdentityModel.Tokens.Jwt; +using Microsoft.AspNetCore.Http; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.WsFederation; +using Microsoft.IdentityModel.Tokens; +using Microsoft.IdentityModel.Tokens.Saml; +using Microsoft.IdentityModel.Tokens.Saml2; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// Configuration options for + /// + public class WsFederationOptions : RemoteAuthenticationOptions + { + private ICollection _securityTokenHandlers = new Collection() + { + new Saml2SecurityTokenHandler(), + new SamlSecurityTokenHandler(), + new JwtSecurityTokenHandler() + }; + private TokenValidationParameters _tokenValidationParameters = new TokenValidationParameters(); + + /// + /// Initializes a new + /// + public WsFederationOptions() + { + CallbackPath = "/signin-wsfed"; + // In ADFS the cleanup messages are sent to the same callback path as the initial login. + // In AAD it sends the cleanup message to a random Reply Url and there's no deterministic way to configure it. + // If you manage to get it configured, then you can set RemoteSignOutPath accordingly. + RemoteSignOutPath = "/signin-wsfed"; + Events = new WsFederationEvents(); + } + + /// + /// Check that the options are valid. Should throw an exception if things are not ok. + /// + public override void Validate() + { + base.Validate(); + + if (ConfigurationManager == null) + { + throw new InvalidOperationException($"Provide {nameof(MetadataAddress)}, " + + $"{nameof(Configuration)}, or {nameof(ConfigurationManager)} to {nameof(WsFederationOptions)}"); + } + } + + /// + /// Configuration provided directly by the developer. If provided, then MetadataAddress and the Backchannel properties + /// will not be used. This information should not be updated during request processing. + /// + public WsFederationConfiguration Configuration { get; set; } + + /// + /// Gets or sets the address to retrieve the wsFederation metadata + /// + public string MetadataAddress { get; set; } + + /// + /// Responsible for retrieving, caching, and refreshing the configuration from metadata. + /// If not provided, then one will be created using the MetadataAddress and Backchannel properties. + /// + public IConfigurationManager ConfigurationManager { get; set; } + + /// + /// Gets or sets if a metadata refresh should be attempted after a SecurityTokenSignatureKeyNotFoundException. This allows for automatic + /// recovery in the event of a signature key rollover. This is enabled by default. + /// + public bool RefreshOnIssuerKeyNotFound { get; set; } = true; + + /// + /// Indicates if requests to the CallbackPath may also be for other components. If enabled the handler will pass + /// requests through that do not contain WsFederation authentication responses. Disabling this and setting the + /// CallbackPath to a dedicated endpoint may provide better error handling. + /// This is disabled by default. + /// + public bool SkipUnrecognizedRequests { get; set; } + + /// + /// Gets or sets the to call when processing WsFederation messages. + /// + public new WsFederationEvents Events + { + get => (WsFederationEvents)base.Events; + set => base.Events = value; + } + + /// + /// Gets or sets the collection of used to read and validate the s. + /// + public ICollection SecurityTokenHandlers + { + get + { + return _securityTokenHandlers; + } + set + { + _securityTokenHandlers = value ?? throw new ArgumentNullException(nameof(SecurityTokenHandlers)); + } + } + + /// + /// Gets or sets the type used to secure data handled by the middleware. + /// + public ISecureDataFormat StateDataFormat { get; set; } + + /// + /// Gets or sets the + /// + /// if 'TokenValidationParameters' is null. + public TokenValidationParameters TokenValidationParameters + { + get + { + return _tokenValidationParameters; + } + set + { + _tokenValidationParameters = value ?? throw new ArgumentNullException(nameof(TokenValidationParameters)); + } + } + + /// + /// Gets or sets the 'wreply'. CallbackPath must be set to match or cleared so it can be generated dynamically. + /// This field is optional. If not set then it will be generated from the current request and the CallbackPath. + /// + public string Wreply { get; set; } + + /// + /// Gets or sets the 'wreply' value used during sign-out. + /// If none is specified then the value from the Wreply field is used. + /// + public string SignOutWreply { get; set; } + + /// + /// Gets or sets the 'wtrealm'. + /// + public string Wtrealm { get; set; } + + /// + /// Indicates that the authentication session lifetime (e.g. cookies) should match that of the authentication token. + /// If the token does not provide lifetime information then normal session lifetimes will be used. + /// This is enabled by default. + /// + public bool UseTokenLifetime { get; set; } = true; + + /// + /// Gets or sets if HTTPS is required for the metadata address or authority. + /// The default is true. This should be disabled only in development environments. + /// + public bool RequireHttpsMetadata { get; set; } = true; + + /// + /// The Ws-Federation protocol allows the user to initiate logins without contacting the application for a Challenge first. + /// However, that flow is susceptible to XSRF and other attacks so it is disabled here by default. + /// + public bool AllowUnsolicitedLogins { get; set; } + + /// + /// Requests received on this path will cause the handler to invoke SignOut using the SignOutScheme. + /// + public PathString RemoteSignOutPath { get; set; } + + /// + /// The Authentication Scheme to use with SignOutAsync from RemoteSignOutPath. SignInScheme will be used if this + /// is not set. + /// + public string SignOutScheme { get; set; } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationPostConfigureOptions.cs b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationPostConfigureOptions.cs new file mode 100644 index 000000000..62647d4fc --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/WsFederationPostConfigureOptions.cs @@ -0,0 +1,89 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Net.Http; +using Microsoft.AspNetCore.DataProtection; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Options; +using Microsoft.IdentityModel.Protocols; +using Microsoft.IdentityModel.Protocols.WsFederation; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + /// + /// Used to setup defaults for all . + /// + public class WsFederationPostConfigureOptions : IPostConfigureOptions + { + private readonly IDataProtectionProvider _dp; + + /// + /// + /// + /// + public WsFederationPostConfigureOptions(IDataProtectionProvider dataProtection) + { + _dp = dataProtection; + } + + /// + /// Invoked to post configure a TOptions instance. + /// + /// The name of the options instance being configured. + /// The options instance to configure. + public void PostConfigure(string name, WsFederationOptions options) + { + options.DataProtectionProvider = options.DataProtectionProvider ?? _dp; + + if (string.IsNullOrEmpty(options.SignOutScheme)) + { + options.SignOutScheme = options.SignInScheme; + } + + if (options.StateDataFormat == null) + { + var dataProtector = options.DataProtectionProvider.CreateProtector( + typeof(WsFederationHandler).FullName, name, "v1"); + options.StateDataFormat = new PropertiesDataFormat(dataProtector); + } + + if (!options.CallbackPath.HasValue && !string.IsNullOrEmpty(options.Wreply) && Uri.TryCreate(options.Wreply, UriKind.Absolute, out var wreply)) + { + // Wreply must be a very specific, case sensitive value, so we can't generate it. Instead we generate CallbackPath from it. + options.CallbackPath = PathString.FromUriComponent(wreply); + } + + if (string.IsNullOrEmpty(options.TokenValidationParameters.ValidAudience)) + { + options.TokenValidationParameters.ValidAudience = options.Wtrealm; + } + + if (options.Backchannel == null) + { + options.Backchannel = new HttpClient(options.BackchannelHttpHandler ?? new HttpClientHandler()); + options.Backchannel.DefaultRequestHeaders.UserAgent.ParseAdd("Microsoft ASP.NET Core WsFederation handler"); + options.Backchannel.Timeout = options.BackchannelTimeout; + options.Backchannel.MaxResponseContentBufferSize = 1024 * 1024 * 10; // 10 MB + } + + if (options.ConfigurationManager == null) + { + if (options.Configuration != null) + { + options.ConfigurationManager = new StaticConfigurationManager(options.Configuration); + } + else if (!string.IsNullOrEmpty(options.MetadataAddress)) + { + if (options.RequireHttpsMetadata && !options.MetadataAddress.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) + { + throw new InvalidOperationException("The MetadataAddress must use HTTPS unless disabled for development by setting RequireHttpsMetadata=false."); + } + + options.ConfigurationManager = new ConfigurationManager(options.MetadataAddress, new WsFederationConfigurationRetriever(), + new HttpDocumentRetriever(options.Backchannel) { RequireHttps = options.RequireHttpsMetadata }); + } + } + } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication.WsFederation/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication.WsFederation/baseline.netcore.json new file mode 100644 index 000000000..41150cbc0 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication.WsFederation/baseline.netcore.json @@ -0,0 +1,1314 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication.WsFederation, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.Extensions.DependencyInjection.WsFederationExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddWsFederation", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddWsFederation", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddWsFederation", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddWsFederation", + "Parameters": [ + { + "Name": "builder", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.AuthenticationFailedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Exception", + "Parameters": [], + "ReturnType": "System.Exception", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Exception", + "Parameters": [ + { + "Name": "value", + "Type": "System.Exception" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.MessageReceivedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.RedirectContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.PropertiesContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Handled", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleResponse", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.RemoteSignOutContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions" + }, + { + "Name": "message", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.SecurityTokenReceivedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.SecurityTokenValidatedContext", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_ProtocolMessage", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ProtocolMessage", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationMessage" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SecurityToken", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Tokens.SecurityToken", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SecurityToken", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Tokens.SecurityToken" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions" + }, + { + "Name": "principal", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationEvents", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_OnAuthenticationFailed", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnAuthenticationFailed", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnMessageReceived", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnMessageReceived", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnRedirectToIdentityProvider", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnRedirectToIdentityProvider", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnRemoteSignOut", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnRemoteSignOut", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnSecurityTokenReceived", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnSecurityTokenReceived", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_OnSecurityTokenValidated", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnSecurityTokenValidated", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AuthenticationFailed", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.AuthenticationFailedContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "MessageReceived", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.MessageReceivedContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RedirectToIdentityProvider", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.RedirectContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "RemoteSignOut", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.RemoteSignOutContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SecurityTokenReceived", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.SecurityTokenReceivedContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SecurityTokenValidated", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.SecurityTokenValidatedContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationDefaults", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Field", + "Name": "UserstatePropertiesKey", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "ReadOnly": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Field", + "Name": "AuthenticationScheme", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"WsFederation\"" + }, + { + "Kind": "Field", + "Name": "DisplayName", + "Parameters": [], + "ReturnType": "System.String", + "Static": true, + "Visibility": "Public", + "GenericParameter": [], + "Constant": true, + "Literal": "\"WsFederation\"" + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationHandler", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IAuthenticationSignOutHandler" + ], + "Members": [ + { + "Kind": "Method", + "Name": "HandleRequestAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationRequestHandler", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationEvents", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Events", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationEvents" + } + ], + "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "CreateEventsAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleChallengeAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleRemoteAuthenticateAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "SignOutAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationSignOutHandler", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleRemoteSignOutAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Validate", + "Parameters": [], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Configuration", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationConfiguration", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Configuration", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.WsFederation.WsFederationConfiguration" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_MetadataAddress", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MetadataAddress", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_ConfigurationManager", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Protocols.IConfigurationManager", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ConfigurationManager", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Protocols.IConfigurationManager" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RefreshOnIssuerKeyNotFound", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RefreshOnIssuerKeyNotFound", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SkipUnrecognizedRequests", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SkipUnrecognizedRequests", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationEvents", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Events", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationEvents" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SecurityTokenHandlers", + "Parameters": [], + "ReturnType": "System.Collections.Generic.ICollection", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SecurityTokenHandlers", + "Parameters": [ + { + "Name": "value", + "Type": "System.Collections.Generic.ICollection" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_StateDataFormat", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_StateDataFormat", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.ISecureDataFormat" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_TokenValidationParameters", + "Parameters": [], + "ReturnType": "Microsoft.IdentityModel.Tokens.TokenValidationParameters", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_TokenValidationParameters", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.IdentityModel.Tokens.TokenValidationParameters" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Wreply", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Wreply", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SignOutWreply", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SignOutWreply", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Wtrealm", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Wtrealm", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_UseTokenLifetime", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_UseTokenLifetime", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RequireHttpsMetadata", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RequireHttpsMetadata", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_AllowUnsolicitedLogins", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_AllowUnsolicitedLogins", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_RemoteSignOutPath", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_RemoteSignOutPath", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.PathString" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_SignOutScheme", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_SignOutScheme", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationPostConfigureOptions", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.Extensions.Options.IPostConfigureOptions" + ], + "Members": [ + { + "Kind": "Method", + "Name": "PostConfigure", + "Parameters": [ + { + "Name": "name", + "Type": "System.String" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.WsFederation.WsFederationOptions" + } + ], + "ReturnType": "System.Void", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.Extensions.Options.IPostConfigureOptions", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "dataProtection", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationBuilder.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationBuilder.cs index c29bdeae2..401b1f488 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationBuilder.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationBuilder.cs @@ -25,18 +25,10 @@ public AuthenticationBuilder(IServiceCollection services) /// public virtual IServiceCollection Services { get; } - /// - /// Adds a which can be used by . - /// - /// The type to configure the handler."/>. - /// The used to handle this scheme. - /// The name of this scheme. - /// The display name of this scheme. - /// Used to configure the scheme options. - /// The builder. - public virtual AuthenticationBuilder AddScheme(string authenticationScheme, string displayName, Action configureOptions) - where TOptions : AuthenticationSchemeOptions, new() - where THandler : AuthenticationHandler + + private AuthenticationBuilder AddSchemeHelper(string authenticationScheme, string displayName, Action configureOptions) + where TOptions : class, new() + where THandler : class, IAuthenticationHandler { Services.Configure(o => { @@ -53,6 +45,20 @@ public virtual AuthenticationBuilder AddScheme(string authen return this; } + /// + /// Adds a which can be used by . + /// + /// The type to configure the handler."/>. + /// The used to handle this scheme. + /// The name of this scheme. + /// The display name of this scheme. + /// Used to configure the scheme options. + /// The builder. + public virtual AuthenticationBuilder AddScheme(string authenticationScheme, string displayName, Action configureOptions) + where TOptions : AuthenticationSchemeOptions, new() + where THandler : AuthenticationHandler + => AddSchemeHelper(authenticationScheme, displayName, configureOptions); + /// /// Adds a which can be used by . /// @@ -84,7 +90,18 @@ public virtual AuthenticationBuilder AddRemoteScheme(string return AddScheme(authenticationScheme, displayName, configureOptions: configureOptions); } - // Used to ensure that there's always a default data protection provider + /// + /// Adds a based authentication handler which can be used to + /// redirect to other authentication schemes. + /// + /// The name of this scheme. + /// The display name of this scheme. + /// Used to configure the scheme options. + /// The builder. + public virtual AuthenticationBuilder AddPolicyScheme(string authenticationScheme, string displayName, Action configureOptions) + => AddSchemeHelper(authenticationScheme, displayName, configureOptions); + + // Used to ensure that there's always a default sign in scheme that's not itself private class EnsureSignInScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions { private readonly AuthenticationOptions _authOptions; @@ -96,7 +113,7 @@ public EnsureSignInScheme(IOptions authOptions) public void PostConfigure(string name, TOptions options) { - options.SignInScheme = options.SignInScheme ?? _authOptions.DefaultSignInScheme; + options.SignInScheme = options.SignInScheme ?? _authOptions.DefaultSignInScheme ?? _authOptions.DefaultScheme; } } } diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs index d1d09f074..5c9a6473f 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationHandler.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Security.Claims; using System.Text.Encodings.Web; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; @@ -22,12 +21,12 @@ namespace Microsoft.AspNetCore.Authentication protected HttpRequest Request { - get { return Context.Request; } + get => Context.Request; } protected HttpResponse Response { - get { return Context.Response; } + get => Context.Response; } protected PathString OriginalPath => Context.Features.Get()?.OriginalPath ?? Request.Path; @@ -52,10 +51,7 @@ protected HttpResponse Response protected string CurrentUri { - get - { - return Request.Scheme + "://" + Request.Host + Request.PathBase + Request.Path + Request.QueryString; - } + get => Request.Scheme + "://" + Request.Host + Request.PathBase + Request.Path + Request.QueryString; } protected AuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) @@ -87,7 +83,7 @@ public async Task InitializeAsync(AuthenticationScheme scheme, HttpContext conte Context = context; Options = OptionsMonitor.Get(Scheme.Name) ?? new TOptions(); - Options.Validate(); + Options.Validate(Scheme.Name); await InitializeEventsAsync(); await InitializeHandlerAsync(); @@ -116,18 +112,29 @@ protected virtual async Task InitializeEventsAsync() /// Called after options/events have been initialized for the handler to finish initializing itself. /// /// A task - protected virtual Task InitializeHandlerAsync() - { - return Task.CompletedTask; - } + protected virtual Task InitializeHandlerAsync() => Task.CompletedTask; protected string BuildRedirectUri(string targetPath) + => Request.Scheme + "://" + Request.Host + OriginalPathBase + targetPath; + + protected virtual string ResolveTarget(string scheme) { - return Request.Scheme + "://" + Request.Host + OriginalPathBase + targetPath; + var target = scheme ?? Options.ForwardDefaultSelector?.Invoke(Context) ?? Options.ForwardDefault; + + // Prevent self targetting + return string.Equals(target, Scheme.Name, StringComparison.Ordinal) + ? null + : target; } public async Task AuthenticateAsync() { + var target = ResolveTarget(Options.ForwardAuthenticate); + if (target != null) + { + return await Context.AuthenticateAsync(target); + } + // Calling Authenticate more than once should always return the original value. var result = await HandleAuthenticateOnceAsync(); if (result?.Failure == null) @@ -142,7 +149,7 @@ public async Task AuthenticateAsync() Logger.AuthenticationSchemeNotAuthenticated(Scheme.Name); } } - else if (result?.Failure != null) + else { Logger.AuthenticationSchemeNotAuthenticatedWithFailure(Scheme.Name, result.Failure.Message); } @@ -208,6 +215,13 @@ protected virtual Task HandleChallengeAsync(AuthenticationProperties properties) public async Task ChallengeAsync(AuthenticationProperties properties) { + var target = ResolveTarget(Options.ForwardChallenge); + if (target != null) + { + await Context.ChallengeAsync(target, properties); + return; + } + properties = properties ?? new AuthenticationProperties(); await HandleChallengeAsync(properties); Logger.AuthenticationSchemeChallenged(Scheme.Name); @@ -215,6 +229,13 @@ public async Task ChallengeAsync(AuthenticationProperties properties) public async Task ForbidAsync(AuthenticationProperties properties) { + var target = ResolveTarget(Options.ForwardForbid); + if (target != null) + { + await Context.ForbidAsync(target, properties); + return; + } + properties = properties ?? new AuthenticationProperties(); await HandleForbiddenAsync(properties); Logger.AuthenticationSchemeForbidden(Scheme.Name); diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs index 0e86b3a9f..a547d203b 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationSchemeOptions.cs @@ -2,6 +2,7 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; +using Microsoft.AspNetCore.Http; namespace Microsoft.AspNetCore.Authentication { @@ -13,9 +14,14 @@ public class AuthenticationSchemeOptions /// /// Check that the options are valid. Should throw an exception if things are not ok. /// - public virtual void Validate() - { - } + public virtual void Validate() { } + + /// + /// Checks that the options are valid for a specific scheme + /// + /// The scheme being validated. + public virtual void Validate(string scheme) + => Validate(); /// /// Gets or sets the issuer that should be used for any claims that are created @@ -31,5 +37,57 @@ public virtual void Validate() /// If set, will be used as the service type to get the Events instance instead of the property. /// public Type EventsType { get; set; } + + /// + /// If set, this specifies a default scheme that authentication handlers should forward all authentication operations to + /// by default. The default forwarding logic will check the most specific ForwardAuthenticate/Challenge/Forbid/SignIn/SignOut + /// setting first, followed by checking the ForwardDefaultSelector, followed by ForwardDefault. The first non null result + /// will be used as the target scheme to forward to. + /// + public string ForwardDefault { get; set; } + + /// + /// If set, this specifies the target scheme that this scheme should forward AuthenticateAsync calls to. + /// For example Context.AuthenticateAsync("ThisScheme") => Context.AuthenticateAsync("ForwardAuthenticateValue"); + /// Set the target to the current scheme to disable forwarding and allow normal processing. + /// + public string ForwardAuthenticate { get; set; } + + /// + /// If set, this specifies the target scheme that this scheme should forward ChallengeAsync calls to. + /// For example Context.ChallengeAsync("ThisScheme") => Context.ChallengeAsync("ForwardChallengeValue"); + /// Set the target to the current scheme to disable forwarding and allow normal processing. + /// + public string ForwardChallenge { get; set; } + + /// + /// If set, this specifies the target scheme that this scheme should forward ForbidAsync calls to. + /// For example Context.ForbidAsync("ThisScheme") => Context.ForbidAsync("ForwardForbidValue"); + /// Set the target to the current scheme to disable forwarding and allow normal processing. + /// + public string ForwardForbid { get; set; } + + /// + /// If set, this specifies the target scheme that this scheme should forward SignInAsync calls to. + /// For example Context.SignInAsync("ThisScheme") => Context.SignInAsync("ForwardSignInValue"); + /// Set the target to the current scheme to disable forwarding and allow normal processing. + /// + public string ForwardSignIn { get; set; } + + /// + /// If set, this specifies the target scheme that this scheme should forward SignOutAsync calls to. + /// For example Context.SignOutAsync("ThisScheme") => Context.SignInAsync("ForwardSignOutValue"); + /// Set the target to the current scheme to disable forwarding and allow normal processing. + /// + public string ForwardSignOut { get; set; } + + /// + /// Used to select a default scheme for the current request that authentication handlers should forward all authentication operations to + /// by default. The default forwarding logic will check the most specific ForwardAuthenticate/Challenge/Forbid/SignIn/SignOut + /// setting first, followed by checking the ForwardDefaultSelector, followed by ForwardDefault. The first non null result + /// will be used as the target scheme to forward to. + /// + public Func ForwardDefaultSelector { get; set; } + } } diff --git a/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs b/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs index e1beea7b6..6c381b84a 100644 --- a/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication/AuthenticationServiceCollectionExtensions.cs @@ -46,46 +46,7 @@ public static AuthenticationBuilder AddAuthentication(this IServiceCollection se return builder; } - // REMOVE below once callers have been updated - public static IServiceCollection AddScheme(this IServiceCollection services, string authenticationScheme, string displayName, Action configureScheme, Action configureOptions) - where TOptions : AuthenticationSchemeOptions, new() - where THandler : AuthenticationHandler - { - services.AddAuthentication(o => - { - o.AddScheme(authenticationScheme, scheme => { - scheme.HandlerType = typeof(THandler); - scheme.DisplayName = displayName; - configureScheme?.Invoke(scheme); - }); - }); - if (configureOptions != null) - { - services.Configure(authenticationScheme, configureOptions); - } - services.AddTransient(); - return services; - } - - public static IServiceCollection AddScheme(this IServiceCollection services, string authenticationScheme, Action configureOptions) - where TOptions : AuthenticationSchemeOptions, new() - where THandler : AuthenticationHandler - => services.AddScheme(authenticationScheme, displayName: null, configureScheme: null, configureOptions: configureOptions); - - public static IServiceCollection AddScheme(this IServiceCollection services, string authenticationScheme, string displayName, Action configureOptions) - where TOptions : AuthenticationSchemeOptions, new() - where THandler : AuthenticationHandler - => services.AddScheme(authenticationScheme, displayName, configureScheme: null, configureOptions: configureOptions); - - public static IServiceCollection AddRemoteScheme(this IServiceCollection services, string authenticationScheme, string displayName, Action configureOptions) - where TOptions : RemoteAuthenticationOptions, new() - where THandler : RemoteAuthenticationHandler - { - services.TryAddEnumerable(ServiceDescriptor.Singleton, EnsureSignInScheme>()); - return services.AddScheme(authenticationScheme, displayName, configureScheme: null, configureOptions: configureOptions); - } - - // Used to ensure that there's always a default data protection provider + // Used to ensure that there's always a sign in scheme private class EnsureSignInScheme : IPostConfigureOptions where TOptions : RemoteAuthenticationOptions { private readonly AuthenticationOptions _authOptions; diff --git a/src/Microsoft.AspNetCore.Authentication/Data/TextEncoder.cs b/src/Microsoft.AspNetCore.Authentication/Data/TextEncoder.cs index c0663295c..1f7ecc718 100644 --- a/src/Microsoft.AspNetCore.Authentication/Data/TextEncoder.cs +++ b/src/Microsoft.AspNetCore.Authentication/Data/TextEncoder.cs @@ -13,7 +13,7 @@ public static class Base64UrlTextEncoder /// Base64 encoded string modified with non-URL encodable characters public static string Encode(byte[] data) { - return WebUtilities.Base64UrlTextEncoder.Encode(data); + return WebUtilities.WebEncoders.Base64UrlEncode(data); } /// @@ -24,7 +24,7 @@ public static string Encode(byte[] data) /// The decoded data. public static byte[] Decode(string text) { - return WebUtilities.Base64UrlTextEncoder.Decode(text); + return WebUtilities.WebEncoders.Base64UrlDecode(text); } } } diff --git a/src/Microsoft.AspNetCore.Authentication/Events/AccessDeniedContext.cs b/src/Microsoft.AspNetCore.Authentication/Events/AccessDeniedContext.cs new file mode 100644 index 000000000..f01d69453 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication/Events/AccessDeniedContext.cs @@ -0,0 +1,44 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Provides access denied failure context information to handler providers. + /// + public class AccessDeniedContext : HandleRequestContext + { + public AccessDeniedContext( + HttpContext context, + AuthenticationScheme scheme, + RemoteAuthenticationOptions options) + : base(context, scheme, options) + { + } + + /// + /// Gets or sets the endpoint path the user agent will be redirected to. + /// By default, this property is set to . + /// + public PathString AccessDeniedPath { get; set; } + + /// + /// Additional state values for the authentication session. + /// + public AuthenticationProperties Properties { get; set; } + + /// + /// Gets or sets the return URL that will be flowed up to the access denied page. + /// If is not set, this property is not used. + /// + public string ReturnUrl { get; set; } + + /// + /// Gets or sets the parameter name that will be used to flow the return URL. + /// By default, this property is set to . + /// + public string ReturnUrlParameter { get; set; } + } +} diff --git a/src/Microsoft.AspNetCore.Authentication/Events/RemoteAuthenticationEvents.cs b/src/Microsoft.AspNetCore.Authentication/Events/RemoteAuthenticationEvents.cs index ca0f4a5c0..be4c6ab49 100644 --- a/src/Microsoft.AspNetCore.Authentication/Events/RemoteAuthenticationEvents.cs +++ b/src/Microsoft.AspNetCore.Authentication/Events/RemoteAuthenticationEvents.cs @@ -8,12 +8,18 @@ namespace Microsoft.AspNetCore.Authentication { public class RemoteAuthenticationEvents { + public Func OnAccessDenied { get; set; } = context => Task.CompletedTask; public Func OnRemoteFailure { get; set; } = context => Task.CompletedTask; public Func OnTicketReceived { get; set; } = context => Task.CompletedTask; /// - /// Invoked when there is a remote failure + /// Invoked when an access denied error was returned by the remote server. + /// + public virtual Task AccessDenied(AccessDeniedContext context) => OnAccessDenied(context); + + /// + /// Invoked when there is a remote failure. /// public virtual Task RemoteFailure(RemoteFailureContext context) => OnRemoteFailure(context); diff --git a/src/Microsoft.AspNetCore.Authentication/Events/RemoteFailureContext.cs b/src/Microsoft.AspNetCore.Authentication/Events/RemoteFailureContext.cs index becdfb543..6b3598f40 100644 --- a/src/Microsoft.AspNetCore.Authentication/Events/RemoteFailureContext.cs +++ b/src/Microsoft.AspNetCore.Authentication/Events/RemoteFailureContext.cs @@ -25,5 +25,10 @@ public RemoteFailureContext( /// User friendly error message for the error. /// public Exception Failure { get; set; } + + /// + /// Additional state values for the authentication session. + /// + public AuthenticationProperties Properties { get; set; } } } diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationResult.cs b/src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs similarity index 69% rename from src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationResult.cs rename to src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs index 8bcd2be01..da9b6ea01 100644 --- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationResult.cs +++ b/src/Microsoft.AspNetCore.Authentication/HandleRequestResult.cs @@ -33,7 +33,7 @@ public class HandleRequestResult : AuthenticateResult { throw new ArgumentNullException(nameof(ticket)); } - return new HandleRequestResult() { Ticket = ticket }; + return new HandleRequestResult() { Ticket = ticket, Properties = ticket.Properties }; } /// @@ -49,13 +49,31 @@ public class HandleRequestResult : AuthenticateResult /// /// Indicates that there was a failure during authentication. /// - /// The failure message. + /// The failure exception. + /// Additional state values for the authentication session. /// The result. - public static new HandleRequestResult Fail(string failureMessage) + public static new HandleRequestResult Fail(Exception failure, AuthenticationProperties properties) { - return new HandleRequestResult() { Failure = new Exception(failureMessage) }; + return new HandleRequestResult() { Failure = failure, Properties = properties }; } + /// + /// Indicates that there was a failure during authentication. + /// + /// The failure message. + /// The result. + public static new HandleRequestResult Fail(string failureMessage) + => Fail(new Exception(failureMessage)); + + /// + /// Indicates that there was a failure during authentication. + /// + /// The failure message. + /// Additional state values for the authentication session. + /// The result. + public static new HandleRequestResult Fail(string failureMessage, AuthenticationProperties properties) + => Fail(new Exception(failureMessage), properties); + /// /// Discontinue all processing for this request and return to the client. /// The caller is responsible for generating the full response. diff --git a/src/Microsoft.AspNetCore.Authentication/LoggingExtensions.cs b/src/Microsoft.AspNetCore.Authentication/LoggingExtensions.cs index 46223d647..042bfabca 100644 --- a/src/Microsoft.AspNetCore.Authentication/LoggingExtensions.cs +++ b/src/Microsoft.AspNetCore.Authentication/LoggingExtensions.cs @@ -7,17 +7,20 @@ namespace Microsoft.Extensions.Logging { internal static class LoggingExtensions { - private static Action _authSchemeAuthenticated; - private static Action _authSchemeNotAuthenticated; - private static Action _authSchemeNotAuthenticatedWithFailure; - private static Action _authSchemeChallenged; - private static Action _authSchemeForbidden; - private static Action _remoteAuthenticationError; - private static Action _signInHandled; - private static Action _signInSkipped; - private static Action _correlationPropertyNotFound; - private static Action _correlationCookieNotFound; - private static Action _unexpectedCorrelationCookieValue; + private static readonly Action _authSchemeAuthenticated; + private static readonly Action _authSchemeNotAuthenticated; + private static readonly Action _authSchemeNotAuthenticatedWithFailure; + private static readonly Action _authSchemeChallenged; + private static readonly Action _authSchemeForbidden; + private static readonly Action _remoteAuthenticationError; + private static readonly Action _signInHandled; + private static readonly Action _signInSkipped; + private static readonly Action _correlationPropertyNotFound; + private static readonly Action _correlationCookieNotFound; + private static readonly Action _unexpectedCorrelationCookieValue; + private static readonly Action _accessDeniedError; + private static readonly Action _accessDeniedContextHandled; + private static readonly Action _accessDeniedContextSkipped; static LoggingExtensions() { @@ -39,7 +42,7 @@ static LoggingExtensions() formatString: "{AuthenticationScheme} was not authenticated. Failure message: {FailureMessage}"); _authSchemeAuthenticated = LoggerMessage.Define( eventId: 8, - logLevel: LogLevel.Information, + logLevel: LogLevel.Debug, formatString: "AuthenticationScheme: {AuthenticationScheme} was successfully authenticated."); _authSchemeNotAuthenticated = LoggerMessage.Define( eventId: 9, @@ -65,6 +68,18 @@ static LoggingExtensions() eventId: 16, logLevel: LogLevel.Warning, formatString: "The correlation cookie value '{CorrelationCookieName}' did not match the expected value '{CorrelationCookieValue}'."); + _accessDeniedError = LoggerMessage.Define( + eventId: 17, + logLevel: LogLevel.Information, + formatString: "Access was denied by the resource owner or by the remote server."); + _accessDeniedContextHandled = LoggerMessage.Define( + eventId: 18, + logLevel: LogLevel.Debug, + formatString: "The AccessDenied event returned Handled."); + _accessDeniedContextSkipped = LoggerMessage.Define( + eventId: 19, + logLevel: LogLevel.Debug, + formatString: "The AccessDenied event returned Skipped."); } public static void AuthenticationSchemeAuthenticated(this ILogger logger, string authenticationScheme) @@ -121,5 +136,20 @@ public static void UnexpectedCorrelationCookieValue(this ILogger logger, string { _unexpectedCorrelationCookieValue(logger, cookieName, cookieValue, null); } + + public static void AccessDeniedError(this ILogger logger) + { + _accessDeniedError(logger, null); + } + + public static void AccessDeniedContextHandled(this ILogger logger) + { + _accessDeniedContextHandled(logger, null); + } + + public static void AccessDeniedContextSkipped(this ILogger logger) + { + _accessDeniedContextSkipped(logger, null); + } } } diff --git a/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj b/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj index 8a1f97042..9ae0a7bcc 100644 --- a/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj +++ b/src/Microsoft.AspNetCore.Authentication/Microsoft.AspNetCore.Authentication.csproj @@ -1,24 +1,22 @@  - - ASP.NET Core common types used by the various authentication middleware components. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authentication;security - - - - - - - - + + + + + + + + diff --git a/src/Microsoft.AspNetCore.Authentication/PolicySchemeHandler.cs b/src/Microsoft.AspNetCore.Authentication/PolicySchemeHandler.cs new file mode 100644 index 000000000..4dbbb7de2 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication/PolicySchemeHandler.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// PolicySchemes are used to redirect authentication methods to another scheme. + /// + public class PolicySchemeHandler : SignInAuthenticationHandler + { + public PolicySchemeHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { } + + protected override Task HandleChallengeAsync(AuthenticationProperties properties) + => throw new NotImplementedException(); + + protected override Task HandleForbiddenAsync(AuthenticationProperties properties) + => throw new NotImplementedException(); + + protected override Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + => throw new NotImplementedException(); + + protected override Task HandleSignOutAsync(AuthenticationProperties properties) + => throw new NotImplementedException(); + + protected override Task HandleAuthenticateAsync() + => throw new NotImplementedException(); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication/PolicySchemeOptions.cs b/src/Microsoft.AspNetCore.Authentication/PolicySchemeOptions.cs new file mode 100644 index 000000000..1921c77ec --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication/PolicySchemeOptions.cs @@ -0,0 +1,11 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Contains the options used by the . + /// + public class PolicySchemeOptions : AuthenticationSchemeOptions + { } +} diff --git a/src/Microsoft.AspNetCore.Authentication/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authentication/Properties/Resources.Designer.cs index 11e2e4586..b1941a7dc 100644 --- a/src/Microsoft.AspNetCore.Authentication/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authentication/Properties/Resources.Designer.cs @@ -15,65 +15,71 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_DefaultDpapiRequiresAppNameKey { - get { return GetString("Exception_DefaultDpapiRequiresAppNameKey"); } + get => GetString("Exception_DefaultDpapiRequiresAppNameKey"); } /// /// The default data protection provider may only be used when the IApplicationBuilder.Properties contains an appropriate 'host.AppName' key. /// internal static string FormatException_DefaultDpapiRequiresAppNameKey() - { - return GetString("Exception_DefaultDpapiRequiresAppNameKey"); - } + => GetString("Exception_DefaultDpapiRequiresAppNameKey"); /// /// The state passed to UnhookAuthentication may only be the return value from HookAuthentication. /// internal static string Exception_UnhookAuthenticationStateType { - get { return GetString("Exception_UnhookAuthenticationStateType"); } + get => GetString("Exception_UnhookAuthenticationStateType"); } /// /// The state passed to UnhookAuthentication may only be the return value from HookAuthentication. /// internal static string FormatException_UnhookAuthenticationStateType() - { - return GetString("Exception_UnhookAuthenticationStateType"); - } + => GetString("Exception_UnhookAuthenticationStateType"); /// /// The AuthenticationTokenProvider's required synchronous events have not been registered. /// internal static string Exception_AuthenticationTokenDoesNotProvideSyncMethods { - get { return GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods"); } + get => GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods"); } /// /// The AuthenticationTokenProvider's required synchronous events have not been registered. /// internal static string FormatException_AuthenticationTokenDoesNotProvideSyncMethods() - { - return GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods"); - } + => GetString("Exception_AuthenticationTokenDoesNotProvideSyncMethods"); /// /// The '{0}' option must be provided. /// internal static string Exception_OptionMustBeProvided { - get { return GetString("Exception_OptionMustBeProvided"); } + get => GetString("Exception_OptionMustBeProvided"); } /// /// The '{0}' option must be provided. /// internal static string FormatException_OptionMustBeProvided(object p0) + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); + + /// + /// The SignInScheme for a remote authentication handler cannot be set to itself. If it was not explicitly set, the AuthenticationOptions.DefaultSignInScheme or DefaultScheme is used. + /// + internal static string Exception_RemoteSignInSchemeCannotBeSelf { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_OptionMustBeProvided"), p0); + get => GetString("Exception_RemoteSignInSchemeCannotBeSelf"); } + /// + /// The SignInScheme for a remote authentication handler cannot be set to itself. If it was not explicitly set, the AuthenticationOptions.DefaultSignInScheme or DefaultScheme is used. + /// + internal static string FormatException_RemoteSignInSchemeCannotBeSelf() + => GetString("Exception_RemoteSignInSchemeCannotBeSelf"); + private static string GetString(string name, params string[] formatterNames) { var value = _resourceManager.GetString(name); diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs index bcd598364..c72583b5c 100644 --- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs +++ b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationHandler.cs @@ -5,6 +5,7 @@ using System.Security.Cryptography; using System.Text.Encodings.Web; using System.Threading.Tasks; +using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.Logging; using Microsoft.Extensions.Options; @@ -49,6 +50,7 @@ public virtual async Task HandleRequestAsync() AuthenticationTicket ticket = null; Exception exception = null; + AuthenticationProperties properties = null; try { var authResult = await HandleRemoteAuthenticateAsync(); @@ -66,11 +68,11 @@ public virtual async Task HandleRequestAsync() } else if (!authResult.Succeeded) { - exception = authResult.Failure ?? - new InvalidOperationException("Invalid return state, unable to redirect."); + exception = authResult.Failure ?? new InvalidOperationException("Invalid return state, unable to redirect."); + properties = authResult.Properties; } - ticket = authResult.Ticket; + ticket = authResult?.Ticket; } catch (Exception ex) { @@ -80,7 +82,10 @@ public virtual async Task HandleRequestAsync() if (exception != null) { Logger.RemoteAuthenticationError(exception.Message); - var errorContext = new RemoteFailureContext(Context, Scheme, Options, exception); + var errorContext = new RemoteFailureContext(Context, Scheme, Options, exception) + { + Properties = properties + }; await Events.RemoteFailure(errorContext); if (errorContext.Result != null) @@ -93,9 +98,16 @@ public virtual async Task HandleRequestAsync() { return false; } + else if (errorContext.Result.Failure != null) + { + throw new Exception("An error was returned from the RemoteFailure event.", errorContext.Result.Failure); + } } - throw exception; + if (errorContext.Failure != null) + { + throw new Exception("An error was encountered while handling the remote login.", errorContext.Failure); + } } // We have a ticket if we get here @@ -103,7 +115,7 @@ public virtual async Task HandleRequestAsync() { ReturnUri = ticket.Properties.RedirectUri }; - // REVIEW: is this safe or good? + ticket.Properties.RedirectUri = null; // Mark which provider produced this identity so we can cross-check later in HandleAuthenticateAsync @@ -230,5 +242,48 @@ protected virtual bool ValidateCorrelationId(AuthenticationProperties properties return true; } + + protected virtual async Task HandleAccessDeniedErrorAsync(AuthenticationProperties properties) + { + Logger.AccessDeniedError(); + var context = new AccessDeniedContext(Context, Scheme, Options) + { + AccessDeniedPath = Options.AccessDeniedPath, + Properties = properties, + ReturnUrl = properties?.RedirectUri, + ReturnUrlParameter = Options.ReturnUrlParameter + }; + await Events.AccessDenied(context); + + if (context.Result != null) + { + if (context.Result.Handled) + { + Logger.AccessDeniedContextHandled(); + } + else if (context.Result.Skipped) + { + Logger.AccessDeniedContextSkipped(); + } + + return context.Result; + } + + // If an access denied endpoint was specified, redirect the user agent. + // Otherwise, invoke the RemoteFailure event for further processing. + if (context.AccessDeniedPath.HasValue) + { + string uri = context.AccessDeniedPath; + if (!string.IsNullOrEmpty(context.ReturnUrlParameter) && !string.IsNullOrEmpty(context.ReturnUrl)) + { + uri = QueryHelpers.AddQueryString(uri, context.ReturnUrlParameter, context.ReturnUrl); + } + Response.Redirect(uri); + + return HandleRequestResult.Handle(); + } + + return HandleRequestResult.Fail("Access was denied by the resource owner or by the remote server.", properties); + } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationOptions.cs b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationOptions.cs index 3b34cf43e..188b7a991 100644 --- a/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationOptions.cs +++ b/src/Microsoft.AspNetCore.Authentication/RemoteAuthenticationOptions.cs @@ -29,9 +29,23 @@ public RemoteAuthenticationOptions() HttpOnly = true, SameSite = SameSiteMode.None, SecurePolicy = CookieSecurePolicy.SameAsRequest, + IsEssential = true, }; } + /// + /// Checks that the options are valid for a specific scheme + /// + /// The scheme being validated. + public override void Validate(string scheme) + { + base.Validate(scheme); + if (string.Equals(scheme, SignInScheme, StringComparison.Ordinal)) + { + throw new InvalidOperationException(Resources.Exception_RemoteSignInSchemeCannotBeSelf); + } + } + /// /// Check that the options are valid. Should throw an exception if things are not ok. /// @@ -75,6 +89,22 @@ public override void Validate() /// public PathString CallbackPath { get; set; } + /// + /// Gets or sets the optional path the user agent is redirected to if the user + /// doesn't approve the authorization demand requested by the remote server. + /// This property is not set by default. In this case, an exception is thrown + /// if an access_denied response is returned by the remote authorization server. + /// + public PathString AccessDeniedPath { get; set; } + + /// + /// Gets or sets the name of the parameter used to convey the original location + /// of the user before the remote challenge was triggered up to the access denied page. + /// This property is only used when the is explicitly specified. + /// + // Note: this deliberately matches the default parameter name used by the cookie handler. + public string ReturnUrlParameter { get; set; } = "ReturnUrl"; + /// /// Gets or sets the authentication scheme corresponding to the middleware /// responsible of persisting user's identity after a successful authentication. diff --git a/src/Microsoft.AspNetCore.Authentication/Resources.resx b/src/Microsoft.AspNetCore.Authentication/Resources.resx index 54d22bcc9..9e831dc74 100644 --- a/src/Microsoft.AspNetCore.Authentication/Resources.resx +++ b/src/Microsoft.AspNetCore.Authentication/Resources.resx @@ -129,4 +129,7 @@ The '{0}' option must be provided. + + The SignInScheme for a remote authentication handler cannot be set to itself. If it was not explicitly set, the AuthenticationOptions.DefaultSignInScheme or DefaultScheme is used. + \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication/SignInAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/SignInAuthenticationHandler.cs new file mode 100644 index 000000000..dbd612dc1 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication/SignInAuthenticationHandler.cs @@ -0,0 +1,39 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Adds support for SignInAsync + /// + public abstract class SignInAuthenticationHandler : SignOutAuthenticationHandler, IAuthenticationSignInHandler + where TOptions : AuthenticationSchemeOptions, new() + { + public SignInAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { } + + public virtual Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + { + var target = ResolveTarget(Options.ForwardSignIn); + return (target != null) + ? Context.SignInAsync(target, user, properties) + : HandleSignInAsync(user, properties ?? new AuthenticationProperties()); + } + + /// + /// Override this method to handle SignIn. + /// + /// + /// + /// A Task. + protected abstract Task HandleSignInAsync(ClaimsPrincipal user, AuthenticationProperties properties); + + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication/SignOutAuthenticationHandler.cs b/src/Microsoft.AspNetCore.Authentication/SignOutAuthenticationHandler.cs new file mode 100644 index 000000000..015cb39e0 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authentication/SignOutAuthenticationHandler.cs @@ -0,0 +1,36 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication +{ + /// + /// Adds support for SignOutAsync + /// + public abstract class SignOutAuthenticationHandler : AuthenticationHandler, IAuthenticationSignOutHandler + where TOptions : AuthenticationSchemeOptions, new() + { + public SignOutAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { } + + public virtual Task SignOutAsync(AuthenticationProperties properties) + { + var target = ResolveTarget(Options.ForwardSignOut); + return (target != null) + ? Context.SignOutAsync(target, properties) + : HandleSignOutAsync(properties ?? new AuthenticationProperties()); + } + + /// + /// Override this method to handle SignOut. + /// + /// + /// A Task. + protected abstract Task HandleSignOutAsync(AuthenticationProperties properties); + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication/baseline.netcore.json b/src/Microsoft.AspNetCore.Authentication/baseline.netcore.json index 1f69da5a8..08eeb5e7b 100644 --- a/src/Microsoft.AspNetCore.Authentication/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication/baseline.netcore.json @@ -1,488 +1,565 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authentication, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authentication, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { - "Name": "Microsoft.AspNetCore.Builder.AuthenticationOptions", + "Name": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Visibility": "Public", "Kind": "Class", - "Abstract": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_AuthenticationScheme", + "Name": "get_Services", "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_AuthenticationScheme", + "Name": "AddScheme", "Parameters": [ { - "Name": "value", + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_AutomaticAuthenticate", - "Parameters": [], - "ReturnType": "System.Boolean", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Virtual": true, "Visibility": "Public", - "GenericParameter": [] + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationHandler" + ] + } + ] }, { "Kind": "Method", - "Name": "set_AutomaticAuthenticate", + "Name": "AddScheme", "Parameters": [ { - "Name": "value", - "Type": "System.Boolean" + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_AutomaticChallenge", - "Parameters": [], - "ReturnType": "System.Boolean", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Virtual": true, "Visibility": "Public", - "GenericParameter": [] + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationHandler" + ] + } + ] }, { "Kind": "Method", - "Name": "set_AutomaticChallenge", + "Name": "AddRemoteScheme", "Parameters": [ { - "Name": "value", - "Type": "System.Boolean" + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_ClaimsIssuer", - "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Virtual": true, "Visibility": "Public", - "GenericParameter": [] + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler" + ] + } + ] }, { "Kind": "Method", - "Name": "set_ClaimsIssuer", + "Name": "AddPolicyScheme", "Parameters": [ { - "Name": "value", + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" } ], - "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Description", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationDescription", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "set_Description", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationDescription" + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" } ], - "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", - "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Builder.ClaimsTransformationAppBuilderExtensions", + "Name": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", "Visibility": "Public", "Kind": "Class", "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IAuthenticationHandler" + ], "Members": [ { "Kind": "Method", - "Name": "UseClaimsTransformation", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, + "Name": "get_Scheme", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationScheme", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "UseClaimsTransformation", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - }, - { - "Name": "transform", - "Type": "System.Func>" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, + "Name": "get_Options", + "Parameters": [], + "ReturnType": "T0", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "UseClaimsTransformation", - "Parameters": [ - { - "Name": "app", - "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" - }, - { - "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.ClaimsTransformationOptions" - } - ], - "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", - "Static": true, - "Extension": true, - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Builder.ClaimsTransformationOptions", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Transformer", + "Name": "get_Context", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.IClaimsTransformer", - "Visibility": "Public", + "ReturnType": "Microsoft.AspNetCore.Http.HttpContext", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Transformer", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.IClaimsTransformer" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", + "Name": "get_Request", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.HttpRequest", + "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "get_Response", "Parameters": [], - "Visibility": "Public", + "ReturnType": "Microsoft.AspNetCore.Http.HttpResponse", + "Visibility": "Protected", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Builder.AuthenticationOptions", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "get_BackchannelTimeout", + "Name": "get_OriginalPath", "Parameters": [], - "ReturnType": "System.TimeSpan", - "Visibility": "Public", + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_BackchannelTimeout", - "Parameters": [ - { - "Name": "value", - "Type": "System.TimeSpan" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", + "Name": "get_OriginalPathBase", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.PathString", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_BackchannelHttpHandler", + "Name": "get_Logger", "Parameters": [], - "ReturnType": "System.Net.Http.HttpMessageHandler", - "Visibility": "Public", + "ReturnType": "Microsoft.Extensions.Logging.ILogger", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_BackchannelHttpHandler", - "Parameters": [ - { - "Name": "value", - "Type": "System.Net.Http.HttpMessageHandler" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", + "Name": "get_UrlEncoder", + "Parameters": [], + "ReturnType": "System.Text.Encodings.Web.UrlEncoder", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_CallbackPath", + "Name": "get_Clock", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Visibility": "Public", + "ReturnType": "Microsoft.AspNetCore.Authentication.ISystemClock", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_CallbackPath", - "Parameters": [ - { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.PathString" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", + "Name": "get_OptionsMonitor", + "Parameters": [], + "ReturnType": "Microsoft.Extensions.Options.IOptionsMonitor", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_SignInScheme", + "Name": "get_Events", "Parameters": [], - "ReturnType": "System.String", - "Visibility": "Public", + "ReturnType": "System.Object", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SignInScheme", + "Name": "set_Events", "Parameters": [ { "Name": "value", - "Type": "System.String" + "Type": "System.Object" } ], "ReturnType": "System.Void", - "Visibility": "Public", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_DisplayName", + "Name": "get_ClaimsIssuer", "Parameters": [], "ReturnType": "System.String", - "Visibility": "Public", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_CurrentUri", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_DisplayName", + "Name": "InitializeAsync", "Parameters": [ { - "Name": "value", - "Type": "System.String" + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" } ], - "ReturnType": "System.Void", + "ReturnType": "System.Threading.Tasks.Task", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationHandler", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_RemoteAuthenticationTimeout", + "Name": "InitializeEventsAsync", "Parameters": [], - "ReturnType": "System.TimeSpan", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_RemoteAuthenticationTimeout", - "Parameters": [ - { - "Name": "value", - "Type": "System.TimeSpan" - } - ], - "ReturnType": "System.Void", - "Visibility": "Public", + "Name": "CreateEventsAsync", + "Parameters": [], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_SaveTokens", + "Name": "InitializeHandlerAsync", "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SaveTokens", + "Name": "BuildRedirectUri", "Parameters": [ { - "Name": "value", - "Type": "System.Boolean" + "Name": "targetPath", + "Type": "System.String" } ], - "ReturnType": "System.Void", - "Visibility": "Public", + "ReturnType": "System.String", + "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", + "Kind": "Method", + "Name": "ResolveTarget", + "Parameters": [ + { + "Name": "scheme", + "Type": "System.String" + } + ], + "ReturnType": "System.String", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Field", - "Name": "Events", + "Kind": "Method", + "Name": "AuthenticateAsync", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", + "ReturnType": "System.Threading.Tasks.Task", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationHandler", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.AuthenticateResult", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "get_Succeeded", + "Name": "HandleAuthenticateOnceAsync", "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Ticket", + "Name": "HandleAuthenticateOnceSafeAsync", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Failure", + "Name": "HandleAuthenticateAsync", "Parameters": [], - "ReturnType": "System.Exception", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Skipped", - "Parameters": [], - "ReturnType": "System.Boolean", + "Name": "HandleForbiddenAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleChallengeAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ChallengeAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationHandler", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Success", + "Name": "ForbidAsync", "Parameters": [ { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticateResult", - "Static": true, + "ReturnType": "System.Threading.Tasks.Task", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationHandler", "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + } + ], + "Visibility": "Protected", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "Skip", + "Name": "get_Schemes", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticateResult", - "Static": true, + "ReturnType": "Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Fail", + "Name": "set_Schemes", "Parameters": [ { - "Name": "failure", - "Type": "System.Exception" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider" } ], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticateResult", - "Static": true, + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Fail", + "Name": "Invoke", "Parameters": [ { - "Name": "failureMessage", - "Type": "System.String" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "next", + "Type": "Microsoft.AspNetCore.Http.RequestDelegate" + }, + { + "Name": "schemes", + "Type": "Microsoft.AspNetCore.Authentication.IAuthenticationSchemeProvider" } ], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticateResult", - "Static": true, "Visibility": "Public", "GenericParameter": [] } @@ -490,148 +567,154 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "Name": "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", "Visibility": "Public", "Kind": "Class", - "Abstract": true, - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler" - ], + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_SignInAccepted", + "Name": "Validate", "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Protected", + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SignInAccepted", + "Name": "Validate", "Parameters": [ { - "Name": "value", - "Type": "System.Boolean" + "Name": "scheme", + "Type": "System.String" } ], "ReturnType": "System.Void", - "Visibility": "Protected", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_SignOutAccepted", + "Name": "get_ClaimsIssuer", "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Protected", + "ReturnType": "System.String", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_SignOutAccepted", + "Name": "set_ClaimsIssuer", "Parameters": [ { "Name": "value", - "Type": "System.Boolean" + "Type": "System.String" } ], "ReturnType": "System.Void", - "Visibility": "Protected", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_ChallengeCalled", + "Name": "get_Events", "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Protected", + "ReturnType": "System.Object", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ChallengeCalled", + "Name": "set_Events", "Parameters": [ { "Name": "value", - "Type": "System.Boolean" + "Type": "System.Object" } ], "ReturnType": "System.Void", - "Visibility": "Protected", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Context", + "Name": "get_EventsType", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.HttpContext", - "Visibility": "Protected", + "ReturnType": "System.Type", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Request", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.HttpRequest", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Response", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.HttpResponse", - "Visibility": "Protected", + "Name": "set_EventsType", + "Parameters": [ + { + "Name": "value", + "Type": "System.Type" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_OriginalPathBase", + "Name": "get_ForwardDefault", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Visibility": "Protected", + "ReturnType": "System.String", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_OriginalPath", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.PathString", - "Visibility": "Protected", + "Name": "set_ForwardDefault", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Logger", + "Name": "get_ForwardAuthenticate", "Parameters": [], - "ReturnType": "Microsoft.Extensions.Logging.ILogger", - "Visibility": "Protected", + "ReturnType": "System.String", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_UrlEncoder", - "Parameters": [], - "ReturnType": "System.Text.Encodings.Web.UrlEncoder", - "Visibility": "Protected", + "Name": "set_ForwardAuthenticate", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_PriorHandler", + "Name": "get_ForwardChallenge", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_PriorHandler", + "Name": "set_ForwardChallenge", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler" + "Type": "System.String" } ], "ReturnType": "System.Void", @@ -640,460 +723,480 @@ }, { "Kind": "Method", - "Name": "get_CurrentUri", + "Name": "get_ForwardForbid", "Parameters": [], "ReturnType": "System.String", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Options", - "Parameters": [], - "ReturnType": "T0", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "InitializeAsync", - "Parameters": [ - { - "Name": "options", - "Type": "T0" - }, - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "logger", - "Type": "Microsoft.Extensions.Logging.ILogger" - }, - { - "Name": "encoder", - "Type": "System.Text.Encodings.Web.UrlEncoder" - } - ], - "ReturnType": "System.Threading.Tasks.Task", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "BuildRedirectUri", + "Name": "set_ForwardForbid", "Parameters": [ { - "Name": "targetPath", + "Name": "value", "Type": "System.String" } ], - "ReturnType": "System.String", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "FinishResponseAsync", - "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleRequestAsync", + "Name": "get_ForwardSignIn", "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetDescriptions", + "Name": "set_ForwardSignIn", "Parameters": [ { - "Name": "describeContext", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.DescribeSchemesContext" + "Name": "value", + "Type": "System.String" } ], "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "ShouldHandleScheme", - "Parameters": [ - { - "Name": "authenticationScheme", - "Type": "System.String" - }, - { - "Name": "handleAutomatic", - "Type": "System.Boolean" - } - ], - "ReturnType": "System.Boolean", + "Name": "get_ForwardSignOut", + "Parameters": [], + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "AuthenticateAsync", + "Name": "set_ForwardSignOut", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.AuthenticateContext" + "Name": "value", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleAuthenticateOnceAsync", - "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "HandleAuthenticateAsync", + "Name": "get_ForwardDefaultSelector", "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Abstract": true, - "Visibility": "Protected", + "ReturnType": "System.Func", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SignInAsync", + "Name": "set_ForwardDefaultSelector", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignInContext" + "Name": "value", + "Type": "System.Func" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.IDataSerializer", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "HandleSignInAsync", + "Name": "Serialize", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignInContext" + "Name": "model", + "Type": "T0" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "System.Byte[]", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SignOutAsync", + "Name": "Deserialize", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignOutContext" + "Name": "data", + "Type": "System.Byte[]" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", - "Visibility": "Public", + "ReturnType": "T0", "GenericParameter": [] - }, + } + ], + "GenericParameters": [ + { + "ParameterName": "TModel", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "HandleSignOutAsync", + "Name": "Protect", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignOutContext" + "Name": "data", + "Type": "T0" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleForbiddenAsync", + "Name": "Protect", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext" + "Name": "data", + "Type": "T0" + }, + { + "Name": "purpose", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleUnauthorizedAsync", + "Name": "Unprotect", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext" + "Name": "protectedText", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "T0", "GenericParameter": [] }, { "Kind": "Method", - "Name": "ChallengeAsync", + "Name": "Unprotect", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext" + "Name": "protectedText", + "Type": "System.String" + }, + { + "Name": "purpose", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Protected", + "ReturnType": "T0", "GenericParameter": [] } ], "GenericParameters": [ { - "ParameterName": "TOptions", + "ParameterName": "TData", "ParameterPosition": 0, - "BaseTypeOrInterfaces": [ - "Microsoft.AspNetCore.Builder.AuthenticationOptions" - ] + "BaseTypeOrInterfaces": [] } ] }, { - "Name": "Microsoft.AspNetCore.Authentication.AuthenticationMiddleware", + "Name": "Microsoft.AspNetCore.Authentication.PropertiesDataFormat", "Visibility": "Public", "Kind": "Class", - "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Authentication.SecureDataFormat", "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "protector", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtector" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.PropertiesSerializer", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IDataSerializer" + ], "Members": [ { "Kind": "Method", - "Name": "get_AuthenticationScheme", + "Name": "get_Default", "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.PropertiesSerializer", + "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_AuthenticationScheme", + "Name": "Serialize", "Parameters": [ { - "Name": "value", - "Type": "System.String" + "Name": "model", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", + "ReturnType": "System.Byte[]", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Options", - "Parameters": [], - "ReturnType": "T0", + "Name": "Deserialize", + "Parameters": [ + { + "Name": "data", + "Type": "System.Byte[]" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Options", + "Name": "Write", "Parameters": [ { - "Name": "value", - "Type": "T0" + "Name": "writer", + "Type": "System.IO.BinaryWriter" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "ReturnType": "System.Void", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Logger", - "Parameters": [], - "ReturnType": "Microsoft.Extensions.Logging.ILogger", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "set_Logger", + "Name": "Read", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.Extensions.Logging.ILogger" + "Name": "reader", + "Type": "System.IO.BinaryReader" } ], - "ReturnType": "System.Void", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "get_UrlEncoder", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [], - "ReturnType": "System.Text.Encodings.Web.UrlEncoder", "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.SecureDataFormat", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.ISecureDataFormat" + ], + "Members": [ { "Kind": "Method", - "Name": "set_UrlEncoder", + "Name": "Protect", "Parameters": [ { - "Name": "value", - "Type": "System.Text.Encodings.Web.UrlEncoder" + "Name": "data", + "Type": "T0" } ], - "ReturnType": "System.Void", + "ReturnType": "System.String", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Invoke", + "Name": "Protect", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "data", + "Type": "T0" + }, + { + "Name": "purpose", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.String", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "CreateHandler", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "Name": "Unprotect", + "Parameters": [ + { + "Name": "protectedText", + "Type": "System.String" + } + ], + "ReturnType": "T0", + "Sealed": true, "Virtual": true, - "Abstract": true, - "Visibility": "Protected", + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "Unprotect", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" + "Name": "protectedText", + "Type": "System.String" }, { - "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" - }, + "Name": "purpose", + "Type": "System.String" + } + ], + "ReturnType": "T0", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ { - "Name": "loggerFactory", - "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + "Name": "serializer", + "Type": "Microsoft.AspNetCore.Authentication.IDataSerializer" }, { - "Name": "encoder", - "Type": "System.Text.Encodings.Web.UrlEncoder" + "Name": "protector", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtector" } ], - "Visibility": "Protected", + "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [ { - "ParameterName": "TOptions", + "ParameterName": "TData", "ParameterPosition": 0, - "New": true, - "BaseTypeOrInterfaces": [ - "Microsoft.AspNetCore.Builder.AuthenticationOptions" - ] + "BaseTypeOrInterfaces": [] } ] }, { - "Name": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", + "Name": "Microsoft.AspNetCore.Authentication.Base64UrlTextEncoder", "Visibility": "Public", "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_AuthenticationScheme", - "Parameters": [], + "Name": "Encode", + "Parameters": [ + { + "Name": "data", + "Type": "System.Byte[]" + } + ], "ReturnType": "System.String", + "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Principal", - "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsPrincipal", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Properties", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "Name": "Decode", + "Parameters": [ + { + "Name": "text", + "Type": "System.String" + } + ], + "ReturnType": "System.Byte[]", + "Static": true, "Visibility": "Public", "GenericParameter": [] - }, + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.TicketDataFormat", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.SecureDataFormat", + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { - "Name": "principal", - "Type": "System.Security.Claims.ClaimsPrincipal" - }, - { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - }, - { - "Name": "authenticationScheme", - "Type": "System.String" + "Name": "protector", + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtector" } ], "Visibility": "Public", @@ -1103,245 +1206,206 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.AuthenticationToken", + "Name": "Microsoft.AspNetCore.Authentication.TicketSerializer", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [], + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IDataSerializer" + ], "Members": [ { "Kind": "Method", - "Name": "get_Name", + "Name": "get_Default", "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Authentication.TicketSerializer", + "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Name", + "Name": "Serialize", "Parameters": [ { - "Name": "value", - "Type": "System.String" + "Name": "ticket", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], - "ReturnType": "System.Void", + "ReturnType": "System.Byte[]", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Value", - "Parameters": [], - "ReturnType": "System.String", + "Name": "Deserialize", + "Parameters": [ + { + "Name": "data", + "Type": "System.Byte[]" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Value", + "Name": "Write", "Parameters": [ { - "Name": "value", - "Type": "System.String" + "Name": "writer", + "Type": "System.IO.BinaryWriter" + }, + { + "Name": "ticket", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], "ReturnType": "System.Void", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.ClaimsTransformationContext", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "get_Context", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.HttpContext", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "get_Principal", - "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Principal", + "Name": "WriteIdentity", "Parameters": [ { - "Name": "value", - "Type": "System.Security.Claims.ClaimsPrincipal" + "Name": "writer", + "Type": "System.IO.BinaryWriter" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" } ], "ReturnType": "System.Void", - "Visibility": "Public", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", + "Kind": "Method", + "Name": "WriteClaim", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "writer", + "Type": "System.IO.BinaryWriter" + }, + { + "Name": "claim", + "Type": "System.Security.Claims.Claim" } ], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.ClaimsTransformationHandler", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler" - ], - "Members": [ - { - "Kind": "Method", - "Name": "get_PriorHandler", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", - "Visibility": "Public", + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_PriorHandler", + "Name": "Read", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler" + "Name": "reader", + "Type": "System.IO.BinaryReader" } ], - "ReturnType": "System.Void", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "AuthenticateAsync", + "Name": "ReadIdentity", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.AuthenticateContext" + "Name": "reader", + "Type": "System.IO.BinaryReader" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, + "ReturnType": "System.Security.Claims.ClaimsIdentity", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "ChallengeAsync", + "Name": "ReadClaim", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext" + "Name": "reader", + "Type": "System.IO.BinaryReader" + }, + { + "Name": "identity", + "Type": "System.Security.Claims.ClaimsIdentity" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, + "ReturnType": "System.Security.Claims.Claim", "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.BaseContext", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ { "Kind": "Method", - "Name": "GetDescriptions", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.DescribeSchemesContext" - } - ], - "ReturnType": "System.Void", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", + "Name": "get_Scheme", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationScheme", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SignInAsync", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignInContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", + "Name": "get_Options", + "Parameters": [], + "ReturnType": "T0", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SignOutAsync", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignOutContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", + "Name": "get_HttpContext", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.HttpContext", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "RegisterAuthenticationHandler", - "Parameters": [ - { - "Name": "auth", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature" - } - ], - "ReturnType": "System.Void", + "Name": "get_Request", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.HttpRequest", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "UnregisterAuthenticationHandler", - "Parameters": [ - { - "Name": "auth", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.IHttpAuthenticationFeature" - } - ], - "ReturnType": "System.Void", + "Name": "get_Response", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.HttpResponse", "Visibility": "Public", "GenericParameter": [] }, @@ -1350,57 +1414,73 @@ "Name": ".ctor", "Parameters": [ { - "Name": "transform", - "Type": "Microsoft.AspNetCore.Authentication.IClaimsTransformer" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" }, { - "Name": "httpContext", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "options", + "Type": "T0" } ], - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] } ], - "GenericParameters": [] + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + } + ] }, { - "Name": "Microsoft.AspNetCore.Authentication.ClaimsTransformationMiddleware", + "Name": "Microsoft.AspNetCore.Authentication.HandleRequestContext", "Visibility": "Public", "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Options", + "Name": "get_Result", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.ClaimsTransformationOptions", + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Options", + "Name": "set_Result", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Builder.ClaimsTransformationOptions" + "Type": "Microsoft.AspNetCore.Authentication.HandleRequestResult" } ], "ReturnType": "System.Void", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleResponse", + "Parameters": [], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Invoke", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", + "Name": "SkipHandler", + "Parameters": [], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, @@ -1409,237 +1489,150 @@ "Name": ".ctor", "Parameters": [ { - "Name": "next", - "Type": "Microsoft.AspNetCore.Http.RequestDelegate" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" }, { "Name": "options", - "Type": "Microsoft.Extensions.Options.IOptions" + "Type": "T0" } ], - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] } ], - "GenericParameters": [] + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + } + ] }, { - "Name": "Microsoft.AspNetCore.Authentication.ClaimsTransformer", + "Name": "Microsoft.AspNetCore.Authentication.PrincipalContext", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.IClaimsTransformer" - ], + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Authentication.PropertiesContext", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_OnTransform", + "Name": "get_Principal", "Parameters": [], - "ReturnType": "System.Func>", + "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnTransform", + "Name": "set_Principal", "Parameters": [ { "Name": "value", - "Type": "System.Func>" + "Type": "System.Security.Claims.ClaimsPrincipal" } ], "ReturnType": "System.Void", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "TransformAsync", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.ClaimsTransformationContext" + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "T0" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IClaimsTransformer", - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.IClaimsTransformer", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ - { - "Kind": "Method", - "Name": "TransformAsync", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.ClaimsTransformationContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", + "Visibility": "Protected", "GenericParameter": [] } ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.ISystemClock", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ + "GenericParameters": [ { - "Kind": "Method", - "Name": "get_UtcNow", - "Parameters": [], - "ReturnType": "System.DateTimeOffset", - "GenericParameter": [] + "ParameterName": "TOptions", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] } - ], - "GenericParameters": [] + ] }, { - "Name": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", + "Name": "Microsoft.AspNetCore.Authentication.PropertiesContext", "Visibility": "Public", "Kind": "Class", "Abstract": true, - "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "HandleRequestAsync", + "Name": "get_Properties", "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", "Virtual": true, - "Override": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleRemoteCallbackAsync", - "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "HandleRemoteAuthenticateAsync", - "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Abstract": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "HandleAuthenticateAsync", - "Parameters": [], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Override": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "HandleSignOutAsync", + "Name": "set_Properties", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignOutContext" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", "Virtual": true, - "Override": true, "Visibility": "Protected", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "HandleSignInAsync", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.SignInContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Override": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "HandleForbiddenAsync", - "Parameters": [ + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.Features.Authentication.ChallengeContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "Override": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "GenerateCorrelationId", - "Parameters": [ + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - } - ], - "ReturnType": "System.Void", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "ValidateCorrelationId", - "Parameters": [ + "Name": "options", + "Type": "T0" + }, { "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Boolean", - "Virtual": true, - "Visibility": "Protected", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], "Visibility": "Protected", "GenericParameter": [] } @@ -1649,20 +1642,21 @@ "ParameterName": "TOptions", "ParameterPosition": 0, "BaseTypeOrInterfaces": [ - "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions" + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" ] } ] }, { - "Name": "Microsoft.AspNetCore.Authentication.SharedAuthenticationOptions", + "Name": "Microsoft.AspNetCore.Authentication.RedirectContext", "Visibility": "Public", "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.PropertiesContext", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_SignInScheme", + "Name": "get_RedirectUri", "Parameters": [], "ReturnType": "System.String", "Visibility": "Public", @@ -1670,7 +1664,7 @@ }, { "Kind": "Method", - "Name": "set_SignInScheme", + "Name": "set_RedirectUri", "Parameters": [ { "Name": "value", @@ -1684,277 +1678,243 @@ { "Kind": "Constructor", "Name": ".ctor", - "Parameters": [], + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "T0" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + }, + { + "Name": "redirectUri", + "Type": "System.String" + } + ], "Visibility": "Public", "GenericParameter": [] } ], - "GenericParameters": [] + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + } + ] }, { - "Name": "Microsoft.AspNetCore.Authentication.SystemClock", + "Name": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.ISystemClock" - ], + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Authentication.HandleRequestContext", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_UtcNow", + "Name": "get_Principal", "Parameters": [], - "ReturnType": "System.DateTimeOffset", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISystemClock", + "ReturnType": "System.Security.Claims.ClaimsPrincipal", "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.AuthenticationTokenExtensions", - "Visibility": "Public", - "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, - "ImplementedInterfaces": [], - "Members": [ { "Kind": "Method", - "Name": "StoreTokens", + "Name": "set_Principal", "Parameters": [ { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - }, - { - "Name": "tokens", - "Type": "System.Collections.Generic.IEnumerable" + "Name": "value", + "Type": "System.Security.Claims.ClaimsPrincipal" } ], "ReturnType": "System.Void", - "Static": true, - "Extension": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetTokenValue", - "Parameters": [ - { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" - }, - { - "Name": "tokenName", - "Type": "System.String" - } - ], - "ReturnType": "System.String", - "Static": true, - "Extension": true, + "Name": "get_Properties", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetTokens", + "Name": "set_Properties", "Parameters": [ { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Collections.Generic.IEnumerable", - "Static": true, - "Extension": true, + "ReturnType": "System.Void", + "Virtual": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Success", + "Parameters": [], + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetTokenAsync", + "Name": "Fail", "Parameters": [ { - "Name": "manager", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationManager" - }, - { - "Name": "tokenName", - "Type": "System.String" + "Name": "failure", + "Type": "System.Exception" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Static": true, - "Extension": true, + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "GetTokenAsync", + "Name": "Fail", "Parameters": [ { - "Name": "manager", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationManager" - }, - { - "Name": "signInScheme", - "Type": "System.String" - }, - { - "Name": "tokenName", + "Name": "failureMessage", "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Static": true, - "Extension": true, + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.IDataSerializer", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ + }, { - "Kind": "Method", - "Name": "Serialize", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "model", + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", "Type": "T0" - } - ], - "ReturnType": "System.Byte[]", - "GenericParameter": [] - }, - { - "Kind": "Method", - "Name": "Deserialize", - "Parameters": [ + }, { - "Name": "data", - "Type": "System.Byte[]" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "T0", + "Visibility": "Protected", "GenericParameter": [] } ], "GenericParameters": [ { - "ParameterName": "TModel", + "ParameterName": "TOptions", "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] } ] }, { - "Name": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "Name": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, + "Kind": "Class", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "Protect", + "Name": "get_OnRemoteFailure", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnRemoteFailure", "Parameters": [ { - "Name": "data", - "Type": "T0" + "Name": "value", + "Type": "System.Func" } ], - "ReturnType": "System.String", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Protect", + "Name": "get_OnTicketReceived", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_OnTicketReceived", "Parameters": [ { - "Name": "data", - "Type": "T0" - }, - { - "Name": "purpose", - "Type": "System.String" + "Name": "value", + "Type": "System.Func" } ], - "ReturnType": "System.String", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Unprotect", + "Name": "RemoteFailure", "Parameters": [ { - "Name": "protectedText", - "Type": "System.String" + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.RemoteFailureContext" } ], - "ReturnType": "T0", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Unprotect", + "Name": "TicketReceived", "Parameters": [ { - "Name": "protectedText", - "Type": "System.String" - }, - { - "Name": "purpose", - "Type": "System.String" + "Name": "context", + "Type": "Microsoft.AspNetCore.Authentication.TicketReceivedContext" } ], - "ReturnType": "T0", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [ - { - "ParameterName": "TData", - "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] - } - ] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.PropertiesDataFormat", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.SecureDataFormat", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Constructor", "Name": ".ctor", - "Parameters": [ - { - "Name": "protector", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtector" - } - ], + "Parameters": [], "Visibility": "Public", "GenericParameter": [] } @@ -1962,88 +1922,75 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.PropertiesSerializer", + "Name": "Microsoft.AspNetCore.Authentication.RemoteFailureContext", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.IDataSerializer" - ], + "BaseType": "Microsoft.AspNetCore.Authentication.HandleRequestContext", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Default", + "Name": "get_Failure", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.PropertiesSerializer", - "Static": true, + "ReturnType": "System.Exception", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Serialize", + "Name": "set_Failure", "Parameters": [ { - "Name": "model", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Name": "value", + "Type": "System.Exception" } ], - "ReturnType": "System.Byte[]", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Deserialize", - "Parameters": [ - { - "Name": "data", - "Type": "System.Byte[]" - } - ], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", + "Name": "get_Properties", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Write", + "Name": "set_Properties", "Parameters": [ { - "Name": "writer", - "Type": "System.IO.BinaryWriter" - }, - { - "Name": "properties", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "ReturnType": "System.Void", - "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { - "Kind": "Method", - "Name": "Read", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [ { - "Name": "reader", - "Type": "System.IO.BinaryReader" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions" + }, + { + "Name": "failure", + "Type": "System.Exception" } ], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", - "Virtual": true, - "Visibility": "Public", - "GenericParameter": [] - }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], "Visibility": "Public", "GenericParameter": [] } @@ -2051,82 +1998,102 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.SecureDataFormat", + "Name": "Microsoft.AspNetCore.Authentication.ResultContext", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.ISecureDataFormat" - ], + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "Protect", - "Parameters": [ - { - "Name": "data", - "Type": "T0" - } - ], - "ReturnType": "System.String", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "Name": "get_Principal", + "Parameters": [], + "ReturnType": "System.Security.Claims.ClaimsPrincipal", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Protect", + "Name": "set_Principal", "Parameters": [ { - "Name": "data", - "Type": "T0" - }, - { - "Name": "purpose", - "Type": "System.String" + "Name": "value", + "Type": "System.Security.Claims.ClaimsPrincipal" } ], - "ReturnType": "System.String", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Unprotect", + "Name": "get_Properties", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationProperties", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Properties", "Parameters": [ { - "Name": "protectedText", - "Type": "System.String" + "Name": "value", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "T0", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Unprotect", - "Parameters": [ - { - "Name": "protectedText", - "Type": "System.String" - }, + "Name": "get_Result", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticateResult", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Success", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "NoResult", + "Parameters": [], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Fail", + "Parameters": [ { - "Name": "purpose", + "Name": "failure", + "Type": "System.Exception" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Fail", + "Parameters": [ + { + "Name": "failureMessage", "Type": "System.String" } ], - "ReturnType": "T0", - "Sealed": true, - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISecureDataFormat", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, @@ -2135,80 +2102,79 @@ "Name": ".ctor", "Parameters": [ { - "Name": "serializer", - "Type": "Microsoft.AspNetCore.Authentication.IDataSerializer" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" }, { - "Name": "protector", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtector" + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "T0" } ], - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] } ], "GenericParameters": [ { - "ParameterName": "TData", + "ParameterName": "TOptions", "ParameterPosition": 0, - "BaseTypeOrInterfaces": [] + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] } ] }, { - "Name": "Microsoft.AspNetCore.Authentication.Base64UrlTextEncoder", + "Name": "Microsoft.AspNetCore.Authentication.TicketReceivedContext", "Visibility": "Public", "Kind": "Class", - "Abstract": true, - "Static": true, - "Sealed": true, + "BaseType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationContext", "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "Encode", - "Parameters": [ - { - "Name": "data", - "Type": "System.Byte[]" - } - ], + "Name": "get_ReturnUri", + "Parameters": [], "ReturnType": "System.String", - "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Decode", + "Name": "set_ReturnUri", "Parameters": [ { - "Name": "text", + "Name": "value", "Type": "System.String" } ], - "ReturnType": "System.Byte[]", - "Static": true, + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.TicketDataFormat", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.SecureDataFormat", - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Constructor", "Name": ".ctor", "Parameters": [ { - "Name": "protector", - "Type": "Microsoft.AspNetCore.DataProtection.IDataProtector" + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "scheme", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationScheme" + }, + { + "Name": "options", + "Type": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions" + }, + { + "Name": "ticket", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], "Visibility": "Public", @@ -2218,150 +2184,122 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.TicketSerializer", + "Name": "Microsoft.AspNetCore.Authentication.HandleRequestResult", "Visibility": "Public", "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.IDataSerializer" - ], + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticateResult", + "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_Default", + "Name": "get_Handled", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.TicketSerializer", - "Static": true, + "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Serialize", - "Parameters": [ - { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" - } - ], - "ReturnType": "System.Byte[]", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", + "Name": "get_Skipped", + "Parameters": [], + "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Deserialize", + "Name": "Success", "Parameters": [ { - "Name": "data", - "Type": "System.Byte[]" + "Name": "ticket", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" } ], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IDataSerializer", + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", + "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Write", + "Name": "Fail", "Parameters": [ { - "Name": "writer", - "Type": "System.IO.BinaryWriter" - }, - { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + "Name": "failure", + "Type": "System.Exception" } ], - "ReturnType": "System.Void", - "Virtual": true, + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", + "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "WriteIdentity", + "Name": "Fail", "Parameters": [ { - "Name": "writer", - "Type": "System.IO.BinaryWriter" + "Name": "failure", + "Type": "System.Exception" }, { - "Name": "identity", - "Type": "System.Security.Claims.ClaimsIdentity" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "System.Void", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", + "Static": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "WriteClaim", + "Name": "Fail", "Parameters": [ { - "Name": "writer", - "Type": "System.IO.BinaryWriter" - }, - { - "Name": "claim", - "Type": "System.Security.Claims.Claim" + "Name": "failureMessage", + "Type": "System.String" } ], - "ReturnType": "System.Void", - "Virtual": true, - "Visibility": "Protected", + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", + "Static": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "Read", + "Name": "Fail", "Parameters": [ { - "Name": "reader", - "Type": "System.IO.BinaryReader" + "Name": "failureMessage", + "Type": "System.String" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", - "Virtual": true, + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", + "Static": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "ReadIdentity", - "Parameters": [ - { - "Name": "reader", - "Type": "System.IO.BinaryReader" - } - ], - "ReturnType": "System.Security.Claims.ClaimsIdentity", - "Virtual": true, - "Visibility": "Protected", + "Name": "Handle", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", + "Static": true, + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "ReadClaim", - "Parameters": [ - { - "Name": "reader", - "Type": "System.IO.BinaryReader" - }, - { - "Name": "identity", - "Type": "System.Security.Claims.ClaimsIdentity" - } - ], - "ReturnType": "System.Security.Claims.Claim", - "Virtual": true, - "Visibility": "Protected", + "Name": "SkipHandler", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.HandleRequestResult", + "Static": true, + "Visibility": "Public", "GenericParameter": [] }, { @@ -2375,34 +2313,101 @@ "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.BaseContext", + "Name": "Microsoft.AspNetCore.Authentication.ISystemClock", "Visibility": "Public", - "Kind": "Class", + "Kind": "Interface", "Abstract": true, "ImplementedInterfaces": [], "Members": [ { "Kind": "Method", - "Name": "get_HttpContext", + "Name": "get_UtcNow", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.HttpContext", - "Visibility": "Public", + "ReturnType": "System.DateTimeOffset", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.PolicySchemeHandler", + "Visibility": "Public", + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.SignInAuthenticationHandler", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "HandleChallengeAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Request", - "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.HttpRequest", - "Visibility": "Public", + "Name": "HandleForbiddenAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Response", + "Name": "HandleSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleSignOutAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleAuthenticateAsync", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.HttpResponse", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { @@ -2410,109 +2415,174 @@ "Name": ".ctor", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], - "Visibility": "Protected", + "Visibility": "Public", "GenericParameter": [] } ], "GenericParameters": [] }, { - "Name": "Microsoft.AspNetCore.Authentication.BaseControlContext", + "Name": "Microsoft.AspNetCore.Authentication.PolicySchemeOptions", "Visibility": "Public", "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseContext", + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", "ImplementedInterfaces": [], "Members": [ { - "Kind": "Method", - "Name": "get_State", + "Kind": "Constructor", + "Name": ".ctor", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.EventResultState", "Visibility": "Public", "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IAuthenticationRequestHandler" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_SignInScheme", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Events", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", + "Visibility": "Protected", + "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_State", + "Name": "set_Events", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.EventResultState" + "Type": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents" } ], "ReturnType": "System.Void", - "Visibility": "Public", + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_HandledResponse", + "Name": "CreateEventsAsync", "Parameters": [], - "ReturnType": "System.Boolean", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Skipped", + "Name": "ShouldHandleRequestAsync", "Parameters": [], - "ReturnType": "System.Boolean", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "HandleResponse", + "Name": "HandleRequestAsync", "Parameters": [], - "ReturnType": "System.Void", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationRequestHandler", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "SkipToNextMiddleware", + "Name": "HandleRemoteAuthenticateAsync", "Parameters": [], - "ReturnType": "System.Void", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "get_Ticket", + "Name": "HandleAuthenticateAsync", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationTicket", - "Visibility": "Public", + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Ticket", + "Name": "HandleForbiddenAsync", "Parameters": [ { - "Name": "value", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Override": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "GenerateCorrelationId", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "ReturnType": "System.Void", - "Visibility": "Public", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { "Kind": "Method", - "Name": "CheckEventResult", + "Name": "ValidateCorrelationId", "Parameters": [ { - "Name": "result", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticateResult", - "Direction": "Out" + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" } ], "ReturnType": "System.Boolean", - "Visibility": "Public", + "Virtual": true, + "Visibility": "Protected", "GenericParameter": [] }, { @@ -2520,69 +2590,84 @@ "Name": ".ctor", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], "Visibility": "Protected", "GenericParameter": [] } ], - "GenericParameters": [] + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions" + ] + } + ] }, { - "Name": "Microsoft.AspNetCore.Authentication.EventResultState", + "Name": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions", "Visibility": "Public", - "Kind": "Enumeration", - "Sealed": true, + "Kind": "Class", + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions", "ImplementedInterfaces": [], "Members": [ { - "Kind": "Field", - "Name": "Continue", - "Parameters": [], - "GenericParameter": [], - "Literal": "0" + "Kind": "Method", + "Name": "Validate", + "Parameters": [ + { + "Name": "scheme", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] }, { - "Kind": "Field", - "Name": "Skipped", + "Kind": "Method", + "Name": "Validate", "Parameters": [], - "GenericParameter": [], - "Literal": "1" + "ReturnType": "System.Void", + "Virtual": true, + "Override": true, + "Visibility": "Public", + "GenericParameter": [] }, - { - "Kind": "Field", - "Name": "HandledResponse", - "Parameters": [], - "GenericParameter": [], - "Literal": "2" - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.FailureContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseControlContext", - "ImplementedInterfaces": [], - "Members": [ { "Kind": "Method", - "Name": "get_Failure", + "Name": "get_BackchannelTimeout", "Parameters": [], - "ReturnType": "System.Exception", + "ReturnType": "System.TimeSpan", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Failure", + "Name": "set_BackchannelTimeout", "Parameters": [ { "Name": "value", - "Type": "System.Exception" + "Type": "System.TimeSpan" } ], "ReturnType": "System.Void", @@ -2590,81 +2675,62 @@ "GenericParameter": [] }, { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" - }, - { - "Name": "failure", - "Type": "System.Exception" - } - ], + "Kind": "Method", + "Name": "get_BackchannelHttpHandler", + "Parameters": [], + "ReturnType": "System.Net.Http.HttpMessageHandler", "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", - "Visibility": "Public", - "Kind": "Interface", - "Abstract": true, - "ImplementedInterfaces": [], - "Members": [ + }, { "Kind": "Method", - "Name": "RemoteFailure", + "Name": "set_BackchannelHttpHandler", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.FailureContext" + "Name": "value", + "Type": "System.Net.Http.HttpMessageHandler" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "TicketReceived", + "Name": "get_Backchannel", + "Parameters": [], + "ReturnType": "System.Net.Http.HttpClient", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_Backchannel", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.TicketReceivedContext" + "Name": "value", + "Type": "System.Net.Http.HttpClient" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Void", + "Visibility": "Public", "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", - "Visibility": "Public", - "Kind": "Class", - "ImplementedInterfaces": [ - "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents" - ], - "Members": [ + }, { "Kind": "Method", - "Name": "get_OnRemoteFailure", + "Name": "get_DataProtectionProvider", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnRemoteFailure", + "Name": "set_DataProtectionProvider", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "Microsoft.AspNetCore.DataProtection.IDataProtectionProvider" } ], "ReturnType": "System.Void", @@ -2673,19 +2739,19 @@ }, { "Kind": "Method", - "Name": "get_OnTicketReceived", + "Name": "get_CallbackPath", "Parameters": [], - "ReturnType": "System.Func", + "ReturnType": "Microsoft.AspNetCore.Http.PathString", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_OnTicketReceived", + "Name": "set_CallbackPath", "Parameters": [ { "Name": "value", - "Type": "System.Func" + "Type": "Microsoft.AspNetCore.Http.PathString" } ], "ReturnType": "System.Void", @@ -2694,66 +2760,40 @@ }, { "Kind": "Method", - "Name": "RemoteFailure", - "Parameters": [ - { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.FailureContext" - } - ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", + "Name": "get_SignInScheme", + "Parameters": [], + "ReturnType": "System.String", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "TicketReceived", + "Name": "set_SignInScheme", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Authentication.TicketReceivedContext" + "Name": "value", + "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", - "Virtual": true, - "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", + "ReturnType": "System.Void", "Visibility": "Public", "GenericParameter": [] }, - { - "Kind": "Constructor", - "Name": ".ctor", - "Parameters": [], - "Visibility": "Public", - "GenericParameter": [] - } - ], - "GenericParameters": [] - }, - { - "Name": "Microsoft.AspNetCore.Authentication.TicketReceivedContext", - "Visibility": "Public", - "Kind": "Class", - "BaseType": "Microsoft.AspNetCore.Authentication.BaseControlContext", - "ImplementedInterfaces": [], - "Members": [ { "Kind": "Method", - "Name": "get_Principal", + "Name": "get_RemoteAuthenticationTimeout", "Parameters": [], - "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "ReturnType": "System.TimeSpan", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Principal", + "Name": "set_RemoteAuthenticationTimeout", "Parameters": [ { "Name": "value", - "Type": "System.Security.Claims.ClaimsPrincipal" + "Type": "System.TimeSpan" } ], "ReturnType": "System.Void", @@ -2762,19 +2802,19 @@ }, { "Kind": "Method", - "Name": "get_Properties", + "Name": "get_Events", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties", + "ReturnType": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Properties", + "Name": "set_Events", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Http.Authentication.AuthenticationProperties" + "Type": "Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents" } ], "ReturnType": "System.Void", @@ -2783,19 +2823,19 @@ }, { "Kind": "Method", - "Name": "get_Options", + "Name": "get_SaveTokens", "Parameters": [], - "ReturnType": "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", + "ReturnType": "System.Boolean", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_Options", + "Name": "set_SaveTokens", "Parameters": [ { "Name": "value", - "Type": "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions" + "Type": "System.Boolean" } ], "ReturnType": "System.Void", @@ -2804,19 +2844,19 @@ }, { "Kind": "Method", - "Name": "get_ReturnUri", + "Name": "get_CorrelationCookie", "Parameters": [], - "ReturnType": "System.String", + "ReturnType": "Microsoft.AspNetCore.Http.CookieBuilder", "Visibility": "Public", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ReturnUri", + "Name": "set_CorrelationCookie", "Parameters": [ { "Name": "value", - "Type": "System.String" + "Type": "Microsoft.AspNetCore.Http.CookieBuilder" } ], "ReturnType": "System.Void", @@ -2826,24 +2866,227 @@ { "Kind": "Constructor", "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.SignInAuthenticationHandler", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Authentication.SignOutAuthenticationHandler", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IAuthenticationSignInHandler" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SignInAsync", "Parameters": [ { - "Name": "context", - "Type": "Microsoft.AspNetCore.Http.HttpContext" + "Name": "user", + "Type": "System.Security.Claims.ClaimsPrincipal" }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationSignInHandler", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleSignInAsync", + "Parameters": [ + { + "Name": "user", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ { "Name": "options", - "Type": "Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions" + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" }, { - "Name": "ticket", - "Type": "Microsoft.AspNetCore.Authentication.AuthenticationTicket" + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.SignOutAuthenticationHandler", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "BaseType": "Microsoft.AspNetCore.Authentication.AuthenticationHandler", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.IAuthenticationSignOutHandler" + ], + "Members": [ + { + "Kind": "Method", + "Name": "SignOutAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.IAuthenticationSignOutHandler", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "HandleSignOutAsync", + "Parameters": [ + { + "Name": "properties", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticationProperties" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "Abstract": true, + "Visibility": "Protected", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptionsMonitor" + }, + { + "Name": "logger", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + }, + { + "Name": "encoder", + "Type": "System.Text.Encodings.Web.UrlEncoder" + }, + { + "Name": "clock", + "Type": "Microsoft.AspNetCore.Authentication.ISystemClock" } ], "Visibility": "Public", "GenericParameter": [] } ], + "GenericParameters": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + } + ] + }, + { + "Name": "Microsoft.AspNetCore.Authentication.SystemClock", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authentication.ISystemClock" + ], + "Members": [ + { + "Kind": "Method", + "Name": "get_UtcNow", + "Parameters": [], + "ReturnType": "System.DateTimeOffset", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authentication.ISystemClock", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Builder.AuthAppBuilderExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "UseAuthentication", + "Parameters": [ + { + "Name": "app", + "Type": "Microsoft.AspNetCore.Builder.IApplicationBuilder" + } + ], + "ReturnType": "Microsoft.AspNetCore.Builder.IApplicationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], "GenericParameters": [] }, { @@ -2864,7 +3107,26 @@ "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" } ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddAuthentication", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "defaultScheme", + "Type": "System.String" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, "Extension": true, "Visibility": "Public", @@ -2880,18 +3142,189 @@ }, { "Name": "configureOptions", - "Type": "System.Action" + "Type": "System.Action" } ], - "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "ReturnType": "Microsoft.AspNetCore.Authentication.AuthenticationBuilder", "Static": true, "Extension": true, "Visibility": "Public", "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AddScheme", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureScheme", + "Type": "System.Action" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationHandler" + ] + } + ] + }, + { + "Kind": "Method", + "Name": "AddScheme", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationHandler" + ] + } + ] + }, + { + "Kind": "Method", + "Name": "AddScheme", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.AuthenticationHandler" + ] + } + ] + }, + { + "Kind": "Method", + "Name": "AddRemoteScheme", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + }, + { + "Name": "authenticationScheme", + "Type": "System.String" + }, + { + "Name": "displayName", + "Type": "System.String" + }, + { + "Name": "configureOptions", + "Type": "System.Action" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [ + { + "ParameterName": "TOptions", + "ParameterPosition": 0, + "New": true, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions" + ] + }, + { + "ParameterName": "THandler", + "ParameterPosition": 1, + "BaseTypeOrInterfaces": [ + "Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler" + ] + } + ] } ], "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authentication/breakingchanges.netcore.json b/src/Microsoft.AspNetCore.Authentication/breakingchanges.netcore.json index 81dc1133e..d5bc6954e 100644 --- a/src/Microsoft.AspNetCore.Authentication/breakingchanges.netcore.json +++ b/src/Microsoft.AspNetCore.Authentication/breakingchanges.netcore.json @@ -1,116 +1,22 @@ [ - { - "TypeId": "public abstract class Microsoft.AspNetCore.Authentication.AuthenticationHandler : Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler where T0 : Microsoft.AspNetCore.Builder.AuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public abstract class Microsoft.AspNetCore.Authentication.AuthenticationMiddleware where T0 : Microsoft.AspNetCore.Builder.AuthenticationOptions, new()", - "Kind": "Removal" - }, - { - "TypeId": "public abstract class Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler : Microsoft.AspNetCore.Authentication.AuthenticationHandler where T0 : Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public abstract class Microsoft.AspNetCore.Builder.AuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.AuthenticateResult", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.AuthenticationTicket", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.AuthenticationToken", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.ClaimsTransformationContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.ClaimsTransformationHandler : Microsoft.AspNetCore.Http.Features.Authentication.IAuthenticationHandler", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.ClaimsTransformationMiddleware", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.ClaimsTransformer : Microsoft.AspNetCore.Authentication.IClaimsTransformer", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.PropertiesDataFormat : Microsoft.AspNetCore.Authentication.SecureDataFormat", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.PropertiesSerializer : Microsoft.AspNetCore.Authentication.IDataSerializer", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.RemoteAuthenticationEvents : Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.SharedAuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.ClaimsTransformationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Builder.RemoteAuthenticationOptions : Microsoft.AspNetCore.Builder.AuthenticationOptions", - "Kind": "Removal" - }, - { - "TypeId": "public interface Microsoft.AspNetCore.Authentication.IClaimsTransformer", - "Kind": "Removal" - }, - { - "TypeId": "public interface Microsoft.AspNetCore.Authentication.IRemoteAuthenticationEvents", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Authentication.AuthenticationTokenExtensions", - "Kind": "Removal" - }, - { - "TypeId": "public static class Microsoft.AspNetCore.Builder.ClaimsTransformationAppBuilderExtensions", - "Kind": "Removal" - }, { "TypeId": "public static class Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions", - "MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.Action configureOptions)", - "Kind": "Removal" - }, - { - "TypeId": "public abstract class Microsoft.AspNetCore.Authentication.BaseContext", + "MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddRemoteScheme(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.String authenticationScheme, System.String displayName, System.Action configureOptions) where T0 : Microsoft.AspNetCore.Authentication.RemoteAuthenticationOptions, new() where T1 : Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler", "Kind": "Removal" }, { - "TypeId": "public class Microsoft.AspNetCore.Authentication.BaseControlContext : Microsoft.AspNetCore.Authentication.BaseContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.FailureContext : Microsoft.AspNetCore.Authentication.BaseControlContext", - "Kind": "Removal" - }, - { - "TypeId": "public class Microsoft.AspNetCore.Authentication.TicketReceivedContext : Microsoft.AspNetCore.Authentication.BaseControlContext", + "TypeId": "public static class Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions", + "MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddScheme(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.String authenticationScheme, System.Action configureOptions) where T0 : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions, new() where T1 : Microsoft.AspNetCore.Authentication.AuthenticationHandler", "Kind": "Removal" }, { "TypeId": "public static class Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions", - "MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddAuthentication(this Microsoft.Extensions.DependencyInjection.IServiceCollection services)", + "MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddScheme(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.String authenticationScheme, System.String displayName, System.Action configureScheme, System.Action configureOptions) where T0 : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions, new() where T1 : Microsoft.AspNetCore.Authentication.AuthenticationHandler", "Kind": "Removal" }, { - "TypeId": "public enum Microsoft.AspNetCore.Authentication.EventResultState", + "TypeId": "public static class Microsoft.Extensions.DependencyInjection.AuthenticationServiceCollectionExtensions", + "MemberId": "public static Microsoft.Extensions.DependencyInjection.IServiceCollection AddScheme(this Microsoft.Extensions.DependencyInjection.IServiceCollection services, System.String authenticationScheme, System.String displayName, System.Action configureOptions) where T0 : Microsoft.AspNetCore.Authentication.AuthenticationSchemeOptions, new() where T1 : Microsoft.AspNetCore.Authentication.AuthenticationHandler", "Kind": "Removal" } ] \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationAppBuilderExtensions.cs b/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationAppBuilderExtensions.cs new file mode 100644 index 000000000..66f0eb6f1 --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationAppBuilderExtensions.cs @@ -0,0 +1,29 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Authorization; + +namespace Microsoft.AspNetCore.Builder +{ + /// + /// Extension methods to add authorization capabilities to an HTTP application pipeline. + /// + public static class AuthorizationAppBuilderExtensions + { + /// + /// Adds the to the specified , which enables authorization capabilities. + /// + /// The to add the middleware to. + /// A reference to this instance after the operation has completed. + public static IApplicationBuilder UseAuthorization(this IApplicationBuilder app) + { + if (app == null) + { + throw new ArgumentNullException(nameof(app)); + } + + return app.UseMiddleware(); + } + } +} diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationEndpointConventionBuilderExtensions.cs b/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationEndpointConventionBuilderExtensions.cs new file mode 100644 index 000000000..81b05d70a --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationEndpointConventionBuilderExtensions.cs @@ -0,0 +1,50 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Routing; + +namespace Microsoft.AspNetCore.Builder +{ + public static class AuthorizationEndpointConventionBuilderExtensions + { + public static IEndpointConventionBuilder RequireAuthorization(this IEndpointConventionBuilder builder, params IAuthorizeData[] authorizeData) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (authorizeData == null) + { + throw new ArgumentNullException(nameof(authorizeData)); + } + + builder.Apply(endpointBuilder => + { + foreach (var data in authorizeData) + { + endpointBuilder.Metadata.Add(data); + } + }); + return builder; + } + + public static IEndpointConventionBuilder RequireAuthorization(this IEndpointConventionBuilder builder, params string[] policyNames) + { + if (builder == null) + { + throw new ArgumentNullException(nameof(builder)); + } + + if (policyNames == null) + { + throw new ArgumentNullException(nameof(policyNames)); + } + + return builder.RequireAuthorization(policyNames.Select(n => new AuthorizeAttribute(n)).ToArray()); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationMiddleware.cs b/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationMiddleware.cs new file mode 100644 index 000000000..1da5b123f --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/AuthorizationMiddleware.cs @@ -0,0 +1,104 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization.Policy; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Endpoints; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; + +namespace Microsoft.AspNetCore.Authorization +{ + public class AuthorizationMiddleware + { + private readonly RequestDelegate _next; + private readonly IAuthorizationPolicyProvider _policyProvider; + + public AuthorizationMiddleware(RequestDelegate next, IAuthorizationPolicyProvider policyProvider) + { + if (next == null) + { + throw new ArgumentNullException(nameof(next)); + } + + if (policyProvider == null) + { + throw new ArgumentNullException(nameof(policyProvider)); + } + + _next = next; + _policyProvider = policyProvider; + } + + public async Task Invoke(HttpContext context) + { + if (context == null) + { + throw new ArgumentNullException(nameof(context)); + } + + var endpoint = context.GetEndpoint(); + var authorizeData = endpoint?.Metadata.GetOrderedMetadata() ?? Array.Empty(); + var policy = await AuthorizationPolicy.CombineAsync(_policyProvider, authorizeData); + if (policy == null) + { + await _next(context); + return; + } + + // Policy evaluator has transient lifetime so it fetched from request services instead of injecting in constructor + var policyEvaluator = context.RequestServices.GetRequiredService(); + + var authenticateResult = await policyEvaluator.AuthenticateAsync(policy, context); + + // Allow Anonymous skips all authorization + if (endpoint?.Metadata.GetMetadata() != null) + { + await _next(context); + return; + } + + // Note that the resource will be null if there is no matched endpoint + var authorizeResult = await policyEvaluator.AuthorizeAsync(policy, authenticateResult, context, resource: endpoint); + + if (authorizeResult.Challenged) + { + if (policy.AuthenticationSchemes.Any()) + { + foreach (var scheme in policy.AuthenticationSchemes) + { + await context.ChallengeAsync(scheme); + } + } + else + { + await context.ChallengeAsync(); + } + + return; + } + else if (authorizeResult.Forbidden) + { + if (policy.AuthenticationSchemes.Any()) + { + foreach (var scheme in policy.AuthenticationSchemes) + { + await context.ForbidAsync(scheme); + } + } + else + { + await context.ForbidAsync(); + } + + return; + } + + await _next(context); + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj b/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj index 83530a2be..d3ad99a51 100644 --- a/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj +++ b/src/Microsoft.AspNetCore.Authorization.Policy/Microsoft.AspNetCore.Authorization.Policy.csproj @@ -1,11 +1,9 @@  - - ASP.NET Core authorization policy helper classes. - netstandard2.0 - $(NoWarn);CS1591 + netcoreapp3.0 + $(NoWarn);CS1591;NU1605 true aspnetcore;authorization @@ -15,8 +13,10 @@ - - + + + + diff --git a/src/Microsoft.AspNetCore.Authorization.Policy/baseline.netcore.json b/src/Microsoft.AspNetCore.Authorization.Policy/baseline.netcore.json new file mode 100644 index 000000000..e8708538d --- /dev/null +++ b/src/Microsoft.AspNetCore.Authorization.Policy/baseline.netcore.json @@ -0,0 +1,211 @@ +{ + "AssemblyIdentity": "Microsoft.AspNetCore.Authorization.Policy, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "Types": [ + { + "Name": "Microsoft.AspNetCore.Authorization.Policy.IPolicyEvaluator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AuthenticateAsync", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationPolicy" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AuthorizeAsync", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationPolicy" + }, + { + "Name": "authenticationResult", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticateResult" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "resource", + "Type": "System.Object" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Challenged", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Forbidden", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Succeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Challenge", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Forbid", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Success", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authorization.Policy.PolicyAuthorizationResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authorization.Policy.PolicyEvaluator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authorization.Policy.IPolicyEvaluator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "AuthenticateAsync", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationPolicy" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authorization.Policy.IPolicyEvaluator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "AuthorizeAsync", + "Parameters": [ + { + "Name": "policy", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationPolicy" + }, + { + "Name": "authenticationResult", + "Type": "Microsoft.AspNetCore.Authentication.AuthenticateResult" + }, + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Http.HttpContext" + }, + { + "Name": "resource", + "Type": "System.Object" + } + ], + "ReturnType": "System.Threading.Tasks.Task", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authorization.Policy.IPolicyEvaluator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "authorization", + "Type": "Microsoft.AspNetCore.Authorization.IAuthorizationService" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.Extensions.DependencyInjection.PolicyServiceCollectionExtensions", + "Visibility": "Public", + "Kind": "Class", + "Abstract": true, + "Static": true, + "Sealed": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "AddAuthorizationPolicyEvaluator", + "Parameters": [ + { + "Name": "services", + "Type": "Microsoft.Extensions.DependencyInjection.IServiceCollection" + } + ], + "ReturnType": "Microsoft.Extensions.DependencyInjection.IServiceCollection", + "Static": true, + "Extension": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + } + ] +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationOptions.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationOptions.cs index 6899913af..f0c5527d2 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizationOptions.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationOptions.cs @@ -20,13 +20,25 @@ public class AuthorizationOptions public bool InvokeHandlersAfterFailure { get; set; } = true; /// - /// Gets or sets the default authorization policy. + /// Gets or sets the default authorization policy. Defaults to require authenticated users. /// /// - /// The default policy is to require any authenticated user. + /// The default policy used when evaluating with no policy name specified. /// public AuthorizationPolicy DefaultPolicy { get; set; } = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + /// + /// Gets or sets the required authorization policy. Defaults to null. + /// + /// + /// By default the required policy is null. + /// + /// If a required policy has been specified then it is always evaluated, even if there are no + /// instances for a resource. If a resource has + /// then they are evaluated together with the required policy. + /// + public AuthorizationPolicy RequiredPolicy { get; set; } + /// /// Add an authorization policy with the provided name. /// diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs index 36e0ca7c3..b3b54f300 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicy.cs @@ -120,46 +120,74 @@ public static async Task CombineAsync(IAuthorizationPolicyP throw new ArgumentNullException(nameof(authorizeData)); } - var policyBuilder = new AuthorizationPolicyBuilder(); - var any = false; - foreach (var authorizeDatum in authorizeData) + // Avoid allocating enumerator if the data is known to be empty + var skipEnumeratingData = false; + if (authorizeData is IList dataList) { - any = true; - var useDefaultPolicy = true; - if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy)) + skipEnumeratingData = dataList.Count == 0; + } + + AuthorizationPolicyBuilder policyBuilder = null; + if (!skipEnumeratingData) + { + foreach (var authorizeDatum in authorizeData) { - var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy); - if (policy == null) + if (policyBuilder == null) { - throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy)); + policyBuilder = new AuthorizationPolicyBuilder(); } - policyBuilder.Combine(policy); - useDefaultPolicy = false; - } - var rolesSplit = authorizeDatum.Roles?.Split(','); - if (rolesSplit != null && rolesSplit.Any()) - { - var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim()); - policyBuilder.RequireRole(trimmedRolesSplit); - useDefaultPolicy = false; - } - var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(','); - if (authTypesSplit != null && authTypesSplit.Any()) - { - foreach (var authType in authTypesSplit) + + var useDefaultPolicy = true; + if (!string.IsNullOrWhiteSpace(authorizeDatum.Policy)) + { + var policy = await policyProvider.GetPolicyAsync(authorizeDatum.Policy); + if (policy == null) + { + throw new InvalidOperationException(Resources.FormatException_AuthorizationPolicyNotFound(authorizeDatum.Policy)); + } + policyBuilder.Combine(policy); + useDefaultPolicy = false; + } + + var rolesSplit = authorizeDatum.Roles?.Split(','); + if (rolesSplit != null && rolesSplit.Any()) + { + var trimmedRolesSplit = rolesSplit.Where(r => !string.IsNullOrWhiteSpace(r)).Select(r => r.Trim()); + policyBuilder.RequireRole(trimmedRolesSplit); + useDefaultPolicy = false; + } + + var authTypesSplit = authorizeDatum.AuthenticationSchemes?.Split(','); + if (authTypesSplit != null && authTypesSplit.Any()) { - if (!string.IsNullOrWhiteSpace(authType)) + foreach (var authType in authTypesSplit) { - policyBuilder.AuthenticationSchemes.Add(authType.Trim()); + if (!string.IsNullOrWhiteSpace(authType)) + { + policyBuilder.AuthenticationSchemes.Add(authType.Trim()); + } } } + + if (useDefaultPolicy) + { + policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync()); + } } - if (useDefaultPolicy) + } + + var requiredPolicy = await policyProvider.GetRequiredPolicyAsync(); + if (requiredPolicy != null) + { + if (policyBuilder == null) { - policyBuilder.Combine(await policyProvider.GetDefaultPolicyAsync()); + policyBuilder = new AuthorizationPolicyBuilder(); } + + policyBuilder.Combine(requiredPolicy); } - return any ? policyBuilder.Build() : null; + + return policyBuilder?.Build(); } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicyBuilder.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicyBuilder.cs index 37335df8f..2f483e37c 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicyBuilder.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizationPolicyBuilder.cs @@ -96,16 +96,16 @@ public AuthorizationPolicyBuilder Combine(AuthorizationPolicy policy) /// to the current instance. /// /// The claim type required. - /// Values the claim must process one or more of for evaluation to succeed. + /// Values the claim must process one or more of for evaluation to succeed. /// A reference to this instance after the operation has completed. - public AuthorizationPolicyBuilder RequireClaim(string claimType, params string[] requiredValues) + public AuthorizationPolicyBuilder RequireClaim(string claimType, params string[] allowedValues) { if (claimType == null) { throw new ArgumentNullException(nameof(claimType)); } - return RequireClaim(claimType, (IEnumerable)requiredValues); + return RequireClaim(claimType, (IEnumerable)allowedValues); } /// @@ -113,16 +113,16 @@ public AuthorizationPolicyBuilder RequireClaim(string claimType, params string[] /// to the current instance. /// /// The claim type required. - /// Values the claim must process one or more of for evaluation to succeed. + /// Values the claim must process one or more of for evaluation to succeed. /// A reference to this instance after the operation has completed. - public AuthorizationPolicyBuilder RequireClaim(string claimType, IEnumerable requiredValues) + public AuthorizationPolicyBuilder RequireClaim(string claimType, IEnumerable allowedValues) { if (claimType == null) { throw new ArgumentNullException(nameof(claimType)); } - Requirements.Add(new ClaimsAuthorizationRequirement(claimType, requiredValues)); + Requirements.Add(new ClaimsAuthorizationRequirement(claimType, allowedValues)); return this; } @@ -147,7 +147,7 @@ public AuthorizationPolicyBuilder RequireClaim(string claimType) /// Adds a /// to the current instance. /// - /// The roles required. + /// The allowed roles. /// A reference to this instance after the operation has completed. public AuthorizationPolicyBuilder RequireRole(params string[] roles) { @@ -163,7 +163,7 @@ public AuthorizationPolicyBuilder RequireRole(params string[] roles) /// Adds a /// to the current instance. /// - /// The roles required. + /// The allowed roles. /// A reference to this instance after the operation has completed. public AuthorizationPolicyBuilder RequireRole(IEnumerable roles) { @@ -247,4 +247,4 @@ public AuthorizationPolicy Build() return new AuthorizationPolicy(Requirements, AuthenticationSchemes.Distinct()); } } -} \ No newline at end of file +} diff --git a/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs b/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs index 63bfa30d4..b1f0f8bae 100644 --- a/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs +++ b/src/Microsoft.AspNetCore.Authorization/AuthorizeAttribute.cs @@ -39,15 +39,5 @@ public AuthorizeAttribute(string policy) /// Gets or sets a comma delimited list of schemes from which user information is constructed. /// public string AuthenticationSchemes { get; set; } - - /// - /// Gets or sets a comma delimited list of schemes from which user information is constructed. - /// - [Obsolete("Use AuthenticationSchemes instead.", error: false)] - public string ActiveAuthenticationSchemes - { - get => AuthenticationSchemes; - set => AuthenticationSchemes = value; - } } } diff --git a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationPolicyProvider.cs b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationPolicyProvider.cs index 0e4329dcc..f5e665273 100644 --- a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationPolicyProvider.cs +++ b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationPolicyProvider.cs @@ -14,6 +14,8 @@ namespace Microsoft.AspNetCore.Authorization public class DefaultAuthorizationPolicyProvider : IAuthorizationPolicyProvider { private readonly AuthorizationOptions _options; + private Task _cachedDefaultPolicy; + private Task _cachedRequiredPolicy; /// /// Creates a new instance of . @@ -35,7 +37,26 @@ public DefaultAuthorizationPolicyProvider(IOptions options /// The default authorization policy. public Task GetDefaultPolicyAsync() { - return Task.FromResult(_options.DefaultPolicy); + return GetCachedPolicy(ref _cachedDefaultPolicy, _options.DefaultPolicy); + } + + /// + /// Gets the required authorization policy. + /// + /// The required authorization policy. + public Task GetRequiredPolicyAsync() + { + return GetCachedPolicy(ref _cachedRequiredPolicy, _options.RequiredPolicy); + } + + private Task GetCachedPolicy(ref Task cachedPolicy, AuthorizationPolicy currentPolicy) + { + var local = cachedPolicy; + if (local == null || local.Result != currentPolicy) + { + cachedPolicy = local = Task.FromResult(currentPolicy); + } + return local; } /// diff --git a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs index 3380f64b1..bc5d571c4 100644 --- a/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs +++ b/src/Microsoft.AspNetCore.Authorization/DefaultAuthorizationService.cs @@ -30,7 +30,7 @@ public class DefaultAuthorizationService : IAuthorizationService /// The handlers used to fulfill s. /// The logger used to log messages, warnings and errors. /// The used to create the context to handle the authorization. - /// The used to determine if authorzation was successful. + /// The used to determine if authorization was successful. /// The used. public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, IAuthorizationHandlerProvider handlers, ILogger logger, IAuthorizationHandlerContextFactory contextFactory, IAuthorizationEvaluator evaluator, IOptions options) { @@ -74,7 +74,7 @@ public DefaultAuthorizationService(IAuthorizationPolicyProvider policyProvider, /// The resource to evaluate the requirements against. /// The requirements to evaluate. /// - /// A flag indicating whether authorization has succeded. + /// A flag indicating whether authorization has succeeded. /// This value is true when the user fulfills the policy otherwise false. /// public async Task AuthorizeAsync(ClaimsPrincipal user, object resource, IEnumerable requirements) @@ -98,37 +98,15 @@ public async Task AuthorizeAsync(ClaimsPrincipal user, obje var result = _evaluator.Evaluate(authContext); if (result.Succeeded) { - _logger.UserAuthorizationSucceeded(GetUserNameForLogging(user)); + _logger.UserAuthorizationSucceeded(); } else { - _logger.UserAuthorizationFailed(GetUserNameForLogging(user)); + _logger.UserAuthorizationFailed(); } return result; } - private string GetUserNameForLogging(ClaimsPrincipal user) - { - var identity = user?.Identity; - if (identity != null) - { - var name = identity.Name; - if (name != null) - { - return name; - } - return GetClaimValue(identity, "sub") - ?? GetClaimValue(identity, ClaimTypes.Name) - ?? GetClaimValue(identity, ClaimTypes.NameIdentifier); - } - return null; - } - - private static string GetClaimValue(IIdentity identity, string claimsType) - { - return (identity as ClaimsIdentity)?.FindFirst(claimsType)?.Value; - } - /// /// Checks if a user meets a specific authorization policy. /// @@ -136,7 +114,7 @@ private static string GetClaimValue(IIdentity identity, string claimsType) /// The resource the policy should be checked with. /// The name of the policy to check against a specific context. /// - /// A flag indicating whether authorization has succeded. + /// A flag indicating whether authorization has succeeded. /// This value is true when the user fulfills the policy otherwise false. /// public async Task AuthorizeAsync(ClaimsPrincipal user, object resource, string policyName) diff --git a/src/Microsoft.AspNetCore.Authorization/IAuthorizationPolicyProvider.cs b/src/Microsoft.AspNetCore.Authorization/IAuthorizationPolicyProvider.cs index 9e9d0f468..4560d11e0 100644 --- a/src/Microsoft.AspNetCore.Authorization/IAuthorizationPolicyProvider.cs +++ b/src/Microsoft.AspNetCore.Authorization/IAuthorizationPolicyProvider.cs @@ -22,5 +22,11 @@ public interface IAuthorizationPolicyProvider /// /// The default authorization policy. Task GetDefaultPolicyAsync(); + + /// + /// Gets the required authorization policy. + /// + /// The required authorization policy. + Task GetRequiredPolicyAsync(); } } diff --git a/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs b/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs index 32af74607..8976425ba 100644 --- a/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs +++ b/src/Microsoft.AspNetCore.Authorization/IAuthorizationService.cs @@ -43,7 +43,7 @@ public interface IAuthorizationService /// /// A flag indicating whether authorization has succeeded. /// Returns a flag indicating whether the user, and optional resource has fulfilled the policy. - /// true when the the policy has been fulfilled; otherwise false. + /// true when the policy has been fulfilled; otherwise false. /// /// /// Resource is an optional parameter and may be null. Please ensure that you check it is not diff --git a/src/Microsoft.AspNetCore.Authorization/Infrastructure/PassThroughAuthorizationHandler.cs b/src/Microsoft.AspNetCore.Authorization/Infrastructure/PassThroughAuthorizationHandler.cs index 60fd66b85..6f0b8293f 100644 --- a/src/Microsoft.AspNetCore.Authorization/Infrastructure/PassThroughAuthorizationHandler.cs +++ b/src/Microsoft.AspNetCore.Authorization/Infrastructure/PassThroughAuthorizationHandler.cs @@ -7,7 +7,7 @@ namespace Microsoft.AspNetCore.Authorization.Infrastructure { /// - /// Infrastructre class which allows an to + /// Infrastructure class which allows an to /// be its own . /// public class PassThroughAuthorizationHandler : IAuthorizationHandler diff --git a/src/Microsoft.AspNetCore.Authorization/LoggingExtensions.cs b/src/Microsoft.AspNetCore.Authorization/LoggingExtensions.cs index 1d524dd74..386df85e0 100644 --- a/src/Microsoft.AspNetCore.Authorization/LoggingExtensions.cs +++ b/src/Microsoft.AspNetCore.Authorization/LoggingExtensions.cs @@ -7,29 +7,25 @@ namespace Microsoft.Extensions.Logging { internal static class LoggingExtensions { - private static Action _userAuthorizationFailed; - private static Action _userAuthorizationSucceeded; + private static Action _userAuthorizationFailed; + private static Action _userAuthorizationSucceeded; static LoggingExtensions() { - _userAuthorizationSucceeded = LoggerMessage.Define( + _userAuthorizationSucceeded = LoggerMessage.Define( eventId: 1, logLevel: LogLevel.Information, - formatString: "Authorization was successful for user: {UserName}."); - _userAuthorizationFailed = LoggerMessage.Define( + formatString: "Authorization was successful."); + _userAuthorizationFailed = LoggerMessage.Define( eventId: 2, logLevel: LogLevel.Information, - formatString: "Authorization failed for user: {UserName}."); + formatString: "Authorization failed."); } - public static void UserAuthorizationSucceeded(this ILogger logger, string userName) - { - _userAuthorizationSucceeded(logger, userName, null); - } + public static void UserAuthorizationSucceeded(this ILogger logger) + => _userAuthorizationSucceeded(logger, null); - public static void UserAuthorizationFailed(this ILogger logger, string userName) - { - _userAuthorizationFailed(logger, userName, null); - } + public static void UserAuthorizationFailed(this ILogger logger) + => _userAuthorizationFailed(logger, null); } } diff --git a/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj b/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj index 15100fbbc..9192ac8cf 100644 --- a/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj +++ b/src/Microsoft.AspNetCore.Authorization/Microsoft.AspNetCore.Authorization.csproj @@ -1,13 +1,11 @@ - - - + ASP.NET Core authorization classes. Commonly used types: Microsoft.AspNetCore.Authorization.AllowAnonymousAttribute Microsoft.AspNetCore.Authorization.AuthorizeAttribute - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore;authorization @@ -15,8 +13,8 @@ Microsoft.AspNetCore.Authorization.AuthorizeAttribute - - + + diff --git a/src/Microsoft.AspNetCore.Authorization/Properties/Resources.Designer.cs b/src/Microsoft.AspNetCore.Authorization/Properties/Resources.Designer.cs index 116001e65..c83fa9ea5 100644 --- a/src/Microsoft.AspNetCore.Authorization/Properties/Resources.Designer.cs +++ b/src/Microsoft.AspNetCore.Authorization/Properties/Resources.Designer.cs @@ -15,48 +15,42 @@ private static readonly ResourceManager _resourceManager /// internal static string Exception_AuthorizationPolicyEmpty { - get { return GetString("Exception_AuthorizationPolicyEmpty"); } + get => GetString("Exception_AuthorizationPolicyEmpty"); } /// /// AuthorizationPolicy must have at least one requirement. /// internal static string FormatException_AuthorizationPolicyEmpty() - { - return GetString("Exception_AuthorizationPolicyEmpty"); - } + => GetString("Exception_AuthorizationPolicyEmpty"); /// /// The AuthorizationPolicy named: '{0}' was not found. /// internal static string Exception_AuthorizationPolicyNotFound { - get { return GetString("Exception_AuthorizationPolicyNotFound"); } + get => GetString("Exception_AuthorizationPolicyNotFound"); } /// /// The AuthorizationPolicy named: '{0}' was not found. /// internal static string FormatException_AuthorizationPolicyNotFound(object p0) - { - return string.Format(CultureInfo.CurrentCulture, GetString("Exception_AuthorizationPolicyNotFound"), p0); - } + => string.Format(CultureInfo.CurrentCulture, GetString("Exception_AuthorizationPolicyNotFound"), p0); /// /// At least one role must be specified. /// internal static string Exception_RoleRequirementEmpty { - get { return GetString("Exception_RoleRequirementEmpty"); } + get => GetString("Exception_RoleRequirementEmpty"); } /// /// At least one role must be specified. /// internal static string FormatException_RoleRequirementEmpty() - { - return GetString("Exception_RoleRequirementEmpty"); - } + => GetString("Exception_RoleRequirementEmpty"); private static string GetString(string name, params string[] formatterNames) { diff --git a/src/Microsoft.AspNetCore.Authorization/baseline.netcore.json b/src/Microsoft.AspNetCore.Authorization/baseline.netcore.json index 8ae585270..9910c93f6 100644 --- a/src/Microsoft.AspNetCore.Authorization/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.Authorization/baseline.netcore.json @@ -1,5 +1,5 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.Authorization, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.Authorization, Version=2.0.3.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Extensions.DependencyInjection.AuthorizationServiceCollectionExtensions", @@ -66,6 +66,54 @@ ], "GenericParameters": [] }, + { + "Name": "Microsoft.AspNetCore.Authorization.AuthorizationFailure", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_FailCalled", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_FailedRequirements", + "Parameters": [], + "ReturnType": "System.Collections.Generic.IEnumerable", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "ExplicitFail", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationFailure", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Failed", + "Parameters": [ + { + "Name": "failed", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationFailure", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, { "Name": "Microsoft.AspNetCore.Authorization.AuthorizationHandler", "Visibility": "Public", @@ -208,6 +256,7 @@ "Name": "get_Requirements", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -216,6 +265,7 @@ "Name": "get_User", "Parameters": [], "ReturnType": "System.Security.Claims.ClaimsPrincipal", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -224,6 +274,7 @@ "Name": "get_Resource", "Parameters": [], "ReturnType": "System.Object", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -232,6 +283,7 @@ "Name": "get_PendingRequirements", "Parameters": [], "ReturnType": "System.Collections.Generic.IEnumerable", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -240,6 +292,7 @@ "Name": "get_HasFailed", "Parameters": [], "ReturnType": "System.Boolean", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -248,6 +301,7 @@ "Name": "get_HasSucceeded", "Parameters": [], "ReturnType": "System.Boolean", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -256,6 +310,7 @@ "Name": "Fail", "Parameters": [], "ReturnType": "System.Void", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -269,6 +324,7 @@ } ], "ReturnType": "System.Void", + "Virtual": true, "Visibility": "Public", "GenericParameter": [] }, @@ -301,6 +357,27 @@ "Kind": "Class", "ImplementedInterfaces": [], "Members": [ + { + "Kind": "Method", + "Name": "get_InvokeHandlersAfterFailure", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_InvokeHandlersAfterFailure", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Method", "Name": "get_DefaultPolicy", @@ -714,6 +791,63 @@ ], "GenericParameters": [] }, + { + "Name": "Microsoft.AspNetCore.Authorization.AuthorizationResult", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "get_Succeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_Failure", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationFailure", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Success", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Failed", + "Parameters": [ + { + "Name": "failure", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationFailure" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "Failed", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationResult", + "Static": true, + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, { "Name": "Microsoft.AspNetCore.Authorization.AuthorizationServiceExtensions", "Visibility": "Public", @@ -744,7 +878,7 @@ "Type": "Microsoft.AspNetCore.Authorization.IAuthorizationRequirement" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Static": true, "Extension": true, "Visibility": "Public", @@ -771,7 +905,7 @@ "Type": "Microsoft.AspNetCore.Authorization.AuthorizationPolicy" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Static": true, "Extension": true, "Visibility": "Public", @@ -794,7 +928,7 @@ "Type": "Microsoft.AspNetCore.Authorization.AuthorizationPolicy" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Static": true, "Extension": true, "Visibility": "Public", @@ -817,7 +951,7 @@ "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Static": true, "Extension": true, "Visibility": "Public", @@ -891,7 +1025,7 @@ }, { "Kind": "Method", - "Name": "get_ActiveAuthenticationSchemes", + "Name": "get_AuthenticationSchemes", "Parameters": [], "ReturnType": "System.String", "Sealed": true, @@ -902,7 +1036,7 @@ }, { "Kind": "Method", - "Name": "set_ActiveAuthenticationSchemes", + "Name": "set_AuthenticationSchemes", "Parameters": [ { "Name": "value", @@ -916,6 +1050,27 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_ActiveAuthenticationSchemes", + "Parameters": [], + "ReturnType": "System.String", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ActiveAuthenticationSchemes", + "Parameters": [ + { + "Name": "value", + "Type": "System.String" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Constructor", "Name": ".ctor", @@ -938,6 +1093,120 @@ ], "GenericParameters": [] }, + { + "Name": "Microsoft.AspNetCore.Authorization.DefaultAuthorizationEvaluator", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator" + ], + "Members": [ + { + "Kind": "Method", + "Name": "Evaluate", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationResult", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerContextFactory", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory" + ], + "Members": [ + { + "Kind": "Method", + "Name": "CreateContext", + "Parameters": [ + { + "Name": "requirements", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "user", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "resource", + "Type": "System.Object" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext", + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authorization.DefaultAuthorizationHandlerProvider", + "Visibility": "Public", + "Kind": "Class", + "ImplementedInterfaces": [ + "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider" + ], + "Members": [ + { + "Kind": "Method", + "Name": "GetHandlersAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "Sealed": true, + "Virtual": true, + "ImplementedInterface": "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "handlers", + "Type": "System.Collections.Generic.IEnumerable" + } + ], + "Visibility": "Public", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, { "Name": "Microsoft.AspNetCore.Authorization.DefaultAuthorizationPolicyProvider", "Visibility": "Public", @@ -1012,7 +1281,7 @@ "Type": "System.Collections.Generic.IEnumerable" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.AspNetCore.Authorization.IAuthorizationService", @@ -1036,7 +1305,7 @@ "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "Sealed": true, "Virtual": true, "ImplementedInterface": "Microsoft.AspNetCore.Authorization.IAuthorizationService", @@ -1053,11 +1322,23 @@ }, { "Name": "handlers", - "Type": "System.Collections.Generic.IEnumerable" + "Type": "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider" }, { "Name": "logger", "Type": "Microsoft.Extensions.Logging.ILogger" + }, + { + "Name": "contextFactory", + "Type": "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory" + }, + { + "Name": "evaluator", + "Type": "Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator" + }, + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" } ], "Visibility": "Public", @@ -1075,6 +1356,28 @@ "Members": [], "GenericParameters": [] }, + { + "Name": "Microsoft.AspNetCore.Authorization.IAuthorizationEvaluator", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "Evaluate", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationResult", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, { "Name": "Microsoft.AspNetCore.Authorization.IAuthorizationHandler", "Visibility": "Public", @@ -1097,6 +1400,58 @@ ], "GenericParameters": [] }, + { + "Name": "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerContextFactory", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "CreateContext", + "Parameters": [ + { + "Name": "requirements", + "Type": "System.Collections.Generic.IEnumerable" + }, + { + "Name": "user", + "Type": "System.Security.Claims.ClaimsPrincipal" + }, + { + "Name": "resource", + "Type": "System.Object" + } + ], + "ReturnType": "Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, + { + "Name": "Microsoft.AspNetCore.Authorization.IAuthorizationHandlerProvider", + "Visibility": "Public", + "Kind": "Interface", + "Abstract": true, + "ImplementedInterfaces": [], + "Members": [ + { + "Kind": "Method", + "Name": "GetHandlersAsync", + "Parameters": [ + { + "Name": "context", + "Type": "Microsoft.AspNetCore.Authorization.AuthorizationHandlerContext" + } + ], + "ReturnType": "System.Threading.Tasks.Task>", + "GenericParameter": [] + } + ], + "GenericParameters": [] + }, { "Name": "Microsoft.AspNetCore.Authorization.IAuthorizationPolicyProvider", "Visibility": "Public", @@ -1159,7 +1514,7 @@ "Type": "System.Collections.Generic.IEnumerable" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "GenericParameter": [] }, { @@ -1179,7 +1534,7 @@ "Type": "System.String" } ], - "ReturnType": "System.Threading.Tasks.Task", + "ReturnType": "System.Threading.Tasks.Task", "GenericParameter": [] } ], @@ -1232,14 +1587,14 @@ }, { "Kind": "Method", - "Name": "get_ActiveAuthenticationSchemes", + "Name": "get_AuthenticationSchemes", "Parameters": [], "ReturnType": "System.String", "GenericParameter": [] }, { "Kind": "Method", - "Name": "set_ActiveAuthenticationSchemes", + "Name": "set_AuthenticationSchemes", "Parameters": [ { "Name": "value", @@ -1588,6 +1943,5 @@ ], "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.CookiePolicy/AppendCookieContext.cs b/src/Microsoft.AspNetCore.CookiePolicy/AppendCookieContext.cs index 1b13251f7..bbb4899c0 100644 --- a/src/Microsoft.AspNetCore.CookiePolicy/AppendCookieContext.cs +++ b/src/Microsoft.AspNetCore.CookiePolicy/AppendCookieContext.cs @@ -19,5 +19,8 @@ public AppendCookieContext(HttpContext context, CookieOptions options, string na public CookieOptions CookieOptions { get; } public string CookieName { get; set; } public string CookieValue { get; set; } + public bool IsConsentNeeded { get; internal set; } + public bool HasConsent { get; internal set; } + public bool IssueCookie { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs index 92adac967..1a810b7d5 100644 --- a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs +++ b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyMiddleware.cs @@ -6,6 +6,8 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.Abstractions; using Microsoft.Extensions.Options; namespace Microsoft.AspNetCore.CookiePolicy @@ -13,13 +15,20 @@ namespace Microsoft.AspNetCore.CookiePolicy public class CookiePolicyMiddleware { private readonly RequestDelegate _next; + private readonly ILogger _logger; - public CookiePolicyMiddleware( - RequestDelegate next, - IOptions options) + public CookiePolicyMiddleware(RequestDelegate next, IOptions options, ILoggerFactory factory) + { + Options = options.Value; + _next = next ?? throw new ArgumentNullException(nameof(next)); + _logger = factory.CreateLogger(); + } + + public CookiePolicyMiddleware(RequestDelegate next, IOptions options) { Options = options.Value; _next = next; + _logger = NullLogger.Instance; } public CookiePolicyOptions Options { get; set; } @@ -27,157 +36,21 @@ public CookiePolicyMiddleware( public Task Invoke(HttpContext context) { var feature = context.Features.Get() ?? new ResponseCookiesFeature(context.Features); - context.Features.Set(new CookiesWrapperFeature(context, Options, feature)); + var wrapper = new ResponseCookiesWrapper(context, Options, feature, _logger); + context.Features.Set(new CookiesWrapperFeature(wrapper)); + context.Features.Set(wrapper); + return _next(context); } private class CookiesWrapperFeature : IResponseCookiesFeature { - public CookiesWrapperFeature(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature) - { - Wrapper = new CookiesWrapper(context, options, feature); - } - - public IResponseCookies Wrapper { get; } - - public IResponseCookies Cookies - { - get - { - return Wrapper; - } - } - } - - private class CookiesWrapper : IResponseCookies - { - public CookiesWrapper(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature) - { - Context = context; - Feature = feature; - Policy = options; - } - - public HttpContext Context { get; } - - public IResponseCookiesFeature Feature { get; } - - public IResponseCookies Cookies + public CookiesWrapperFeature(ResponseCookiesWrapper wrapper) { - get - { - return Feature.Cookies; - } + Cookies = wrapper; } - public CookiePolicyOptions Policy { get; } - - private bool PolicyRequiresCookieOptions() - { - return Policy.MinimumSameSitePolicy != SameSiteMode.None || Policy.HttpOnly != HttpOnlyPolicy.None || Policy.Secure != CookieSecurePolicy.None; - } - - public void Append(string key, string value) - { - if (PolicyRequiresCookieOptions() || Policy.OnAppendCookie != null) - { - Append(key, value, new CookieOptions()); - } - else - { - Cookies.Append(key, value); - } - } - - public void Append(string key, string value, CookieOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - ApplyPolicy(options); - if (Policy.OnAppendCookie != null) - { - var context = new AppendCookieContext(Context, options, key, value); - Policy.OnAppendCookie(context); - key = context.CookieName; - value = context.CookieValue; - } - Cookies.Append(key, value, options); - } - - public void Delete(string key) - { - if (PolicyRequiresCookieOptions() || Policy.OnDeleteCookie != null) - { - Delete(key, new CookieOptions()); - } - else - { - Cookies.Delete(key); - } - } - - public void Delete(string key, CookieOptions options) - { - if (options == null) - { - throw new ArgumentNullException(nameof(options)); - } - - ApplyPolicy(options); - if (Policy.OnDeleteCookie != null) - { - var context = new DeleteCookieContext(Context, options, key); - Policy.OnDeleteCookie(context); - key = context.CookieName; - } - Cookies.Delete(key, options); - } - - private void ApplyPolicy(CookieOptions options) - { - switch (Policy.Secure) - { - case CookieSecurePolicy.Always: - options.Secure = true; - break; - case CookieSecurePolicy.SameAsRequest: - options.Secure = Context.Request.IsHttps; - break; - case CookieSecurePolicy.None: - break; - default: - throw new InvalidOperationException(); - } - switch (Policy.MinimumSameSitePolicy) - { - case SameSiteMode.None: - break; - case SameSiteMode.Lax: - if (options.SameSite == SameSiteMode.None) - { - options.SameSite = SameSiteMode.Lax; - } - break; - case SameSiteMode.Strict: - options.SameSite = SameSiteMode.Strict; - break; - default: - throw new InvalidOperationException($"Unrecognized {nameof(SameSiteMode)} value {Policy.MinimumSameSitePolicy.ToString()}"); - } - switch (Policy.HttpOnly) - { - case HttpOnlyPolicy.Always: - options.HttpOnly = true; - break; - case HttpOnlyPolicy.None: - break; - default: - throw new InvalidOperationException($"Unrecognized {nameof(HttpOnlyPolicy)} value {Policy.HttpOnly.ToString()}"); - } - } + public IResponseCookies Cookies { get; } } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs index 1e474bfe2..32d047297 100644 --- a/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs +++ b/src/Microsoft.AspNetCore.CookiePolicy/CookiePolicyOptions.cs @@ -27,6 +27,18 @@ public class CookiePolicyOptions /// public CookieSecurePolicy Secure { get; set; } = CookieSecurePolicy.None; + public CookieBuilder ConsentCookie { get; set; } = new CookieBuilder() + { + Name = ".AspNet.Consent", + Expiration = TimeSpan.FromDays(365), + IsEssential = true, + }; + + /// + /// Checks if consent policies should be evaluated on this request. The default is false. + /// + public Func CheckConsentNeeded { get; set; } + /// /// Called when a cookie is appended. /// diff --git a/src/Microsoft.AspNetCore.CookiePolicy/DeleteCookieContext.cs b/src/Microsoft.AspNetCore.CookiePolicy/DeleteCookieContext.cs index f0693bf71..fd79ea8d4 100644 --- a/src/Microsoft.AspNetCore.CookiePolicy/DeleteCookieContext.cs +++ b/src/Microsoft.AspNetCore.CookiePolicy/DeleteCookieContext.cs @@ -17,5 +17,8 @@ public DeleteCookieContext(HttpContext context, CookieOptions options, string na public HttpContext Context { get; } public CookieOptions CookieOptions { get; } public string CookieName { get; set; } + public bool IsConsentNeeded { get; internal set; } + public bool HasConsent { get; internal set; } + public bool IssueCookie { get; set; } } } \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.CookiePolicy/LoggingExtensions.cs b/src/Microsoft.AspNetCore.CookiePolicy/LoggingExtensions.cs new file mode 100644 index 000000000..21b04facc --- /dev/null +++ b/src/Microsoft.AspNetCore.CookiePolicy/LoggingExtensions.cs @@ -0,0 +1,105 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; + +namespace Microsoft.Extensions.Logging +{ + internal static class LoggingExtensions + { + private static Action _needsConsent; + private static Action _hasConsent; + private static Action _consentGranted; + private static Action _consentWithdrawn; + private static Action _cookieSuppressed; + private static Action _deleteCookieSuppressed; + private static Action _upgradedToSecure; + private static Action _upgradedSameSite; + private static Action _upgradedToHttpOnly; + + static LoggingExtensions() + { + _needsConsent = LoggerMessage.Define( + eventId: 1, + logLevel: LogLevel.Trace, + formatString: "Needs consent: {needsConsent}."); + _hasConsent = LoggerMessage.Define( + eventId: 2, + logLevel: LogLevel.Trace, + formatString: "Has consent: {hasConsent}."); + _consentGranted = LoggerMessage.Define( + eventId: 3, + logLevel: LogLevel.Debug, + formatString: "Consent granted."); + _consentWithdrawn = LoggerMessage.Define( + eventId: 4, + logLevel: LogLevel.Debug, + formatString: "Consent withdrawn."); + _cookieSuppressed = LoggerMessage.Define( + eventId: 5, + logLevel: LogLevel.Debug, + formatString: "Cookie '{key}' suppressed due to consent policy."); + _deleteCookieSuppressed = LoggerMessage.Define( + eventId: 6, + logLevel: LogLevel.Debug, + formatString: "Delete cookie '{key}' suppressed due to developer policy."); + _upgradedToSecure = LoggerMessage.Define( + eventId: 7, + logLevel: LogLevel.Debug, + formatString: "Cookie '{key}' upgraded to 'secure'."); + _upgradedSameSite = LoggerMessage.Define( + eventId: 8, + logLevel: LogLevel.Debug, + formatString: "Cookie '{key}' same site mode upgraded to '{mode}'."); + _upgradedToHttpOnly = LoggerMessage.Define( + eventId: 9, + logLevel: LogLevel.Debug, + formatString: "Cookie '{key}' upgraded to 'httponly'."); + } + + public static void NeedsConsent(this ILogger logger, bool needsConsent) + { + _needsConsent(logger, needsConsent, null); + } + + public static void HasConsent(this ILogger logger, bool hasConsent) + { + _hasConsent(logger, hasConsent, null); + } + + public static void ConsentGranted(this ILogger logger) + { + _consentGranted(logger, null); + } + + public static void ConsentWithdrawn(this ILogger logger) + { + _consentWithdrawn(logger, null); + } + + public static void CookieSuppressed(this ILogger logger, string key) + { + _cookieSuppressed(logger, key, null); + } + + public static void DeleteCookieSuppressed(this ILogger logger, string key) + { + _deleteCookieSuppressed(logger, key, null); + } + + public static void CookieUpgradedToSecure(this ILogger logger, string key) + { + _upgradedToSecure(logger, key, null); + } + + public static void CookieSameSiteUpgraded(this ILogger logger, string key, string mode) + { + _upgradedSameSite(logger, key, mode, null); + } + + public static void CookieUpgradedToHttpOnly(this ILogger logger, string key) + { + _upgradedToHttpOnly(logger, key, null); + } + } +} diff --git a/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj b/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj index 5dc2cd928..81760d50a 100644 --- a/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj +++ b/src/Microsoft.AspNetCore.CookiePolicy/Microsoft.AspNetCore.CookiePolicy.csproj @@ -1,18 +1,17 @@  - - ASP.NET Core cookie policy classes to control the behavior of cookies. - netstandard2.0 + netcoreapp3.0 $(NoWarn);CS1591 true aspnetcore - - + + + diff --git a/src/Microsoft.AspNetCore.CookiePolicy/ResponseCookiesWrapper.cs b/src/Microsoft.AspNetCore.CookiePolicy/ResponseCookiesWrapper.cs new file mode 100644 index 000000000..126c4d7bd --- /dev/null +++ b/src/Microsoft.AspNetCore.CookiePolicy/ResponseCookiesWrapper.cs @@ -0,0 +1,281 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.Logging; + +namespace Microsoft.AspNetCore.CookiePolicy +{ + internal class ResponseCookiesWrapper : IResponseCookies, ITrackingConsentFeature + { + private const string ConsentValue = "yes"; + private readonly ILogger _logger; + private bool? _isConsentNeeded; + private bool? _hasConsent; + + public ResponseCookiesWrapper(HttpContext context, CookiePolicyOptions options, IResponseCookiesFeature feature, ILogger logger) + { + Context = context; + Feature = feature; + Options = options; + _logger = logger; + } + + private HttpContext Context { get; } + + private IResponseCookiesFeature Feature { get; } + + private IResponseCookies Cookies => Feature.Cookies; + + private CookiePolicyOptions Options { get; } + + public bool IsConsentNeeded + { + get + { + if (!_isConsentNeeded.HasValue) + { + _isConsentNeeded = Options.CheckConsentNeeded == null ? false + : Options.CheckConsentNeeded(Context); + _logger.NeedsConsent(_isConsentNeeded.Value); + } + + return _isConsentNeeded.Value; + } + } + + public bool HasConsent + { + get + { + if (!_hasConsent.HasValue) + { + var cookie = Context.Request.Cookies[Options.ConsentCookie.Name]; + _hasConsent = string.Equals(cookie, ConsentValue, StringComparison.Ordinal); + _logger.HasConsent(_hasConsent.Value); + } + + return _hasConsent.Value; + } + } + + public bool CanTrack => !IsConsentNeeded || HasConsent; + + public void GrantConsent() + { + if (!HasConsent && !Context.Response.HasStarted) + { + var cookieOptions = Options.ConsentCookie.Build(Context); + // Note policy will be applied. We don't want to bypass policy because we want HttpOnly, Secure, etc. to apply. + Append(Options.ConsentCookie.Name, ConsentValue, cookieOptions); + _logger.ConsentGranted(); + } + _hasConsent = true; + } + + public void WithdrawConsent() + { + if (HasConsent && !Context.Response.HasStarted) + { + var cookieOptions = Options.ConsentCookie.Build(Context); + // Note policy will be applied. We don't want to bypass policy because we want HttpOnly, Secure, etc. to apply. + Delete(Options.ConsentCookie.Name, cookieOptions); + _logger.ConsentWithdrawn(); + } + _hasConsent = false; + } + + // Note policy will be applied. We don't want to bypass policy because we want HttpOnly, Secure, etc. to apply. + public string CreateConsentCookie() + { + var key = Options.ConsentCookie.Name; + var value = ConsentValue; + var options = Options.ConsentCookie.Build(Context); + ApplyAppendPolicy(ref key, ref value, options); + + var setCookieHeaderValue = new Net.Http.Headers.SetCookieHeaderValue( + Uri.EscapeDataString(key), + Uri.EscapeDataString(value)) + { + Domain = options.Domain, + Path = options.Path, + Expires = options.Expires, + MaxAge = options.MaxAge, + Secure = options.Secure, + SameSite = (Net.Http.Headers.SameSiteMode)options.SameSite, + HttpOnly = options.HttpOnly + }; + + return setCookieHeaderValue.ToString(); + } + + private bool CheckPolicyRequired() + { + return !CanTrack + || Options.MinimumSameSitePolicy != SameSiteMode.None + || Options.HttpOnly != HttpOnlyPolicy.None + || Options.Secure != CookieSecurePolicy.None; + } + + public void Append(string key, string value) + { + if (CheckPolicyRequired() || Options.OnAppendCookie != null) + { + Append(key, value, new CookieOptions()); + } + else + { + Cookies.Append(key, value); + } + } + + public void Append(string key, string value, CookieOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + if (ApplyAppendPolicy(ref key, ref value, options)) + { + Cookies.Append(key, value, options); + } + else + { + _logger.CookieSuppressed(key); + } + } + + private bool ApplyAppendPolicy(ref string key, ref string value, CookieOptions options) + { + var issueCookie = CanTrack || options.IsEssential; + ApplyPolicy(key, options); + if (Options.OnAppendCookie != null) + { + var context = new AppendCookieContext(Context, options, key, value) + { + IsConsentNeeded = IsConsentNeeded, + HasConsent = HasConsent, + IssueCookie = issueCookie, + }; + Options.OnAppendCookie(context); + + key = context.CookieName; + value = context.CookieValue; + issueCookie = context.IssueCookie; + } + + return issueCookie; + } + + public void Delete(string key) + { + if (CheckPolicyRequired() || Options.OnDeleteCookie != null) + { + Delete(key, new CookieOptions()); + } + else + { + Cookies.Delete(key); + } + } + + public void Delete(string key, CookieOptions options) + { + if (options == null) + { + throw new ArgumentNullException(nameof(options)); + } + + // Assume you can always delete cookies unless directly overridden in the user event. + var issueCookie = true; + ApplyPolicy(key, options); + if (Options.OnDeleteCookie != null) + { + var context = new DeleteCookieContext(Context, options, key) + { + IsConsentNeeded = IsConsentNeeded, + HasConsent = HasConsent, + IssueCookie = issueCookie, + }; + Options.OnDeleteCookie(context); + + key = context.CookieName; + issueCookie = context.IssueCookie; + } + + if (issueCookie) + { + Cookies.Delete(key, options); + } + else + { + _logger.DeleteCookieSuppressed(key); + } + } + + private void ApplyPolicy(string key, CookieOptions options) + { + switch (Options.Secure) + { + case CookieSecurePolicy.Always: + if (!options.Secure) + { + options.Secure = true; + _logger.CookieUpgradedToSecure(key); + } + break; + case CookieSecurePolicy.SameAsRequest: + // Never downgrade a cookie + if (Context.Request.IsHttps && !options.Secure) + { + options.Secure = true; + _logger.CookieUpgradedToSecure(key); + } + break; + case CookieSecurePolicy.None: + break; + default: + throw new InvalidOperationException(); + } + switch (Options.MinimumSameSitePolicy) + { + case SameSiteMode.None: + break; + case SameSiteMode.Lax: + if (options.SameSite == SameSiteMode.None) + { + options.SameSite = SameSiteMode.Lax; + _logger.CookieSameSiteUpgraded(key, "lax"); + } + break; + case SameSiteMode.Strict: + if (options.SameSite != SameSiteMode.Strict) + { + options.SameSite = SameSiteMode.Strict; + _logger.CookieSameSiteUpgraded(key, "strict"); + } + break; + default: + throw new InvalidOperationException($"Unrecognized {nameof(SameSiteMode)} value {Options.MinimumSameSitePolicy.ToString()}"); + } + switch (Options.HttpOnly) + { + case HttpOnlyPolicy.Always: + if (!options.HttpOnly) + { + options.HttpOnly = true; + _logger.CookieUpgradedToHttpOnly(key); + } + break; + case HttpOnlyPolicy.None: + break; + default: + throw new InvalidOperationException($"Unrecognized {nameof(HttpOnlyPolicy)} value {Options.HttpOnly.ToString()}"); + } + } + } +} \ No newline at end of file diff --git a/src/Microsoft.AspNetCore.CookiePolicy/baseline.netcore.json b/src/Microsoft.AspNetCore.CookiePolicy/baseline.netcore.json index 8eef347eb..01a16c57a 100644 --- a/src/Microsoft.AspNetCore.CookiePolicy/baseline.netcore.json +++ b/src/Microsoft.AspNetCore.CookiePolicy/baseline.netcore.json @@ -1,5 +1,5 @@ { - "AssemblyIdentity": "Microsoft.AspNetCore.CookiePolicy, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.AspNetCore.CookiePolicy, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.AspNetCore.Builder.CookiePolicyAppBuilderExtensions", @@ -53,6 +53,27 @@ "Kind": "Class", "ImplementedInterfaces": [], "Members": [ + { + "Kind": "Method", + "Name": "get_MinimumSameSitePolicy", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.SameSiteMode", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_MinimumSameSitePolicy", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.SameSiteMode" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Method", "Name": "get_HttpOnly", @@ -95,6 +116,48 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_ConsentCookie", + "Parameters": [], + "ReturnType": "Microsoft.AspNetCore.Http.CookieBuilder", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_ConsentCookie", + "Parameters": [ + { + "Name": "value", + "Type": "Microsoft.AspNetCore.Http.CookieBuilder" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_CheckConsentNeeded", + "Parameters": [], + "ReturnType": "System.Func", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_CheckConsentNeeded", + "Parameters": [ + { + "Name": "value", + "Type": "System.Func" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Method", "Name": "get_OnAppendCookie", @@ -211,6 +274,43 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_IsConsentNeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_HasConsent", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IssueCookie", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IssueCookie", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Constructor", "Name": ".ctor", @@ -278,6 +378,26 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Constructor", + "Name": ".ctor", + "Parameters": [ + { + "Name": "next", + "Type": "Microsoft.AspNetCore.Http.RequestDelegate" + }, + { + "Name": "options", + "Type": "Microsoft.Extensions.Options.IOptions" + }, + { + "Name": "factory", + "Type": "Microsoft.Extensions.Logging.ILoggerFactory" + } + ], + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Constructor", "Name": ".ctor", @@ -340,6 +460,43 @@ "Visibility": "Public", "GenericParameter": [] }, + { + "Kind": "Method", + "Name": "get_IsConsentNeeded", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_HasConsent", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "get_IssueCookie", + "Parameters": [], + "ReturnType": "System.Boolean", + "Visibility": "Public", + "GenericParameter": [] + }, + { + "Kind": "Method", + "Name": "set_IssueCookie", + "Parameters": [ + { + "Name": "value", + "Type": "System.Boolean" + } + ], + "ReturnType": "System.Void", + "Visibility": "Public", + "GenericParameter": [] + }, { "Kind": "Constructor", "Name": ".ctor", @@ -387,6 +544,5 @@ ], "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/src/Microsoft.Owin.Security.Interop/ChunkingCookieManager.cs b/src/Microsoft.Owin.Security.Interop/ChunkingCookieManager.cs index b323258d9..1ae4f00cd 100644 --- a/src/Microsoft.Owin.Security.Interop/ChunkingCookieManager.cs +++ b/src/Microsoft.Owin.Security.Interop/ChunkingCookieManager.cs @@ -25,7 +25,6 @@ public ChunkingCookieManager() // See http://browsercookielimits.x64.me/. // Leave at least 20 in case CookiePolicy tries to add 'secure' and/or 'httponly'. ChunkSize = 4070; - ThrowForPartialCookies = true; } /// diff --git a/src/Microsoft.Owin.Security.Interop/Microsoft.Owin.Security.Interop.csproj b/src/Microsoft.Owin.Security.Interop/Microsoft.Owin.Security.Interop.csproj index 10a8be30f..a12bc6563 100644 --- a/src/Microsoft.Owin.Security.Interop/Microsoft.Owin.Security.Interop.csproj +++ b/src/Microsoft.Owin.Security.Interop/Microsoft.Owin.Security.Interop.csproj @@ -1,7 +1,5 @@  - - A compatibility layer for sharing authentication tickets between Microsoft.Owin.Security and Microsoft.AspNetCore.Authentication. net461 @@ -11,8 +9,8 @@ - - + + diff --git a/src/Microsoft.Owin.Security.Interop/baseline.netframework.json b/src/Microsoft.Owin.Security.Interop/baseline.netframework.json index 1fc242ec5..bfc0c0076 100644 --- a/src/Microsoft.Owin.Security.Interop/baseline.netframework.json +++ b/src/Microsoft.Owin.Security.Interop/baseline.netframework.json @@ -1,5 +1,5 @@ { - "AssemblyIdentity": "Microsoft.Owin.Security.Interop, Version=1.0.0.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", + "AssemblyIdentity": "Microsoft.Owin.Security.Interop, Version=2.1.1.0, Culture=neutral, PublicKeyToken=adb9793829ddae60", "Types": [ { "Name": "Microsoft.Owin.Security.Interop.AspNetTicketDataFormat", @@ -368,6 +368,5 @@ ], "GenericParameters": [] } - ], - "SourceFilters": [] + ] } \ No newline at end of file diff --git a/test/Directory.Build.props b/test/Directory.Build.props new file mode 100644 index 000000000..c42d54465 --- /dev/null +++ b/test/Directory.Build.props @@ -0,0 +1,12 @@ + + + + + + + + + + + + diff --git a/test/Microsoft.AspNetCore.Authentication.Test/ClaimActionTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/ClaimActionTests.cs new file mode 100644 index 000000000..b083e9d76 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/ClaimActionTests.cs @@ -0,0 +1,112 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Linq; +using System.Security.Claims; +using Microsoft.AspNetCore.Testing.xunit; +using Xunit; +using Microsoft.AspNetCore.Authentication.OAuth.Claims; +using Newtonsoft.Json.Linq; + +namespace Microsoft.AspNetCore.Authentication +{ + public class ClaimActionTests + { + [Fact] + public void CanMapSingleValueUserDataToClaim() + { + var userData = new JObject + { + ["name"] = "test" + }; + + var identity = new ClaimsIdentity(); + + var action = new JsonKeyClaimAction("name", "name", "name"); + action.Run(userData, identity, "iss"); + + Assert.Equal("name", identity.FindFirst("name").Type); + Assert.Equal("test", identity.FindFirst("name").Value); + } + + [Fact] + public void CanMapArrayValueUserDataToClaims() + { + var userData = new JObject + { + ["role"] = new JArray { "role1", "role2" } + }; + + var identity = new ClaimsIdentity(); + + var action = new JsonKeyClaimAction("role", "role", "role"); + action.Run(userData, identity, "iss"); + + var roleClaims = identity.FindAll("role").ToList(); + Assert.Equal(2, roleClaims.Count); + Assert.Equal("role", roleClaims[0].Type); + Assert.Equal("role1", roleClaims[0].Value); + Assert.Equal("role", roleClaims[1].Type); + Assert.Equal("role2", roleClaims[1].Value); + } + + [Fact] + public void MapAllSucceeds() + { + var userData = new JObject + { + ["name0"] = "value0", + ["name1"] = "value1", + }; + + var identity = new ClaimsIdentity(); + var action = new MapAllClaimsAction(); + action.Run(userData, identity, "iss"); + + Assert.Equal("name0", identity.FindFirst("name0").Type); + Assert.Equal("value0", identity.FindFirst("name0").Value); + Assert.Equal("name1", identity.FindFirst("name1").Type); + Assert.Equal("value1", identity.FindFirst("name1").Value); + } + + [Fact] + public void MapAllAllowesDulicateKeysWithUniqueValues() + { + var userData = new JObject + { + ["name0"] = "value0", + ["name1"] = "value1", + }; + + var identity = new ClaimsIdentity(); + identity.AddClaim(new Claim("name0", "value2")); + identity.AddClaim(new Claim("name1", "value3")); + var action = new MapAllClaimsAction(); + action.Run(userData, identity, "iss"); + + Assert.Equal(2, identity.FindAll("name0").Count()); + Assert.Equal(2, identity.FindAll("name1").Count()); + } + + [Fact] + public void MapAllSkipsDuplicateValues() + { + var userData = new JObject + { + ["name0"] = "value0", + ["name1"] = "value1", + }; + + var identity = new ClaimsIdentity(); + identity.AddClaim(new Claim("name0", "value0")); + identity.AddClaim(new Claim("name1", "value1")); + var action = new MapAllClaimsAction(); + action.Run(userData, identity, "iss"); + + Assert.Single(identity.FindAll("name0")); + Assert.Single(identity.FindAll("name1")); + } + } +} diff --git a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs index c711fde49..107fc5db1 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/CookieTests.cs @@ -2,7 +2,6 @@ // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. using System; -using System.Diagnostics; using System.Linq; using System.Net; using System.Net.Http; @@ -22,21 +21,16 @@ namespace Microsoft.AspNetCore.Authentication.Cookies { - public class CookieTests + public class CookieTests : SharedAuthenticationTests { private TestClock _clock = new TestClock(); - [Fact] - public async Task VerifySchemeDefaults() + protected override string DefaultScheme => CookieAuthenticationDefaults.AuthenticationScheme; + protected override Type HandlerType => typeof(CookieAuthenticationHandler); + + protected override void RegisterAuth(AuthenticationBuilder services, Action configure) { - var services = new ServiceCollection(); - services.AddAuthentication().AddCookie(); - var sp = services.BuildServiceProvider(); - var schemeProvider = sp.GetRequiredService(); - var scheme = await schemeProvider.GetSchemeAsync(CookieAuthenticationDefaults.AuthenticationScheme); - Assert.NotNull(scheme); - Assert.Equal("CookieAuthenticationHandler", scheme.HandlerType.Name); - Assert.Null(scheme.DisplayName); + services.AddCookie(configure); } [Fact] @@ -54,8 +48,8 @@ public async Task AjaxLoginRedirectToReturnUrlTurnsInto200WithLocationHeader() var transaction = await SendAsync(server, "http://example.com/challenge?X-Requested-With=XMLHttpRequest"); Assert.Equal(HttpStatusCode.Unauthorized, transaction.Response.StatusCode); var responded = transaction.Response.Headers.GetValues("Location"); - Assert.Equal(1, responded.Count()); - Assert.True(responded.Single().StartsWith("http://example.com/login")); + Assert.Single(responded); + Assert.StartsWith("http://example.com/login", responded.Single()); } [Fact] @@ -65,8 +59,8 @@ public async Task AjaxForbidTurnsInto403WithLocationHeader() var transaction = await SendAsync(server, "http://example.com/forbid?X-Requested-With=XMLHttpRequest"); Assert.Equal(HttpStatusCode.Forbidden, transaction.Response.StatusCode); var responded = transaction.Response.Headers.GetValues("Location"); - Assert.Equal(1, responded.Count()); - Assert.True(responded.Single().StartsWith("http://example.com/denied")); + Assert.Single(responded); + Assert.StartsWith("http://example.com/denied", responded.Single()); } [Fact] @@ -76,8 +70,8 @@ public async Task AjaxLogoutRedirectToReturnUrlTurnsInto200WithLocationHeader() var transaction = await SendAsync(server, "http://example.com/signout?X-Requested-With=XMLHttpRequest&ReturnUrl=/"); Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); var responded = transaction.Response.Headers.GetValues("Location"); - Assert.Equal(1, responded.Count()); - Assert.True(responded.Single().StartsWith("/")); + Assert.Single(responded); + Assert.StartsWith("/", responded.Single()); } [Fact] @@ -87,8 +81,8 @@ public async Task AjaxChallengeRedirectTurnsInto200WithLocationHeader() var transaction = await SendAsync(server, "http://example.com/challenge?X-Requested-With=XMLHttpRequest&ReturnUrl=/"); Assert.Equal(HttpStatusCode.Unauthorized, transaction.Response.StatusCode); var responded = transaction.Response.Headers.GetValues("Location"); - Assert.Equal(1, responded.Count()); - Assert.True(responded.Single().StartsWith("http://example.com/Account/Login")); + Assert.Single(responded); + Assert.StartsWith("http://example.com/Account/Login", responded.Single()); } [Fact] @@ -105,8 +99,10 @@ public async Task ProtectedCustomRequestShouldRedirectToCustomRedirectUri() private Task SignInAsAlice(HttpContext context) { + var user = new ClaimsIdentity(new GenericIdentity("Alice", "Cookies")); + user.AddClaim(new Claim("marker", "true")); return context.SignInAsync("Cookies", - new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))), + new ClaimsPrincipal(user), new AuthenticationProperties()); } @@ -439,7 +435,7 @@ public async Task CookieNotRenewedAfterSignOut() // signout wins over renew var transaction4 = await server.SendAsync("http://example.com/signout", transaction3.SetCookie[0]); - Assert.Equal(1, transaction4.SetCookie.Count()); + Assert.Single(transaction4.SetCookie); Assert.Contains(".AspNetCore.Cookies=; expires=", transaction4.SetCookie[0]); } @@ -488,6 +484,80 @@ public async Task CookieCanBeRenewedByValidator() Assert.Null(FindClaimValue(transaction5, ClaimTypes.Name)); } + [Fact] + public async Task CookieCanBeReplacedByValidator() + { + var server = CreateServer(o => + { + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = ctx => + { + ctx.ShouldRenew = true; + ctx.ReplacePrincipal(new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice2", "Cookies2")))); + return Task.FromResult(0); + } + }; + }, + context => + context.SignInAsync("Cookies", + new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))))); + + var transaction1 = await SendAsync(server, "http://example.com/testpath"); + + var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.NotNull(transaction2.SetCookie); + Assert.Equal("Alice2", FindClaimValue(transaction2, ClaimTypes.Name)); + } + + [Fact] + public async Task CookieCanBeUpdatedByValidatorDuringRefresh() + { + var replace = false; + var server = CreateServer(o => + { + o.ExpireTimeSpan = TimeSpan.FromMinutes(10); + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = ctx => + { + if (replace) + { + ctx.ShouldRenew = true; + ctx.ReplacePrincipal(new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice2", "Cookies2")))); + ctx.Properties.Items["updated"] = "yes"; + } + return Task.FromResult(0); + } + }; + }, + context => + context.SignInAsync("Cookies", + new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))))); + + var transaction1 = await SendAsync(server, "http://example.com/testpath"); + + var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name)); + + var transaction3 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name)); + Assert.Null(FindPropertiesValue(transaction3, "updated")); + + replace = true; + + var transaction4 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.NotNull(transaction4.SetCookie); + Assert.Equal("Alice2", FindClaimValue(transaction4, ClaimTypes.Name)); + Assert.Equal("yes", FindPropertiesValue(transaction4, "updated")); + + replace = false; + + var transaction5 = await SendAsync(server, "http://example.com/me/Cookies", transaction4.CookieNameValue); + Assert.Equal("Alice2", FindClaimValue(transaction5, ClaimTypes.Name)); + Assert.Equal("yes", FindPropertiesValue(transaction4, "updated")); + } + [Fact] public async Task CookieCanBeRenewedByValidatorWithSlidingExpiry() { @@ -532,6 +602,61 @@ public async Task CookieCanBeRenewedByValidatorWithSlidingExpiry() Assert.Null(FindClaimValue(transaction5, ClaimTypes.Name)); } + [Fact] + public async Task CookieCanBeRenewedByValidatorWithModifiedProperties() + { + var server = CreateServer(o => + { + o.ExpireTimeSpan = TimeSpan.FromMinutes(10); + o.Events = new CookieAuthenticationEvents + { + OnValidatePrincipal = ctx => + { + ctx.ShouldRenew = true; + var id = ctx.Principal.Identities.First(); + var claim = id.FindFirst("counter"); + if (claim == null) + { + id.AddClaim(new Claim("counter", "1")); + } + else + { + id.RemoveClaim(claim); + id.AddClaim(new Claim("counter", claim.Value + "1")); + } + return Task.FromResult(0); + } + }; + }, + context => + context.SignInAsync("Cookies", + new ClaimsPrincipal(new ClaimsIdentity(new GenericIdentity("Alice", "Cookies"))))); + + var transaction1 = await SendAsync(server, "http://example.com/testpath"); + + var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.NotNull(transaction2.SetCookie); + Assert.Equal("1", FindClaimValue(transaction2, "counter")); + + _clock.Add(TimeSpan.FromMinutes(5)); + + var transaction3 = await SendAsync(server, "http://example.com/me/Cookies", transaction2.CookieNameValue); + Assert.NotNull(transaction3.SetCookie); + Assert.Equal("11", FindClaimValue(transaction3, "counter")); + + _clock.Add(TimeSpan.FromMinutes(6)); + + var transaction4 = await SendAsync(server, "http://example.com/me/Cookies", transaction3.CookieNameValue); + Assert.NotNull(transaction4.SetCookie); + Assert.Equal("111", FindClaimValue(transaction4, "counter")); + + _clock.Add(TimeSpan.FromMinutes(11)); + + var transaction5 = await SendAsync(server, "http://example.com/me/Cookies", transaction4.CookieNameValue); + Assert.Null(transaction5.SetCookie); + Assert.Null(FindClaimValue(transaction5, "counter")); + } + [Fact] public async Task CookieValidatorOnlyCalledOnce() { @@ -704,6 +829,51 @@ public async Task CookieIsRenewedWithSlidingExpiration() Assert.Equal("Alice", FindClaimValue(transaction5, ClaimTypes.Name)); } + [Fact] + public async Task CookieIsRenewedWithSlidingExpirationWithoutTransformations() + { + var server = CreateServer(o => + { + o.ExpireTimeSpan = TimeSpan.FromMinutes(10); + o.SlidingExpiration = true; + o.Events.OnValidatePrincipal = c => + { + // https://github.com/aspnet/Security/issues/1607 + // On sliding refresh the transformed principal should not be serialized into the cookie, only the original principal. + Assert.Single(c.Principal.Identities); + Assert.True(c.Principal.Identities.First().HasClaim("marker", "true")); + return Task.CompletedTask; + }; + }, + SignInAsAlice, + claimsTransform: true); + + var transaction1 = await SendAsync(server, "http://example.com/testpath"); + + var transaction2 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.Null(transaction2.SetCookie); + Assert.Equal("Alice", FindClaimValue(transaction2, ClaimTypes.Name)); + + _clock.Add(TimeSpan.FromMinutes(4)); + + var transaction3 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.Null(transaction3.SetCookie); + Assert.Equal("Alice", FindClaimValue(transaction3, ClaimTypes.Name)); + + _clock.Add(TimeSpan.FromMinutes(4)); + + // transaction4 should arrive with a new SetCookie value + var transaction4 = await SendAsync(server, "http://example.com/me/Cookies", transaction1.CookieNameValue); + Assert.NotNull(transaction4.SetCookie); + Assert.Equal("Alice", FindClaimValue(transaction4, ClaimTypes.Name)); + + _clock.Add(TimeSpan.FromMinutes(4)); + + var transaction5 = await SendAsync(server, "http://example.com/me/Cookies", transaction4.CookieNameValue); + Assert.Null(transaction5.SetCookie); + Assert.Equal("Alice", FindClaimValue(transaction5, ClaimTypes.Name)); + } + [Fact] public async Task CookieUsesPathBaseByDefault() { @@ -717,7 +887,7 @@ public async Task CookieUsesPathBaseByDefault() new Uri("http://example.com/base")); var transaction1 = await SendAsync(server, "http://example.com/base/testpath"); - Assert.True(transaction1.SetCookie.Contains("path=/base")); + Assert.Contains("path=/base", transaction1.SetCookie); } [Fact] @@ -848,7 +1018,7 @@ public async Task CanConfigureDefaultCookieInstance() var transaction = await server.SendAsync("http://example.com"); Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); - Assert.True(transaction.SetCookie[0].StartsWith("One=")); + Assert.StartsWith("One=", transaction.SetCookie[0]); } [Fact] @@ -871,7 +1041,7 @@ public async Task CanConfigureNamedCookieInstance() var transaction = await server.SendAsync("http://example.com"); Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); - Assert.True(transaction.SetCookie[0].StartsWith("One=")); + Assert.StartsWith("One=", transaction.SetCookie[0]); } [Fact] @@ -1218,6 +1388,16 @@ private static string FindClaimValue(Transaction transaction, string claimType) return claim.Attribute("value").Value; } + private static string FindPropertiesValue(Transaction transaction, string key) + { + var property = transaction.ResponseElement.Elements("extra").SingleOrDefault(elt => elt.Attribute("type").Value == key); + if (property == null) + { + return null; + } + return property.Attribute("value").Value; + } + private static async Task GetAuthData(TestServer server, string url, string cookie) { var request = new HttpRequestMessage(HttpMethod.Get, url); @@ -1233,6 +1413,13 @@ private class ClaimsTransformer : IClaimsTransformation { public Task TransformAsync(ClaimsPrincipal p) { + var firstId = p.Identities.First(); + if (firstId.HasClaim("marker", "true")) + { + firstId.RemoveClaim(firstId.FindFirst("marker")); + } + // TransformAsync could be called twice on one request if you have a default scheme and also + // call AuthenticateAsync. if (!p.Identities.Any(i => i.AuthenticationType == "xform")) { var id = new ClaimsIdentity("xform"); @@ -1248,7 +1435,10 @@ private TestServer CreateServer(Action configureOpt { s.AddSingleton(_clock); s.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(configureOptions); - s.AddSingleton(); + if (claimsTransform) + { + s.AddSingleton(); + } }, testpath, baseAddress); private static TestServer CreateServerWithServices(Action configureServices, Func testpath = null, Uri baseAddress = null) @@ -1325,11 +1515,11 @@ private static void Describe(HttpResponse res, AuthenticateResult result) res.StatusCode = 200; res.ContentType = "text/xml"; var xml = new XElement("xml"); - if (result != null && result?.Ticket?.Principal != null) + if (result?.Ticket?.Principal != null) { xml.Add(result.Ticket.Principal.Claims.Select(claim => new XElement("claim", new XAttribute("type", claim.Type), new XAttribute("value", claim.Value)))); } - if (result != null && result?.Ticket?.Properties != null) + if (result?.Ticket?.Properties != null) { xml.Add(result.Ticket.Properties.Items.Select(extra => new XElement("extra", new XAttribute("type", extra.Key), new XAttribute("value", extra.Value)))); } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs index 75de0652e..964af2ce7 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/FacebookTests.cs @@ -1,13 +1,6 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Builder; @@ -18,23 +11,38 @@ using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; using Newtonsoft.Json; +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; using Xunit; namespace Microsoft.AspNetCore.Authentication.Facebook { - public class FacebookTests + public class FacebookTests : RemoteAuthenticationTests { - [Fact] - public async Task VerifySchemeDefaults() + protected override string DefaultScheme => FacebookDefaults.AuthenticationScheme; + protected override Type HandlerType => typeof(FacebookHandler); + protected override bool SupportsSignIn { get => false; } + protected override bool SupportsSignOut { get => false; } + + protected override void RegisterAuth(AuthenticationBuilder services, Action configure) { - var services = new ServiceCollection(); - services.AddAuthentication().AddFacebook(); - var sp = services.BuildServiceProvider(); - var schemeProvider = sp.GetRequiredService(); - var scheme = await schemeProvider.GetSchemeAsync(FacebookDefaults.AuthenticationScheme); - Assert.NotNull(scheme); - Assert.Equal("FacebookHandler", scheme.HandlerType.Name); - Assert.Equal(FacebookDefaults.AuthenticationScheme, scheme.DisplayName); + services.AddFacebook(o => + { + ConfigureDefaults(o); + configure.Invoke(o); + }); + } + + protected override void ConfigureDefaults(FacebookOptions o) + { + o.AppId = "whatever"; + o.AppSecret = "whatever"; + o.SignInScheme = "auth1"; } [Fact] @@ -43,10 +51,9 @@ public async Task ThrowsIfAppIdMissing() var server = CreateServer( app => { }, services => services.AddAuthentication().AddFacebook(o => o.SignInScheme = "Whatever"), - context => + async context => { - // REVIEW: Gross. - Assert.Throws("AppId", () => context.ChallengeAsync("Facebook").GetAwaiter().GetResult()); + await Assert.ThrowsAsync("AppId", () => context.ChallengeAsync("Facebook")); return true; }); var transaction = await server.SendAsync("http://example.com/challenge"); @@ -59,10 +66,9 @@ public async Task ThrowsIfAppSecretMissing() var server = CreateServer( app => { }, services => services.AddAuthentication().AddFacebook(o => o.AppId = "Whatever"), - context => + async context => { - // REVIEW: Gross. - Assert.Throws("AppSecret", () => context.ChallengeAsync("Facebook").GetAwaiter().GetResult()); + await Assert.ThrowsAsync("AppSecret", () => context.ChallengeAsync("Facebook")); return true; }); var transaction = await server.SendAsync("http://example.com/challenge"); @@ -95,10 +101,9 @@ public async Task ChallengeWillTriggerApplyRedirectEvent() }; }); }, - context => + async context => { - // REVIEW: Gross. - context.ChallengeAsync("Facebook").GetAwaiter().GetResult(); + await context.ChallengeAsync("Facebook"); return true; }); var transaction = await server.SendAsync("http://example.com/challenge"); @@ -107,6 +112,97 @@ public async Task ChallengeWillTriggerApplyRedirectEvent() Assert.Contains("custom=test", query); } + [Fact] + public async Task ChallengeWillIncludeScopeAsConfigured() + { + var server = CreateServer( + app => app.UseAuthentication(), + services => + { + services.AddAuthentication().AddFacebook(o => + { + o.AppId = "Test App Id"; + o.AppSecret = "Test App Secret"; + o.Scope.Clear(); + o.Scope.Add("foo"); + o.Scope.Add("bar"); + }); + }, + async context => + { + await context.ChallengeAsync(FacebookDefaults.AuthenticationScheme); + return true; + }); + + var transaction = await server.SendAsync("http://example.com/challenge"); + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=foo,bar", res.Headers.Location.Query); + } + + [Fact] + public async Task ChallengeWillIncludeScopeAsOverwritten() + { + var server = CreateServer( + app => app.UseAuthentication(), + services => + { + services.AddAuthentication().AddFacebook(o => + { + o.AppId = "Test App Id"; + o.AppSecret = "Test App Secret"; + o.Scope.Clear(); + o.Scope.Add("foo"); + o.Scope.Add("bar"); + }); + }, + async context => + { + var properties = new OAuthChallengeProperties(); + properties.SetScope("baz", "qux"); + await context.ChallengeAsync(FacebookDefaults.AuthenticationScheme, properties); + return true; + }); + + var transaction = await server.SendAsync("http://example.com/challenge"); + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=baz,qux", res.Headers.Location.Query); + } + + [Fact] + public async Task ChallengeWillIncludeScopeAsOverwrittenWithBaseAuthenticationProperties() + { + var server = CreateServer( + app => app.UseAuthentication(), + services => + { + services.AddAuthentication().AddFacebook(o => + { + o.AppId = "Test App Id"; + o.AppSecret = "Test App Secret"; + o.Scope.Clear(); + o.Scope.Add("foo"); + o.Scope.Add("bar"); + }); + }, + async context => + { + var properties = new AuthenticationProperties(); + properties.SetParameter(OAuthChallengeProperties.ScopeKey, new string[] { "baz", "qux" }); + await context.ChallengeAsync(FacebookDefaults.AuthenticationScheme, properties); + return true; + }); + + var transaction = await server.SendAsync("http://example.com/challenge"); + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=baz,qux", res.Headers.Location.Query); + } + [Fact] public async Task NestedMapWillNotAffectRedirect() { @@ -130,7 +226,7 @@ public async Task NestedMapWillNotAffectRedirect() var transaction = await server.SendAsync("http://example.com/base/login"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.AbsoluteUri; - Assert.Contains("https://www.facebook.com/v2.6/dialog/oauth", location); + Assert.Contains("https://www.facebook.com/v3.1/dialog/oauth", location); Assert.Contains("response_type=code", location); Assert.Contains("client_id=", location); Assert.Contains("redirect_uri=" + UrlEncoder.Default.Encode("http://example.com/base/signin-facebook"), location); @@ -162,10 +258,10 @@ public async Task MapWillNotAffectRedirect() var transaction = await server.SendAsync("http://example.com/login"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.AbsoluteUri; - Assert.Contains("https://www.facebook.com/v2.6/dialog/oauth", location); + Assert.Contains("https://www.facebook.com/v3.1/dialog/oauth", location); Assert.Contains("response_type=code", location); Assert.Contains("client_id=", location); - Assert.Contains("redirect_uri="+ UrlEncoder.Default.Encode("http://example.com/signin-facebook"), location); + Assert.Contains("redirect_uri=" + UrlEncoder.Default.Encode("http://example.com/signin-facebook"), location); Assert.Contains("scope=", location); Assert.Contains("state=", location); } @@ -188,16 +284,15 @@ public async Task ChallengeWillTriggerRedirection() o.AppSecret = "Test App Secret"; }); }, - context => + async context => { - // REVIEW: gross - context.ChallengeAsync("Facebook").GetAwaiter().GetResult(); + await context.ChallengeAsync("Facebook"); return true; }); var transaction = await server.SendAsync("http://example.com/challenge"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.AbsoluteUri; - Assert.Contains("https://www.facebook.com/v2.6/dialog/oauth", location); + Assert.Contains("https://www.facebook.com/v3.1/dialog/oauth", location); Assert.Contains("response_type=code", location); Assert.Contains("client_id=", location); Assert.Contains("redirect_uri=", location); @@ -217,7 +312,7 @@ public async Task CustomUserInfoEndpointHasValidGraphQuery() { services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme) .AddCookie() - .AddFacebook(o => + .AddFacebook(o => { o.AppId = "Test App Id"; o.AppSecret = "Test App Secret"; @@ -273,7 +368,7 @@ public async Task CustomUserInfoEndpointHasValidGraphQuery() Assert.Contains("&access_token=", finalUserInfoEndpoint); } - private static TestServer CreateServer(Action configure, Action configureServices, Func handler) + private static TestServer CreateServer(Action configure, Action configureServices, Func> handler) { var builder = new WebHostBuilder() .Configure(app => @@ -281,7 +376,7 @@ private static TestServer CreateServer(Action configure, Ac configure?.Invoke(app); app.Use(async (context, next) => { - if (handler == null || !handler(context)) + if (handler == null || !await handler(context)) { await next(); } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs index f1038bb51..ce158eaf2 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/GoogleTests.cs @@ -1,42 +1,49 @@ // Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Claims; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.DataProtection; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Configuration; +using Microsoft.AspNetCore.WebUtilities; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; using Xunit; namespace Microsoft.AspNetCore.Authentication.Google { - public class GoogleTests + public class GoogleTests : RemoteAuthenticationTests { - [Fact] - public async Task VerifySchemeDefaults() + protected override string DefaultScheme => GoogleDefaults.AuthenticationScheme; + protected override Type HandlerType => typeof(GoogleHandler); + protected override bool SupportsSignIn { get => false; } + protected override bool SupportsSignOut { get => false; } + + protected override void RegisterAuth(AuthenticationBuilder services, Action configure) { - var services = new ServiceCollection(); - services.AddAuthentication().AddGoogle(); - var sp = services.BuildServiceProvider(); - var schemeProvider = sp.GetRequiredService(); - var scheme = await schemeProvider.GetSchemeAsync(GoogleDefaults.AuthenticationScheme); - Assert.NotNull(scheme); - Assert.Equal("GoogleHandler", scheme.HandlerType.Name); - Assert.Equal(GoogleDefaults.AuthenticationScheme, scheme.DisplayName); + services.AddGoogle(o => + { + ConfigureDefaults(o); + configure.Invoke(o); + }); + } + + protected override void ConfigureDefaults(GoogleOptions o) + { + o.ClientId = "whatever"; + o.ClientSecret = "whatever"; + o.SignInScheme = "auth1"; } [Fact] @@ -50,7 +57,7 @@ public async Task ChallengeWillTriggerRedirection() var transaction = await server.SendAsync("https://example.com/challenge"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.ToString(); - Assert.Contains("https://accounts.google.com/o/oauth2/auth?response_type=code", location); + Assert.Contains("https://accounts.google.com/o/oauth2/v2/auth?response_type=code", location); Assert.Contains("&client_id=", location); Assert.Contains("&redirect_uri=", location); Assert.Contains("&scope=", location); @@ -138,43 +145,160 @@ public async Task ChallengeWillSetDefaultScope() } [Fact] - public async Task ChallengeWillUseAuthenticationPropertiesAsParameters() + public async Task ChallengeWillUseAuthenticationPropertiesParametersAsQueryArguments() { + var stateFormat = new PropertiesDataFormat(new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("GoogleTest")); var server = CreateServer(o => { o.ClientId = "Test Id"; o.ClientSecret = "Test Secret"; - //AutomaticChallenge = true + o.StateDataFormat = stateFormat; }, context => + { + var req = context.Request; + var res = context.Response; + if (req.Path == new PathString("/challenge2")) { - var req = context.Request; - var res = context.Response; - if (req.Path == new PathString("/challenge2")) + return context.ChallengeAsync("Google", new GoogleChallengeProperties { - return context.ChallengeAsync("Google", new AuthenticationProperties( - new Dictionary() - { - { "scope", "https://www.googleapis.com/auth/plus.login" }, - { "access_type", "offline" }, - { "approval_prompt", "force" }, - { "prompt", "consent" }, - { "login_hint", "test@example.com" }, - { "include_granted_scopes", "false" } - })); - } + Scope = new string[] { "openid", "https://www.googleapis.com/auth/plus.login" }, + AccessType = "offline", + ApprovalPrompt = "force", + Prompt = "consent", + LoginHint = "test@example.com", + IncludeGrantedScopes = false, + }); + } - return Task.FromResult(null); - }); + return Task.FromResult(null); + }); var transaction = await server.SendAsync("https://example.com/challenge2"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); - var query = transaction.Response.Headers.Location.Query; - Assert.Contains("scope=" + UrlEncoder.Default.Encode("https://www.googleapis.com/auth/plus.login"), query); - Assert.Contains("access_type=offline", query); - Assert.Contains("approval_prompt=force", query); - Assert.Contains("prompt=consent", query); - Assert.Contains("include_granted_scopes=false", query); - Assert.Contains("login_hint=" + UrlEncoder.Default.Encode("test@example.com"), query); + + // verify query arguments + var query = QueryHelpers.ParseQuery(transaction.Response.Headers.Location.Query); + Assert.Equal("openid https://www.googleapis.com/auth/plus.login", query["scope"]); + Assert.Equal("offline", query["access_type"]); + Assert.Equal("force", query["approval_prompt"]); + Assert.Equal("consent", query["prompt"]); + Assert.Equal("false", query["include_granted_scopes"]); + Assert.Equal("test@example.com", query["login_hint"]); + + // verify that the passed items were not serialized + var stateProperties = stateFormat.Unprotect(query["state"]); + Assert.DoesNotContain("scope", stateProperties.Items.Keys); + Assert.DoesNotContain("access_type", stateProperties.Items.Keys); + Assert.DoesNotContain("include_granted_scopes", stateProperties.Items.Keys); + Assert.DoesNotContain("approval_prompt", stateProperties.Items.Keys); + Assert.DoesNotContain("prompt", stateProperties.Items.Keys); + Assert.DoesNotContain("login_hint", stateProperties.Items.Keys); + } + + [Fact] + public async Task ChallengeWillUseAuthenticationPropertiesItemsAsParameters() + { + var stateFormat = new PropertiesDataFormat(new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("GoogleTest")); + var server = CreateServer(o => + { + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.StateDataFormat = stateFormat; + }, + context => + { + var req = context.Request; + var res = context.Response; + if (req.Path == new PathString("/challenge2")) + { + return context.ChallengeAsync("Google", new AuthenticationProperties(new Dictionary() + { + { "scope", "https://www.googleapis.com/auth/plus.login" }, + { "access_type", "offline" }, + { "approval_prompt", "force" }, + { "prompt", "consent" }, + { "login_hint", "test@example.com" }, + { "include_granted_scopes", "false" } + })); + } + + return Task.FromResult(null); + }); + var transaction = await server.SendAsync("https://example.com/challenge2"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + + // verify query arguments + var query = QueryHelpers.ParseQuery(transaction.Response.Headers.Location.Query); + Assert.Equal("https://www.googleapis.com/auth/plus.login", query["scope"]); + Assert.Equal("offline", query["access_type"]); + Assert.Equal("force", query["approval_prompt"]); + Assert.Equal("consent", query["prompt"]); + Assert.Equal("false", query["include_granted_scopes"]); + Assert.Equal("test@example.com", query["login_hint"]); + + // verify that the passed items were not serialized + var stateProperties = stateFormat.Unprotect(query["state"]); + Assert.DoesNotContain("scope", stateProperties.Items.Keys); + Assert.DoesNotContain("access_type", stateProperties.Items.Keys); + Assert.DoesNotContain("include_granted_scopes", stateProperties.Items.Keys); + Assert.DoesNotContain("approval_prompt", stateProperties.Items.Keys); + Assert.DoesNotContain("prompt", stateProperties.Items.Keys); + Assert.DoesNotContain("login_hint", stateProperties.Items.Keys); + } + + [Fact] + public async Task ChallengeWillUseAuthenticationPropertiesItemsAsQueryArgumentsButParametersWillOverwrite() + { + var stateFormat = new PropertiesDataFormat(new EphemeralDataProtectionProvider(NullLoggerFactory.Instance).CreateProtector("GoogleTest")); + var server = CreateServer(o => + { + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.StateDataFormat = stateFormat; + }, + context => + { + var req = context.Request; + var res = context.Response; + if (req.Path == new PathString("/challenge2")) + { + return context.ChallengeAsync("Google", new GoogleChallengeProperties(new Dictionary + { + ["scope"] = "https://www.googleapis.com/auth/plus.login", + ["access_type"] = "offline", + ["include_granted_scopes"] = "false", + ["approval_prompt"] = "force", + ["prompt"] = "login", + ["login_hint"] = "this-will-be-overwritten@example.com", + }) + { + Prompt = "consent", + LoginHint = "test@example.com", + }); + } + + return Task.FromResult(null); + }); + var transaction = await server.SendAsync("https://example.com/challenge2"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + + // verify query arguments + var query = QueryHelpers.ParseQuery(transaction.Response.Headers.Location.Query); + Assert.Equal("https://www.googleapis.com/auth/plus.login", query["scope"]); + Assert.Equal("offline", query["access_type"]); + Assert.Equal("force", query["approval_prompt"]); + Assert.Equal("consent", query["prompt"]); + Assert.Equal("false", query["include_granted_scopes"]); + Assert.Equal("test@example.com", query["login_hint"]); + + // verify that the passed items were not serialized + var stateProperties = stateFormat.Unprotect(query["state"]); + Assert.DoesNotContain("scope", stateProperties.Items.Keys); + Assert.DoesNotContain("access_type", stateProperties.Items.Keys); + Assert.DoesNotContain("include_granted_scopes", stateProperties.Items.Keys); + Assert.DoesNotContain("approval_prompt", stateProperties.Items.Keys); + Assert.DoesNotContain("prompt", stateProperties.Items.Keys); + Assert.DoesNotContain("login_hint", stateProperties.Items.Keys); } [Fact] @@ -233,6 +357,70 @@ public async Task ReplyPathWithoutStateQueryStringWillBeRejected() Assert.Equal("The oauth state was missing or invalid.", error.GetBaseException().Message); } + [Theory] + [InlineData(true)] + [InlineData(false)] + public async Task ReplyPathWithAccessDeniedErrorFails(bool redirect) + { + var server = CreateServer(o => + { + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.StateDataFormat = new TestStateDataFormat(); + o.Events = redirect ? new OAuthEvents() + { + OnAccessDenied = ctx => + { + ctx.Response.Redirect("/error?FailureMessage=AccessDenied"); + ctx.HandleResponse(); + return Task.FromResult(0); + } + } : new OAuthEvents(); + }); + var sendTask = server.SendAsync("https://example.com/signin-google?error=access_denied&error_description=SoBad&error_uri=foobar&state=protected_state", + ".AspNetCore.Correlation.Google.correlationId=N"); + if (redirect) + { + var transaction = await sendTask; + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + Assert.Equal("/error?FailureMessage=AccessDenied", transaction.Response.Headers.GetValues("Location").First()); + } + else + { + var error = await Assert.ThrowsAnyAsync(() => sendTask); + Assert.Equal("Access was denied by the resource owner or by the remote server.", error.GetBaseException().Message); + } + } + + [Fact] + public async Task ReplyPathWithAccessDeniedError_AllowsCustomizingPath() + { + var server = CreateServer(o => + { + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.StateDataFormat = new TestStateDataFormat(); + o.AccessDeniedPath = "/access-denied"; + o.Events = new OAuthEvents() + { + OnAccessDenied = ctx => + { + Assert.Equal("/access-denied", ctx.AccessDeniedPath.Value); + Assert.Equal("http://testhost/redirect", ctx.ReturnUrl); + Assert.Equal("ReturnUrl", ctx.ReturnUrlParameter); + ctx.AccessDeniedPath = "/custom-denied-page"; + ctx.ReturnUrl = "http://www.google.com/"; + ctx.ReturnUrlParameter = "rurl"; + return Task.FromResult(0); + } + }; + }); + var transaction = await server.SendAsync("https://example.com/signin-google?error=access_denied&error_description=SoBad&error_uri=foobar&state=protected_state", + ".AspNetCore.Correlation.Google.correlationId=N"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + Assert.Equal("/custom-denied-page?rurl=http%3A%2F%2Fwww.google.com%2F", transaction.Response.Headers.GetValues("Location").First()); + } + [Theory] [InlineData(true)] [InlineData(false)] @@ -242,6 +430,7 @@ public async Task ReplyPathWithErrorFails(bool redirect) { o.ClientId = "Test Id"; o.ClientSecret = "Test Secret"; + o.StateDataFormat = new TestStateDataFormat(); o.Events = redirect ? new OAuthEvents() { OnRemoteFailure = ctx => @@ -252,7 +441,8 @@ public async Task ReplyPathWithErrorFails(bool redirect) } } : new OAuthEvents(); }); - var sendTask = server.SendAsync("https://example.com/signin-google?error=OMG&error_description=SoBad&error_uri=foobar"); + var sendTask = server.SendAsync("https://example.com/signin-google?error=OMG&error_description=SoBad&error_uri=foobar&state=protected_state", + ".AspNetCore.Correlation.Google.correlationId=N"); if (redirect) { var transaction = await sendTask; @@ -977,7 +1167,7 @@ private static TestServer CreateServer(Action configureOptions, F var res = context.Response; if (req.Path == new PathString("/challenge")) { - await context.ChallengeAsync("Google"); + await context.ChallengeAsync(); } else if (req.Path == new PathString("/challengeFacebook")) { @@ -1048,21 +1238,48 @@ private static TestServer CreateServer(Action configureOptions, F .ConfigureServices(services => { services.AddTransient(); - services.AddAuthentication(o => - { - o.DefaultScheme = TestExtensions.CookieAuthenticationScheme; - o.DefaultChallengeScheme = GoogleDefaults.AuthenticationScheme; - }); - services.AddAuthentication() - .AddCookie(TestExtensions.CookieAuthenticationScheme) + services.AddAuthentication(TestExtensions.CookieAuthenticationScheme) + .AddCookie(TestExtensions.CookieAuthenticationScheme, o => o.ForwardChallenge = GoogleDefaults.AuthenticationScheme) .AddGoogle(configureOptions) .AddFacebook(o => - { - o.AppId = "Test AppId"; - o.AppSecret = "Test AppSecrent"; - }); + { + o.ClientId = "Test ClientId"; + o.ClientSecret = "Test AppSecrent"; + }); }); return new TestServer(builder); } + + private class TestStateDataFormat : ISecureDataFormat + { + private AuthenticationProperties Data { get; set; } + + public string Protect(AuthenticationProperties data) + { + return "protected_state"; + } + + public string Protect(AuthenticationProperties data, string purpose) + { + throw new NotImplementedException(); + } + + public AuthenticationProperties Unprotect(string protectedText) + { + Assert.Equal("protected_state", protectedText); + var properties = new AuthenticationProperties(new Dictionary() + { + { ".xsrf", "correlationId" }, + { "testkey", "testvalue" } + }); + properties.RedirectUri = "http://testhost/redirect"; + return properties; + } + + public AuthenticationProperties Unprotect(string protectedText, string purpose) + { + throw new NotImplementedException(); + } + } } } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs index 97adb2105..ad77803cd 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/JwtBearerTests.cs @@ -1,8 +1,14 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; using System; -using System.Collections.Generic; +using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net; using System.Net.Http; @@ -11,51 +17,102 @@ using System.Text; using System.Threading.Tasks; using System.Xml.Linq; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.TestHost; -using Microsoft.AspNetCore.Testing.xunit; -using Microsoft.Extensions.Configuration; -using Microsoft.Extensions.DependencyInjection; -using Microsoft.Extensions.Options; -using Microsoft.IdentityModel.Tokens; using Xunit; namespace Microsoft.AspNetCore.Authentication.JwtBearer { - public class JwtBearerTests + public class JwtBearerTests : SharedAuthenticationTests { - [Fact] - public async Task VerifySchemeDefaults() + protected override string DefaultScheme => JwtBearerDefaults.AuthenticationScheme; + protected override Type HandlerType => typeof(JwtBearerHandler); + protected override bool SupportsSignIn { get => false; } + protected override bool SupportsSignOut { get => false; } + + protected override void RegisterAuth(AuthenticationBuilder services, Action configure) + { + services.AddJwtBearer(o => + { + ConfigureDefaults(o); + configure.Invoke(o); + }); + } + + private void ConfigureDefaults(JwtBearerOptions o) { - var services = new ServiceCollection(); - services.AddAuthentication().AddJwtBearer(); - var sp = services.BuildServiceProvider(); - var schemeProvider = sp.GetRequiredService(); - var scheme = await schemeProvider.GetSchemeAsync(JwtBearerDefaults.AuthenticationScheme); - Assert.NotNull(scheme); - Assert.Equal("JwtBearerHandler", scheme.HandlerType.Name); - Assert.Null(scheme.DisplayName); } - [ConditionalFact(Skip = "Need to remove dependency on AAD since the generated tokens will expire")] - [FrameworkSkipCondition(RuntimeFrameworks.Mono)] - // https://github.com/AzureAD/azure-activedirectory-identitymodel-extensions-for-dotnet/issues/179 + [Fact] public async Task BearerTokenValidation() { + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(new string('a', 128))); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var claims = new[] + { + new Claim(ClaimTypes.NameIdentifier, "Bob") + }; + + var token = new JwtSecurityToken( + issuer: "issuer.contoso.com", + audience: "audience.contoso.com", + claims: claims, + expires: DateTime.Now.AddMinutes(30), + signingCredentials: creds); + + var tokenText = new JwtSecurityTokenHandler().WriteToken(token); + var server = CreateServer(o => { - o.Authority = "https://login.windows.net/tushartest.onmicrosoft.com"; - o.Audience = "https://TusharTest.onmicrosoft.com/TodoListService-ManualJwt"; - o.TokenValidationParameters.ValidateLifetime = false; + o.TokenValidationParameters = new TokenValidationParameters() + { + ValidIssuer = "issuer.contoso.com", + ValidAudience = "audience.contoso.com", + IssuerSigningKey = key, + }; }); - var newBearerToken = "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImtyaU1QZG1Cdng2OHNrVDgtbVBBQjNCc2VlQSJ9.eyJhdWQiOiJodHRwczovL1R1c2hhclRlc3Qub25taWNyb3NvZnQuY29tL1RvZG9MaXN0U2VydmljZS1NYW51YWxKd3QiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC9hZmJlY2UwMy1hZWFhLTRmM2YtODVlNy1jZTA4ZGQyMGNlNTAvIiwiaWF0IjoxNDE4MzMwNjE0LCJuYmYiOjE0MTgzMzA2MTQsImV4cCI6MTQxODMzNDUxNCwidmVyIjoiMS4wIiwidGlkIjoiYWZiZWNlMDMtYWVhYS00ZjNmLTg1ZTctY2UwOGRkMjBjZTUwIiwiYW1yIjpbInB3ZCJdLCJvaWQiOiI1Mzk3OTdjMi00MDE5LTQ2NTktOWRiNS03MmM0Yzc3NzhhMzMiLCJ1cG4iOiJWaWN0b3JAVHVzaGFyVGVzdC5vbm1pY3Jvc29mdC5jb20iLCJ1bmlxdWVfbmFtZSI6IlZpY3RvckBUdXNoYXJUZXN0Lm9ubWljcm9zb2Z0LmNvbSIsInN1YiI6IkQyMm9aMW9VTzEzTUFiQXZrdnFyd2REVE80WXZJdjlzMV9GNWlVOVUwYnciLCJmYW1pbHlfbmFtZSI6Ikd1cHRhIiwiZ2l2ZW5fbmFtZSI6IlZpY3RvciIsImFwcGlkIjoiNjEzYjVhZjgtZjJjMy00MWI2LWExZGMtNDE2Yzk3ODAzMGI3IiwiYXBwaWRhY3IiOiIwIiwic2NwIjoidXNlcl9pbXBlcnNvbmF0aW9uIiwiYWNyIjoiMSJ9.N_Kw1EhoVGrHbE6hOcm7ERdZ7paBQiNdObvp2c6T6n5CE8p0fZqmUd-ya_EqwElcD6SiKSiP7gj0gpNUnOJcBl_H2X8GseaeeMxBrZdsnDL8qecc6_ygHruwlPltnLTdka67s1Ow4fDSHaqhVTEk6lzGmNEcbNAyb0CxQxU6o7Fh0yHRiWoLsT8yqYk8nKzsHXfZBNby4aRo3_hXaa4i0SZLYfDGGYPdttG4vT_u54QGGd4Wzbonv2gjDlllOVGOwoJS6kfl1h8mk0qxdiIaT_ChbDWgkWvTB7bTvBE-EgHgV0XmAo0WtJeSxgjsG3KhhEPsONmqrSjhIUV4IVnF2w"; + var newBearerToken = "Bearer " + tokenText; var response = await SendAsync(server, "http://example.com/oauth", newBearerToken); Assert.Equal(HttpStatusCode.OK, response.Response.StatusCode); } + [Fact] + public async Task SaveBearerToken() + { + var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(new string('a', 128))); + var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256); + + var claims = new[] + { + new Claim(ClaimTypes.NameIdentifier, "Bob") + }; + + var token = new JwtSecurityToken( + issuer: "issuer.contoso.com", + audience: "audience.contoso.com", + claims: claims, + expires: DateTime.Now.AddMinutes(30), + signingCredentials: creds); + + var tokenText = new JwtSecurityTokenHandler().WriteToken(token); + + var server = CreateServer(o => + { + o.SaveToken = true; + o.TokenValidationParameters = new TokenValidationParameters() + { + ValidIssuer = "issuer.contoso.com", + ValidAudience = "audience.contoso.com", + IssuerSigningKey = key, + }; + }); + + var newBearerToken = "Bearer " + tokenText; + var response = await SendAsync(server, "http://example.com/token", newBearerToken); + Assert.Equal(HttpStatusCode.OK, response.Response.StatusCode); + Assert.Equal(tokenText, await response.Response.Content.ReadAsStringAsync()); + } + [Fact] public async Task SignInThrows() { @@ -728,6 +785,11 @@ private static TestServer CreateServer(Action options = null, await context.Response.WriteAsync(identifier.Value); } + else if (context.Request.Path == new PathString("/token")) + { + var token = await context.GetTokenAsync("access_token"); + await context.Response.WriteAsync(token); + } else if (context.Request.Path == new PathString("/unauthorized")) { // Simulate Authorization failure diff --git a/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj b/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj index 1529dc803..c7a380868 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj +++ b/test/Microsoft.AspNetCore.Authentication.Test/Microsoft.AspNetCore.Authentication.Test.csproj @@ -1,12 +1,34 @@  - - - netcoreapp2.0;net461 - netcoreapp2.0 + netcoreapp3.0 + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + PreserveNewest + + + + + + + + @@ -14,17 +36,12 @@ - - + - - - - - + diff --git a/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs index 2e249a833..26a5484c8 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/MicrosoftAccountTests.cs @@ -1,14 +1,5 @@ // Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. -using System; -using System.Collections.Generic; -using System.Linq; -using System.Net; -using System.Net.Http; -using System.Security.Claims; -using System.Text; -using System.Text.Encodings.Web; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.MicrosoftAccount; using Microsoft.AspNetCore.Authentication.OAuth; using Microsoft.AspNetCore.Builder; @@ -16,28 +7,42 @@ using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging.Abstractions; -using Microsoft.Extensions.Options; using Newtonsoft.Json; +using System; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Claims; +using System.Text; +using System.Text.Encodings.Web; +using System.Threading.Tasks; using Xunit; namespace Microsoft.AspNetCore.Authentication.Tests.MicrosoftAccount { - public class MicrosoftAccountTests + public class MicrosoftAccountTests : RemoteAuthenticationTests { - [Fact] - public async Task VerifySchemeDefaults() + protected override string DefaultScheme => MicrosoftAccountDefaults.AuthenticationScheme; + protected override Type HandlerType => typeof(MicrosoftAccountHandler); + protected override bool SupportsSignIn { get => false; } + protected override bool SupportsSignOut { get => false; } + + protected override void RegisterAuth(AuthenticationBuilder services, Action configure) + { + services.AddMicrosoftAccount(o => + { + ConfigureDefaults(o); + configure.Invoke(o); + }); + } + + protected override void ConfigureDefaults(MicrosoftAccountOptions o) { - var services = new ServiceCollection(); - services.AddAuthentication().AddMicrosoftAccount(); - var sp = services.BuildServiceProvider(); - var schemeProvider = sp.GetRequiredService(); - var scheme = await schemeProvider.GetSchemeAsync(MicrosoftAccountDefaults.AuthenticationScheme); - Assert.NotNull(scheme); - Assert.Equal("MicrosoftAccountHandler", scheme.HandlerType.Name); - Assert.Equal(MicrosoftAccountDefaults.AuthenticationScheme, scheme.DisplayName); + o.ClientId = "whatever"; + o.ClientSecret = "whatever"; + o.SignInScheme = "auth1"; } [Fact] @@ -117,6 +122,57 @@ public async Task ChallengeWillTriggerRedirection() Assert.Contains("state=", location); } + [Fact] + public async Task ChallengeWillIncludeScopeAsConfigured() + { + var server = CreateServer(o => + { + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.Scope.Clear(); + o.Scope.Add("foo"); + o.Scope.Add("bar"); + }); + var transaction = await server.SendAsync("http://example.com/challenge"); + var res = transaction.Response; + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=foo%20bar", res.Headers.Location.Query); + } + + [Fact] + public async Task ChallengeWillIncludeScopeAsOverwritten() + { + var server = CreateServer(o => + { + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.Scope.Clear(); + o.Scope.Add("foo"); + o.Scope.Add("bar"); + }); + var transaction = await server.SendAsync("http://example.com/challengeWithOtherScope"); + var res = transaction.Response; + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=baz%20qux", res.Headers.Location.Query); + } + + [Fact] + public async Task ChallengeWillIncludeScopeAsOverwrittenWithBaseAuthenticationProperties() + { + var server = CreateServer(o => + { + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.Scope.Clear(); + o.Scope.Add("foo"); + o.Scope.Add("bar"); + }); + var transaction = await server.SendAsync("http://example.com/challengeWithOtherScopeWithBaseAuthenticationProperties"); + var res = transaction.Response; + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=baz%20qux", res.Headers.Location.Query); + } + [Fact] public async Task AuthenticatedEventCanGetRefreshToken() { @@ -200,6 +256,18 @@ private static TestServer CreateServer(Action configure { await context.ChallengeAsync("Microsoft"); } + else if (req.Path == new PathString("/challengeWithOtherScope")) + { + var properties = new OAuthChallengeProperties(); + properties.SetScope("baz", "qux"); + await context.ChallengeAsync("Microsoft", properties); + } + else if (req.Path == new PathString("/challengeWithOtherScopeWithBaseAuthenticationProperties")) + { + var properties = new AuthenticationProperties(); + properties.SetParameter(OAuthChallengeProperties.ScopeKey, new string[] { "baz", "qux" }); + await context.ChallengeAsync("Microsoft", properties); + } else if (req.Path == new PathString("/me")) { res.Describe(context.User); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OAuthChallengePropertiesTest.cs b/test/Microsoft.AspNetCore.Authentication.Test/OAuthChallengePropertiesTest.cs new file mode 100644 index 000000000..c359bb0e8 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/OAuthChallengePropertiesTest.cs @@ -0,0 +1,149 @@ +using System; +using Microsoft.AspNetCore.Authentication.Google; +using Microsoft.AspNetCore.Authentication.OAuth; +using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication.Test +{ + public class OAuthChallengePropertiesTest + { + [Fact] + public void ScopeProperty() + { + var properties = new OAuthChallengeProperties + { + Scope = new string[] { "foo", "bar" } + }; + Assert.Equal(new string[] { "foo", "bar" }, properties.Scope); + Assert.Equal(new string[] { "foo", "bar" }, properties.Parameters["scope"]); + } + + [Fact] + public void ScopeProperty_NullValue() + { + var properties = new OAuthChallengeProperties(); + properties.Parameters["scope"] = new string[] { "foo", "bar" }; + Assert.Equal(new string[] { "foo", "bar" }, properties.Scope); + + properties.Scope = null; + Assert.Null(properties.Scope); + } + + [Fact] + public void SetScope() + { + var properties = new OAuthChallengeProperties(); + properties.SetScope("foo", "bar"); + Assert.Equal(new string[] { "foo", "bar" }, properties.Scope); + Assert.Equal(new string[] { "foo", "bar" }, properties.Parameters["scope"]); + } + + [Fact] + public void OidcMaxAge() + { + var properties = new OpenIdConnectChallengeProperties() + { + MaxAge = TimeSpan.FromSeconds(200) + }; + Assert.Equal(TimeSpan.FromSeconds(200), properties.MaxAge); + } + + [Fact] + public void OidcMaxAge_NullValue() + { + var properties = new OpenIdConnectChallengeProperties(); + properties.Parameters["max_age"] = TimeSpan.FromSeconds(500); + Assert.Equal(TimeSpan.FromSeconds(500), properties.MaxAge); + + properties.MaxAge = null; + Assert.Null(properties.MaxAge); + } + + [Fact] + public void OidcPrompt() + { + var properties = new OpenIdConnectChallengeProperties() + { + Prompt = "login" + }; + Assert.Equal("login", properties.Prompt); + Assert.Equal("login", properties.Parameters["prompt"]); + } + + [Fact] + public void OidcPrompt_NullValue() + { + var properties = new OpenIdConnectChallengeProperties(); + properties.Parameters["prompt"] = "consent"; + Assert.Equal("consent", properties.Prompt); + + properties.Prompt = null; + Assert.Null(properties.Prompt); + } + + [Fact] + public void GoogleProperties() + { + var properties = new GoogleChallengeProperties() + { + AccessType = "offline", + ApprovalPrompt = "force", + LoginHint = "test@example.com", + Prompt = "login", + }; + Assert.Equal("offline", properties.AccessType); + Assert.Equal("offline", properties.Parameters["access_type"]); + Assert.Equal("force", properties.ApprovalPrompt); + Assert.Equal("force", properties.Parameters["approval_prompt"]); + Assert.Equal("test@example.com", properties.LoginHint); + Assert.Equal("test@example.com", properties.Parameters["login_hint"]); + Assert.Equal("login", properties.Prompt); + Assert.Equal("login", properties.Parameters["prompt"]); + } + + [Fact] + public void GoogleProperties_NullValues() + { + var properties = new GoogleChallengeProperties(); + properties.Parameters["access_type"] = "offline"; + properties.Parameters["approval_prompt"] = "force"; + properties.Parameters["login_hint"] = "test@example.com"; + properties.Parameters["prompt"] = "login"; + Assert.Equal("offline", properties.AccessType); + Assert.Equal("force", properties.ApprovalPrompt); + Assert.Equal("test@example.com", properties.LoginHint); + Assert.Equal("login", properties.Prompt); + + properties.AccessType = null; + Assert.Null(properties.AccessType); + + properties.ApprovalPrompt = null; + Assert.Null(properties.ApprovalPrompt); + + properties.LoginHint = null; + Assert.Null(properties.LoginHint); + + properties.Prompt = null; + Assert.Null(properties.Prompt); + } + + [Fact] + public void GoogleIncludeGrantedScopes() + { + var properties = new GoogleChallengeProperties() + { + IncludeGrantedScopes = true + }; + Assert.True(properties.IncludeGrantedScopes); + Assert.Equal(true, properties.Parameters["include_granted_scopes"]); + + properties.IncludeGrantedScopes = false; + Assert.False(properties.IncludeGrantedScopes); + Assert.Equal(false, properties.Parameters["include_granted_scopes"]); + + properties.IncludeGrantedScopes = null; + Assert.Null(properties.IncludeGrantedScopes); + } + } +} diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OAuthTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OAuthTests.cs index aeb313daa..838798cea 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OAuthTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OAuthTests.cs @@ -1,39 +1,40 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. -using System; -using System.Net; -using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using System; +using System.Collections.Generic; +using System.Net; +using System.Threading.Tasks; using Xunit; namespace Microsoft.AspNetCore.Authentication.OAuth { - public class OAuthTests + public class OAuthTests : RemoteAuthenticationTests { - [Fact] - public async Task VerifySchemeDefaults() + protected override string DefaultScheme => OAuthDefaults.DisplayName; + protected override Type HandlerType => typeof(OAuthHandler); + protected override bool SupportsSignIn { get => false; } + protected override bool SupportsSignOut { get => false; } + + protected override void RegisterAuth(AuthenticationBuilder services, Action configure) { - var services = new ServiceCollection(); - services.AddAuthentication().AddOAuth("oauth", o => { }); - var sp = services.BuildServiceProvider(); - var schemeProvider = sp.GetRequiredService(); - var scheme = await schemeProvider.GetSchemeAsync("oauth"); - Assert.NotNull(scheme); - Assert.Equal("OAuthHandler`1", scheme.HandlerType.Name); - Assert.Equal(OAuthDefaults.DisplayName, scheme.DisplayName); + services.AddOAuth(DefaultScheme, o => + { + ConfigureDefaults(o); + configure.Invoke(o); + }); } [Fact] public async Task ThrowsIfClientIdMissing() { var server = CreateServer( - app => { }, services => services.AddAuthentication().AddOAuth("weeblie", o => { o.SignInScheme = "whatever"; @@ -41,22 +42,14 @@ public async Task ThrowsIfClientIdMissing() o.ClientSecret = "whatever"; o.TokenEndpoint = "/"; o.AuthorizationEndpoint = "/"; - }), - context => - { - // REVIEW: Gross. - Assert.Throws("ClientId", () => context.ChallengeAsync("weeblie").GetAwaiter().GetResult()); - return true; - }); - var transaction = await server.SendAsync("http://example.com/challenge"); - Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); + })); + await Assert.ThrowsAsync("ClientId", () => server.SendAsync("http://example.com/")); } [Fact] public async Task ThrowsIfClientSecretMissing() { var server = CreateServer( - app => { }, services => services.AddAuthentication().AddOAuth("weeblie", o => { o.SignInScheme = "whatever"; @@ -64,22 +57,14 @@ public async Task ThrowsIfClientSecretMissing() o.CallbackPath = "/"; o.TokenEndpoint = "/"; o.AuthorizationEndpoint = "/"; - }), - context => - { - // REVIEW: Gross. - Assert.Throws("ClientSecret", () => context.ChallengeAsync("weeblie").GetAwaiter().GetResult()); - return true; - }); - var transaction = await server.SendAsync("http://example.com/challenge"); - Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); + })); + await Assert.ThrowsAsync("ClientSecret", () => server.SendAsync("http://example.com/")); } [Fact] public async Task ThrowsIfCallbackPathMissing() { var server = CreateServer( - app => { }, services => services.AddAuthentication().AddOAuth("weeblie", o => { o.ClientId = "Whatever;"; @@ -87,22 +72,14 @@ public async Task ThrowsIfCallbackPathMissing() o.TokenEndpoint = "/"; o.AuthorizationEndpoint = "/"; o.SignInScheme = "eh"; - }), - context => - { - // REVIEW: Gross. - Assert.Throws("CallbackPath", () => context.ChallengeAsync("weeblie").GetAwaiter().GetResult()); - return true; - }); - var transaction = await server.SendAsync("http://example.com/challenge"); - Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); + })); + await Assert.ThrowsAsync("CallbackPath", () => server.SendAsync("http://example.com/")); } [Fact] public async Task ThrowsIfTokenEndpointMissing() { var server = CreateServer( - app => { }, services => services.AddAuthentication().AddOAuth("weeblie", o => { o.ClientId = "Whatever;"; @@ -110,22 +87,14 @@ public async Task ThrowsIfTokenEndpointMissing() o.CallbackPath = "/"; o.AuthorizationEndpoint = "/"; o.SignInScheme = "eh"; - }), - context => - { - // REVIEW: Gross. - Assert.Throws("TokenEndpoint", () => context.ChallengeAsync("weeblie").GetAwaiter().GetResult()); - return true; - }); - var transaction = await server.SendAsync("http://example.com/challenge"); - Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); + })); + await Assert.ThrowsAsync("TokenEndpoint", () => server.SendAsync("http://example.com/")); } [Fact] public async Task ThrowsIfAuthorizationEndpointMissing() { var server = CreateServer( - app => { }, services => services.AddAuthentication().AddOAuth("weeblie", o => { o.ClientId = "Whatever;"; @@ -133,36 +102,23 @@ public async Task ThrowsIfAuthorizationEndpointMissing() o.CallbackPath = "/"; o.TokenEndpoint = "/"; o.SignInScheme = "eh"; - }), - context => - { - // REVIEW: Gross. - Assert.Throws("AuthorizationEndpoint", () => context.ChallengeAsync("weeblie").GetAwaiter().GetResult()); - return true; - }); - var transaction = await server.SendAsync("http://example.com/challenge"); - Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); + })); + await Assert.ThrowsAsync("AuthorizationEndpoint", () => server.SendAsync("http://example.com/")); } [Fact] public async Task RedirectToIdentityProvider_SetsCorrelationIdCookiePath_ToCallBackPath() { var server = CreateServer( - app => { }, s => s.AddAuthentication().AddOAuth( "Weblie", opt => { - opt.ClientId = "Test Id"; - opt.ClientSecret = "secret"; - opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; - opt.AuthorizationEndpoint = "https://example.com/provider/login"; - opt.TokenEndpoint = "https://example.com/provider/token"; - opt.CallbackPath = "/oauth-callback"; + ConfigureDefaults(opt); }), - ctx => + async ctx => { - ctx.ChallengeAsync("Weblie").ConfigureAwait(false).GetAwaiter().GetResult(); + await ctx.ChallengeAsync("Weblie"); return true; }); @@ -180,22 +136,16 @@ public async Task RedirectToIdentityProvider_SetsCorrelationIdCookiePath_ToCallB public async Task RedirectToAuthorizeEndpoint_CorrelationIdCookieOptions_CanBeOverriden() { var server = CreateServer( - app => { }, s => s.AddAuthentication().AddOAuth( "Weblie", opt => { - opt.ClientId = "Test Id"; - opt.ClientSecret = "secret"; - opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; - opt.AuthorizationEndpoint = "https://example.com/provider/login"; - opt.TokenEndpoint = "https://example.com/provider/token"; - opt.CallbackPath = "/oauth-callback"; + ConfigureDefaults(opt); opt.CorrelationCookie.Path = "/"; }), - ctx => + async ctx => { - ctx.ChallengeAsync("Weblie").ConfigureAwait(false).GetAwaiter().GetResult(); + await ctx.ChallengeAsync("Weblie"); return true; }); @@ -209,15 +159,237 @@ public async Task RedirectToAuthorizeEndpoint_CorrelationIdCookieOptions_CanBeOv Assert.Contains("path=/", correlation); } - private static TestServer CreateServer(Action configure, Action configureServices, Func handler) + [Fact] + public async Task RedirectToAuthorizeEndpoint_HasScopeAsConfigured() + { + var server = CreateServer( + s => s.AddAuthentication().AddOAuth( + "Weblie", + opt => + { + ConfigureDefaults(opt); + opt.Scope.Clear(); + opt.Scope.Add("foo"); + opt.Scope.Add("bar"); + }), + async ctx => + { + await ctx.ChallengeAsync("Weblie"); + return true; + }); + + var transaction = await server.SendAsync("https://www.example.com/challenge"); + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=foo%20bar", res.Headers.Location.Query); + } + + [Fact] + public async Task RedirectToAuthorizeEndpoint_HasScopeAsOverwritten() + { + var server = CreateServer( + s => s.AddAuthentication().AddOAuth( + "Weblie", + opt => + { + ConfigureDefaults(opt); + opt.Scope.Clear(); + opt.Scope.Add("foo"); + opt.Scope.Add("bar"); + }), + async ctx => + { + var properties = new OAuthChallengeProperties(); + properties.SetScope("baz", "qux"); + await ctx.ChallengeAsync("Weblie", properties); + return true; + }); + + var transaction = await server.SendAsync("https://www.example.com/challenge"); + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=baz%20qux", res.Headers.Location.Query); + } + + [Fact] + public async Task RedirectToAuthorizeEndpoint_HasScopeAsOverwrittenWithBaseAuthenticationProperties() + { + var server = CreateServer( + s => s.AddAuthentication().AddOAuth( + "Weblie", + opt => + { + ConfigureDefaults(opt); + opt.Scope.Clear(); + opt.Scope.Add("foo"); + opt.Scope.Add("bar"); + }), + async ctx => + { + var properties = new AuthenticationProperties(); + properties.SetParameter(OAuthChallengeProperties.ScopeKey, new string[] { "baz", "qux" }); + await ctx.ChallengeAsync("Weblie", properties); + return true; + }); + + var transaction = await server.SendAsync("https://www.example.com/challenge"); + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + Assert.Contains("scope=baz%20qux", res.Headers.Location.Query); + } + + protected override void ConfigureDefaults(OAuthOptions o) + { + o.ClientId = "Test Id"; + o.ClientSecret = "secret"; + o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + o.AuthorizationEndpoint = "https://example.com/provider/login"; + o.TokenEndpoint = "https://example.com/provider/token"; + o.CallbackPath = "/oauth-callback"; + } + + [Fact] + public async Task HandleRequestAsync_RedirectsToAccessDeniedPathWhenExplicitlySet() + { + var server = CreateServer( + s => s.AddAuthentication().AddOAuth( + "Weblie", + opt => + { + opt.ClientId = "Test Id"; + opt.ClientSecret = "secret"; + opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + opt.AuthorizationEndpoint = "https://example.com/provider/login"; + opt.TokenEndpoint = "https://example.com/provider/token"; + opt.CallbackPath = "/oauth-callback"; + opt.AccessDeniedPath = "/access-denied"; + opt.StateDataFormat = new TestStateDataFormat(); + opt.Events.OnRemoteFailure = context => throw new InvalidOperationException("This event should not be called."); + })); + + var transaction = await server.SendAsync("https://www.example.com/oauth-callback?error=access_denied&state=protected_state", + ".AspNetCore.Correlation.Weblie.correlationId=N"); + + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + Assert.Equal("/access-denied?ReturnUrl=http%3A%2F%2Ftesthost%2Fredirect", transaction.Response.Headers.Location.ToString()); + } + + [Fact] + public async Task HandleRequestAsync_InvokesAccessDeniedEvent() + { + var server = CreateServer( + s => s.AddAuthentication().AddOAuth( + "Weblie", + opt => + { + opt.ClientId = "Test Id"; + opt.ClientSecret = "secret"; + opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + opt.AuthorizationEndpoint = "https://example.com/provider/login"; + opt.TokenEndpoint = "https://example.com/provider/token"; + opt.CallbackPath = "/oauth-callback"; + opt.StateDataFormat = new TestStateDataFormat(); + opt.Events = new OAuthEvents() + { + OnAccessDenied = context => + { + Assert.Equal("testvalue", context.Properties.Items["testkey"]); + context.Response.StatusCode = StatusCodes.Status406NotAcceptable; + context.HandleResponse(); + return Task.CompletedTask; + } + }; + })); + + var transaction = await server.SendAsync("https://www.example.com/oauth-callback?error=access_denied&state=protected_state", + ".AspNetCore.Correlation.Weblie.correlationId=N"); + + Assert.Equal(HttpStatusCode.NotAcceptable, transaction.Response.StatusCode); + Assert.Null(transaction.Response.Headers.Location); + } + + [Fact] + public async Task HandleRequestAsync_InvokesRemoteFailureEventWhenAccessDeniedPathIsNotExplicitlySet() + { + var server = CreateServer( + s => s.AddAuthentication().AddOAuth( + "Weblie", + opt => + { + opt.ClientId = "Test Id"; + opt.ClientSecret = "secret"; + opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + opt.AuthorizationEndpoint = "https://example.com/provider/login"; + opt.TokenEndpoint = "https://example.com/provider/token"; + opt.CallbackPath = "/oauth-callback"; + opt.StateDataFormat = new TestStateDataFormat(); + opt.Events = new OAuthEvents() + { + OnRemoteFailure = context => + { + Assert.Equal("Access was denied by the resource owner or by the remote server.", context.Failure.Message); + Assert.Equal("testvalue", context.Properties.Items["testkey"]); + context.Response.StatusCode = StatusCodes.Status406NotAcceptable; + context.HandleResponse(); + return Task.CompletedTask; + } + }; + })); + + var transaction = await server.SendAsync("https://www.example.com/oauth-callback?error=access_denied&state=protected_state", + ".AspNetCore.Correlation.Weblie.correlationId=N"); + + Assert.Equal(HttpStatusCode.NotAcceptable, transaction.Response.StatusCode); + Assert.Null(transaction.Response.Headers.Location); + } + + [Fact] + public async Task RemoteAuthenticationFailed_OAuthError_IncludesProperties() + { + var server = CreateServer( + s => s.AddAuthentication().AddOAuth( + "Weblie", + opt => + { + opt.ClientId = "Test Id"; + opt.ClientSecret = "secret"; + opt.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + opt.AuthorizationEndpoint = "https://example.com/provider/login"; + opt.TokenEndpoint = "https://example.com/provider/token"; + opt.CallbackPath = "/oauth-callback"; + opt.StateDataFormat = new TestStateDataFormat(); + opt.Events = new OAuthEvents() + { + OnRemoteFailure = context => + { + Assert.Contains("custom_error", context.Failure.Message); + Assert.Equal("testvalue", context.Properties.Items["testkey"]); + context.Response.StatusCode = StatusCodes.Status406NotAcceptable; + context.HandleResponse(); + return Task.CompletedTask; + } + }; + })); + + var transaction = await server.SendAsync("https://www.example.com/oauth-callback?error=custom_error&state=protected_state", + ".AspNetCore.Correlation.Weblie.correlationId=N"); + + Assert.Equal(HttpStatusCode.NotAcceptable, transaction.Response.StatusCode); + Assert.Null(transaction.Response.Headers.Location); + } + + private static TestServer CreateServer(Action configureServices, Func> handler = null) { var builder = new WebHostBuilder() .Configure(app => { - configure?.Invoke(app); + app.UseAuthentication(); app.Use(async (context, next) => { - if (handler == null || !handler(context)) + if (handler == null || ! await handler(context)) { await next(); } @@ -226,5 +398,37 @@ private static TestServer CreateServer(Action configure, Ac .ConfigureServices(configureServices); return new TestServer(builder); } + + private class TestStateDataFormat : ISecureDataFormat + { + private AuthenticationProperties Data { get; set; } + + public string Protect(AuthenticationProperties data) + { + return "protected_state"; + } + + public string Protect(AuthenticationProperties data, string purpose) + { + throw new NotImplementedException(); + } + + public AuthenticationProperties Unprotect(string protectedText) + { + Assert.Equal("protected_state", protectedText); + var properties = new AuthenticationProperties(new Dictionary() + { + { ".xsrf", "correlationId" }, + { "testkey", "testvalue" } + }); + properties.RedirectUri = "http://testhost/redirect"; + return properties; + } + + public AuthenticationProperties Unprotect(string protectedText, string purpose) + { + throw new NotImplementedException(); + } + } } } diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectChallengeTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectChallengeTests.cs index fb08ae278..cbafc4622 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectChallengeTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectChallengeTests.cs @@ -51,14 +51,14 @@ public async Task ChallengeRedirectIsIssuedCorrectly() [Fact] public async Task AuthorizationRequestDoesNotIncludeTelemetryParametersWhenDisabled() { - var setting = new TestSettings(opt => + var settings = new TestSettings(opt => { opt.ClientId = "Test Id"; opt.Authority = TestServerBuilder.DefaultAuthority; opt.DisableTelemetry = true; }); - var server = setting.CreateTestServer(); + var server = settings.CreateTestServer(); var transaction = await server.SendAsync(ChallengeEndpoint); var res = transaction.Response; @@ -409,5 +409,208 @@ public async Task Challenge_WithEmptyConfig_Fails() var exception = await Assert.ThrowsAsync(() => server.SendAsync(ChallengeEndpoint)); Assert.Equal("Cannot redirect to the authorization endpoint, the configuration may be missing or invalid.", exception.Message); } + + [Fact] + public async Task Challenge_WithDefaultMaxAge_HasExpectedMaxAgeParam() + { + var settings = new TestSettings( + opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + }); + + var server = settings.CreateTestServer(); + var transaction = await server.SendAsync(ChallengeEndpoint); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect( + res.Headers.Location, + OpenIdConnectParameterNames.MaxAge); + } + + [Fact] + public async Task Challenge_WithSpecificMaxAge_HasExpectedMaxAgeParam() + { + var settings = new TestSettings( + opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.MaxAge = TimeSpan.FromMinutes(20); + }); + + var server = settings.CreateTestServer(); + var transaction = await server.SendAsync(ChallengeEndpoint); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect( + res.Headers.Location, + OpenIdConnectParameterNames.MaxAge); + } + + [Fact] + public async Task Challenge_HasExpectedPromptParam() + { + var settings = new TestSettings(opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.Prompt = "consent"; + }); + + var server = settings.CreateTestServer(); + var transaction = await server.SendAsync(ChallengeEndpoint); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect(res.Headers.Location, OpenIdConnectParameterNames.Prompt); + Assert.Contains("prompt=consent", res.Headers.Location.Query); + } + + [Fact] + public async Task Challenge_HasOverwrittenPromptParam() + { + var settings = new TestSettings(opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.Prompt = "consent"; + }); + var properties = new OpenIdConnectChallengeProperties() + { + Prompt = "login", + }; + + var server = settings.CreateTestServer(properties); + var transaction = await server.SendAsync(TestServerBuilder.TestHost + TestServerBuilder.ChallengeWithProperties); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect(res.Headers.Location); + Assert.Contains("prompt=login", res.Headers.Location.Query); + } + + [Fact] + public async Task Challenge_HasOverwrittenPromptParamFromBaseAuthenticationProperties() + { + var settings = new TestSettings(opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.Prompt = "consent"; + }); + var properties = new AuthenticationProperties(); + properties.SetParameter(OpenIdConnectChallengeProperties.PromptKey, "login"); + + var server = settings.CreateTestServer(properties); + var transaction = await server.SendAsync(TestServerBuilder.TestHost + TestServerBuilder.ChallengeWithProperties); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect(res.Headers.Location); + Assert.Contains("prompt=login", res.Headers.Location.Query); + } + + [Fact] + public async Task Challenge_HasOverwrittenScopeParam() + { + var settings = new TestSettings(opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.Scope.Clear(); + opt.Scope.Add("foo"); + opt.Scope.Add("bar"); + }); + var properties = new OpenIdConnectChallengeProperties(); + properties.SetScope("baz", "qux"); + + var server = settings.CreateTestServer(properties); + var transaction = await server.SendAsync(TestServerBuilder.TestHost + TestServerBuilder.ChallengeWithProperties); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect(res.Headers.Location); + Assert.Contains("scope=baz%20qux", res.Headers.Location.Query); + } + + [Fact] + public async Task Challenge_HasOverwrittenScopeParamFromBaseAuthenticationProperties() + { + var settings = new TestSettings(opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.Scope.Clear(); + opt.Scope.Add("foo"); + opt.Scope.Add("bar"); + }); + var properties = new AuthenticationProperties(); + properties.SetParameter(OpenIdConnectChallengeProperties.ScopeKey, new string[] { "baz", "qux" }); + + var server = settings.CreateTestServer(properties); + var transaction = await server.SendAsync(TestServerBuilder.TestHost + TestServerBuilder.ChallengeWithProperties); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect(res.Headers.Location); + Assert.Contains("scope=baz%20qux", res.Headers.Location.Query); + } + + [Fact] + public async Task Challenge_HasOverwrittenMaxAgeParam() + { + var settings = new TestSettings(opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.MaxAge = TimeSpan.FromSeconds(500); + }); + var properties = new OpenIdConnectChallengeProperties() + { + MaxAge = TimeSpan.FromSeconds(1234), + }; + + var server = settings.CreateTestServer(properties); + var transaction = await server.SendAsync(TestServerBuilder.TestHost + TestServerBuilder.ChallengeWithProperties); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect(res.Headers.Location); + Assert.Contains("max_age=1234", res.Headers.Location.Query); + } + + [Fact] + public async Task Challenge_HasOverwrittenMaxAgeParaFromBaseAuthenticationPropertiesm() + { + var settings = new TestSettings(opt => + { + opt.ClientId = "Test Id"; + opt.Authority = TestServerBuilder.DefaultAuthority; + opt.MaxAge = TimeSpan.FromSeconds(500); + }); + var properties = new AuthenticationProperties(); + properties.SetParameter(OpenIdConnectChallengeProperties.MaxAgeKey, TimeSpan.FromSeconds(1234)); + + var server = settings.CreateTestServer(properties); + var transaction = await server.SendAsync(TestServerBuilder.TestHost + TestServerBuilder.ChallengeWithProperties); + + var res = transaction.Response; + + Assert.Equal(HttpStatusCode.Redirect, res.StatusCode); + settings.ValidateChallengeRedirect(res.Headers.Location); + Assert.Contains("max_age=1234", res.Headers.Location.Query); + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectConfigurationTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectConfigurationTests.cs index 74f00c8f9..ed368c1ef 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectConfigurationTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectConfigurationTests.cs @@ -3,12 +3,14 @@ using System; using System.Net; +using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication.OpenIdConnect; +using Microsoft.AspNetCore.Authentication.Tests; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; -using Microsoft.AspNetCore.Testing.xunit; using Microsoft.Extensions.DependencyInjection; using Xunit; @@ -16,6 +18,421 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect { public class OpenIdConnectConfigurationTests { + private void ConfigureDefaults(OpenIdConnectOptions o) + { + o.Authority = TestServerBuilder.DefaultAuthority; + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + o.SignInScheme = "auth1"; + } + + [Fact] + public async Task CanForwardDefault() + { + var services = new ServiceCollection().AddLogging(); + + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + }); + + var forwardDefault = new TestHandler(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + + await context.AuthenticateAsync(); + Assert.Equal(1, forwardDefault.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, forwardDefault.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, forwardDefault.ChallengeCount); + + await context.SignOutAsync(); + Assert.Equal(1, forwardDefault.SignOutCount); + + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + + [Fact] + public async Task ForwardSignInThrows() + { + var services = new ServiceCollection().AddLogging(); + + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardSignOut = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + + [Fact] + public async Task ForwardSignOutWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardSignOut = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.SignOutAsync(); + Assert.Equal(1, specific.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + + [Fact] + public async Task ForwardForbidWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardForbid = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.ForbidAsync(); + Assert.Equal(0, specific.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(1, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + + [Fact] + public async Task ForwardAuthenticateWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardAuthenticate = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(0, specific.SignOutCount); + Assert.Equal(1, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + + [Fact] + public async Task ForwardChallengeWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("specific", "specific"); + o.AddScheme("auth1", "auth1"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardChallenge = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.ChallengeAsync(); + Assert.Equal(0, specific.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(1, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + + [Fact] + public async Task ForwardSelectorWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("selector", "selector"); + o.AddScheme("specific", "specific"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardDefaultSelector = _ => "selector"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + var selector = new TestHandler3(); + services.AddSingleton(selector); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(1, selector.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, selector.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, selector.ChallengeCount); + + await context.SignOutAsync(); + Assert.Equal(1, selector.SignOutCount); + + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + Assert.Equal(0, specific.SignOutCount); + } + + [Fact] + public async Task NullForwardSelectorUsesDefault() + { + var services = new ServiceCollection().AddLogging(); + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("selector", "selector"); + o.AddScheme("specific", "specific"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardDefaultSelector = _ => null; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + var selector = new TestHandler3(); + services.AddSingleton(selector); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(1, forwardDefault.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, forwardDefault.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, forwardDefault.ChallengeCount); + + await context.SignOutAsync(); + Assert.Equal(1, forwardDefault.SignOutCount); + + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + + Assert.Equal(0, selector.AuthenticateCount); + Assert.Equal(0, selector.ForbidCount); + Assert.Equal(0, selector.ChallengeCount); + Assert.Equal(0, selector.SignInCount); + Assert.Equal(0, selector.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + Assert.Equal(0, specific.SignOutCount); + } + + [Fact] + public async Task SpecificForwardWinsOverSelectorAndDefault() + { + var services = new ServiceCollection().AddLogging(); + services.AddAuthentication(o => + { + o.DefaultScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("selector", "selector"); + o.AddScheme("specific", "specific"); + }) + .AddOpenIdConnect(o => + { + ConfigureDefaults(o); + o.ForwardDefault = "auth1"; + o.ForwardDefaultSelector = _ => "selector"; + o.ForwardAuthenticate = "specific"; + o.ForwardChallenge = "specific"; + o.ForwardSignIn = "specific"; + o.ForwardSignOut = "specific"; + o.ForwardForbid = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + var selector = new TestHandler3(); + services.AddSingleton(selector); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(1, specific.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, specific.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, specific.ChallengeCount); + + await context.SignOutAsync(); + Assert.Equal(1, specific.SignOutCount); + + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + Assert.Equal(0, selector.AuthenticateCount); + Assert.Equal(0, selector.ForbidCount); + Assert.Equal(0, selector.ChallengeCount); + Assert.Equal(0, selector.SignInCount); + Assert.Equal(0, selector.SignOutCount); + } + [Fact] public async Task MetadataAddressIsGeneratedFromAuthorityWhenMissing() { @@ -46,6 +463,20 @@ public async Task MetadataAddressIsGeneratedFromAuthorityWhenMissing() Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); } + [Fact] + public Task ThrowsWhenSignInSchemeIsSetToSelf() + { + return TestConfigurationException( + o => + { + o.SignInScheme = OpenIdConnectDefaults.AuthenticationScheme; + o.Authority = TestServerBuilder.DefaultAuthority; + o.ClientId = "Test Id"; + o.ClientSecret = "Test Secret"; + }, + ex => Assert.Contains("cannot be set to itself", ex.Message)); + } + [Fact] public Task ThrowsWhenClientIdIsMissing() { @@ -102,6 +533,21 @@ public Task ThrowsWhenMetadataAddressIsNotHttps() ); } + [Fact] + public Task ThrowsWhenMaxAgeIsNegative() + { + return TestConfigurationException( + o => + { + o.SignInScheme = "TestScheme"; + o.ClientId = "Test Id"; + o.Authority = TestServerBuilder.DefaultAuthority; + o.MaxAge = TimeSpan.FromSeconds(-1); + }, + ex => Assert.StartsWith("The value must not be a negative TimeSpan.", ex.Message) + ); + } + private TestServer BuildTestServer(Action options) { var builder = new WebHostBuilder() diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectEventTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectEventTests.cs index ed20d2f5a..090bf3dec 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectEventTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectEventTests.cs @@ -16,1808 +16,1252 @@ using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.Http.Authentication; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Primitives; using Microsoft.IdentityModel.Protocols.OpenIdConnect; using Microsoft.IdentityModel.Tokens; +using Microsoft.Net.Http.Headers; using Xunit; namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect { public class OpenIdConnectEventTests { - private readonly Func MessageNotImpl = context => { throw new NotImplementedException("Message"); }; - private readonly Func TokenNotImpl = context => { throw new NotImplementedException("Token"); }; - private readonly Func CodeNotImpl = context => { throw new NotImplementedException("Code"); }; - private readonly Func TokenResponseNotImpl = context => { throw new NotImplementedException("TokenResponse"); }; - private readonly Func UserNotImpl = context => { throw new NotImplementedException("User"); }; - private readonly Func FailedNotImpl = context => { throw new NotImplementedException("Failed", context.Exception); }; - private readonly Func TicketNotImpl = context => { throw new NotImplementedException("Ticket"); }; - private readonly Func FailureNotImpl = context => { throw new NotImplementedException("Failure", context.Failure); }; - private readonly Func RedirectNotImpl = context => { throw new NotImplementedException("Redirect"); }; - private readonly Func RemoteSignOutNotImpl = context => { throw new NotImplementedException("Remote"); }; + private readonly RequestDelegate AppWritePath = context => context.Response.WriteAsync(context.Request.Path); private readonly RequestDelegate AppNotImpl = context => { throw new NotImplementedException("App"); }; [Fact] public async Task OnMessageReceived_Skip_NoMoreEventsRun() { - var messageReceived = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - context.SkipHandler(); - return Task.FromResult(0); - }, - OnTokenValidated = TokenNotImpl, - OnAuthorizationCodeReceived = CodeNotImpl, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnTicketReceived = TicketNotImpl, - OnRemoteFailure = FailureNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + }; + events.OnMessageReceived = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", ""); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); + events.ValidateExpectations(); } [Fact] public async Task OnMessageReceived_Fail_NoMoreEventsRun() { - var messageReceived = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - context.Fail("Authentication was aborted from user code."); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - return Task.FromResult(0); - }, - OnTokenValidated = TokenNotImpl, - OnAuthorizationCodeReceived = CodeNotImpl, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + ExpectRemoteFailure = true, + }; + events.OnMessageReceived = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.Fail("Authentication was aborted from user code."); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var exception = await Assert.ThrowsAsync(delegate { return PostAsync(server, "signin-oidc", ""); }); - Assert.Equal("Authentication was aborted from user code.", exception.Message); - - Assert.True(messageReceived); - Assert.True(remoteFailure); + Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message); + events.ValidateExpectations(); } [Fact] public async Task OnMessageReceived_Handled_NoMoreEventsRun() { - var messageReceived = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnTokenValidated = TokenNotImpl, - OnAuthorizationCodeReceived = CodeNotImpl, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + ExpectMessageReceived = true, + }; + events.OnMessageReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", ""); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); + events.ValidateExpectations(); } [Fact] public async Task OnTokenValidated_Skip_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.SkipHandler(); - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = CodeNotImpl, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + ExpectTokenValidated = true, + }; + events.OnTokenValidated = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); + events.ValidateExpectations(); } [Fact] public async Task OnTokenValidated_Fail_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.Fail("Authentication was aborted from user code."); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = CodeNotImpl, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectRemoteFailure = true, + }; + events.OnTokenValidated = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.Fail("Authentication was aborted from user code."); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var exception = await Assert.ThrowsAsync(delegate { return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state"); }); - Assert.Equal("Authentication was aborted from user code.", exception.Message); - - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(remoteFailure); + Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message); + events.ValidateExpectations(); } [Fact] public async Task OnTokenValidated_HandledWithoutTicket_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.HandleResponse(); - context.Principal = null; - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = CodeNotImpl, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + ExpectMessageReceived = true, + ExpectTokenValidated = true, + }; + events.OnTokenValidated = context => + { + context.HandleResponse(); + context.Principal = null; + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); + events.ValidateExpectations(); } - // TODO: Do any other events depend on the presence of the ticket? It's strange we have to double handle this event. [Fact] public async Task OnTokenValidated_HandledWithTicket_SkipToTicketReceived() { - var messageReceived = false; - var tokenValidated = false; - var ticketReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.Success(); - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = CodeNotImpl, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticketReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectTicketReceived = true, + }; + events.OnTokenValidated = context => + { + context.HandleResponse(); + context.Principal = null; + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + events.OnTokenValidated = context => + { + context.Success(); + return Task.FromResult(0); + }; + events.OnTicketReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(ticketReceived); + events.ValidateExpectations(); } [Fact] public async Task OnAuthorizationCodeReceived_Skip_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - context.SkipHandler(); - return Task.FromResult(0); - }, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + }; + events.OnAuthorizationCodeReceived = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); + events.ValidateExpectations(); } [Fact] public async Task OnAuthorizationCodeReceived_Fail_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - context.Fail("Authentication was aborted from user code."); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectRemoteFailure = true, + }; + events.OnAuthorizationCodeReceived = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.Fail("Authentication was aborted from user code."); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var exception = await Assert.ThrowsAsync(delegate { return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); }); - Assert.Equal("Authentication was aborted from user code.", exception.Message); - - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(remoteFailure); + Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message); + events.ValidateExpectations(); } [Fact] public async Task OnAuthorizationCodeReceived_HandledWithoutTicket_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - context.HandleResponse(); - context.Principal = null; - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + }; + events.OnAuthorizationCodeReceived = context => + { + context.HandleResponse(); + context.Principal = null; + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); + events.ValidateExpectations(); } [Fact] public async Task OnAuthorizationCodeReceived_HandledWithTicket_SkipToTicketReceived() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var ticketReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - context.Success(); - return Task.FromResult(0); - }, - OnTokenResponseReceived = TokenResponseNotImpl, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticketReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTicketReceived = true, + }; + events.OnAuthorizationCodeReceived = context => + { + context.Success(); + return Task.FromResult(0); + }; + events.OnTicketReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(ticketReceived); + events.ValidateExpectations(); } [Fact] public async Task OnTokenResponseReceived_Skip_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - context.SkipHandler(); - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + }; + events.OnTokenResponseReceived = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); + events.ValidateExpectations(); } [Fact] public async Task OnTokenResponseReceived_Fail_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - context.Fail("Authentication was aborted from user code."); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + var events = new ExpectedOidcEvents() { - return context.Response.WriteAsync(context.Request.Path); - }); + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectRemoteFailure = true, + }; + events.OnTokenResponseReceived = context => + { + context.Fail("Authentication was aborted from user code."); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var exception = await Assert.ThrowsAsync(delegate { return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); }); - Assert.Equal("Authentication was aborted from user code.", exception.Message); - - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(remoteFailure); + Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message); + events.ValidateExpectations(); } [Fact] public async Task OnTokenResponseReceived_HandledWithoutTicket_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - context.Principal = null; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + }; + events.OnTokenResponseReceived = context => + { + context.Principal = null; + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); + events.ValidateExpectations(); } [Fact] public async Task OnTokenResponseReceived_HandledWithTicket_SkipToTicketReceived() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var ticketReceived = false; - var tokenResponseReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - context.Success(); - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticketReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectTicketReceived = true, + }; + events.OnTokenResponseReceived = context => + { + context.Success(); + return Task.FromResult(0); + }; + events.OnTicketReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(ticketReceived); + events.ValidateExpectations(); } [Fact] public async Task OnTokenValidatedBackchannel_Skip_NoMoreEventsRun() { - var messageReceived = false; - var codeReceived = false; - var tokenResponseReceived = false; - var tokenValidated = false; - var server = CreateServer(new OpenIdConnectEvents() + var events = new ExpectedOidcEvents() { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.SkipHandler(); - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + }; + events.OnTokenValidated = context => { - return context.Response.WriteAsync(context.Request.Path); - }); + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(tokenValidated); + events.ValidateExpectations(); } [Fact] public async Task OnTokenValidatedBackchannel_Fail_NoMoreEventsRun() { - var messageReceived = false; - var codeReceived = false; - var tokenResponseReceived = false; - var tokenValidated = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.Fail("Authentication was aborted from user code."); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + var events = new ExpectedOidcEvents() { - return context.Response.WriteAsync(context.Request.Path); - }); + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectRemoteFailure = true, + }; + events.OnTokenValidated = context => + { + context.Fail("Authentication was aborted from user code."); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var exception = await Assert.ThrowsAsync(delegate { return PostAsync(server, "signin-oidc", "state=protected_state&code=my_code"); }); - Assert.Equal("Authentication was aborted from user code.", exception.Message); - - Assert.True(messageReceived); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(tokenValidated); - Assert.True(remoteFailure); + Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message); + events.ValidateExpectations(); } [Fact] public async Task OnTokenValidatedBackchannel_HandledWithoutTicket_NoMoreEventsRun() { - var messageReceived = false; - var codeReceived = false; - var tokenResponseReceived = false; - var tokenValidated = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.Principal = null; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + }; + events.OnTokenValidated = context => + { + context.Principal = null; + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(tokenValidated); + events.ValidateExpectations(); } [Fact] public async Task OnTokenValidatedBackchannel_HandledWithTicket_SkipToTicketReceived() { - var messageReceived = false; - var codeReceived = false; - var ticketReceived = false; - var tokenResponseReceived = false; - var tokenValidated = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - context.Success(); - return Task.FromResult(0); - }, - OnUserInformationReceived = UserNotImpl, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticketReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectTicketReceived = true, + }; + events.OnTokenValidated = context => + { + context.Success(); + return Task.FromResult(0); + }; + events.OnTicketReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(tokenValidated); - Assert.True(ticketReceived); + events.ValidateExpectations(); } [Fact] public async Task OnUserInformationReceived_Skip_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - context.SkipHandler(); - return Task.FromResult(0); - }, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + var events = new ExpectedOidcEvents() { - return context.Response.WriteAsync(context.Request.Path); - }); + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + }; + events.OnUserInformationReceived = context => + { + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); + events.ValidateExpectations(); } [Fact] public async Task OnUserInformationReceived_Fail_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - context.Fail("Authentication was aborted from user code."); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - return Task.FromResult(0); - }, - OnAuthenticationFailed = FailedNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => - { - return context.Response.WriteAsync(context.Request.Path); - }); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectRemoteFailure = true, + }; + events.OnUserInformationReceived = context => + { + context.Fail("Authentication was aborted from user code."); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var exception = await Assert.ThrowsAsync(delegate { return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); }); - Assert.Equal("Authentication was aborted from user code.", exception.Message); - - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(remoteFailure); + Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message); + events.ValidateExpectations(); } [Fact] public async Task OnUserInformationReceived_HandledWithoutTicket_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - context.Principal = null; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + }; + events.OnUserInformationReceived = context => + { + context.Principal = null; + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); + events.ValidateExpectations(); } [Fact] public async Task OnUserInformationReceived_HandledWithTicket_SkipToTicketReceived() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var ticketReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - // context.Ticket = null; - context.Success(); - return Task.FromResult(0); - }, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticketReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectTicketReceived = true, + }; + events.OnUserInformationReceived = context => + { + context.Success(); + return Task.FromResult(0); + }; + events.OnTicketReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(ticketReceived); + events.ValidateExpectations(); } [Fact] public async Task OnAuthenticationFailed_Skip_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var authFailed = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - throw new NotImplementedException("TestException"); - }, - OnAuthenticationFailed = context => - { - authFailed = true; - Assert.Equal("TestException", context.Exception.Message); - context.SkipHandler(); - return Task.FromResult(0); - }, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => - { - return context.Response.WriteAsync(context.Request.Path); - }); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectAuthenticationFailed = true, + }; + events.OnUserInformationReceived = context => + { + throw new NotImplementedException("TestException"); + }; + events.OnAuthenticationFailed = context => + { + Assert.Equal("TestException", context.Exception.Message); + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(authFailed); + events.ValidateExpectations(); } [Fact] public async Task OnAuthenticationFailed_Fail_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var authFailed = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - throw new NotImplementedException("TestException"); - }, - OnAuthenticationFailed = context => - { - authFailed = true; - Assert.Equal("TestException", context.Exception.Message); - context.Fail("Authentication was aborted from user code."); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - return Task.FromResult(0); - }, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => - { - return context.Response.WriteAsync(context.Request.Path); - }); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectAuthenticationFailed = true, + ExpectRemoteFailure = true, + }; + events.OnUserInformationReceived = context => + { + throw new NotImplementedException("TestException"); + }; + events.OnAuthenticationFailed = context => + { + Assert.Equal("TestException", context.Exception.Message); + context.Fail("Authentication was aborted from user code."); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var exception = await Assert.ThrowsAsync(delegate { return PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); }); - Assert.Equal("Authentication was aborted from user code.", exception.Message); - - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(authFailed); - Assert.True(remoteFailure); + Assert.Equal("Authentication was aborted from user code.", exception.InnerException.Message); + events.ValidateExpectations(); } [Fact] public async Task OnAuthenticationFailed_HandledWithoutTicket_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var authFailed = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - throw new NotImplementedException("TestException"); - }, - OnAuthenticationFailed = context => - { - authFailed = true; - Assert.Equal("TestException", context.Exception.Message); - Assert.Null(context.Principal); - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectAuthenticationFailed = true, + }; + events.OnUserInformationReceived = context => + { + throw new NotImplementedException("TestException"); + }; + events.OnAuthenticationFailed = context => + { + Assert.Equal("TestException", context.Exception.Message); + Assert.Null(context.Principal); + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(authFailed); + events.ValidateExpectations(); } [Fact] public async Task OnAuthenticationFailed_HandledWithTicket_SkipToTicketReceived() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var ticketReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var authFailed = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - throw new NotImplementedException("TestException"); - }, - OnAuthenticationFailed = context => - { - authFailed = true; - Assert.Equal("TestException", context.Exception.Message); - Assert.Null(context.Principal); - - var claims = new[] - { - new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"), - new Claim(ClaimTypes.Email, "bob@contoso.com"), - new Claim(ClaimsIdentity.DefaultNameClaimType, "bob") - }; - - context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)); - context.Success(); - return Task.FromResult(0); - }, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticketReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectAuthenticationFailed = true, + ExpectTicketReceived = true, + }; + events.OnUserInformationReceived = context => + { + throw new NotImplementedException("TestException"); + }; + events.OnAuthenticationFailed = context => + { + Assert.Equal("TestException", context.Exception.Message); + Assert.Null(context.Principal); + + var claims = new[] + { + new Claim(ClaimTypes.NameIdentifier, "Bob le Magnifique"), + new Claim(ClaimTypes.Email, "bob@contoso.com"), + new Claim(ClaimsIdentity.DefaultNameClaimType, "bob") + }; + + context.Principal = new ClaimsPrincipal(new ClaimsIdentity(claims, context.Scheme.Name)); + context.Success(); + return Task.FromResult(0); + }; + events.OnTicketReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(authFailed); - Assert.True(ticketReceived); + events.ValidateExpectations(); } [Fact] - public async Task OnRemoteFailure_Skip_NoMoreEventsRun() + public async Task OnAccessDenied_Skip_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var authFailed = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - throw new NotImplementedException("TestException"); - }, - OnAuthenticationFailed = context => - { - authFailed = true; - Assert.Equal("TestException", context.Exception.Message); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - Assert.Equal("TestException", context.Failure.Message); - context.SkipHandler(); - return Task.FromResult(0); - }, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => + var events = new ExpectedOidcEvents() { - return context.Response.WriteAsync(context.Request.Path); - }); + ExpectMessageReceived = true, + ExpectAccessDenied = true + }; + events.OnAccessDenied = context => + { + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); + + var response = await PostAsync(server, "signin-oidc", "error=access_denied&state=protected_state"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); + events.ValidateExpectations(); + } + + [Fact] + public async Task OnAccessDenied_Handled_NoMoreEventsRun() + { + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectAccessDenied = true + }; + events.OnAccessDenied = context => + { + Assert.Equal("testvalue", context.Properties.Items["testkey"]); + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); + + var response = await PostAsync(server, "signin-oidc", "error=access_denied&state=protected_state"); + + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + Assert.Equal("", await response.Content.ReadAsStringAsync()); + events.ValidateExpectations(); + } + + [Fact] + public async Task OnRemoteFailure_Skip_NoMoreEventsRun() + { + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectAuthenticationFailed = true, + ExpectRemoteFailure = true, + }; + events.OnUserInformationReceived = context => + { + throw new NotImplementedException("TestException"); + }; + events.OnAuthenticationFailed = context => + { + Assert.Equal("TestException", context.Exception.Message); + return Task.FromResult(0); + }; + events.OnRemoteFailure = context => + { + Assert.Equal("TestException", context.Failure.Message); + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(authFailed); - Assert.True(remoteFailure); + events.ValidateExpectations(); } [Fact] public async Task OnRemoteFailure_Handled_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var authFailed = false; - var remoteFailure = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - throw new NotImplementedException("TestException"); - }, - OnAuthenticationFailed = context => - { - authFailed = true; - Assert.Equal("TestException", context.Exception.Message); - return Task.FromResult(0); - }, - OnRemoteFailure = context => - { - remoteFailure = true; - Assert.Equal("TestException", context.Failure.Message); - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - OnTicketReceived = TicketNotImpl, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectAuthenticationFailed = true, + ExpectRemoteFailure = true, + }; + events.OnUserInformationReceived = context => + { + throw new NotImplementedException("TestException"); + }; + events.OnRemoteFailure = context => + { + Assert.Equal("TestException", context.Failure.Message); + Assert.Equal("testvalue", context.Properties.Items["testkey"]); + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(authFailed); - Assert.True(remoteFailure); + events.ValidateExpectations(); } [Fact] public async Task OnTicketReceived_Skip_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var ticektReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - return Task.FromResult(0); - }, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticektReceived = true; - context.SkipHandler(); - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - context => - { - return context.Response.WriteAsync(context.Request.Path); - }); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectTicketReceived = true, + }; + events.OnTicketReceived = context => + { + context.SkipHandler(); + return Task.FromResult(0); + }; + var server = CreateServer(events, AppWritePath); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.OK, response.StatusCode); Assert.Equal("/signin-oidc", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(ticektReceived); + events.ValidateExpectations(); } [Fact] public async Task OnTicketReceived_Handled_NoMoreEventsRun() { - var messageReceived = false; - var tokenValidated = false; - var codeReceived = false; - var tokenResponseReceived = false; - var userInfoReceived = false; - var ticektReceived = false; - var server = CreateServer(new OpenIdConnectEvents() - { - OnMessageReceived = context => - { - messageReceived = true; - return Task.FromResult(0); - }, - OnTokenValidated = context => - { - tokenValidated = true; - return Task.FromResult(0); - }, - OnAuthorizationCodeReceived = context => - { - codeReceived = true; - return Task.FromResult(0); - }, - OnTokenResponseReceived = context => - { - tokenResponseReceived = true; - return Task.FromResult(0); - }, - OnUserInformationReceived = context => - { - userInfoReceived = true; - return Task.FromResult(0); - }, - OnAuthenticationFailed = FailedNotImpl, - OnRemoteFailure = FailureNotImpl, - OnTicketReceived = context => - { - ticektReceived = true; - context.HandleResponse(); - context.Response.StatusCode = StatusCodes.Status202Accepted; - return Task.FromResult(0); - }, - - OnRedirectToIdentityProvider = RedirectNotImpl, - OnRedirectToIdentityProviderForSignOut = RedirectNotImpl, - OnRemoteSignOut = RemoteSignOutNotImpl, - }, - AppNotImpl); + var events = new ExpectedOidcEvents() + { + ExpectMessageReceived = true, + ExpectTokenValidated = true, + ExpectAuthorizationCodeReceived = true, + ExpectTokenResponseReceived = true, + ExpectUserInfoReceived = true, + ExpectTicketReceived = true, + }; + events.OnTicketReceived = context => + { + context.HandleResponse(); + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.FromResult(0); + }; + var server = CreateServer(events, AppNotImpl); var response = await PostAsync(server, "signin-oidc", "id_token=my_id_token&state=protected_state&code=my_code"); Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); Assert.Equal("", await response.Content.ReadAsStringAsync()); - Assert.True(messageReceived); - Assert.True(tokenValidated); - Assert.True(codeReceived); - Assert.True(tokenResponseReceived); - Assert.True(userInfoReceived); - Assert.True(ticektReceived); + events.ValidateExpectations(); + } + + [Fact] + public async Task OnRedirectToIdentityProviderForSignOut_Invoked() + { + var events = new ExpectedOidcEvents() + { + ExpectRedirectForSignOut = true, + }; + var server = CreateServer(events, + context => + { + return context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); + }); + + var client = server.CreateClient(); + var response = await client.GetAsync("/"); + + Assert.Equal(HttpStatusCode.Found, response.StatusCode); + Assert.Equal("http://testhost/end", response.Headers.Location.GetLeftPart(UriPartial.Path)); + events.ValidateExpectations(); + } + + [Fact] + public async Task OnRedirectToIdentityProviderForSignOut_Handled_RedirectNotInvoked() + { + var events = new ExpectedOidcEvents() + { + ExpectRedirectForSignOut = true, + }; + events.OnRedirectToIdentityProviderForSignOut = context => + { + context.Response.StatusCode = StatusCodes.Status202Accepted; + context.HandleResponse(); + return Task.CompletedTask; + }; + var server = CreateServer(events, + context => + { + return context.SignOutAsync(OpenIdConnectDefaults.AuthenticationScheme); + }); + + var client = server.CreateClient(); + var response = await client.GetAsync("/"); + + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + Assert.Null(response.Headers.Location); + events.ValidateExpectations(); + } + + [Fact] + public async Task OnRemoteSignOut_Invoked() + { + var events = new ExpectedOidcEvents() + { + ExpectRemoteSignOut = true, + }; + var server = CreateServer(events, AppNotImpl); + + var client = server.CreateClient(); + var response = await client.GetAsync("/signout-oidc"); + + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + events.ValidateExpectations(); + Assert.True(response.Headers.TryGetValues(HeaderNames.SetCookie, out var values)); + Assert.True(SetCookieHeaderValue.TryParseStrictList(values.ToList(), out var parsedValues)); + Assert.Equal(1, parsedValues.Count); + Assert.True(StringSegment.IsNullOrEmpty(parsedValues.Single().Value)); + } + + [Fact] + public async Task OnRemoteSignOut_Handled_NoSignout() + { + var events = new ExpectedOidcEvents() + { + ExpectRemoteSignOut = true, + }; + events.OnRemoteSignOut = context => + { + context.Response.StatusCode = StatusCodes.Status202Accepted; + context.HandleResponse(); + return Task.CompletedTask; + }; + var server = CreateServer(events, AppNotImpl); + + var client = server.CreateClient(); + var response = await client.GetAsync("/signout-oidc"); + + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + events.ValidateExpectations(); + Assert.False(response.Headers.TryGetValues(HeaderNames.SetCookie, out var values)); + } + + [Fact] + public async Task OnRemoteSignOut_Skip_NoSignout() + { + var events = new ExpectedOidcEvents() + { + ExpectRemoteSignOut = true, + }; + events.OnRemoteSignOut = context => + { + context.SkipHandler(); + return Task.CompletedTask; + }; + var server = CreateServer(events, context => + { + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.CompletedTask; + }); + + var client = server.CreateClient(); + var response = await client.GetAsync("/signout-oidc"); + + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + events.ValidateExpectations(); + Assert.False(response.Headers.TryGetValues(HeaderNames.SetCookie, out var values)); + } + + [Fact] + public async Task OnRedirectToSignedOutRedirectUri_Invoked() + { + var events = new ExpectedOidcEvents() + { + ExpectRedirectToSignedOut = true, + }; + var server = CreateServer(events, AppNotImpl); + + var client = server.CreateClient(); + var response = await client.GetAsync("/signout-callback-oidc?state=protected_state"); + + Assert.Equal(HttpStatusCode.Found, response.StatusCode); + Assert.Equal("http://testhost/redirect", response.Headers.Location.AbsoluteUri); + events.ValidateExpectations(); + } + + [Fact] + public async Task OnRedirectToSignedOutRedirectUri_Handled_NoRedirect() + { + var events = new ExpectedOidcEvents() + { + ExpectRedirectToSignedOut = true, + }; + events.OnSignedOutCallbackRedirect = context => + { + context.Response.StatusCode = StatusCodes.Status202Accepted; + context.HandleResponse(); + return Task.CompletedTask; + }; + var server = CreateServer(events, AppNotImpl); + + var client = server.CreateClient(); + var response = await client.GetAsync("/signout-callback-oidc?state=protected_state"); + + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + Assert.Null(response.Headers.Location); + events.ValidateExpectations(); + } + + [Fact] + public async Task OnRedirectToSignedOutRedirectUri_Skipped_NoRedirect() + { + var events = new ExpectedOidcEvents() + { + ExpectRedirectToSignedOut = true, + }; + events.OnSignedOutCallbackRedirect = context => + { + context.SkipHandler(); + return Task.CompletedTask; + }; + var server = CreateServer(events, + context => + { + context.Response.StatusCode = StatusCodes.Status202Accepted; + return Task.CompletedTask; + }); + + var client = server.CreateClient(); + var response = await client.GetAsync("/signout-callback-oidc?state=protected_state"); + + Assert.Equal(HttpStatusCode.Accepted, response.StatusCode); + Assert.Null(response.Headers.Location); + events.ValidateExpectations(); + } + + private class ExpectedOidcEvents : OpenIdConnectEvents + { + public bool ExpectMessageReceived { get; set; } + public bool InvokedMessageReceived { get; set; } + + public bool ExpectTokenValidated { get; set; } + public bool InvokedTokenValidated { get; set; } + + public bool ExpectAccessDenied { get; set; } + public bool InvokedAccessDenied { get; set; } + + public bool ExpectRemoteFailure { get; set; } + public bool InvokedRemoteFailure { get; set; } + + public bool ExpectTicketReceived { get; set; } + public bool InvokedTicketReceived { get; set; } + + public bool ExpectAuthorizationCodeReceived { get; set; } + public bool InvokedAuthorizationCodeReceived { get; set; } + + public bool ExpectTokenResponseReceived { get; set; } + public bool InvokedTokenResponseReceived { get; set; } + + public bool ExpectUserInfoReceived { get; set; } + public bool InvokedUserInfoReceived { get; set; } + + public bool ExpectAuthenticationFailed { get; set; } + public bool InvokeAuthenticationFailed { get; set; } + + public bool ExpectRedirectForSignOut { get; set; } + public bool InvokedRedirectForSignOut { get; set; } + + public bool ExpectRemoteSignOut { get; set; } + public bool InvokedRemoteSignOut { get; set; } + + public bool ExpectRedirectToSignedOut { get; set; } + public bool InvokedRedirectToSignedOut { get; set; } + + public override Task MessageReceived(MessageReceivedContext context) + { + InvokedMessageReceived = true; + return base.MessageReceived(context); + } + + public override Task TokenValidated(TokenValidatedContext context) + { + InvokedTokenValidated = true; + return base.TokenValidated(context); + } + + public override Task AuthorizationCodeReceived(AuthorizationCodeReceivedContext context) + { + InvokedAuthorizationCodeReceived = true; + return base.AuthorizationCodeReceived(context); + } + + public override Task TokenResponseReceived(TokenResponseReceivedContext context) + { + InvokedTokenResponseReceived = true; + return base.TokenResponseReceived(context); + } + + public override Task UserInformationReceived(UserInformationReceivedContext context) + { + InvokedUserInfoReceived = true; + return base.UserInformationReceived(context); + } + + public override Task AuthenticationFailed(AuthenticationFailedContext context) + { + InvokeAuthenticationFailed = true; + return base.AuthenticationFailed(context); + } + + public override Task TicketReceived(TicketReceivedContext context) + { + InvokedTicketReceived = true; + return base.TicketReceived(context); + } + + public override Task AccessDenied(AccessDeniedContext context) + { + InvokedAccessDenied = true; + return base.AccessDenied(context); + } + + public override Task RemoteFailure(RemoteFailureContext context) + { + InvokedRemoteFailure = true; + return base.RemoteFailure(context); + } + + public override Task RedirectToIdentityProviderForSignOut(RedirectContext context) + { + InvokedRedirectForSignOut = true; + return base.RedirectToIdentityProviderForSignOut(context); + } + + public override Task RemoteSignOut(RemoteSignOutContext context) + { + InvokedRemoteSignOut = true; + return base.RemoteSignOut(context); + } + + public override Task SignedOutCallbackRedirect(RemoteSignOutContext context) + { + InvokedRedirectToSignedOut = true; + return base.SignedOutCallbackRedirect(context); + } + + public void ValidateExpectations() + { + Assert.Equal(ExpectMessageReceived, InvokedMessageReceived); + Assert.Equal(ExpectTokenValidated, InvokedTokenValidated); + Assert.Equal(ExpectAuthorizationCodeReceived, InvokedAuthorizationCodeReceived); + Assert.Equal(ExpectTokenResponseReceived, InvokedTokenResponseReceived); + Assert.Equal(ExpectUserInfoReceived, InvokedUserInfoReceived); + Assert.Equal(ExpectAuthenticationFailed, InvokeAuthenticationFailed); + Assert.Equal(ExpectTicketReceived, InvokedTicketReceived); + Assert.Equal(ExpectAccessDenied, InvokedAccessDenied); + Assert.Equal(ExpectRemoteFailure, InvokedRemoteFailure); + Assert.Equal(ExpectRedirectForSignOut, InvokedRedirectForSignOut); + Assert.Equal(ExpectRemoteSignOut, InvokedRemoteSignOut); + Assert.Equal(ExpectRedirectToSignedOut, InvokedRedirectToSignedOut); + } } private TestServer CreateServer(OpenIdConnectEvents events, RequestDelegate appCode) @@ -1825,18 +1269,22 @@ private TestServer CreateServer(OpenIdConnectEvents events, RequestDelegate appC var builder = new WebHostBuilder() .ConfigureServices(services => { - services.AddAuthentication() + services.AddAuthentication(auth => + { + auth.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + auth.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme; + }) .AddCookie() .AddOpenIdConnect(o => { o.Events = events; - o.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; o.ClientId = "ClientId"; o.GetClaimsFromUserInfoEndpoint = true; o.Configuration = new OpenIdConnectConfiguration() { TokenEndpoint = "http://testhost/tokens", UserInfoEndpoint = "http://testhost/user", + EndSessionEndpoint = "http://testhost/end" }; o.StateDataFormat = new TestStateDataFormat(); o.SecurityTokenValidator = new TestTokenValidator(); @@ -1856,7 +1304,7 @@ private TestServer CreateServer(OpenIdConnectEvents events, RequestDelegate appC private Task PostAsync(TestServer server, string path, string form) { var client = server.CreateClient(); - var cookie = ".AspNetCore.Correlation." + OpenIdConnectDefaults.AuthenticationScheme + ".corrilationId=N"; + var cookie = ".AspNetCore.Correlation." + OpenIdConnectDefaults.AuthenticationScheme + ".correlationId=N"; client.DefaultRequestHeaders.Add("Cookie", cookie); return client.PostAsync("signin-oidc", new StringContent(form, Encoding.ASCII, "application/x-www-form-urlencoded")); @@ -1868,7 +1316,7 @@ private class TestStateDataFormat : ISecureDataFormat public string Protect(AuthenticationProperties data) { - throw new NotImplementedException(); + return "protected_state"; } public string Protect(AuthenticationProperties data, string purpose) @@ -1879,11 +1327,14 @@ public string Protect(AuthenticationProperties data, string purpose) public AuthenticationProperties Unprotect(string protectedText) { Assert.Equal("protected_state", protectedText); - return new AuthenticationProperties(new Dictionary() + var properties = new AuthenticationProperties(new Dictionary() { - { ".xsrf", "corrilationId" }, - { OpenIdConnectDefaults.RedirectUriForCodePropertiesKey, "redirect_uri" } + { ".xsrf", "correlationId" }, + { OpenIdConnectDefaults.RedirectUriForCodePropertiesKey, "redirect_uri" }, + { "testkey", "testvalue" } }); + properties.RedirectUri = "http://testhost/redirect"; + return properties; } public AuthenticationProperties Unprotect(string protectedText, string purpose) diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs index 32be26d33..da52e0e4c 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/OpenIdConnectTests.cs @@ -27,7 +27,7 @@ public class OpenIdConnectTests /// /// Tests RedirectForSignOutContext replaces the OpenIdConnectMesssage correctly. - /// summary> + /// /// Task [Fact] public async Task SignOutSettingMessage() diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs index bf9df4038..eb045b438 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/TestSettings.cs @@ -4,10 +4,14 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Linq; +using System.Net.Http; using System.Reflection; using System.Text; using System.Text.Encodings.Web; +using System.Threading; +using System.Threading.Tasks; using System.Xml.Linq; using Microsoft.AspNetCore.Authentication.OpenIdConnect; using Microsoft.AspNetCore.TestHost; @@ -22,6 +26,7 @@ namespace Microsoft.AspNetCore.Authentication.Test.OpenIdConnect internal class TestSettings { private readonly Action _configureOptions; + private OpenIdConnectOptions _options; public TestSettings() : this(configure: null) { @@ -33,6 +38,7 @@ public TestSettings(Action configure) { configure?.Invoke(o); _options = o; + _options.BackchannelHttpHandler = new MockBackchannel(); }; } @@ -189,7 +195,7 @@ private void ValidateParameters( ValidateState(actualValues, errors, htmlEncoded); break; case OpenIdConnectParameterNames.SkuTelemetry: - ValidateSkuTelemetry(actualValues, errors, htmlEncoded); + ValidateSkuTelemetry(actualValues, errors); break; case OpenIdConnectParameterNames.VersionTelemetry: ValidateVersionTelemetry(actualValues, errors, htmlEncoded); @@ -197,14 +203,18 @@ private void ValidateParameters( case OpenIdConnectParameterNames.PostLogoutRedirectUri: ValidatePostLogoutRedirectUri(actualValues, errors, htmlEncoded); break; + case OpenIdConnectParameterNames.MaxAge: + ValidateMaxAge(actualValues, errors, htmlEncoded); + break; + case OpenIdConnectParameterNames.Prompt: + ValidatePrompt(actualValues, errors, htmlEncoded); + break; default: throw new InvalidOperationException($"Unknown parameter \"{paramToValidate}\"."); } } } - OpenIdConnectOptions _options = null; - private void ValidateExpectedAuthority(string absoluteUri, ICollection errors, OpenIdConnectRequestType requestType) { string expectedAuthority; @@ -248,8 +258,13 @@ private void ValidateResource(IDictionary actualParams, ICollect private void ValidateState(IDictionary actualParams, ICollection errors, bool htmlEncoded) => ValidateParameter(OpenIdConnectParameterNames.State, ExpectedState, actualParams, errors, htmlEncoded); - private void ValidateSkuTelemetry(IDictionary actualParams, ICollection errors, bool htmlEncoded) => - ValidateParameter(OpenIdConnectParameterNames.SkuTelemetry, "ID_NET", actualParams, errors, htmlEncoded); + private static void ValidateSkuTelemetry(IDictionary actualParams, ICollection errors) + { + if (!actualParams.ContainsKey(OpenIdConnectParameterNames.SkuTelemetry)) + { + errors.Add($"Parameter {OpenIdConnectParameterNames.SkuTelemetry} is missing"); + } + } private void ValidateVersionTelemetry(IDictionary actualParams, ICollection errors, bool htmlEncoded) => ValidateParameter(OpenIdConnectParameterNames.VersionTelemetry, typeof(OpenIdConnectMessage).GetTypeInfo().Assembly.GetName().Version.ToString(), actualParams, errors, htmlEncoded); @@ -257,6 +272,23 @@ private void ValidateVersionTelemetry(IDictionary actualParams, private void ValidatePostLogoutRedirectUri(IDictionary actualParams, ICollection errors, bool htmlEncoded) => ValidateParameter(OpenIdConnectParameterNames.PostLogoutRedirectUri, "https://example.com/signout-callback-oidc", actualParams, errors, htmlEncoded); + private void ValidateMaxAge(IDictionary actualQuery, ICollection errors, bool htmlEncoded) + { + if(_options.MaxAge.HasValue) + { + Assert.Equal(TimeSpan.FromMinutes(20), _options.MaxAge.Value); + string expectedMaxAge = "1200"; + ValidateParameter(OpenIdConnectParameterNames.MaxAge, expectedMaxAge, actualQuery, errors, htmlEncoded); + } + else if(actualQuery.ContainsKey(OpenIdConnectParameterNames.MaxAge)) + { + errors.Add($"Parameter {OpenIdConnectParameterNames.MaxAge} is present but it should be absent"); + } + } + + private void ValidatePrompt(IDictionary actualParams, ICollection errors, bool htmlEncoded) => + ValidateParameter(OpenIdConnectParameterNames.Prompt, _options.Prompt, actualParams, errors, htmlEncoded); + private void ValidateParameter( string parameterName, string expectedValue, @@ -282,5 +314,37 @@ private void ValidateParameter( errors.Add($"Parameter {parameterName} is missing"); } } + + private class MockBackchannel : HttpMessageHandler + { + protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + if (request.RequestUri.AbsoluteUri.Equals("https://login.microsoftonline.com/common/.well-known/openid-configuration")) + { + return await ReturnResource("wellknownconfig.json"); + } + if (request.RequestUri.AbsoluteUri.Equals("https://login.microsoftonline.com/common/discovery/keys")) + { + return await ReturnResource("wellknownkeys.json"); + } + + throw new NotImplementedException(); + } + + private async Task ReturnResource(string resource) + { + var resourceName = "Microsoft.AspNetCore.Authentication.Test.OpenIdConnect." + resource; + using (var stream = typeof(MockBackchannel).Assembly.GetManifestResourceStream(resourceName)) + using (var reader = new StreamReader(stream)) + { + var body = await reader.ReadToEndAsync(); + var content = new StringContent(body, Encoding.UTF8, "application/json"); + return new HttpResponseMessage() + { + Content = content, + }; + } + } + } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/wellknownconfig.json b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/wellknownconfig.json new file mode 100644 index 000000000..4d46a8cf0 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/wellknownconfig.json @@ -0,0 +1,23 @@ +{ + "authorization_endpoint": "https://login.microsoftonline.com/common/oauth2/authorize", + "token_endpoint": "https://login.microsoftonline.com/common/oauth2/token", + "token_endpoint_auth_methods_supported": [ "client_secret_post", "private_key_jwt", "client_secret_basic" ], + "jwks_uri": "https://login.microsoftonline.com/common/discovery/keys", + "response_modes_supported": [ "query", "fragment", "form_post" ], + "subject_types_supported": [ "pairwise" ], + "id_token_signing_alg_values_supported": [ "RS256" ], + "http_logout_supported": true, + "frontchannel_logout_supported": true, + "end_session_endpoint": "https://login.microsoftonline.com/common/oauth2/logout", + "response_types_supported": [ "code", "id_token", "code id_token", "token id_token", "token" ], + "scopes_supported": [ "openid" ], + "issuer": "https://sts.windows.net/{tenantid}/", + "claims_supported": [ "sub", "iss", "cloud_instance_name", "cloud_instance_host_name", "cloud_graph_host_name", "msgraph_host", "aud", "exp", "iat", "auth_time", "acr", "amr", "nonce", "email", "given_name", "family_name", "nickname" ], + "microsoft_multi_refresh_token": true, + "check_session_iframe": "https://login.microsoftonline.com/common/oauth2/checksession", + "userinfo_endpoint": "https://login.microsoftonline.com/common/openid/userinfo", + "tenant_region_scope": null, + "cloud_instance_name": "microsoftonline.com", + "cloud_graph_host_name": "graph.windows.net", + "msgraph_host": "graph.microsoft.com" +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/wellknownkeys.json b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/wellknownkeys.json new file mode 100644 index 000000000..77cc5562a --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/OpenIdConnect/wellknownkeys.json @@ -0,0 +1,31 @@ +{ + "keys": [ + { + "kty": "RSA", + "use": "sig", + "kid": "SSQdhI1cKvhQEDSJxE2gGYs40Q0", + "x5t": "SSQdhI1cKvhQEDSJxE2gGYs40Q0", + "n": "pJUB90EMxiNjgkVz5CLLUuG5bYwirL2LXfVsq_nnY686WzbinkvFnNs6LvrJ6DWD5NV1-0Tq2eZj7WU8H9ytmDPsRnJ0b49gRCJYOg6-SdOe9Tl0lB0IBJE1aWh3OdCVrZLE4LH4-LGIDrkwnCV8dKFkO3EIUYPaEysL4g4wLx-TCfpMWE37XC09P-nBRVkRNcihrzY38_MC42NkRdDwByZemXkQKddnn5Y5o4rVzPGqQy3vjmTjKolYEIBYa7n3yF0848MG0k338bjnyceJgmZzjxttkWTVDikQXSldbu3QCrCAlipbWPUAXaZK8buY8LP80G4U_wx4LuZ_Krq5OQ", + "e": "AQAB", + "x5c": [ "MIIDBTCCAe2gAwIBAgIQHJ7yHxNEM7tBeqcRTMBhhTANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MDEwODAwMDAwMFoXDTIwMDEwOTAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKSVAfdBDMYjY4JFc+Qiy1LhuW2MIqy9i131bKv552OvOls24p5LxZzbOi76yeg1g+TVdftE6tnmY+1lPB/crZgz7EZydG+PYEQiWDoOvknTnvU5dJQdCASRNWlodznQla2SxOCx+PixiA65MJwlfHShZDtxCFGD2hMrC+IOMC8fkwn6TFhN+1wtPT/pwUVZETXIoa82N/PzAuNjZEXQ8AcmXpl5ECnXZ5+WOaOK1czxqkMt745k4yqJWBCAWGu598hdPOPDBtJN9/G458nHiYJmc48bbZFk1Q4pEF0pXW7t0AqwgJYqW1j1AF2mSvG7mPCz/NBuFP8MeC7mfyq6uTkCAwEAAaMhMB8wHQYDVR0OBBYEFFVWt/4iTcBiWk+EX1dCKZGoAlBQMA0GCSqGSIb3DQEBCwUAA4IBAQCJrLUiCfldfDFhZLG08DpLcwpn8Mvhm61ZpPANyCawRgaoBOsxFJew1qpC2wfXh3yUwJEIkkTGXAsiWENkeqmjd2XXQwZuIHIo5/KyZLfo2m1CmEH/QXL6uRx1f8liPr4efC8H+7/sduf6nPfk0UpNsAHA6RxJFy2jg2wHU+Ux4jK4Gc5d/rJPhJhvyS9Zax1hbTlf+N32ZvS780VMDb/nf2LdtACL0ya/+KSDGVXS3GhS9FLEXrBNjq921ghVIFJhPzm54yfeupIwz+zfISoTHIYw37i7iNUbkvDrm14a27pwLS/tfSuJHKcGPt1sjMu6SS/pf1BlvdoFkKdLLaUb" ] + }, + { + "kty": "RSA", + "use": "sig", + "kid": "FSimuFrFNoC0sJXGmv13nNZceDc", + "x5t": "FSimuFrFNoC0sJXGmv13nNZceDc", + "n": "yCYaJF8uHoV2L31cjZUDdcodK1Y1EsTLkDD-DEXFyGeHaQ92T9t6MU6zazBzHvJRarG6OMI1GwsFxZ9opSVOeuRjuL3H2ehmUyuKOAnL8uT4cfkdfbg9AIN_63COccfFn0br_xUszZ7lkF5mb63sze-G66YQcbdTCWgsXpxR6491b57Gc4HVTV8cEgU4byezhJIiirrPDmt23QJIjr6XtvUMSNW88u0kX7PKOUnVCns2AG8DB2I-JExTiXwhFVu5JUqgpgmjIngvd5eyNzOgFJMnpWNXabKDP3oMLvQxjdq9xwWuTu0IQLpmUxEF9jVc8vKV1Pu2xHcS7ON5xJrUzw", + "e": "AQAB", + "x5c": [ "MIIDBTCCAe2gAwIBAgIQev76BWqjWZxChmKkGqoAfDANBgkqhkiG9w0BAQsFADAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MB4XDTE4MDIxODAwMDAwMFoXDTIwMDIxOTAwMDAwMFowLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMgmGiRfLh6Fdi99XI2VA3XKHStWNRLEy5Aw/gxFxchnh2kPdk/bejFOs2swcx7yUWqxujjCNRsLBcWfaKUlTnrkY7i9x9noZlMrijgJy/Lk+HH5HX24PQCDf+twjnHHxZ9G6/8VLM2e5ZBeZm+t7M3vhuumEHG3UwloLF6cUeuPdW+exnOB1U1fHBIFOG8ns4SSIoq6zw5rdt0CSI6+l7b1DEjVvPLtJF+zyjlJ1Qp7NgBvAwdiPiRMU4l8IRVbuSVKoKYJoyJ4L3eXsjczoBSTJ6VjV2mygz96DC70MY3avccFrk7tCEC6ZlMRBfY1XPLyldT7tsR3EuzjecSa1M8CAwEAAaMhMB8wHQYDVR0OBBYEFIks1srixjpSLXeiR8zES5cTY6fBMA0GCSqGSIb3DQEBCwUAA4IBAQCKthfK4C31DMuDyQZVS3F7+4Evld3hjiwqu2uGDK+qFZas/D/eDunxsFpiwqC01RIMFFN8yvmMjHphLHiBHWxcBTS+tm7AhmAvWMdxO5lzJLS+UWAyPF5ICROe8Mu9iNJiO5JlCo0Wpui9RbB1C81Xhax1gWHK245ESL6k7YWvyMYWrGqr1NuQcNS0B/AIT1Nsj1WY7efMJQOmnMHkPUTWryVZlthijYyd7P2Gz6rY5a81DAFqhDNJl2pGIAE6HWtSzeUEh3jCsHEkoglKfm4VrGJEuXcALmfCMbdfTvtu4rlsaP2hQad+MG/KJFlenoTK34EMHeBPDCpqNDz8UVNk" ] + }, + { + "kty": "RSA", + "use": "sig", + "kid": "2S4SCVGs8Sg9LS6AqLIq6DpW-g8", + "x5t": "2S4SCVGs8Sg9LS6AqLIq6DpW-g8", + "n": "oZ-QQrNuB4ei9ATYrT61ebPtvwwYWnsrTpp4ISSp6niZYb92XM0oUTNgqd_C1vGN8J-y9wCbaJWkpBf46CjdZehrqczPhzhHau8WcRXocSB1u_tuZhv1ooAZ4bAcy79UkeLiG60HkuTNJJC8CfaTp1R97szBhuk0Vz5yt4r5SpfewIlBCnZUYwkDS172H9WapQu-3P2Qjh0l-JLyCkdrhvizZUk0atq5_AIDKRU-A0pRGc-EZhUL0LqUMz6c6M2s_4GnQaScv44A5iZUDD15B6e8Apb2yARohkWmOnmRcTVfes8EkfxjzZEzm3cNkvP0ogILyISHKlkzy2OmlU6iXw", + "e": "AQAB", + "x5c": [ "MIIDKDCCAhCgAwIBAgIQBHJvVNxP1oZO4HYKh+rypDANBgkqhkiG9w0BAQsFADAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwHhcNMTYxMTE2MDgwMDAwWhcNMTgxMTE2MDgwMDAwWjAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXMwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQChn5BCs24Hh6L0BNitPrV5s+2/DBhaeytOmnghJKnqeJlhv3ZczShRM2Cp38LW8Y3wn7L3AJtolaSkF/joKN1l6GupzM+HOEdq7xZxFehxIHW7+25mG/WigBnhsBzLv1SR4uIbrQeS5M0kkLwJ9pOnVH3uzMGG6TRXPnK3ivlKl97AiUEKdlRjCQNLXvYf1ZqlC77c/ZCOHSX4kvIKR2uG+LNlSTRq2rn8AgMpFT4DSlEZz4RmFQvQupQzPpzozaz/gadBpJy/jgDmJlQMPXkHp7wClvbIBGiGRaY6eZFxNV96zwSR/GPNkTObdw2S8/SiAgvIhIcqWTPLY6aVTqJfAgMBAAGjWDBWMFQGA1UdAQRNMEuAEDUj0BrjP0RTbmoRPTRMY3WhJTAjMSEwHwYDVQQDExhsb2dpbi5taWNyb3NvZnRvbmxpbmUudXOCEARyb1TcT9aGTuB2Cofq8qQwDQYJKoZIhvcNAQELBQADggEBAGnLhDHVz2gLDiu9L34V3ro/6xZDiSWhGyHcGqky7UlzQH3pT5so8iF5P0WzYqVtogPsyC2LPJYSTt2vmQugD4xlu/wbvMFLcV0hmNoTKCF1QTVtEQiAiy0Aq+eoF7Al5fV1S3Sune0uQHimuUFHCmUuF190MLcHcdWnPAmzIc8fv7quRUUsExXmxSX2ktUYQXzqFyIOSnDCuWFm6tpfK5JXS8fW5bpqTlrysXXz/OW/8NFGq/alfjrya4ojrOYLpunGriEtNPwK7hxj1AlCYEWaRHRXaUIW1ByoSff/6Y6+ZhXPUe0cDlNRt/qIz5aflwO7+W8baTS4O8m/icu7ItE=" ] + } + ] +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/PolicyTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/PolicyTests.cs new file mode 100644 index 000000000..368026beb --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/PolicyTests.cs @@ -0,0 +1,487 @@ +// Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. + +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication +{ + public class PolicyTests + { + [Fact] + public async Task CanDispatch() + { + var server = CreateServer(services => + { + services.AddLogging().AddAuthentication(o => + { + o.AddScheme("auth1", "auth1"); + o.AddScheme("auth2", "auth2"); + o.AddScheme("auth3", "auth3"); + }) + .AddPolicyScheme("policy1", "policy1", p => + { + p.ForwardDefault = "auth1"; + }) + .AddPolicyScheme("policy2", "policy2", p => + { + p.ForwardAuthenticate = "auth2"; + }); + }); + + var transaction = await server.SendAsync("http://example.com/auth/policy1"); + Assert.Equal("auth1", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth1")); + + transaction = await server.SendAsync("http://example.com/auth/auth1"); + Assert.Equal("auth1", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth1")); + + transaction = await server.SendAsync("http://example.com/auth/auth2"); + Assert.Equal("auth2", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth2")); + + transaction = await server.SendAsync("http://example.com/auth/auth3"); + Assert.Equal("auth3", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth3")); + + transaction = await server.SendAsync("http://example.com/auth/policy2"); + Assert.Equal("auth2", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth2")); + } + + [Fact] + public async Task DefaultTargetSelectorWinsOverDefaultTarget() + { + var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddAuthentication(o => + { + o.AddScheme("auth1", "auth1"); + o.AddScheme("auth2", "auth2"); + }) + .AddPolicyScheme("forward", "forward", p => { + p.ForwardDefault= "auth2"; + p.ForwardDefaultSelector = ctx => "auth1"; + }); + + var handler1 = new TestHandler(); + services.AddSingleton(handler1); + var handler2 = new TestHandler2(); + services.AddSingleton(handler2); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + Assert.Equal(0, handler1.AuthenticateCount); + Assert.Equal(0, handler1.ForbidCount); + Assert.Equal(0, handler1.ChallengeCount); + Assert.Equal(0, handler1.SignInCount); + Assert.Equal(0, handler1.SignOutCount); + Assert.Equal(0, handler2.AuthenticateCount); + Assert.Equal(0, handler2.ForbidCount); + Assert.Equal(0, handler2.ChallengeCount); + Assert.Equal(0, handler2.SignInCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.AuthenticateAsync("forward"); + Assert.Equal(1, handler1.AuthenticateCount); + Assert.Equal(0, handler2.AuthenticateCount); + + await context.ForbidAsync("forward"); + Assert.Equal(1, handler1.ForbidCount); + Assert.Equal(0, handler2.ForbidCount); + + await context.ChallengeAsync("forward"); + Assert.Equal(1, handler1.ChallengeCount); + Assert.Equal(0, handler2.ChallengeCount); + + await context.SignOutAsync("forward"); + Assert.Equal(1, handler1.SignOutCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.SignInAsync("forward", new ClaimsPrincipal()); + Assert.Equal(1, handler1.SignInCount); + Assert.Equal(0, handler2.SignInCount); + } + + [Fact] + public async Task NullDefaultTargetSelectorFallsBacktoDefaultTarget() + { + var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddAuthentication(o => + { + o.AddScheme("auth1", "auth1"); + o.AddScheme("auth2", "auth2"); + }) + .AddPolicyScheme("forward", "forward", p => { + p.ForwardDefault= "auth1"; + p.ForwardDefaultSelector = ctx => null; + }); + + var handler1 = new TestHandler(); + services.AddSingleton(handler1); + var handler2 = new TestHandler2(); + services.AddSingleton(handler2); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + Assert.Equal(0, handler1.AuthenticateCount); + Assert.Equal(0, handler1.ForbidCount); + Assert.Equal(0, handler1.ChallengeCount); + Assert.Equal(0, handler1.SignInCount); + Assert.Equal(0, handler1.SignOutCount); + Assert.Equal(0, handler2.AuthenticateCount); + Assert.Equal(0, handler2.ForbidCount); + Assert.Equal(0, handler2.ChallengeCount); + Assert.Equal(0, handler2.SignInCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.AuthenticateAsync("forward"); + Assert.Equal(1, handler1.AuthenticateCount); + Assert.Equal(0, handler2.AuthenticateCount); + + await context.ForbidAsync("forward"); + Assert.Equal(1, handler1.ForbidCount); + Assert.Equal(0, handler2.ForbidCount); + + await context.ChallengeAsync("forward"); + Assert.Equal(1, handler1.ChallengeCount); + Assert.Equal(0, handler2.ChallengeCount); + + await context.SignOutAsync("forward"); + Assert.Equal(1, handler1.SignOutCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.SignInAsync("forward", new ClaimsPrincipal()); + Assert.Equal(1, handler1.SignInCount); + Assert.Equal(0, handler2.SignInCount); + } + + [Fact] + public async Task SpecificTargetAlwaysWinsOverDefaultTarget() + { + var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddAuthentication(o => + { + o.AddScheme("auth1", "auth1"); + o.AddScheme("auth2", "auth2"); + }) + .AddPolicyScheme("forward", "forward", p => { + p.ForwardDefault= "auth2"; + p.ForwardDefaultSelector = ctx => "auth2"; + p.ForwardAuthenticate = "auth1"; + p.ForwardSignIn = "auth1"; + p.ForwardSignOut = "auth1"; + p.ForwardForbid = "auth1"; + p.ForwardChallenge = "auth1"; + }); + + var handler1 = new TestHandler(); + services.AddSingleton(handler1); + var handler2 = new TestHandler2(); + services.AddSingleton(handler2); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + Assert.Equal(0, handler1.AuthenticateCount); + Assert.Equal(0, handler1.ForbidCount); + Assert.Equal(0, handler1.ChallengeCount); + Assert.Equal(0, handler1.SignInCount); + Assert.Equal(0, handler1.SignOutCount); + Assert.Equal(0, handler2.AuthenticateCount); + Assert.Equal(0, handler2.ForbidCount); + Assert.Equal(0, handler2.ChallengeCount); + Assert.Equal(0, handler2.SignInCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.AuthenticateAsync("forward"); + Assert.Equal(1, handler1.AuthenticateCount); + Assert.Equal(0, handler2.AuthenticateCount); + + await context.ForbidAsync("forward"); + Assert.Equal(1, handler1.ForbidCount); + Assert.Equal(0, handler2.ForbidCount); + + await context.ChallengeAsync("forward"); + Assert.Equal(1, handler1.ChallengeCount); + Assert.Equal(0, handler2.ChallengeCount); + + await context.SignOutAsync("forward"); + Assert.Equal(1, handler1.SignOutCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.SignInAsync("forward", new ClaimsPrincipal()); + Assert.Equal(1, handler1.SignInCount); + Assert.Equal(0, handler2.SignInCount); + } + + [Fact] + public async Task VirtualSchemeTargetsForwardWithDefaultTarget() + { + var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddAuthentication(o => + { + o.AddScheme("auth1", "auth1"); + o.AddScheme("auth2", "auth2"); + }) + .AddPolicyScheme("forward", "forward", p => p.ForwardDefault= "auth1"); + + var handler1 = new TestHandler(); + services.AddSingleton(handler1); + var handler2 = new TestHandler2(); + services.AddSingleton(handler2); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + Assert.Equal(0, handler1.AuthenticateCount); + Assert.Equal(0, handler1.ForbidCount); + Assert.Equal(0, handler1.ChallengeCount); + Assert.Equal(0, handler1.SignInCount); + Assert.Equal(0, handler1.SignOutCount); + Assert.Equal(0, handler2.AuthenticateCount); + Assert.Equal(0, handler2.ForbidCount); + Assert.Equal(0, handler2.ChallengeCount); + Assert.Equal(0, handler2.SignInCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.AuthenticateAsync("forward"); + Assert.Equal(1, handler1.AuthenticateCount); + Assert.Equal(0, handler2.AuthenticateCount); + + await context.ForbidAsync("forward"); + Assert.Equal(1, handler1.ForbidCount); + Assert.Equal(0, handler2.ForbidCount); + + await context.ChallengeAsync("forward"); + Assert.Equal(1, handler1.ChallengeCount); + Assert.Equal(0, handler2.ChallengeCount); + + await context.SignOutAsync("forward"); + Assert.Equal(1, handler1.SignOutCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.SignInAsync("forward", new ClaimsPrincipal()); + Assert.Equal(1, handler1.SignInCount); + Assert.Equal(0, handler2.SignInCount); + } + + [Fact] + public async Task VirtualSchemeTargetsOverrideDefaultTarget() + { + var services = new ServiceCollection().AddOptions().AddLogging(); + services.AddAuthentication(o => + { + o.AddScheme("auth1", "auth1"); + o.AddScheme("auth2", "auth2"); + }) + .AddPolicyScheme("forward", "forward", p => + { + p.ForwardDefault= "auth1"; + p.ForwardChallenge = "auth2"; + p.ForwardSignIn = "auth2"; + }); + + var handler1 = new TestHandler(); + services.AddSingleton(handler1); + var handler2 = new TestHandler2(); + services.AddSingleton(handler2); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + Assert.Equal(0, handler1.AuthenticateCount); + Assert.Equal(0, handler1.ForbidCount); + Assert.Equal(0, handler1.ChallengeCount); + Assert.Equal(0, handler1.SignInCount); + Assert.Equal(0, handler1.SignOutCount); + Assert.Equal(0, handler2.AuthenticateCount); + Assert.Equal(0, handler2.ForbidCount); + Assert.Equal(0, handler2.ChallengeCount); + Assert.Equal(0, handler2.SignInCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.AuthenticateAsync("forward"); + Assert.Equal(1, handler1.AuthenticateCount); + Assert.Equal(0, handler2.AuthenticateCount); + + await context.ForbidAsync("forward"); + Assert.Equal(1, handler1.ForbidCount); + Assert.Equal(0, handler2.ForbidCount); + + await context.ChallengeAsync("forward"); + Assert.Equal(0, handler1.ChallengeCount); + Assert.Equal(1, handler2.ChallengeCount); + + await context.SignOutAsync("forward"); + Assert.Equal(1, handler1.SignOutCount); + Assert.Equal(0, handler2.SignOutCount); + + await context.SignInAsync("forward", new ClaimsPrincipal()); + Assert.Equal(0, handler1.SignInCount); + Assert.Equal(1, handler2.SignInCount); + } + + [Fact] + public async Task CanDynamicTargetBasedOnQueryString() + { + var server = CreateServer(services => + { + services.AddAuthentication(o => + { + o.AddScheme("auth1", "auth1"); + o.AddScheme("auth2", "auth2"); + o.AddScheme("auth3", "auth3"); + }) + .AddPolicyScheme("dynamic", "dynamic", p => + { + p.ForwardDefaultSelector = c => c.Request.QueryString.Value.Substring(1); + }); + }); + + var transaction = await server.SendAsync("http://example.com/auth/dynamic?auth1"); + Assert.Equal("auth1", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth1")); + transaction = await server.SendAsync("http://example.com/auth/dynamic?auth2"); + Assert.Equal("auth2", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth2")); + transaction = await server.SendAsync("http://example.com/auth/dynamic?auth3"); + Assert.Equal("auth3", transaction.FindClaimValue(ClaimTypes.NameIdentifier, "auth3")); + } + + private class TestHandler : IAuthenticationSignInHandler + { + public AuthenticationScheme Scheme { get; set; } + public int SignInCount { get; set; } + public int SignOutCount { get; set; } + public int ForbidCount { get; set; } + public int ChallengeCount { get; set; } + public int AuthenticateCount { get; set; } + + public Task AuthenticateAsync() + { + AuthenticateCount++; + var principal = new ClaimsPrincipal(); + var id = new ClaimsIdentity(); + id.AddClaim(new Claim(ClaimTypes.NameIdentifier, Scheme.Name, ClaimValueTypes.String, Scheme.Name)); + principal.AddIdentity(id); + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + ChallengeCount++; + return Task.CompletedTask; + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + ForbidCount++; + return Task.CompletedTask; + } + + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + Scheme = scheme; + return Task.CompletedTask; + } + + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + { + SignInCount++; + return Task.CompletedTask; + } + + public Task SignOutAsync(AuthenticationProperties properties) + { + SignOutCount++; + return Task.CompletedTask; + } + } + + private class TestHandler2 : IAuthenticationSignInHandler + { + public AuthenticationScheme Scheme { get; set; } + public int SignInCount { get; set; } + public int SignOutCount { get; set; } + public int ForbidCount { get; set; } + public int ChallengeCount { get; set; } + public int AuthenticateCount { get; set; } + + public Task AuthenticateAsync() + { + AuthenticateCount++; + var principal = new ClaimsPrincipal(); + var id = new ClaimsIdentity(); + id.AddClaim(new Claim(ClaimTypes.NameIdentifier, Scheme.Name, ClaimValueTypes.String, Scheme.Name)); + principal.AddIdentity(id); + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + ChallengeCount++; + return Task.CompletedTask; + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + ForbidCount++; + return Task.CompletedTask; + } + + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + Scheme = scheme; + return Task.CompletedTask; + } + + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + { + SignInCount++; + return Task.CompletedTask; + } + + public Task SignOutAsync(AuthenticationProperties properties) + { + SignOutCount++; + return Task.CompletedTask; + } + } + + private static TestServer CreateServer(Action configure = null, string defaultScheme = null) + { + var builder = new WebHostBuilder() + .Configure(app => + { + app.UseAuthentication(); + app.Use(async (context, next) => + { + var req = context.Request; + var res = context.Response; + if (req.Path.StartsWithSegments(new PathString("/auth"), out var remainder)) + { + var name = (remainder.Value.Length > 0) ? remainder.Value.Substring(1) : null; + var result = await context.AuthenticateAsync(name); + res.Describe(result?.Ticket?.Principal); + } + else + { + await next(); + } + }); + }) + .ConfigureServices(services => + { + configure?.Invoke(services); + }); + return new TestServer(builder); + } + } +} diff --git a/test/Microsoft.AspNetCore.Authentication.Test/RemoteAuthenticationTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/RemoteAuthenticationTests.cs new file mode 100644 index 000000000..d477e7534 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/RemoteAuthenticationTests.cs @@ -0,0 +1,92 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication +{ + public abstract class RemoteAuthenticationTests : SharedAuthenticationTests where TOptions : RemoteAuthenticationOptions + { + protected override string DisplayName => DefaultScheme; + + private TestServer CreateServer(Action configureOptions, Func testpath = null, bool isDefault = true) + => CreateServerWithServices(s => + { + var builder = s.AddAuthentication(); + if (isDefault) + { + s.Configure(o => o.DefaultScheme = DefaultScheme); + } + RegisterAuth(builder, configureOptions); + s.AddSingleton(Clock); + }, testpath); + + + protected virtual TestServer CreateServerWithServices(Action configureServices, Func testpath = null) + { + //private static TestServer CreateServer(Action configure, Action configureServices, Func> handler) + var builder = new WebHostBuilder() + .Configure(app => + { + app.Use(async (context, next) => + { + if (testpath != null) + { + await testpath(context); + } + await next(); + }); + }) + .ConfigureServices(configureServices); + return new TestServer(builder); + } + + protected abstract void ConfigureDefaults(TOptions o); + + [Fact] + public async Task VerifySignInSchemeCannotBeSetToSelf() + { + var server = CreateServer( + o => + { + ConfigureDefaults(o); + o.SignInScheme = DefaultScheme; + }, + context => context.ChallengeAsync(DefaultScheme)); + var error = await Assert.ThrowsAsync(() => server.SendAsync("https://example.com/challenge")); + Assert.Contains("cannot be set to itself", error.Message); + } + + [Fact] + public async Task VerifySignInSchemeCannotBeSetToSelfUsingDefaultScheme() + { + var server = CreateServer( + o => o.SignInScheme = null, + context => context.ChallengeAsync(DefaultScheme), + isDefault: true); + var error = await Assert.ThrowsAsync(() => server.SendAsync("https://example.com/challenge")); + Assert.Contains("cannot be set to itself", error.Message); + } + + [Fact] + public async Task VerifySignInSchemeCannotBeSetToSelfUsingDefaultSignInScheme() + { + var server = CreateServerWithServices( + services => + { + var builder = services.AddAuthentication(o => o.DefaultSignInScheme = DefaultScheme); + RegisterAuth(builder, o => o.SignInScheme = null); + }, + context => context.ChallengeAsync(DefaultScheme)); + var error = await Assert.ThrowsAsync(() => server.SendAsync("https://example.com/challenge")); + Assert.Contains("cannot be set to itself", error.Message); + } + } +} diff --git a/test/Microsoft.AspNetCore.Authentication.Test/SharedAuthenticationTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/SharedAuthenticationTests.cs new file mode 100644 index 000000000..4590c4915 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/SharedAuthenticationTests.cs @@ -0,0 +1,510 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using Microsoft.AspNetCore.Authentication.Tests; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Security.Claims; +using System.Threading.Tasks; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication +{ + public abstract class SharedAuthenticationTests where TOptions : AuthenticationSchemeOptions + { + protected TestClock Clock { get; } = new TestClock(); + + protected abstract string DefaultScheme { get; } + protected virtual string DisplayName { get; } + protected abstract Type HandlerType { get; } + + protected virtual bool SupportsSignIn { get => true; } + protected virtual bool SupportsSignOut { get => true; } + + protected abstract void RegisterAuth(AuthenticationBuilder services, Action configure); + + [Fact] + public async Task CanForwardDefault() + { + var services = new ServiceCollection().AddLogging(); + + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + }); + RegisterAuth(builder, o => o.ForwardDefault = "auth1"); + + var forwardDefault = new TestHandler(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + + await context.AuthenticateAsync(); + Assert.Equal(1, forwardDefault.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, forwardDefault.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, forwardDefault.ChallengeCount); + + if (SupportsSignOut) + { + await context.SignOutAsync(); + Assert.Equal(1, forwardDefault.SignOutCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignOutAsync()); + } + + if (SupportsSignIn) + { + await context.SignInAsync(new ClaimsPrincipal()); + Assert.Equal(1, forwardDefault.SignInCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + } + + [Fact] + public async Task ForwardSignInWinsOverDefault() + { + if (SupportsSignIn) + { + var services = new ServiceCollection().AddLogging(); + + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardSignIn = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.SignInAsync(new ClaimsPrincipal()); + Assert.Equal(1, specific.SignInCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignOutCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + } + + [Fact] + public async Task ForwardSignOutWinsOverDefault() + { + if (SupportsSignOut) + { + var services = new ServiceCollection().AddLogging(); + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardSignOut = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.SignOutAsync(); + Assert.Equal(1, specific.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + } + + [Fact] + public async Task ForwardForbidWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardForbid = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.ForbidAsync(); + Assert.Equal(0, specific.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(1, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + + [Fact] + public async Task ForwardAuthenticateWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardAuthenticate = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(0, specific.SignOutCount); + Assert.Equal(1, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + + [Fact] + public async Task ForwardChallengeWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardChallenge = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.ChallengeAsync(); + Assert.Equal(0, specific.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(1, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + } + + [Fact] + public async Task ForwardSelectorWinsOverDefault() + { + var services = new ServiceCollection().AddLogging(); + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("selector", "selector"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardDefaultSelector = _ => "selector"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + var selector = new TestHandler3(); + services.AddSingleton(selector); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(1, selector.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, selector.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, selector.ChallengeCount); + + if (SupportsSignOut) + { + await context.SignOutAsync(); + Assert.Equal(1, selector.SignOutCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignOutAsync()); + } + + if (SupportsSignIn) + { + await context.SignInAsync(new ClaimsPrincipal()); + Assert.Equal(1, selector.SignInCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + Assert.Equal(0, specific.SignOutCount); + } + + [Fact] + public async Task NullForwardSelectorUsesDefault() + { + var services = new ServiceCollection().AddLogging(); + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("selector", "selector"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardDefaultSelector = _ => null; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + var selector = new TestHandler3(); + services.AddSingleton(selector); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(1, forwardDefault.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, forwardDefault.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, forwardDefault.ChallengeCount); + + if (SupportsSignOut) + { + await context.SignOutAsync(); + Assert.Equal(1, forwardDefault.SignOutCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignOutAsync()); + } + + if (SupportsSignIn) + { + await context.SignInAsync(new ClaimsPrincipal()); + Assert.Equal(1, forwardDefault.SignInCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + + Assert.Equal(0, selector.AuthenticateCount); + Assert.Equal(0, selector.ForbidCount); + Assert.Equal(0, selector.ChallengeCount); + Assert.Equal(0, selector.SignInCount); + Assert.Equal(0, selector.SignOutCount); + Assert.Equal(0, specific.AuthenticateCount); + Assert.Equal(0, specific.ForbidCount); + Assert.Equal(0, specific.ChallengeCount); + Assert.Equal(0, specific.SignInCount); + Assert.Equal(0, specific.SignOutCount); + } + + [Fact] + public async Task SpecificForwardWinsOverSelectorAndDefault() + { + var services = new ServiceCollection().AddLogging(); + var builder = services.AddAuthentication(o => + { + o.DefaultScheme = DefaultScheme; + o.AddScheme("auth1", "auth1"); + o.AddScheme("selector", "selector"); + o.AddScheme("specific", "specific"); + }); + RegisterAuth(builder, o => + { + o.ForwardDefault = "auth1"; + o.ForwardDefaultSelector = _ => "selector"; + o.ForwardAuthenticate = "specific"; + o.ForwardChallenge = "specific"; + o.ForwardSignIn = "specific"; + o.ForwardSignOut = "specific"; + o.ForwardForbid = "specific"; + }); + + var specific = new TestHandler(); + services.AddSingleton(specific); + var forwardDefault = new TestHandler2(); + services.AddSingleton(forwardDefault); + var selector = new TestHandler3(); + services.AddSingleton(selector); + + var sp = services.BuildServiceProvider(); + var context = new DefaultHttpContext(); + context.RequestServices = sp; + + await context.AuthenticateAsync(); + Assert.Equal(1, specific.AuthenticateCount); + + await context.ForbidAsync(); + Assert.Equal(1, specific.ForbidCount); + + await context.ChallengeAsync(); + Assert.Equal(1, specific.ChallengeCount); + + if (SupportsSignOut) + { + await context.SignOutAsync(); + Assert.Equal(1, specific.SignOutCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignOutAsync()); + } + + if (SupportsSignIn) + { + await context.SignInAsync(new ClaimsPrincipal()); + Assert.Equal(1, specific.SignInCount); + } + else + { + await Assert.ThrowsAsync(() => context.SignInAsync(new ClaimsPrincipal())); + } + + Assert.Equal(0, forwardDefault.AuthenticateCount); + Assert.Equal(0, forwardDefault.ForbidCount); + Assert.Equal(0, forwardDefault.ChallengeCount); + Assert.Equal(0, forwardDefault.SignInCount); + Assert.Equal(0, forwardDefault.SignOutCount); + Assert.Equal(0, selector.AuthenticateCount); + Assert.Equal(0, selector.ForbidCount); + Assert.Equal(0, selector.ChallengeCount); + Assert.Equal(0, selector.SignInCount); + Assert.Equal(0, selector.SignOutCount); + } + + [Fact] + public async Task VerifySchemeDefaults() + { + var services = new ServiceCollection(); + var builder = services.AddAuthentication(); + RegisterAuth(builder, o => { }); + var sp = services.BuildServiceProvider(); + var schemeProvider = sp.GetRequiredService(); + var scheme = await schemeProvider.GetSchemeAsync(DefaultScheme); + Assert.NotNull(scheme); + Assert.Equal(HandlerType, scheme.HandlerType); + Assert.Equal(DisplayName, scheme.DisplayName); + } + } +} diff --git a/test/Microsoft.AspNetCore.Authentication.Test/TestHandlers.cs b/test/Microsoft.AspNetCore.Authentication.Test/TestHandlers.cs new file mode 100644 index 000000000..cd9fe9fb1 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/TestHandlers.cs @@ -0,0 +1,115 @@ +// Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. + +using System.Security.Claims; +using System.Text.Encodings.Web; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; + +namespace Microsoft.AspNetCore.Authentication.Tests +{ + public class TestAuthHandler : AuthenticationHandler, IAuthenticationSignInHandler + { + public TestAuthHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock) : base(options, logger, encoder, clock) + { } + + public int SignInCount { get; set; } + public int SignOutCount { get; set; } + public int ForbidCount { get; set; } + public int ChallengeCount { get; set; } + public int AuthenticateCount { get; set; } + + protected override Task HandleChallengeAsync(AuthenticationProperties properties) + { + ChallengeCount++; + return Task.CompletedTask; + } + + protected override Task HandleForbiddenAsync(AuthenticationProperties properties) + { + ForbidCount++; + return Task.CompletedTask; + } + + protected override Task HandleAuthenticateAsync() + { + AuthenticateCount++; + var principal = new ClaimsPrincipal(); + var id = new ClaimsIdentity(); + id.AddClaim(new Claim(ClaimTypes.NameIdentifier, Scheme.Name, ClaimValueTypes.String, Scheme.Name)); + principal.AddIdentity(id); + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); + } + + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + { + SignInCount++; + return Task.CompletedTask; + } + + public Task SignOutAsync(AuthenticationProperties properties) + { + SignOutCount++; + return Task.CompletedTask; + } + } + + public class TestHandler : IAuthenticationSignInHandler + { + public AuthenticationScheme Scheme { get; set; } + public int SignInCount { get; set; } + public int SignOutCount { get; set; } + public int ForbidCount { get; set; } + public int ChallengeCount { get; set; } + public int AuthenticateCount { get; set; } + + public Task AuthenticateAsync() + { + AuthenticateCount++; + var principal = new ClaimsPrincipal(); + var id = new ClaimsIdentity(); + id.AddClaim(new Claim(ClaimTypes.NameIdentifier, Scheme.Name, ClaimValueTypes.String, Scheme.Name)); + principal.AddIdentity(id); + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(principal, new AuthenticationProperties(), Scheme.Name))); + } + + public Task ChallengeAsync(AuthenticationProperties properties) + { + ChallengeCount++; + return Task.CompletedTask; + } + + public Task ForbidAsync(AuthenticationProperties properties) + { + ForbidCount++; + return Task.CompletedTask; + } + + public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) + { + Scheme = scheme; + return Task.CompletedTask; + } + + public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) + { + SignInCount++; + return Task.CompletedTask; + } + + public Task SignOutAsync(AuthenticationProperties properties) + { + SignOutCount++; + return Task.CompletedTask; + } + } + + public class TestHandler2 : TestHandler + { + } + + public class TestHandler3 : TestHandler + { + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs index b37ae53d5..a1e58743b 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/TicketSerializerTests.cs @@ -28,7 +28,7 @@ public void CanRoundTripEmptyPrincipal() serializer.Write(writer, ticket); stream.Position = 0; var readTicket = serializer.Read(reader); - Assert.Equal(0, readTicket.Principal.Identities.Count()); + Assert.Empty(readTicket.Principal.Identities); Assert.Equal("bye", readTicket.Properties.RedirectUri); Assert.Equal("Hello", readTicket.AuthenticationScheme); } @@ -50,7 +50,7 @@ public void CanRoundTripBootstrapContext() serializer.Write(writer, ticket); stream.Position = 0; var readTicket = serializer.Read(reader); - Assert.Equal(1, readTicket.Principal.Identities.Count()); + Assert.Single(readTicket.Principal.Identities); Assert.Equal("misc", readTicket.Principal.Identity.AuthenticationType); Assert.Equal("bootstrap", readTicket.Principal.Identities.First().BootstrapContext); } @@ -73,7 +73,7 @@ public void CanRoundTripActorIdentity() serializer.Write(writer, ticket); stream.Position = 0; var readTicket = serializer.Read(reader); - Assert.Equal(1, readTicket.Principal.Identities.Count()); + Assert.Single(readTicket.Principal.Identities); Assert.Equal("misc", readTicket.Principal.Identity.AuthenticationType); var identity = (ClaimsIdentity) readTicket.Principal.Identity; @@ -108,7 +108,7 @@ public void CanRoundTripClaimProperties() serializer.Write(writer, ticket); stream.Position = 0; var readTicket = serializer.Read(reader); - Assert.Equal(1, readTicket.Principal.Identities.Count()); + Assert.Single(readTicket.Principal.Identities); Assert.Equal("misc", readTicket.Principal.Identity.AuthenticationType); var readClaim = readTicket.Principal.FindFirst("type"); diff --git a/test/Microsoft.AspNetCore.Authentication.Test/TokenExtensionTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/TokenExtensionTests.cs index 05e5708de..4d4023bee 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/TokenExtensionTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/TokenExtensionTests.cs @@ -48,7 +48,7 @@ public void SubsequentStoreTokenDeletesPreviousTokens() Assert.Null(props.GetTokenValue("One")); Assert.Null(props.GetTokenValue("Two")); Assert.Null(props.GetTokenValue("Three")); - Assert.Equal(1, props.GetTokens().Count()); + Assert.Single(props.GetTokens()); } [Fact] diff --git a/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs b/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs index 746dfee6a..8eb7a5cd7 100644 --- a/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs +++ b/test/Microsoft.AspNetCore.Authentication.Test/TwitterTests.cs @@ -1,33 +1,43 @@ // Copyright (c) .NET Foundation. All rights reserved. See License.txt in the project root for license information. +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; using System; +using System.Linq; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Text; using System.Threading.Tasks; -using Microsoft.AspNetCore.Builder; -using Microsoft.AspNetCore.Hosting; -using Microsoft.AspNetCore.Http; -using Microsoft.AspNetCore.TestHost; -using Microsoft.Extensions.DependencyInjection; using Xunit; namespace Microsoft.AspNetCore.Authentication.Twitter { - public class TwitterTests + public class TwitterTests : RemoteAuthenticationTests { - [Fact] - public async Task VerifySchemeDefaults() + protected override string DefaultScheme => TwitterDefaults.AuthenticationScheme; + protected override Type HandlerType => typeof(TwitterHandler); + protected override bool SupportsSignIn { get => false; } + protected override bool SupportsSignOut { get => false; } + + protected override void RegisterAuth(AuthenticationBuilder services, Action configure) + { + services.AddTwitter(o => + { + ConfigureDefaults(o); + configure.Invoke(o); + }); + } + + protected override void ConfigureDefaults(TwitterOptions o) { - var services = new ServiceCollection(); - services.AddAuthentication().AddTwitter(); - var sp = services.BuildServiceProvider(); - var schemeProvider = sp.GetRequiredService(); - var scheme = await schemeProvider.GetSchemeAsync(TwitterDefaults.AuthenticationScheme); - Assert.NotNull(scheme); - Assert.Equal("TwitterHandler", scheme.HandlerType.Name); - Assert.Equal(TwitterDefaults.AuthenticationScheme, scheme.DisplayName); + o.ConsumerKey = "whatever"; + o.ConsumerSecret = "whatever"; + o.SignInScheme = "auth1"; } [Fact] @@ -47,26 +57,12 @@ public async Task ChallengeWillTriggerApplyRedirectEvent() }; o.BackchannelHttpHandler = new TestHttpMessageHandler { - Sender = req => - { - if (req.RequestUri.AbsoluteUri == "https://api.twitter.com/oauth/request_token") - { - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = - new StringContent("oauth_callback_confirmed=true&oauth_token=test_oauth_token&oauth_token_secret=test_oauth_token_secret", - Encoding.UTF8, - "application/x-www-form-urlencoded") - }; - } - return null; - } + Sender = BackchannelRequestToken }; }, - context => + async context => { - // REVIEW: Gross - context.ChallengeAsync("Twitter").GetAwaiter().GetResult(); + await context.ChallengeAsync("Twitter"); return true; }); var transaction = await server.SendAsync("http://example.com/challenge"); @@ -155,7 +151,6 @@ public async Task ForbidThrows() Assert.Equal(HttpStatusCode.OK, transaction.Response.StatusCode); } - [Fact] public async Task ChallengeWillTriggerRedirection() { @@ -165,35 +160,159 @@ public async Task ChallengeWillTriggerRedirection() o.ConsumerSecret = "Test Consumer Secret"; o.BackchannelHttpHandler = new TestHttpMessageHandler { - Sender = req => + Sender = BackchannelRequestToken + }; + }, + async context => + { + await context.ChallengeAsync("Twitter"); + return true; + }); + var transaction = await server.SendAsync("http://example.com/challenge"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + var location = transaction.Response.Headers.Location.AbsoluteUri; + Assert.Contains("https://api.twitter.com/oauth/authenticate?oauth_token=", location); + } + + [Fact] + public async Task HandleRequestAsync_RedirectsToAccessDeniedPathWhenExplicitlySet() + { + var server = CreateServer(o => + { + o.ConsumerKey = "Test Consumer Key"; + o.ConsumerSecret = "Test Consumer Secret"; + o.BackchannelHttpHandler = new TestHttpMessageHandler + { + Sender = BackchannelRequestToken + }; + o.AccessDeniedPath = "/access-denied"; + o.Events.OnRemoteFailure = context => throw new InvalidOperationException("This event should not be called."); + }, + async context => + { + var properties = new AuthenticationProperties(); + properties.Items["testkey"] = "testvalue"; + await context.ChallengeAsync("Twitter", properties); + return true; + }); + var transaction = await server.SendAsync("http://example.com/challenge"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + var location = transaction.Response.Headers.Location.AbsoluteUri; + Assert.Contains("https://api.twitter.com/oauth/authenticate?oauth_token=", location); + Assert.True(transaction.Response.Headers.TryGetValues(HeaderNames.SetCookie, out var setCookie)); + Assert.True(SetCookieHeaderValue.TryParseList(setCookie.ToList(), out var setCookieValues)); + Assert.Single(setCookieValues); + var setCookieValue = setCookieValues.Single(); + var cookie = new CookieHeaderValue(setCookieValue.Name, setCookieValue.Value); + + var request = new HttpRequestMessage(HttpMethod.Get, "/signin-twitter?denied=ABCDEFG"); + request.Headers.Add(HeaderNames.Cookie, cookie.ToString()); + var client = server.CreateClient(); + var response = await client.SendAsync(request); + + Assert.Equal(HttpStatusCode.Redirect, response.StatusCode); + Assert.Equal("/access-denied?ReturnUrl=%2Fchallenge", response.Headers.Location.ToString()); + } + + [Fact] + public async Task BadCallbackCallsAccessDeniedWithState() + { + var server = CreateServer(o => + { + o.ConsumerKey = "Test Consumer Key"; + o.ConsumerSecret = "Test Consumer Secret"; + o.BackchannelHttpHandler = new TestHttpMessageHandler + { + Sender = BackchannelRequestToken + }; + o.Events = new TwitterEvents() + { + OnAccessDenied = context => { - if (req.RequestUri.AbsoluteUri == "https://api.twitter.com/oauth/request_token") - { - return new HttpResponseMessage(HttpStatusCode.OK) - { - Content = - new StringContent("oauth_callback_confirmed=true&oauth_token=test_oauth_token&oauth_token_secret=test_oauth_token_secret", - Encoding.UTF8, - "application/x-www-form-urlencoded") - }; - } - return null; + Assert.NotNull(context.Properties); + Assert.Equal("testvalue", context.Properties.Items["testkey"]); + context.Response.StatusCode = StatusCodes.Status406NotAcceptable; + context.HandleResponse(); + return Task.CompletedTask; } }; }, - context => + async context => + { + var properties = new AuthenticationProperties(); + properties.Items["testkey"] = "testvalue"; + await context.ChallengeAsync("Twitter", properties); + return true; + }); + var transaction = await server.SendAsync("http://example.com/challenge"); + Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); + var location = transaction.Response.Headers.Location.AbsoluteUri; + Assert.Contains("https://api.twitter.com/oauth/authenticate?oauth_token=", location); + Assert.True(transaction.Response.Headers.TryGetValues(HeaderNames.SetCookie, out var setCookie)); + Assert.True(SetCookieHeaderValue.TryParseList(setCookie.ToList(), out var setCookieValues)); + Assert.Single(setCookieValues); + var setCookieValue = setCookieValues.Single(); + var cookie = new CookieHeaderValue(setCookieValue.Name, setCookieValue.Value); + + var request = new HttpRequestMessage(HttpMethod.Get, "/signin-twitter?denied=ABCDEFG"); + request.Headers.Add(HeaderNames.Cookie, cookie.ToString()); + var client = server.CreateClient(); + var response = await client.SendAsync(request); + + Assert.Equal(HttpStatusCode.NotAcceptable, response.StatusCode); + } + + [Fact] + public async Task BadCallbackCallsRemoteAuthFailedWithState() + { + var server = CreateServer(o => + { + o.ConsumerKey = "Test Consumer Key"; + o.ConsumerSecret = "Test Consumer Secret"; + o.BackchannelHttpHandler = new TestHttpMessageHandler { - // REVIEW: gross - context.ChallengeAsync("Twitter").GetAwaiter().GetResult(); - return true; - }); + Sender = BackchannelRequestToken + }; + o.Events = new TwitterEvents() + { + OnRemoteFailure = context => + { + Assert.NotNull(context.Failure); + Assert.Equal("Access was denied by the resource owner or by the remote server.", context.Failure.Message); + Assert.NotNull(context.Properties); + Assert.Equal("testvalue", context.Properties.Items["testkey"]); + context.Response.StatusCode = StatusCodes.Status406NotAcceptable; + context.HandleResponse(); + return Task.CompletedTask; + } + }; + }, + async context => + { + var properties = new AuthenticationProperties(); + properties.Items["testkey"] = "testvalue"; + await context.ChallengeAsync("Twitter", properties); + return true; + }); var transaction = await server.SendAsync("http://example.com/challenge"); Assert.Equal(HttpStatusCode.Redirect, transaction.Response.StatusCode); var location = transaction.Response.Headers.Location.AbsoluteUri; Assert.Contains("https://api.twitter.com/oauth/authenticate?oauth_token=", location); + Assert.True(transaction.Response.Headers.TryGetValues(HeaderNames.SetCookie, out var setCookie)); + Assert.True(SetCookieHeaderValue.TryParseList(setCookie.ToList(), out var setCookieValues)); + Assert.Single(setCookieValues); + var setCookieValue = setCookieValues.Single(); + var cookie = new CookieHeaderValue(setCookieValue.Name, setCookieValue.Value); + + var request = new HttpRequestMessage(HttpMethod.Get, "/signin-twitter?denied=ABCDEFG"); + request.Headers.Add(HeaderNames.Cookie, cookie.ToString()); + var client = server.CreateClient(); + var response = await client.SendAsync(request); + + Assert.Equal(HttpStatusCode.NotAcceptable, response.StatusCode); } - private static TestServer CreateServer(Action options, Func handler = null) + private static TestServer CreateServer(Action options, Func> handler = null) { var builder = new WebHostBuilder() .Configure(app => @@ -215,7 +334,7 @@ private static TestServer CreateServer(Action options, Func(() => context.ForbidAsync("Twitter")); } - else if (handler == null || !handler(context)) + else if (handler == null || ! await handler(context)) { await next(); } @@ -234,5 +353,20 @@ private static TestServer CreateServer(Action options, Func + { + public const string ValidStateData = "ValidStateData"; + + private string lastSavedAuthenticationProperties; + private DataContractSerializer serializer = new DataContractSerializer(typeof(AuthenticationProperties)); + + public string Protect(AuthenticationProperties data) + { + lastSavedAuthenticationProperties = Serialize(data); + return ValidStateData; + } + + public string Protect(AuthenticationProperties data, string purpose) + { + return Protect(data); + } + + public AuthenticationProperties Unprotect(string state) + { + return state == ValidStateData ? DeSerialize(lastSavedAuthenticationProperties) : null; + } + + public AuthenticationProperties Unprotect(string protectedText, string purpose) + { + return Unprotect(protectedText); + } + + private string Serialize(AuthenticationProperties data) + { + using (MemoryStream memoryStream = new MemoryStream()) + { + serializer.WriteObject(memoryStream, data); + memoryStream.Position = 0; + return new StreamReader(memoryStream).ReadToEnd(); + } + } + + private AuthenticationProperties DeSerialize(string state) + { + var stateDataAsBytes = Encoding.UTF8.GetBytes(state); + + using (var ms = new MemoryStream(stateDataAsBytes, false)) + { + return (AuthenticationProperties)serializer.ReadObject(ms); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/InvalidToken.xml b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/InvalidToken.xml new file mode 100644 index 000000000..dfdb0d68d --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/InvalidToken.xml @@ -0,0 +1,83 @@ + + + 2014-04-18T20:21:17.341Z + 2014-04-19T08:21:17.341Z + + + +
http://automation1/
+
+
+ + + https://sts.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/ + + + + + + + + + + + Lkq0wTyTFxLUU2cyx0XybJqhka5RzRGj6kC4aIpFg+g= + + + bPwNswOB/B9xcdAljIkin9A2vjq+u94JdyvK03mf8vZFGUYNu9uN/Q6ims1DvW1FnP7SgFBwhIvW5OjZyW8fdYGhC2bq36izkxH6ulkWbciOcyELkyHDACLudvh8kP/Q+IwpicefKzAeI2Qu/5MFq16vFg5YgI+dovg8u1fYPPEPmmptW893RNTHWeh9mLRpLYnHyg7aLG6emNRkEu7w9rzeoICeMFybb9BvJl/q/8MFCW/Z5WemQhCi6YXFSEwCO6zJzCFi/3T6ChU/xYgXbFykDLqulsNOCQxdgutyqxJzugt+3PH5IKHHuoqe7UZNUIyELJ4BgwE1sXCGYIi24rg== + + + MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng + + + + + t0ch1TsP0pi5VoW8q5CGWsCXVZoNtpsg0mbMZPOYb4I + + + + + http://Automation1 + + + + + Test + + + Test + + + user1@praburajgmail.onmicrosoft.com + + + 4afbc689-805b-48cf-a24c-d4aa3248a248 + + + c2f0cd49-5e53-4520-8ed9-4e178dc488c5 + + + https://sts.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/ + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + + + _660ec874-f70a-4997-a9c4-bd591f1c7469 + + + + + _660ec874-f70a-4997-a9c4-bd591f1c7469 + + + http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0 + http://schemas.xmlsoap.org/ws/2005/02/trust/Issue + http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey +
\ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/TestSecurityToken.cs b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/TestSecurityToken.cs new file mode 100644 index 000000000..dfe860724 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/TestSecurityToken.cs @@ -0,0 +1,27 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + internal class TestSecurityToken : SecurityToken + { + public override string Id => "id"; + + public override string Issuer => "issuer"; + + public override SecurityKey SecurityKey => throw new NotImplementedException(); + + public override SecurityKey SigningKey + { + get => throw new NotImplementedException(); + set => throw new NotImplementedException(); + } + + public override DateTime ValidFrom => new DateTime(2008, 3, 22); + + public override DateTime ValidTo => new DateTime(2017, 3, 22); + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/TestSecurityTokenValidator.cs b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/TestSecurityTokenValidator.cs new file mode 100644 index 000000000..05882518f --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/TestSecurityTokenValidator.cs @@ -0,0 +1,31 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System.Security.Claims; +using Microsoft.IdentityModel.Tokens; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + internal class TestSecurityTokenValidator : ISecurityTokenValidator + { + public bool CanValidateToken => true; + + public int MaximumTokenSizeInBytes { get; set; } = 1024 * 5; + + public bool CanReadToken(string securityToken) + { + return true; + } + + public ClaimsPrincipal ValidateToken(string securityToken, TokenValidationParameters validationParameters, out SecurityToken validatedToken) + { + if (!string.IsNullOrEmpty(securityToken) && securityToken.Contains("ThisIsAValidToken")) + { + validatedToken = new TestSecurityToken(); + return new ClaimsPrincipal(new ClaimsIdentity("Test")); + } + + throw new SecurityTokenException("The security token did not contain ThisIsAValidToken"); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/ValidToken.xml b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/ValidToken.xml new file mode 100644 index 000000000..2addae96c --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/ValidToken.xml @@ -0,0 +1,83 @@ + + + 2014-04-18T20:21:17.341Z + 2014-04-19T08:21:17.341Z + + + +
http://automation1/
+
+
+ + + https://sts.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/ + + + + + + + + + + + Lkq0wTyTFxLUU2cyx0XybJqhka5RzRGj6kC4aIpFg+g= + + + bPwNswOB/B9xcdAljIkin9A2vjq+u94JdyvK03mf8vZFGUYNu9uN/Q6ims1DvW1FnP7SgFBwhIvW5OjZyW8fdYGhC2bq36izkxH6ulkWbciOcyELkyHDACLudvh8kP/Q+IwpicefKzAeI2Qu/5MFq16vFg5YgI+dovg8u1fYPPEPmmptW893RNTHWeh9mLRpLYnHyg7aLG6emNRkEu7w9rzeoICeMFybb9BvJl/q/8MFCW/Z5WemQhCi6YXFSEwCO6zJzCFi/3T6ChU/xYgXbFykDLqulsNOCQxdgutyqxJzugt+3PH5IKHHuoqe7UZNUIyELJ4BgwE1sXCGYIi24rg== + + + ThisIsAValidToken + + + + + t0ch1TsP0pi5VoW8q5CGWsCXVZoNtpsg0mbMZPOYb4I + + + + + http://Automation1 + + + + + Test + + + Test + + + user1@praburajgmail.onmicrosoft.com + + + 4afbc689-805b-48cf-a24c-d4aa3248a248 + + + c2f0cd49-5e53-4520-8ed9-4e178dc488c5 + + + https://sts.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/ + + + + + urn:oasis:names:tc:SAML:2.0:ac:classes:Password + + + + + + + _660ec874-f70a-4997-a9c4-bd591f1c7469 + + + + + _660ec874-f70a-4997-a9c4-bd591f1c7469 + + + http://docs.oasis-open.org/wss/oasis-wss-saml-token-profile-1.1#SAMLV2.0 + http://schemas.xmlsoap.org/ws/2005/02/trust/Issue + http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey +
\ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/WsFederationTest.cs b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/WsFederationTest.cs new file mode 100644 index 000000000..bc1ef757f --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/WsFederationTest.cs @@ -0,0 +1,443 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Http; +using System.Security.Claims; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication.Cookies; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Extensions; +using Microsoft.AspNetCore.TestHost; +using Microsoft.AspNetCore.WebUtilities; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.IdentityModel.Tokens; +using Microsoft.Net.Http.Headers; +using Xunit; + +namespace Microsoft.AspNetCore.Authentication.WsFederation +{ + public class WsFederationTest + { + [Fact] + public async Task VerifySchemeDefaults() + { + var services = new ServiceCollection(); + services.AddAuthentication().AddWsFederation(); + var sp = services.BuildServiceProvider(); + var schemeProvider = sp.GetRequiredService(); + var scheme = await schemeProvider.GetSchemeAsync(WsFederationDefaults.AuthenticationScheme); + Assert.NotNull(scheme); + Assert.Equal("WsFederationHandler", scheme.HandlerType.Name); + Assert.Equal(WsFederationDefaults.AuthenticationScheme, scheme.DisplayName); + } + + [Fact] + public async Task MissingConfigurationThrows() + { + var builder = new WebHostBuilder() + .Configure(ConfigureApp) + .ConfigureServices(services => + { + services.AddAuthentication(sharedOptions => + { + sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddWsFederation(); + }); + var server = new TestServer(builder); + var httpClient = server.CreateClient(); + + // Verify if the request is redirected to STS with right parameters + var exception = await Assert.ThrowsAsync(() => httpClient.GetAsync("/")); + Assert.Equal("Provide MetadataAddress, Configuration, or ConfigurationManager to WsFederationOptions", exception.Message); + } + + [Fact] + public async Task ChallengeRedirects() + { + var httpClient = CreateClient(); + + // Verify if the request is redirected to STS with right parameters + var response = await httpClient.GetAsync("/"); + Assert.Equal("https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/wsfed", response.Headers.Location.GetLeftPart(System.UriPartial.Path)); + var queryItems = QueryHelpers.ParseQuery(response.Headers.Location.Query); + + Assert.Equal("http://Automation1", queryItems["wtrealm"]); + Assert.True(queryItems["wctx"].ToString().Equals(CustomStateDataFormat.ValidStateData), "wctx does not equal ValidStateData"); + Assert.Equal(httpClient.BaseAddress + "signin-wsfed", queryItems["wreply"]); + Assert.Equal("wsignin1.0", queryItems["wa"]); + } + + [Fact] + public async Task MapWillNotAffectRedirect() + { + var httpClient = CreateClient(); + + // Verify if the request is redirected to STS with right parameters + var response = await httpClient.GetAsync("/mapped-challenge"); + Assert.Equal("https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/wsfed", response.Headers.Location.GetLeftPart(System.UriPartial.Path)); + var queryItems = QueryHelpers.ParseQuery(response.Headers.Location.Query); + + Assert.Equal("http://Automation1", queryItems["wtrealm"]); + Assert.True(queryItems["wctx"].ToString().Equals(CustomStateDataFormat.ValidStateData), "wctx does not equal ValidStateData"); + Assert.Equal(httpClient.BaseAddress + "signin-wsfed", queryItems["wreply"]); + Assert.Equal("wsignin1.0", queryItems["wa"]); + } + + [Fact] + public async Task PreMappedWillAffectRedirect() + { + var httpClient = CreateClient(); + + // Verify if the request is redirected to STS with right parameters + var response = await httpClient.GetAsync("/premapped-challenge"); + Assert.Equal("https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/wsfed", response.Headers.Location.GetLeftPart(System.UriPartial.Path)); + var queryItems = QueryHelpers.ParseQuery(response.Headers.Location.Query); + + Assert.Equal("http://Automation1", queryItems["wtrealm"]); + Assert.True(queryItems["wctx"].ToString().Equals(CustomStateDataFormat.ValidStateData), "wctx does not equal ValidStateData"); + Assert.Equal(httpClient.BaseAddress + "premapped-challenge/signin-wsfed", queryItems["wreply"]); + Assert.Equal("wsignin1.0", queryItems["wa"]); + } + + [Fact] + public async Task ValidTokenIsAccepted() + { + var httpClient = CreateClient(); + + // Verify if the request is redirected to STS with right parameters + var response = await httpClient.GetAsync("/"); + var queryItems = QueryHelpers.ParseQuery(response.Headers.Location.Query); + + var request = new HttpRequestMessage(HttpMethod.Post, queryItems["wreply"]); + CopyCookies(response, request); + request.Content = CreateSignInContent("WsFederation/ValidToken.xml", queryItems["wctx"]); + response = await httpClient.SendAsync(request); + + Assert.Equal(HttpStatusCode.Found, response.StatusCode); + + request = new HttpRequestMessage(HttpMethod.Get, response.Headers.Location); + CopyCookies(response, request); + response = await httpClient.SendAsync(request); + + // Did the request end in the actual resource requested for + Assert.Equal(WsFederationDefaults.AuthenticationScheme, await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task ValidUnsolicitedTokenIsRefused() + { + var httpClient = CreateClient(); + var form = CreateSignInContent("WsFederation/ValidToken.xml", suppressWctx: true); + var exception = await Assert.ThrowsAsync(() => httpClient.PostAsync(httpClient.BaseAddress + "signin-wsfed", form)); + Assert.Contains("Unsolicited logins are not allowed.", exception.InnerException.Message); + } + + [Fact] + public async Task ValidUnsolicitedTokenIsAcceptedWhenAllowed() + { + var httpClient = CreateClient(allowUnsolicited: true); + + var form = CreateSignInContent("WsFederation/ValidToken.xml", suppressWctx: true); + var response = await httpClient.PostAsync(httpClient.BaseAddress + "signin-wsfed", form); + + Assert.Equal(HttpStatusCode.Found, response.StatusCode); + + var request = new HttpRequestMessage(HttpMethod.Get, response.Headers.Location); + CopyCookies(response, request); + response = await httpClient.SendAsync(request); + + // Did the request end in the actual resource requested for + Assert.Equal(WsFederationDefaults.AuthenticationScheme, await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task InvalidTokenIsRejected() + { + var httpClient = CreateClient(); + + // Verify if the request is redirected to STS with right parameters + var response = await httpClient.GetAsync("/"); + var queryItems = QueryHelpers.ParseQuery(response.Headers.Location.Query); + + var request = new HttpRequestMessage(HttpMethod.Post, queryItems["wreply"]); + CopyCookies(response, request); + request.Content = CreateSignInContent("WsFederation/InvalidToken.xml", queryItems["wctx"]); + response = await httpClient.SendAsync(request); + + // Did the request end in the actual resource requested for + Assert.Equal("AuthenticationFailed", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task RemoteSignoutRequestTriggersSignout() + { + var httpClient = CreateClient(); + + var response = await httpClient.GetAsync("/signin-wsfed?wa=wsignoutcleanup1.0"); + response.EnsureSuccessStatusCode(); + + var cookie = response.Headers.GetValues(HeaderNames.SetCookie).Single(); + Assert.Equal(".AspNetCore.Cookies=; expires=Thu, 01 Jan 1970 00:00:00 GMT; path=/; samesite=lax", cookie); + Assert.Equal("OnRemoteSignOut", response.Headers.GetValues("EventHeader").Single()); + Assert.Equal("", await response.Content.ReadAsStringAsync()); + } + + [Fact] + public async Task EventsResolvedFromDI() + { + var builder = new WebHostBuilder() + .ConfigureServices(services => + { + services.AddSingleton(); + services.AddAuthentication(sharedOptions => + { + sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddWsFederation(options => + { + options.Wtrealm = "http://Automation1"; + options.MetadataAddress = "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/federationmetadata/2007-06/federationmetadata.xml"; + options.BackchannelHttpHandler = new WaadMetadataDocumentHandler(); + options.EventsType = typeof(MyWsFedEvents); + }); + }) + .Configure(app => + { + app.Run(context => context.ChallengeAsync()); + }); + var server = new TestServer(builder); + + var result = await server.CreateClient().GetAsync(""); + Assert.Contains("CustomKey=CustomValue", result.Headers.Location.Query); + } + + private class MyWsFedEvents : WsFederationEvents + { + public override Task RedirectToIdentityProvider(RedirectContext context) + { + context.ProtocolMessage.SetParameter("CustomKey", "CustomValue"); + return base.RedirectToIdentityProvider(context); + } + } + + private FormUrlEncodedContent CreateSignInContent(string tokenFile, string wctx = null, bool suppressWctx = false) + { + var kvps = new List>(); + kvps.Add(new KeyValuePair("wa", "wsignin1.0")); + kvps.Add(new KeyValuePair("wresult", File.ReadAllText(tokenFile))); + if (!string.IsNullOrEmpty(wctx)) + { + kvps.Add(new KeyValuePair("wctx", wctx)); + } + if (suppressWctx) + { + kvps.Add(new KeyValuePair("suppressWctx", "true")); + } + return new FormUrlEncodedContent(kvps); + } + + private void CopyCookies(HttpResponseMessage response, HttpRequestMessage request) + { + var cookies = SetCookieHeaderValue.ParseList(response.Headers.GetValues(HeaderNames.SetCookie).ToList()); + foreach (var cookie in cookies) + { + if (cookie.Value.HasValue) + { + request.Headers.Add(HeaderNames.Cookie, new CookieHeaderValue(cookie.Name, cookie.Value).ToString()); + } + } + } + + private HttpClient CreateClient(bool allowUnsolicited = false) + { + var builder = new WebHostBuilder() + .Configure(ConfigureApp) + .ConfigureServices(services => + { + services.AddAuthentication(sharedOptions => + { + sharedOptions.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme; + sharedOptions.DefaultChallengeScheme = WsFederationDefaults.AuthenticationScheme; + }) + .AddCookie() + .AddWsFederation(options => + { + options.Wtrealm = "http://Automation1"; + options.MetadataAddress = "https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/federationmetadata/2007-06/federationmetadata.xml"; + options.BackchannelHttpHandler = new WaadMetadataDocumentHandler(); + options.StateDataFormat = new CustomStateDataFormat(); + options.SecurityTokenHandlers = new List() { new TestSecurityTokenValidator() }; + options.UseTokenLifetime = false; + options.AllowUnsolicitedLogins = allowUnsolicited; + options.Events = new WsFederationEvents() + { + OnMessageReceived = context => + { + if (!context.ProtocolMessage.Parameters.TryGetValue("suppressWctx", out var suppress)) + { + Assert.True(context.ProtocolMessage.Wctx.Equals("customValue"), "wctx is not my custom value"); + } + context.HttpContext.Items["MessageReceived"] = true; + return Task.FromResult(0); + }, + OnRedirectToIdentityProvider = context => + { + if (context.ProtocolMessage.IsSignInMessage) + { + // Sign in message + context.ProtocolMessage.Wctx = "customValue"; + } + + return Task.FromResult(0); + }, + OnSecurityTokenReceived = context => + { + context.HttpContext.Items["SecurityTokenReceived"] = true; + return Task.FromResult(0); + }, + OnSecurityTokenValidated = context => + { + Assert.True((bool)context.HttpContext.Items["MessageReceived"], "MessageReceived notification not invoked"); + Assert.True((bool)context.HttpContext.Items["SecurityTokenReceived"], "SecurityTokenReceived notification not invoked"); + + if (context.Principal != null) + { + var identity = context.Principal.Identities.Single(); + identity.AddClaim(new Claim("ReturnEndpoint", "true")); + identity.AddClaim(new Claim("Authenticated", "true")); + identity.AddClaim(new Claim(identity.RoleClaimType, "Guest", ClaimValueTypes.String)); + } + + return Task.FromResult(0); + }, + OnAuthenticationFailed = context => + { + context.HttpContext.Items["AuthenticationFailed"] = true; + //Change the request url to something different and skip Wsfed. This new url will handle the request and let us know if this notification was invoked. + context.HttpContext.Request.Path = new PathString("/AuthenticationFailed"); + context.SkipHandler(); + return Task.FromResult(0); + }, + OnRemoteSignOut = context => + { + context.Response.Headers["EventHeader"] = "OnRemoteSignOut"; + return Task.FromResult(0); + } + }; + }); + }); + var server = new TestServer(builder); + return server.CreateClient(); + } + + private void ConfigureApp(IApplicationBuilder app) + { + app.Map("/PreMapped-Challenge", mapped => + { + mapped.UseAuthentication(); + mapped.Run(async context => + { + await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme); + }); + }); + + app.UseAuthentication(); + + app.Map("/Logout", subApp => + { + subApp.Run(async context => + { + if (context.User.Identity.IsAuthenticated) + { + var authProperties = new AuthenticationProperties() { RedirectUri = context.Request.GetEncodedUrl() }; + await context.SignOutAsync(WsFederationDefaults.AuthenticationScheme, authProperties); + await context.Response.WriteAsync("Signing out..."); + } + else + { + await context.Response.WriteAsync("SignedOut"); + } + }); + }); + + app.Map("/AuthenticationFailed", subApp => + { + subApp.Run(async context => + { + await context.Response.WriteAsync("AuthenticationFailed"); + }); + }); + + app.Map("/signout-wsfed", subApp => + { + subApp.Run(async context => + { + await context.Response.WriteAsync("signout-wsfed"); + }); + }); + + app.Map("/mapped-challenge", subApp => + { + subApp.Run(async context => + { + await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme); + }); + }); + + app.Run(async context => + { + var result = context.AuthenticateAsync(); + if (context.User == null || !context.User.Identity.IsAuthenticated) + { + await context.ChallengeAsync(WsFederationDefaults.AuthenticationScheme); + await context.Response.WriteAsync("Unauthorized"); + } + else + { + var identity = context.User.Identities.Single(); + if (identity.NameClaimType == "Name_Failed" && identity.RoleClaimType == "Role_Failed") + { + context.Response.StatusCode = 500; + await context.Response.WriteAsync("SignIn_Failed"); + } + else if (!identity.HasClaim("Authenticated", "true") || !identity.HasClaim("ReturnEndpoint", "true") || !identity.HasClaim(identity.RoleClaimType, "Guest")) + { + await context.Response.WriteAsync("Provider not invoked"); + return; + } + else + { + await context.Response.WriteAsync(WsFederationDefaults.AuthenticationScheme); + } + } + }); + } + + private class WaadMetadataDocumentHandler : HttpMessageHandler + { + protected override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) + { + var metadata = File.ReadAllText(@"WsFederation/federationmetadata.xml"); + var newResponse = new HttpResponseMessage() { Content = new StringContent(metadata, Encoding.UTF8, "text/xml") }; + return Task.FromResult(newResponse); + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/federationmetadata.xml b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/federationmetadata.xml new file mode 100644 index 000000000..920ed66a4 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authentication.Test/WsFederation/federationmetadata.xml @@ -0,0 +1,132 @@ + + + + + + + + + + + + + wFJy/A1QstqtLHauYGcqwwHvn3HUW25DcWI/XLOmXOM= + + + R6fPw+BiFS9XYdkhwNJRjGxVftA2j9TdkF5d5jgR8uG1QMyuEA/Eizeq1HnnUj2Yi+sqNG+HzaZQclECeiJfi88Ry+keorDCo9KgdnjlZZc+WFzrJZeHjaDIvFD6B4OAN0mTq5kbpwr7+idzSbvyRXAnpvJxOrViZKE4HpwltGAZGDTkjsVkd8Z/wfoN7ehN4Ei7u/mOAiEU4FkWYFU/BfSVRVIUDyyQ7DGfQFJvCwHWFvsq+M1wfOUzQO5K+M9EU2m4VEP1qqbexXaZMAbcjqyUn4eN7doWjWE59jkXGbn+GR8qgCJqLOaYwXnH5XD0pMjy71aKGyLNaUb3wCwjkA== + + + MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng + + + + + + + + MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ + + + + + + + MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng + + + + + + UPN + UPN of the user + + + Name + The display name for the user + + + Given Name + First name of the user + + + Surname + Last name of the user + + + Authentication Instant + The time (UTC) at which the user authenticated to the identity provider + + + Authentication Method + The method of authentication used by the identity provider + + + TenantId + Identifier for the user's tenant + + + IdentityProvider + Identity provider for the user. + + + + +
https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/wsfed
+
+
+ + +
https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/wsfed
+
+
+
+ + + + + MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ + + + + + + + MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng + + + + + +
https://sts.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/
+
+
+ + +
https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/wsfed
+
+
+ + +
https://login.windows.net/4afbc689-805b-48cf-a24c-d4aa3248a248/wsfed
+
+
+
+ + + + + MIIDPjCCAiqgAwIBAgIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTQwMTAxMDcwMDAwWhcNMTYwMTAxMDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAkSCWg6q9iYxvJE2NIhSyOiKvqoWCO2GFipgH0sTSAs5FalHQosk9ZNTztX0ywS/AHsBeQPqYygfYVJL6/EgzVuwRk5txr9e3n1uml94fLyq/AXbwo9yAduf4dCHTP8CWR1dnDR+Qnz/4PYlWVEuuHHONOw/blbfdMjhY+C/BYM2E3pRxbohBb3x//CfueV7ddz2LYiH3wjz0QS/7kjPiNCsXcNyKQEOTkbHFi3mu0u13SQwNddhcynd/GTgWN8A+6SN1r4hzpjFKFLbZnBt77ACSiYx+IHK4Mp+NaVEi5wQtSsjQtI++XsokxRDqYLwus1I1SihgbV/STTg5enufuwIDAQABo2IwYDBeBgNVHQEEVzBVgBDLebM6bK3BjWGqIBrBNFeNoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQsRiM0jheFZhKk49YD0SK1TAJBgUrDgMCHQUAA4IBAQCJ4JApryF77EKC4zF5bUaBLQHQ1PNtA1uMDbdNVGKCmSf8M65b8h0NwlIjGGGy/unK8P6jWFdm5IlZ0YPTOgzcRZguXDPj7ajyvlVEQ2K2ICvTYiRQqrOhEhZMSSZsTKXFVwNfW6ADDkN3bvVOVbtpty+nBY5UqnI7xbcoHLZ4wYD251uj5+lo13YLnsVrmQ16NCBYq2nQFNPuNJw6t3XUbwBHXpF46aLT1/eGf/7Xx6iy8yPJX4DyrpFTutDz882RWofGEO5t4Cw+zZg70dJ/hH/ODYRMorfXEW+8uKmXMKmX2wyxMKvfiPbTy5LmAU8Jvjs2tLg4rOBcXWLAIarZ + + + + + + + MIIDPjCCAiqgAwIBAgIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAMC0xKzApBgNVBAMTImFjY291bnRzLmFjY2Vzc2NvbnRyb2wud2luZG93cy5uZXQwHhcNMTIwNjA3MDcwMDAwWhcNMTQwNjA3MDcwMDAwWjAtMSswKQYDVQQDEyJhY2NvdW50cy5hY2Nlc3Njb250cm9sLndpbmRvd3MubmV0MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArCz8Sn3GGXmikH2MdTeGY1D711EORX/lVXpr+ecGgqfUWF8MPB07XkYuJ54DAuYT318+2XrzMjOtqkT94VkXmxv6dFGhG8YZ8vNMPd4tdj9c0lpvWQdqXtL1TlFRpD/P6UMEigfN0c9oWDg9U7Ilymgei0UXtf1gtcQbc5sSQU0S4vr9YJp2gLFIGK11Iqg4XSGdcI0QWLLkkC6cBukhVnd6BCYbLjTYy3fNs4DzNdemJlxGl8sLexFytBF6YApvSdus3nFXaMCtBGx16HzkK9ne3lobAwL2o79bP4imEGqg+ibvyNmbrwFGnQrBc1jTF9LyQX9q+louxVfHs6ZiVwIDAQABo2IwYDBeBgNVHQEEVzBVgBCxDDsLd8xkfOLKm4Q/SzjtoS8wLTErMCkGA1UEAxMiYWNjb3VudHMuYWNjZXNzY29udHJvbC53aW5kb3dzLm5ldIIQVWmXY/+9RqFA/OG9kFulHDAJBgUrDgMCHQUAA4IBAQAkJtxxm/ErgySlNk69+1odTMP8Oy6L0H17z7XGG3w4TqvTUSWaxD4hSFJ0e7mHLQLQD7oV/erACXwSZn2pMoZ89MBDjOMQA+e6QzGB7jmSzPTNmQgMLA8fWCfqPrz6zgH+1F1gNp8hJY57kfeVPBiyjuBmlTEBsBlzolY9dd/55qqfQk6cgSeCbHCy/RU/iep0+UsRMlSgPNNmqhj5gmN2AFVCN96zF694LwuPae5CeR2ZcVknexOWHYjFM0MgUSw0ubnGl0h9AJgGyhvNGcjQqu9vd1xkupFgaN+f7P3p3EVN5csBg5H94jEcQZT7EKeTiZ6bTrpDAnrr8tDCy8ng + + + + + + +
\ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationAppBuilderExtensionsTests.cs b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationAppBuilderExtensionsTests.cs new file mode 100644 index 000000000..c6fb596f1 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationAppBuilderExtensionsTests.cs @@ -0,0 +1,65 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization.Test.TestObjects; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Builder.Internal; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Endpoints; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; +using Xunit; + +namespace Microsoft.AspNetCore.Authorization.Test +{ + public class AuthorizationAppBuilderExtensionsTests + { + [Fact] + public async Task UseAuthorization_RegistersMiddleware() + { + // Arrange + var authenticationService = new TestAuthenticationService(); + var services = CreateServices(authenticationService); + + var app = new ApplicationBuilder(services); + + app.UseAuthorization(); + + var appFunc = app.Build(); + + var endpoint = new Endpoint( + null, + new EndpointMetadataCollection(new AuthorizeAttribute()), + "Test endpoint"); + + var httpContext = new DefaultHttpContext(); + httpContext.RequestServices = services; + httpContext.SetEndpoint(endpoint); + + // Act + await appFunc(httpContext); + + // Assert + Assert.True(authenticationService.ChallengeCalled); + } + + private IServiceProvider CreateServices(IAuthenticationService authenticationService) + { + var services = new ServiceCollection(); + + services.AddAuthorization(options => { }); + services.AddAuthorizationPolicyEvaluator(); + services.AddLogging(); + services.AddSingleton(authenticationService); + + var serviceProvder = services.BuildServiceProvider(); + + return serviceProvder; + } + } +} diff --git a/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationEndpointConventionBuilderExtensionsTests.cs b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationEndpointConventionBuilderExtensionsTests.cs new file mode 100644 index 000000000..a2ab8fa8b --- /dev/null +++ b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationEndpointConventionBuilderExtensionsTests.cs @@ -0,0 +1,63 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Routing; +using Microsoft.AspNetCore.Routing.Patterns; +using Xunit; + +namespace Microsoft.AspNetCore.Authorization.Test +{ + public class AuthorizationEndpointConventionBuilderExtensionsTests + { + [Fact] + public void RequireAuthorization_IAuthorizeData() + { + // Arrange + var builder = new TestEndpointConventionBuilder(); + var metadata = new AuthorizeAttribute(); + + // Act + builder.RequireAuthorization(metadata); + + // Assert + var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointModel((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); + convention(endpointModel); + + Assert.Equal(metadata, Assert.Single(endpointModel.Metadata)); + } + + [Fact] + public void RequireAuthorization_PolicyName() + { + // Arrange + var builder = new TestEndpointConventionBuilder(); + + // Act + builder.RequireAuthorization("policy"); + + // Assert + var convention = Assert.Single(builder.Conventions); + + var endpointModel = new RouteEndpointModel((context) => Task.CompletedTask, RoutePatternFactory.Parse("/"), 0); + convention(endpointModel); + + Assert.Equal("policy", Assert.IsAssignableFrom(Assert.Single(endpointModel.Metadata)).Policy); + } + + private class TestEndpointConventionBuilder : IEndpointConventionBuilder + { + public IList> Conventions { get; } = new List>(); + + public void Apply(Action convention) + { + Conventions.Add(convention); + } + } + } +} diff --git a/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationMiddlewareTests.cs b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationMiddlewareTests.cs new file mode 100644 index 000000000..655a4fbf8 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationMiddlewareTests.cs @@ -0,0 +1,459 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Authorization; +using Microsoft.AspNetCore.Authorization.Infrastructure; +using Microsoft.AspNetCore.Authorization.Test.TestObjects; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Endpoints; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Options; +using Moq; +using Xunit; + +namespace Microsoft.AspNetCore.Authorization.Test +{ + public class AuthorizationMiddlewareTests + { + [Fact] + public async Task NoEndpoint_AnonymousUser_Allows() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.True(next.Called); + } + + [Fact] + public async Task NoEndpointWithRequired_AnonymousUser_Challenges() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + } + + [Fact] + public async Task HasEndpointWithoutAuth_AnonymousUser_Allows() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint()); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.True(next.Called); + } + + [Fact] + public async Task HasEndpointWithRequiredWithoutAuth_AnonymousUser_Challenges() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint()); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + } + + [Fact] + public async Task HasEndpointWithAuth_AnonymousUser_Challenges() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + Assert.True(authenticationService.ChallengeCalled); + } + + [Fact] + public async Task HasEndpointWithAuth_AnonymousUser_ChallengePerScheme() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().AddAuthenticationSchemes("schema1", "schema2").Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + Assert.Equal(2, authenticationService.ChallengeCount); + } + + [Fact] + public async Task OnAuthorizationAsync_WillCallPolicyProvider() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAssertion(_ => true).Build(); + var policyProvider = new Mock(); + var getPolicyCount = 0; + var getRequiredPolicyCount = 0; + policyProvider.Setup(p => p.GetPolicyAsync(It.IsAny())).ReturnsAsync(policy) + .Callback(() => getPolicyCount++); + policyProvider.Setup(p => p.GetRequiredPolicyAsync()).ReturnsAsync(policy) + .Callback(() => getRequiredPolicyCount++); + var next = new TestRequestDelegate(); + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute("whatever"))); + + // Act & Assert + await middleware.Invoke(context); + Assert.Equal(1, getPolicyCount); + Assert.Equal(1, getRequiredPolicyCount); + Assert.Equal(1, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(2, getPolicyCount); + Assert.Equal(2, getRequiredPolicyCount); + Assert.Equal(2, next.CalledCount); + + await middleware.Invoke(context); + Assert.Equal(3, getPolicyCount); + Assert.Equal(3, getRequiredPolicyCount); + Assert.Equal(3, next.CalledCount); + } + + [Fact] + public async Task Invoke_ValidClaimShouldNotFail() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireClaim("Permission", "CanViewPage").Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute())); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.True(next.Called); + } + + [Fact] + public async Task HasEndpointWithAuthAndAllowAnonymous_AnonymousUser_Allows() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(anonymous: true, endpoint: CreateEndpoint(new AuthorizeAttribute(), new AllowAnonymousAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.True(next.Called); + Assert.False(authenticationService.ChallengeCalled); + } + + [Fact] + public async Task HasEndpointWithAuth_AuthenticatedUser_Allows() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.True(next.Called); + Assert.False(authenticationService.ChallengeCalled); + } + + [Fact] + public async Task Invoke_AuthSchemesFailShouldSetEmptyPrincipalOnContext() + { + // Arrange + var policy = new AuthorizationPolicyBuilder("Fails").RequireAuthenticatedUser().Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + Assert.NotNull(context.User?.Identity); + Assert.True(authenticationService.AuthenticateCalled); + Assert.True(authenticationService.ChallengeCalled); + } + + [Fact] + public async Task Invoke_SingleValidClaimShouldSucceed() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireClaim("Permission", "CanViewComment", "CanViewPage").Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute())); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.True(next.Called); + } + + [Fact] + public async Task AuthZResourceShouldBeEndpoint() + { + // Arrange + object resource = null; + var policy = new AuthorizationPolicyBuilder().RequireAssertion(c => + { + resource = c.Resource; + return true; + }).Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var endpoint = CreateEndpoint(new AuthorizeAttribute()); + var context = GetHttpContext(endpoint: endpoint); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.Equal(endpoint, resource); + } + + [Fact] + public async Task Invoke_RequireUnknownRoleShouldForbid() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireRole("Wut").Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + Assert.False(authenticationService.ChallengeCalled); + Assert.True(authenticationService.ForbidCalled); + } + + [Fact] + public async Task Invoke_RequireUnknownRole_ForbidPerScheme() + { + // Arrange + var policy = new AuthorizationPolicyBuilder().RequireRole("Wut").AddAuthenticationSchemes("Basic", "Bearer").Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + Assert.Equal(2, authenticationService.ForbidCount); + } + + [Fact] + public async Task Invoke_InvalidClaimShouldForbid() + { + // Arrange + var policy = new AuthorizationPolicyBuilder() + .RequireClaim("Permission", "CanViewComment") + .Build(); + var policyProvider = new Mock(); + policyProvider.Setup(p => p.GetDefaultPolicyAsync()).ReturnsAsync(policy); + var next = new TestRequestDelegate(); + var authenticationService = new TestAuthenticationService(); + + var middleware = CreateMiddleware(next.Invoke, policyProvider.Object); + var context = GetHttpContext(endpoint: CreateEndpoint(new AuthorizeAttribute()), authenticationService: authenticationService); + + // Act + await middleware.Invoke(context); + + // Assert + Assert.False(next.Called); + Assert.False(authenticationService.ChallengeCalled); + Assert.True(authenticationService.ForbidCalled); + } + + private AuthorizationMiddleware CreateMiddleware(RequestDelegate requestDelegate = null, IAuthorizationPolicyProvider policyProvider = null) + { + requestDelegate = requestDelegate ?? ((context) => Task.CompletedTask); + + return new AuthorizationMiddleware(requestDelegate, policyProvider); + } + + private Endpoint CreateEndpoint(params object[] metadata) + { + return new Endpoint(context => Task.CompletedTask, new EndpointMetadataCollection(metadata), "Test endpoint"); + } + + private HttpContext GetHttpContext( + bool anonymous = false, + Action registerServices = null, + Endpoint endpoint = null, + IAuthenticationService authenticationService = null) + { + var basicPrincipal = new ClaimsPrincipal( + new ClaimsIdentity( + new Claim[] { + new Claim("Permission", "CanViewPage"), + new Claim(ClaimTypes.Role, "Administrator"), + new Claim(ClaimTypes.Role, "User"), + new Claim(ClaimTypes.NameIdentifier, "John")}, + "Basic")); + + var validUser = basicPrincipal; + + var bearerIdentity = new ClaimsIdentity( + new Claim[] { + new Claim("Permission", "CupBearer"), + new Claim(ClaimTypes.Role, "Token"), + new Claim(ClaimTypes.NameIdentifier, "John Bear")}, + "Bearer"); + + validUser.AddIdentity(bearerIdentity); + + // ServiceProvider + var serviceCollection = new ServiceCollection(); + + authenticationService = authenticationService ?? Mock.Of(); + + serviceCollection.AddSingleton(authenticationService); + serviceCollection.AddOptions(); + serviceCollection.AddLogging(); + serviceCollection.AddAuthorization(); + serviceCollection.AddAuthorizationPolicyEvaluator(); + registerServices?.Invoke(serviceCollection); + + var serviceProvider = serviceCollection.BuildServiceProvider(); + + //// HttpContext + var httpContext = new DefaultHttpContext(); + if (endpoint != null) + { + httpContext.SetEndpoint(endpoint); + } + httpContext.RequestServices = serviceProvider; + if (!anonymous) + { + httpContext.User = validUser; + } + + return httpContext; + } + + private class TestRequestDelegate + { + private readonly int _statusCode; + + public bool Called => CalledCount > 0; + public int CalledCount { get; private set; } + + public TestRequestDelegate(int statusCode = 200) + { + _statusCode = statusCode; + } + + public Task Invoke(HttpContext context) + { + CalledCount++; + context.Response.StatusCode = _statusCode; + return Task.CompletedTask; + } + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs index 714b26c7e..143be1b9b 100644 --- a/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs +++ b/test/Microsoft.AspNetCore.Authorization.Test/AuthorizationPolicyFacts.cs @@ -9,7 +9,7 @@ using Microsoft.Extensions.Options; using Xunit; -namespace Microsoft.AspNetCore.Authroization.Test +namespace Microsoft.AspNetCore.Authorization.Test { public class AuthorizationPolicyFacts { @@ -40,12 +40,12 @@ public async Task CanCombineAuthorizeAttributes() // Assert Assert.Equal(2, combined.AuthenticationSchemes.Count()); - Assert.True(combined.AuthenticationSchemes.Contains("dupe")); - Assert.True(combined.AuthenticationSchemes.Contains("roles")); + Assert.Contains("dupe", combined.AuthenticationSchemes); + Assert.Contains("roles", combined.AuthenticationSchemes); Assert.Equal(4, combined.Requirements.Count()); - Assert.True(combined.Requirements.Any(r => r is DenyAnonymousAuthorizationRequirement)); + Assert.Contains(combined.Requirements, r => r is DenyAnonymousAuthorizationRequirement); Assert.Equal(2, combined.Requirements.OfType().Count()); - Assert.Equal(1, combined.Requirements.OfType().Count()); + Assert.Single(combined.Requirements.OfType()); } [Fact] @@ -67,10 +67,10 @@ public async Task CanReplaceDefaultPolicy() // Assert Assert.Equal(2, combined.AuthenticationSchemes.Count()); - Assert.True(combined.AuthenticationSchemes.Contains("dupe")); - Assert.True(combined.AuthenticationSchemes.Contains("default")); + Assert.Contains("dupe", combined.AuthenticationSchemes); + Assert.Contains("default", combined.AuthenticationSchemes); Assert.Equal(2, combined.Requirements.Count()); - Assert.False(combined.Requirements.Any(r => r is DenyAnonymousAuthorizationRequirement)); + Assert.DoesNotContain(combined.Requirements, r => r is DenyAnonymousAuthorizationRequirement); Assert.Equal(2, combined.Requirements.OfType().Count()); } @@ -88,11 +88,11 @@ public async Task CombineMustTrimRoles() var combined = await AuthorizationPolicy.CombineAsync(provider, attributes); // Assert - Assert.True(combined.Requirements.Any(r => r is RolesAuthorizationRequirement)); + Assert.Contains(combined.Requirements, r => r is RolesAuthorizationRequirement); var rolesAuthorizationRequirement = combined.Requirements.OfType().First(); Assert.Equal(2, rolesAuthorizationRequirement.AllowedRoles.Count()); - Assert.True(rolesAuthorizationRequirement.AllowedRoles.Any(r => r.Equals("r1"))); - Assert.True(rolesAuthorizationRequirement.AllowedRoles.Any(r => r.Equals("r2"))); + Assert.Contains(rolesAuthorizationRequirement.AllowedRoles, r => r.Equals("r1")); + Assert.Contains(rolesAuthorizationRequirement.AllowedRoles, r => r.Equals("r2")); } [Fact] @@ -111,8 +111,8 @@ public async Task CombineMustTrimAuthenticationScheme() // Assert Assert.Equal(2, combined.AuthenticationSchemes.Count()); - Assert.True(combined.AuthenticationSchemes.Any(a => a.Equals("a1"))); - Assert.True(combined.AuthenticationSchemes.Any(a => a.Equals("a2"))); + Assert.Contains(combined.AuthenticationSchemes, a => a.Equals("a1")); + Assert.Contains(combined.AuthenticationSchemes, a => a.Equals("a2")); } [Fact] @@ -131,8 +131,8 @@ public async Task CombineMustIgnoreEmptyAuthenticationScheme() // Assert Assert.Equal(2, combined.AuthenticationSchemes.Count()); - Assert.True(combined.AuthenticationSchemes.Any(a => a.Equals("a1"))); - Assert.True(combined.AuthenticationSchemes.Any(a => a.Equals("a2"))); + Assert.Contains(combined.AuthenticationSchemes, a => a.Equals("a1")); + Assert.Contains(combined.AuthenticationSchemes, a => a.Equals("a2")); } [Fact] @@ -149,11 +149,11 @@ public async Task CombineMustIgnoreEmptyRoles() var combined = await AuthorizationPolicy.CombineAsync(provider, attributes); // Assert - Assert.True(combined.Requirements.Any(r => r is RolesAuthorizationRequirement)); + Assert.Contains(combined.Requirements, r => r is RolesAuthorizationRequirement); var rolesAuthorizationRequirement = combined.Requirements.OfType().First(); Assert.Equal(2, rolesAuthorizationRequirement.AllowedRoles.Count()); - Assert.True(rolesAuthorizationRequirement.AllowedRoles.Any(r => r.Equals("r1"))); - Assert.True(rolesAuthorizationRequirement.AllowedRoles.Any(r => r.Equals("r2"))); + Assert.Contains(rolesAuthorizationRequirement.AllowedRoles, r => r.Equals("r1")); + Assert.Contains(rolesAuthorizationRequirement.AllowedRoles, r => r.Equals("r2")); } } } \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs b/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs index ef17b9462..230e95c4c 100644 --- a/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs +++ b/test/Microsoft.AspNetCore.Authorization.Test/DefaultAuthorizationServiceTests.cs @@ -1025,6 +1025,11 @@ public Task GetDefaultPolicyAsync() return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); } + public Task GetRequiredPolicyAsync() + { + return Task.FromResult(null); + } + public Task GetPolicyAsync(string policyName) { return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); @@ -1059,6 +1064,11 @@ public Task GetDefaultPolicyAsync() return Task.FromResult(new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build()); } + public Task GetRequiredPolicyAsync() + { + return Task.FromResult(null); + } + public Task GetPolicyAsync(string policyName) { return Task.FromResult(new AuthorizationPolicyBuilder().RequireClaim(policyName).Build()); diff --git a/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj b/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj index 6eb74aeeb..9ff42a070 100644 --- a/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj +++ b/test/Microsoft.AspNetCore.Authorization.Test/Microsoft.AspNetCore.Authorization.Test.csproj @@ -1,10 +1,8 @@  - - - netcoreapp2.0;net461 - netcoreapp2.0 + netcoreapp3.0 + $(NoWarn);NU1605 @@ -13,13 +11,11 @@ - - - - - - - + + + + + diff --git a/test/Microsoft.AspNetCore.Authorization.Test/TestObjects/TestAuthenticationService.cs b/test/Microsoft.AspNetCore.Authorization.Test/TestObjects/TestAuthenticationService.cs new file mode 100644 index 000000000..0c17693d1 --- /dev/null +++ b/test/Microsoft.AspNetCore.Authorization.Test/TestObjects/TestAuthenticationService.cs @@ -0,0 +1,58 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Linq; +using System.Security.Claims; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Authentication; +using Microsoft.AspNetCore.Http; + +namespace Microsoft.AspNetCore.Authorization.Test.TestObjects +{ + public class TestAuthenticationService : IAuthenticationService + { + public bool ChallengeCalled => ChallengeCount > 0; + public bool ForbidCalled => ForbidCount > 0; + public bool AuthenticateCalled => AuthenticateCount > 0; + + public int ChallengeCount { get; private set; } + public int ForbidCount { get; private set; } + public int AuthenticateCount { get; private set; } + + public Task AuthenticateAsync(HttpContext context, string scheme) + { + AuthenticateCount++; + + var identity = context.User.Identities.SingleOrDefault(i => i.AuthenticationType == scheme); + if (identity != null) + { + return Task.FromResult(AuthenticateResult.Success(new AuthenticationTicket(new ClaimsPrincipal(identity), scheme))); + } + + return Task.FromResult(AuthenticateResult.Fail("Denied")); + } + + public Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + ChallengeCount++; + return Task.CompletedTask; + } + + public Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + ForbidCount++; + return Task.CompletedTask; + } + + public Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) + { + throw new NotImplementedException(); + } + + public Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) + { + throw new NotImplementedException(); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs index 143e1d254..e645745b3 100644 --- a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs +++ b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/CookieChunkingTests.cs @@ -17,7 +17,7 @@ public void AppendLargeCookie_Appended() string testString = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"; new ChunkingCookieManager() { ChunkSize = null }.AppendResponseCookie(context, "TestCookie", testString, new CookieOptions()); var values = context.Response.Headers["Set-Cookie"]; - Assert.Equal(1, values.Count); + Assert.Single(values); Assert.Equal("TestCookie=" + testString + "; path=/; samesite=lax", values[0]); } @@ -81,7 +81,8 @@ public void GetLargeChunkedCookieWithMissingChunk_ThrowingEnabled_Throws() "TestCookieC7=STUVWXYZ" }; - Assert.Throws(() => new ChunkingCookieManager().GetRequestCookie(context, "TestCookie")); + Assert.Throws(() => new ChunkingCookieManager() { ThrowForPartialCookies = true } + .GetRequestCookie(context, "TestCookie")); } [Fact] diff --git a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj index b578f5dcf..2f38abff9 100644 --- a/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj +++ b/test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test/Microsoft.AspNetCore.ChunkingCookieManager.Sources.Test.csproj @@ -1,10 +1,7 @@  - - - netcoreapp2.0;net461 - netcoreapp2.0 + netcoreapp3.0 @@ -12,10 +9,7 @@ - - - - + diff --git a/test/Microsoft.AspNetCore.CookiePolicy.Test/CookieConsentTests.cs b/test/Microsoft.AspNetCore.CookiePolicy.Test/CookieConsentTests.cs new file mode 100644 index 000000000..fffb8cc88 --- /dev/null +++ b/test/Microsoft.AspNetCore.CookiePolicy.Test/CookieConsentTests.cs @@ -0,0 +1,660 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.IO; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Http.Features; +using Microsoft.AspNetCore.TestHost; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Net.Http.Headers; +using Xunit; + +namespace Microsoft.AspNetCore.CookiePolicy.Test +{ + public class CookieConsentTests + { + [Fact] + public async Task ConsentChecksOffByDefault() + { + var httpContext = await RunTestAsync(options => { }, requestContext => { }, context => + { + var feature = context.Features.Get(); + Assert.False(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.True(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value"); + return Task.CompletedTask; + }); + Assert.Equal("Test=Value; path=/; samesite=lax", httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task ConsentEnabledForTemplateScenario() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value"); + return Task.CompletedTask; + }); + Assert.Empty(httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task NonEssentialCookiesWithOptionsExcluded() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value", new CookieOptions() { IsEssential = false }); + return Task.CompletedTask; + }); + Assert.Empty(httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task NonEssentialCookiesCanBeAllowedViaOnAppendCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + options.OnAppendCookie = context => + { + Assert.True(context.IsConsentNeeded); + Assert.False(context.HasConsent); + Assert.False(context.IssueCookie); + context.IssueCookie = true; + }; + }, + requestContext => { }, context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value", new CookieOptions() { IsEssential = false }); + return Task.CompletedTask; + }); + Assert.Equal("Test=Value; path=/; samesite=lax", httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task NeedsConsentDoesNotPreventEssentialCookies() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value", new CookieOptions() { IsEssential = true }); + return Task.CompletedTask; + }); + Assert.Equal("Test=Value; path=/; samesite=lax", httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task EssentialCookiesCanBeExcludedByOnAppendCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + options.OnAppendCookie = context => + { + Assert.True(context.IsConsentNeeded); + Assert.True(context.HasConsent); + Assert.True(context.IssueCookie); + context.IssueCookie = false; + }; + }, + requestContext => + { + requestContext.Request.Headers[HeaderNames.Cookie] = ".AspNet.Consent=yes"; + }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value", new CookieOptions() { IsEssential = true }); + return Task.CompletedTask; + }); + Assert.Empty(httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task HasConsentReadsRequestCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => + { + requestContext.Request.Headers[HeaderNames.Cookie] = ".AspNet.Consent=yes"; + }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value"); + return Task.CompletedTask; + }); + Assert.Equal("Test=Value; path=/; samesite=lax", httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task HasConsentIgnoresInvalidRequestCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => + { + requestContext.Request.Headers[HeaderNames.Cookie] = ".AspNet.Consent=IAmATeapot"; + }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value"); + return Task.CompletedTask; + }); + Assert.Empty(httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task GrantConsentSetsCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + feature.GrantConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + context.Response.Cookies.Append("Test", "Value"); + return Task.CompletedTask; + }); + + var cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers[HeaderNames.SetCookie]); + Assert.Equal(2, cookies.Count); + var consentCookie = cookies[0]; + Assert.Equal(".AspNet.Consent", consentCookie.Name); + Assert.Equal("yes", consentCookie.Value); + Assert.True(consentCookie.Expires.HasValue); + Assert.True(consentCookie.Expires.Value > DateTimeOffset.Now + TimeSpan.FromDays(364)); + Assert.Equal(Net.Http.Headers.SameSiteMode.Lax, consentCookie.SameSite); + Assert.NotNull(consentCookie.Expires); + var testCookie = cookies[1]; + Assert.Equal("Test", testCookie.Name); + Assert.Equal("Value", testCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Lax, testCookie.SameSite); + Assert.Null(testCookie.Expires); + } + + [Fact] + public async Task GrantConsentAppliesPolicyToConsentCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + options.MinimumSameSitePolicy = Http.SameSiteMode.Strict; + options.OnAppendCookie = context => + { + Assert.Equal(".AspNet.Consent", context.CookieName); + Assert.Equal("yes", context.CookieValue); + Assert.Equal(Http.SameSiteMode.Strict, context.CookieOptions.SameSite); + context.CookieName += "1"; + context.CookieValue += "1"; + }; + }, + requestContext => { }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + feature.GrantConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + return Task.CompletedTask; + }); + + var cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers[HeaderNames.SetCookie]); + Assert.Equal(1, cookies.Count); + var consentCookie = cookies[0]; + Assert.Equal(".AspNet.Consent1", consentCookie.Name); + Assert.Equal("yes1", consentCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Strict, consentCookie.SameSite); + Assert.NotNull(consentCookie.Expires); + } + + [Fact] + public async Task GrantConsentWhenAlreadyHasItDoesNotSetCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => + { + requestContext.Request.Headers[HeaderNames.Cookie] = ".AspNet.Consent=yes"; + }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + feature.GrantConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + context.Response.Cookies.Append("Test", "Value"); + return Task.CompletedTask; + }); + + Assert.Equal("Test=Value; path=/; samesite=lax", httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task GrantConsentAfterResponseStartsSetsHasConsentButDoesNotSetCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, + async context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + await context.Response.WriteAsync("Started."); + + feature.GrantConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + Assert.Throws(() => context.Response.Cookies.Append("Test", "Value")); + + await context.Response.WriteAsync("Granted."); + }); + + var reader = new StreamReader(httpContext.Response.Body); + Assert.Equal("Started.Granted.", await reader.ReadToEndAsync()); + Assert.Empty(httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task WithdrawConsentWhenNotHasConsentNoOps() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + feature.WithdrawConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + context.Response.Cookies.Append("Test", "Value"); + return Task.CompletedTask; + }); + + Assert.Empty(httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task WithdrawConsentDeletesCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => + { + requestContext.Request.Headers[HeaderNames.Cookie] = ".AspNet.Consent=yes"; + }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value1"); + + feature.WithdrawConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + context.Response.Cookies.Append("Test", "Value2"); + return Task.CompletedTask; + }); + + var cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers[HeaderNames.SetCookie]); + Assert.Equal(2, cookies.Count); + var testCookie = cookies[0]; + Assert.Equal("Test", testCookie.Name); + Assert.Equal("Value1", testCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Lax, testCookie.SameSite); + Assert.Null(testCookie.Expires); + var consentCookie = cookies[1]; + Assert.Equal(".AspNet.Consent", consentCookie.Name); + Assert.Equal("", consentCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Lax, consentCookie.SameSite); + Assert.NotNull(consentCookie.Expires); + } + + [Fact] + public async Task WithdrawConsentAppliesPolicyToDeleteCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + options.MinimumSameSitePolicy = Http.SameSiteMode.Strict; + options.OnDeleteCookie = context => + { + Assert.Equal(".AspNet.Consent", context.CookieName); + context.CookieName += "1"; + }; + }, + requestContext => + { + requestContext.Request.Headers[HeaderNames.Cookie] = ".AspNet.Consent=yes"; + }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + feature.WithdrawConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + return Task.CompletedTask; + }); + + var cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers[HeaderNames.SetCookie]); + Assert.Equal(1, cookies.Count); + var consentCookie = cookies[0]; + Assert.Equal(".AspNet.Consent1", consentCookie.Name); + Assert.Equal("", consentCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Strict, consentCookie.SameSite); + Assert.NotNull(consentCookie.Expires); + } + + [Fact] + public async Task WithdrawConsentAfterResponseHasStartedDoesNotDeleteCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => + { + requestContext.Request.Headers[HeaderNames.Cookie] = ".AspNet.Consent=yes"; + }, + async context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + context.Response.Cookies.Append("Test", "Value1"); + + await context.Response.WriteAsync("Started."); + + feature.WithdrawConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + // Doesn't throw the normal InvalidOperationException because the cookie is never written + context.Response.Cookies.Append("Test", "Value2"); + + await context.Response.WriteAsync("Withdrawn."); + }); + + var reader = new StreamReader(httpContext.Response.Body); + Assert.Equal("Started.Withdrawn.", await reader.ReadToEndAsync()); + Assert.Equal("Test=Value1; path=/; samesite=lax", httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task DeleteCookieDoesNotRequireConsent() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + context.Response.Cookies.Delete("Test"); + return Task.CompletedTask; + }); + + var cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers[HeaderNames.SetCookie]); + Assert.Equal(1, cookies.Count); + var testCookie = cookies[0]; + Assert.Equal("Test", testCookie.Name); + Assert.Equal("", testCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Lax, testCookie.SameSite); + Assert.NotNull(testCookie.Expires); + } + + [Fact] + public async Task OnDeleteCookieCanSuppressCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + options.OnDeleteCookie = context => + { + Assert.True(context.IsConsentNeeded); + Assert.False(context.HasConsent); + Assert.True(context.IssueCookie); + context.IssueCookie = false; + }; + }, + requestContext => { }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + context.Response.Cookies.Delete("Test"); + return Task.CompletedTask; + }); + + Assert.Empty(httpContext.Response.Headers[HeaderNames.SetCookie]); + } + + [Fact] + public async Task CreateConsentCookieMatchesGrantConsentCookie() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + }, + requestContext => { }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + feature.GrantConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + var cookie = feature.CreateConsentCookie(); + context.Response.Headers["ManualCookie"] = cookie; + + return Task.CompletedTask; + }); + + var cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers[HeaderNames.SetCookie]); + Assert.Equal(1, cookies.Count); + var consentCookie = cookies[0]; + Assert.Equal(".AspNet.Consent", consentCookie.Name); + Assert.Equal("yes", consentCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Lax, consentCookie.SameSite); + Assert.NotNull(consentCookie.Expires); + + cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers["ManualCookie"]); + Assert.Equal(1, cookies.Count); + var manualCookie = cookies[0]; + Assert.Equal(consentCookie.Name, manualCookie.Name); + Assert.Equal(consentCookie.Value, manualCookie.Value); + Assert.Equal(consentCookie.SameSite, manualCookie.SameSite); + Assert.NotNull(manualCookie.Expires); // Expires may not exactly match to the second. + } + + [Fact] + public async Task CreateConsentCookieAppliesPolicy() + { + var httpContext = await RunTestAsync(options => + { + options.CheckConsentNeeded = context => true; + options.MinimumSameSitePolicy = Http.SameSiteMode.Strict; + options.OnAppendCookie = context => + { + Assert.Equal(".AspNet.Consent", context.CookieName); + Assert.Equal("yes", context.CookieValue); + Assert.Equal(Http.SameSiteMode.Strict, context.CookieOptions.SameSite); + context.CookieName += "1"; + context.CookieValue += "1"; + }; + }, + requestContext => { }, + context => + { + var feature = context.Features.Get(); + Assert.True(feature.IsConsentNeeded); + Assert.False(feature.HasConsent); + Assert.False(feature.CanTrack); + + feature.GrantConsent(); + + Assert.True(feature.IsConsentNeeded); + Assert.True(feature.HasConsent); + Assert.True(feature.CanTrack); + + var cookie = feature.CreateConsentCookie(); + context.Response.Headers["ManualCookie"] = cookie; + + return Task.CompletedTask; + }); + + var cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers[HeaderNames.SetCookie]); + Assert.Equal(1, cookies.Count); + var consentCookie = cookies[0]; + Assert.Equal(".AspNet.Consent1", consentCookie.Name); + Assert.Equal("yes1", consentCookie.Value); + Assert.Equal(Net.Http.Headers.SameSiteMode.Strict, consentCookie.SameSite); + Assert.NotNull(consentCookie.Expires); + + cookies = SetCookieHeaderValue.ParseList(httpContext.Response.Headers["ManualCookie"]); + Assert.Equal(1, cookies.Count); + var manualCookie = cookies[0]; + Assert.Equal(consentCookie.Name, manualCookie.Name); + Assert.Equal(consentCookie.Value, manualCookie.Value); + Assert.Equal(consentCookie.SameSite, manualCookie.SameSite); + Assert.NotNull(manualCookie.Expires); // Expires may not exactly match to the second. + } + + private Task RunTestAsync(Action configureOptions, Action configureRequest, RequestDelegate handleRequest) + { + var builder = new WebHostBuilder() + .ConfigureServices(services => + { + services.Configure(configureOptions); + }) + .Configure(app => + { + app.UseCookiePolicy(); + app.Run(handleRequest); + }); + var server = new TestServer(builder); + return server.SendAsync(configureRequest); + } + } +} \ No newline at end of file diff --git a/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs b/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs index 7c34f950a..a2592e557 100644 --- a/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs +++ b/test/Microsoft.AspNetCore.CookiePolicy.Test/CookiePolicyTests.cs @@ -102,7 +102,7 @@ await RunTest("/secureSame", Assert.Equal("A=A; path=/; samesite=lax", transaction.SetCookie[0]); Assert.Equal("B=B; path=/; samesite=lax", transaction.SetCookie[1]); Assert.Equal("C=C; path=/; samesite=lax", transaction.SetCookie[2]); - Assert.Equal("D=D; path=/; samesite=lax", transaction.SetCookie[3]); + Assert.Equal("D=D; path=/; secure; samesite=lax", transaction.SetCookie[3]); }), new RequestTest("https://example.com/secureSame", transaction => diff --git a/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj b/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj index f513de4b3..3dc3df2e2 100644 --- a/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj +++ b/test/Microsoft.AspNetCore.CookiePolicy.Test/Microsoft.AspNetCore.CookiePolicy.Test.csproj @@ -1,9 +1,7 @@ - + - - netcoreapp2.0;net461 - netcoreapp2.0 + netcoreapp3.0 @@ -12,11 +10,8 @@ - - - - - + + diff --git a/test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs b/test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs index e2e4fd7d0..24bf09722 100644 --- a/test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs +++ b/test/Microsoft.Owin.Security.Interop.Test/CookieInteropTests.cs @@ -1,6 +1,10 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +/* See https://github.com/aspnet/AspNetCore/issues/4074. + +This test is was disabled as a part of changing frameworks. This test will need to be re-written using separate .NET Core and .NET Framework processes. + using System.Collections.Generic; using System.IO; using System.Linq; @@ -330,3 +334,4 @@ private class Transaction } } +*/ diff --git a/test/Microsoft.Owin.Security.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj b/test/Microsoft.Owin.Security.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj index 14c0aa2f1..57b224ef6 100644 --- a/test/Microsoft.Owin.Security.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj +++ b/test/Microsoft.Owin.Security.Interop.Test/Microsoft.Owin.Security.Interop.Test.csproj @@ -1,25 +1,19 @@  - - net461 + + false - - - - - - - - - + + + diff --git a/test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs b/test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs index b14ea0d74..ccff1e936 100644 --- a/test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs +++ b/test/Microsoft.Owin.Security.Interop.Test/TicketInteropTests.cs @@ -1,6 +1,8 @@ // Copyright (c) .NET Foundation. All rights reserved. // Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. +/* + using System; using System.Linq; using System.Security.Claims; @@ -35,7 +37,7 @@ public void NewSerializerCanReadInteropTicket() var newTicket = newSerializer.Deserialize(bytes); Assert.NotNull(newTicket); - Assert.Equal(1, newTicket.Principal.Identities.Count()); + Assert.Single(newTicket.Principal.Identities); var newIdentity = newTicket.Principal.Identity as ClaimsIdentity; Assert.NotNull(newIdentity); Assert.Equal("scheme", newIdentity.AuthenticationType); @@ -87,5 +89,6 @@ public void InteropSerializerCanReadNewTicket() } } } +*/ diff --git a/version.props b/version.props new file mode 100644 index 000000000..71a78cddd --- /dev/null +++ b/version.props @@ -0,0 +1,12 @@ + + + 3.0.0 + alpha1 + $(VersionPrefix) + $(VersionPrefix)-$(VersionSuffix)-final + t000 + a- + $(FeatureBranchVersionPrefix)$(VersionSuffix)-$([System.Text.RegularExpressions.Regex]::Replace('$(FeatureBranchVersionSuffix)', '[^\w-]', '-')) + $(VersionSuffix)-$(BuildNumber) + + diff --git a/version.xml b/version.xml deleted file mode 100644 index 3c05022b7..000000000 --- a/version.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - dev - 2.1.0 - preview1 - -