├── .gitignore ├── .travis.yml ├── Directory.Build.props ├── IdentityServer4.Contrib.AspNetCore.Testing.sln ├── IdentityServer4.Contrib.AspNetCore.Testing.sln.DotSettings ├── LICENSE ├── README.md ├── assets └── icon.jpg ├── changelog.md ├── docs └── IdentityServerTestWebHostBuilder.md ├── global.json ├── icon.jpg ├── publish-packages.sh ├── src ├── Directory.Build.props └── IdentityServer4.Contrib.AspNetCore.Testing │ ├── Builder │ ├── AbstractIdentityServerHostBuilder.cs │ ├── IdentityServerHostBuilder.cs │ ├── IdentityServerTestHostBuilder.cs │ └── IdentityServerTestWebHostBuilder.cs │ ├── Configuration │ ├── ClientConfiguration.cs │ └── UserLoginConfiguration.cs │ ├── IdentityServer4.Contrib.AspNetCore.Testing.csproj │ ├── Misc │ ├── DefaultLoggerProvider.cs │ └── IdentityServerEventCaptureStore.cs │ ├── Services │ ├── AbstractIdentityServerProxy.cs │ ├── IdentityServerHostProxy.cs │ ├── IdentityServerProxy.cs │ └── IdentityServerWebHostProxy.cs │ └── Sinks │ └── EventCaptureSink.cs ├── test.sh └── test ├── IdentityServer4.Api ├── AuthController.cs ├── IdentityServer4.Api.csproj ├── Program.cs └── Startup.cs ├── IdentityServer4.Contrib.AspNetCore.Testing.Tests ├── IdentityServer4.Contrib.AspNetCore.Testing.Tests.csproj ├── IdentityServerHostProxyTests.cs ├── IdentityServerTestHostBuilderTests.cs ├── IdentityServerWebHostBuilderTests.cs ├── IdentityServerWebHostProxyTests.cs ├── Services │ └── SimpleProfileService.cs ├── Shared │ ├── ContainerBuilderConfiguration.cs │ └── Dependency.cs ├── Validators │ ├── ExtensionsGrantValidator.cs │ ├── ResourceOwnerValidatorWithDependencies.cs │ └── SimpleResourceOwnerPasswordValidator.cs └── testappsettings.json └── IdentityServer4.Server ├── IdentityServer4.Server.csproj ├── Models ├── ApiResources.cs ├── Clients.cs ├── IdentityResources.cs └── TestUsers.cs ├── Program.cs └── Startup.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | ## 4 | ## Get latest from https://github.com/github/gitignore/blob/master/VisualStudio.gitignore 5 | 6 | # User-specific files 7 | *.suo 8 | *.user 9 | *.userosscache 10 | *.sln.docstates 11 | 12 | # User-specific files (MonoDevelop/Xamarin Studio) 13 | *.userprefs 14 | 15 | # Build results 16 | [Dd]ebug/ 17 | [Dd]ebugPublic/ 18 | [Rr]elease/ 19 | [Rr]eleases/ 20 | x64/ 21 | x86/ 22 | bld/ 23 | [Bb]in/ 24 | [Oo]bj/ 25 | [Ll]og/ 26 | 27 | # Visual Studio 2015/2017 cache/options directory 28 | .vs/ 29 | # Uncomment if you have tasks that create the project's static files in wwwroot 30 | #wwwroot/ 31 | 32 | # Visual Studio 2017 auto generated files 33 | Generated\ Files/ 34 | 35 | # MSTest test Results 36 | [Tt]est[Rr]esult*/ 37 | [Bb]uild[Ll]og.* 38 | 39 | # NUNIT 40 | *.VisualState.xml 41 | TestResult.xml 42 | 43 | # Build Results of an ATL Project 44 | [Dd]ebugPS/ 45 | [Rr]eleasePS/ 46 | dlldata.c 47 | 48 | # Benchmark Results 49 | BenchmarkDotNet.Artifacts/ 50 | 51 | # .NET Core 52 | project.lock.json 53 | project.fragment.lock.json 54 | artifacts/ 55 | **/Properties/launchSettings.json 56 | 57 | # StyleCop 58 | StyleCopReport.xml 59 | 60 | # Files built by Visual Studio 61 | *_i.c 62 | *_p.c 63 | *_i.h 64 | *.ilk 65 | *.meta 66 | *.obj 67 | *.iobj 68 | *.pch 69 | *.pdb 70 | *.ipdb 71 | *.pgc 72 | *.pgd 73 | *.rsp 74 | *.sbr 75 | *.tlb 76 | *.tli 77 | *.tlh 78 | *.tmp 79 | *.tmp_proj 80 | *.log 81 | *.vspscc 82 | *.vssscc 83 | .builds 84 | *.pidb 85 | *.svclog 86 | *.scc 87 | 88 | # Chutzpah Test files 89 | _Chutzpah* 90 | 91 | # Visual C++ cache files 92 | ipch/ 93 | *.aps 94 | *.ncb 95 | *.opendb 96 | *.opensdf 97 | *.sdf 98 | *.cachefile 99 | *.VC.db 100 | *.VC.VC.opendb 101 | 102 | # Visual Studio profiler 103 | *.psess 104 | *.vsp 105 | *.vspx 106 | *.sap 107 | 108 | # Visual Studio Trace Files 109 | *.e2e 110 | 111 | # TFS 2012 Local Workspace 112 | $tf/ 113 | 114 | # Guidance Automation Toolkit 115 | *.gpState 116 | 117 | # ReSharper is a .NET coding add-in 118 | _ReSharper*/ 119 | *.[Rr]e[Ss]harper 120 | *.DotSettings.user 121 | 122 | # JustCode is a .NET coding add-in 123 | .JustCode 124 | 125 | # TeamCity is a build add-in 126 | _TeamCity* 127 | 128 | # DotCover is a Code Coverage Tool 129 | *.dotCover 130 | 131 | # AxoCover is a Code Coverage Tool 132 | .axoCover/* 133 | !.axoCover/settings.json 134 | 135 | # Visual Studio code coverage results 136 | *.coverage 137 | *.coveragexml 138 | 139 | # NCrunch 140 | _NCrunch_* 141 | .*crunch*.local.xml 142 | nCrunchTemp_* 143 | 144 | # MightyMoose 145 | *.mm.* 146 | AutoTest.Net/ 147 | 148 | # Web workbench (sass) 149 | .sass-cache/ 150 | 151 | # Installshield output folder 152 | [Ee]xpress/ 153 | 154 | # DocProject is a documentation generator add-in 155 | DocProject/buildhelp/ 156 | DocProject/Help/*.HxT 157 | DocProject/Help/*.HxC 158 | DocProject/Help/*.hhc 159 | DocProject/Help/*.hhk 160 | DocProject/Help/*.hhp 161 | DocProject/Help/Html2 162 | DocProject/Help/html 163 | 164 | # Click-Once directory 165 | publish/ 166 | 167 | # Publish Web Output 168 | *.[Pp]ublish.xml 169 | *.azurePubxml 170 | # Note: Comment the next line if you want to checkin your web deploy settings, 171 | # but database connection strings (with potential passwords) will be unencrypted 172 | *.pubxml 173 | *.publishproj 174 | 175 | # Microsoft Azure Web App publish settings. Comment the next line if you want to 176 | # checkin your Azure Web App publish settings, but sensitive information contained 177 | # in these scripts will be unencrypted 178 | PublishScripts/ 179 | 180 | # NuGet Packages 181 | *.nupkg 182 | # The packages folder can be ignored because of Package Restore 183 | **/[Pp]ackages/* 184 | # except build/, which is used as an MSBuild target. 185 | !**/[Pp]ackages/build/ 186 | # Uncomment if necessary however generally it will be regenerated when needed 187 | #!**/[Pp]ackages/repositories.config 188 | # NuGet v3's project.json files produces more ignorable files 189 | *.nuget.props 190 | *.nuget.targets 191 | 192 | # Microsoft Azure Build Output 193 | csx/ 194 | *.build.csdef 195 | 196 | # Microsoft Azure Emulator 197 | ecf/ 198 | rcf/ 199 | 200 | # Windows Store app package directories and files 201 | AppPackages/ 202 | BundleArtifacts/ 203 | Package.StoreAssociation.xml 204 | _pkginfo.txt 205 | *.appx 206 | 207 | # Visual Studio cache files 208 | # files ending in .cache can be ignored 209 | *.[Cc]ache 210 | # but keep track of directories ending in .cache 211 | !*.[Cc]ache/ 212 | 213 | # Others 214 | ClientBin/ 215 | ~$* 216 | *~ 217 | *.dbmdl 218 | *.dbproj.schemaview 219 | *.jfm 220 | *.pfx 221 | *.publishsettings 222 | orleans.codegen.cs 223 | 224 | # Including strong name files can present a security risk 225 | # (https://github.com/github/gitignore/pull/2483#issue-259490424) 226 | #*.snk 227 | 228 | # Since there are multiple workflows, uncomment next line to ignore bower_components 229 | # (https://github.com/github/gitignore/pull/1529#issuecomment-104372622) 230 | #bower_components/ 231 | 232 | # RIA/Silverlight projects 233 | Generated_Code/ 234 | 235 | # Backup & report files from converting an old project file 236 | # to a newer Visual Studio version. Backup files are not needed, 237 | # because we have git ;-) 238 | _UpgradeReport_Files/ 239 | Backup*/ 240 | UpgradeLog*.XML 241 | UpgradeLog*.htm 242 | ServiceFabricBackup/ 243 | *.rptproj.bak 244 | 245 | # SQL Server files 246 | *.mdf 247 | *.ldf 248 | *.ndf 249 | 250 | # Business Intelligence projects 251 | *.rdl.data 252 | *.bim.layout 253 | *.bim_*.settings 254 | *.rptproj.rsuser 255 | 256 | # Microsoft Fakes 257 | FakesAssemblies/ 258 | 259 | # GhostDoc plugin setting file 260 | *.GhostDoc.xml 261 | 262 | # Node.js Tools for Visual Studio 263 | .ntvs_analysis.dat 264 | node_modules/ 265 | 266 | # Visual Studio 6 build log 267 | *.plg 268 | 269 | # Visual Studio 6 workspace options file 270 | *.opt 271 | 272 | # Visual Studio 6 auto-generated workspace file (contains which files were open etc.) 273 | *.vbw 274 | 275 | # Visual Studio LightSwitch build output 276 | **/*.HTMLClient/GeneratedArtifacts 277 | **/*.DesktopClient/GeneratedArtifacts 278 | **/*.DesktopClient/ModelManifest.xml 279 | **/*.Server/GeneratedArtifacts 280 | **/*.Server/ModelManifest.xml 281 | _Pvt_Extensions 282 | 283 | # Paket dependency manager 284 | .paket/paket.exe 285 | paket-files/ 286 | 287 | # FAKE - F# Make 288 | .fake/ 289 | 290 | # JetBrains Rider 291 | .idea/ 292 | *.sln.iml 293 | 294 | # CodeRush 295 | .cr/ 296 | 297 | # Python Tools for Visual Studio (PTVS) 298 | __pycache__/ 299 | *.pyc 300 | 301 | # Cake - Uncomment if you are using it 302 | # tools/** 303 | # !tools/packages.config 304 | 305 | # Tabs Studio 306 | *.tss 307 | 308 | # Telerik's JustMock configuration file 309 | *.jmconfig 310 | 311 | # BizTalk build output 312 | *.btp.cs 313 | *.btm.cs 314 | *.odx.cs 315 | *.xsd.cs 316 | 317 | # OpenCover UI analysis results 318 | OpenCover/ 319 | 320 | # Azure Stream Analytics local run output 321 | ASALocalRun/ 322 | 323 | # MSBuild Binary and Structured Log 324 | *.binlog 325 | 326 | # NVidia Nsight GPU debugger configuration file 327 | *.nvuser 328 | 329 | # MFractors (Xamarin productivity tool) working folder 330 | .mfractor/ 331 | 332 | # JetBrains Rider 333 | .idea/ 334 | 335 | # CodeCoverage 336 | coverage/ 337 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: bionic 3 | language: csharp 4 | mono: none 5 | dotnet: 3.1.403 6 | solution: IdentityServer4.Contrib.AspNetCore.Testing.sln 7 | 8 | addons: 9 | apt: 10 | update: true 11 | 12 | stages: 13 | - build 14 | - test 15 | - publish 16 | 17 | jobs: 18 | include: 19 | - stage: build 20 | name: dotnet-build 21 | if: tag IS NOT present 22 | script: 23 | - dotnet build 24 | 25 | - stage: test 26 | name: dotnet-test 27 | before_script: 28 | - sudo apt-get update && sudo apt-get install curl -y 29 | script: 30 | - ./test.sh 31 | 32 | - stage: publish 33 | name: publish-packages 34 | if: tag IS present 35 | script: ./publish-packages.sh "$NUGET_SOURCE" "$IDENTITYSERVER_NUGET_APIKEY" "$TRAVIS_TAG" 36 | 37 | -------------------------------------------------------------------------------- /Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8.0 4 | true 5 | 2021©Sami Al Khatib 6 | Sami Al Khatib 7 | OpenID Connect and OAuth 2.0 Testing-Framework for ASP.NET Core and IdentityServer4 8 | OAuth2;OAuth 2.0;OpenID Connect;Security;Identity;IdentityServer;IdentityServer4;AspNetCore;AspNetCore Testing;IntegrationTesting;Testing;IdentityServer Proxy; 9 | icon.png 10 | https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing 11 | MIT 12 | https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/blob/master/changelog.md 13 | git 14 | git://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing 15 | 5.0.0 16 | 5.0.0 17 | true 18 | true 19 | S3881 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | all 29 | 30 | 31 | all 32 | 33 | 34 | -------------------------------------------------------------------------------- /IdentityServer4.Contrib.AspNetCore.Testing.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 16 4 | VisualStudioVersion = 16.0.28729.10 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{171C9B08-8431-4E4C-A343-CA8D277A2419}" 7 | ProjectSection(SolutionItems) = preProject 8 | src\Directory.Build.props = src\Directory.Build.props 9 | EndProjectSection 10 | EndProject 11 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{FED78C85-8EE1-4C8E-9758-44CAF0070C10}" 12 | EndProject 13 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer4.Contrib.AspNetCore.Testing", "src\IdentityServer4.Contrib.AspNetCore.Testing\IdentityServer4.Contrib.AspNetCore.Testing.csproj", "{5169FE1E-64A2-4BF0-9DA0-2480107FD444}" 14 | EndProject 15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "misc", "misc", "{E2FB8DD0-B811-4E10-ADD0-1CA4E934EBAD}" 16 | ProjectSection(SolutionItems) = preProject 17 | .gitignore = .gitignore 18 | changelog.md = changelog.md 19 | LICENSE = LICENSE 20 | publish-packages.sh = publish-packages.sh 21 | .travis.yml = .travis.yml 22 | README.md = README.md 23 | global.json = global.json 24 | test.sh = test.sh 25 | EndProjectSection 26 | EndProject 27 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer4.Contrib.AspNetCore.Testing.Tests", "test\IdentityServer4.Contrib.AspNetCore.Testing.Tests\IdentityServer4.Contrib.AspNetCore.Testing.Tests.csproj", "{4EB5ED1E-87BA-4E7A-AE4E-CDAA8923483A}" 28 | EndProject 29 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentityServer4.Server", "test\IdentityServer4.Server\IdentityServer4.Server.csproj", "{C5F387EC-B6A4-4A5C-8C35-9411CE71B35A}" 30 | EndProject 31 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentityServer4.Api", "test\IdentityServer4.Api\IdentityServer4.Api.csproj", "{CBDE7CC3-DC74-44CE-86E7-6A902504B227}" 32 | EndProject 33 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{4D0745D0-8182-488D-8E06-403D9EC57785}" 34 | ProjectSection(SolutionItems) = preProject 35 | docs\IdentityServerTestWebHostBuilder.md = docs\IdentityServerTestWebHostBuilder.md 36 | EndProjectSection 37 | EndProject 38 | Global 39 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 40 | Debug|Any CPU = Debug|Any CPU 41 | Release|Any CPU = Release|Any CPU 42 | EndGlobalSection 43 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 44 | {5169FE1E-64A2-4BF0-9DA0-2480107FD444}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 45 | {5169FE1E-64A2-4BF0-9DA0-2480107FD444}.Debug|Any CPU.Build.0 = Debug|Any CPU 46 | {5169FE1E-64A2-4BF0-9DA0-2480107FD444}.Release|Any CPU.ActiveCfg = Release|Any CPU 47 | {5169FE1E-64A2-4BF0-9DA0-2480107FD444}.Release|Any CPU.Build.0 = Release|Any CPU 48 | {4EB5ED1E-87BA-4E7A-AE4E-CDAA8923483A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 49 | {4EB5ED1E-87BA-4E7A-AE4E-CDAA8923483A}.Debug|Any CPU.Build.0 = Debug|Any CPU 50 | {4EB5ED1E-87BA-4E7A-AE4E-CDAA8923483A}.Release|Any CPU.ActiveCfg = Release|Any CPU 51 | {4EB5ED1E-87BA-4E7A-AE4E-CDAA8923483A}.Release|Any CPU.Build.0 = Release|Any CPU 52 | {C5F387EC-B6A4-4A5C-8C35-9411CE71B35A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 53 | {C5F387EC-B6A4-4A5C-8C35-9411CE71B35A}.Debug|Any CPU.Build.0 = Debug|Any CPU 54 | {C5F387EC-B6A4-4A5C-8C35-9411CE71B35A}.Release|Any CPU.ActiveCfg = Release|Any CPU 55 | {C5F387EC-B6A4-4A5C-8C35-9411CE71B35A}.Release|Any CPU.Build.0 = Release|Any CPU 56 | {CBDE7CC3-DC74-44CE-86E7-6A902504B227}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 57 | {CBDE7CC3-DC74-44CE-86E7-6A902504B227}.Debug|Any CPU.Build.0 = Debug|Any CPU 58 | {CBDE7CC3-DC74-44CE-86E7-6A902504B227}.Release|Any CPU.ActiveCfg = Release|Any CPU 59 | {CBDE7CC3-DC74-44CE-86E7-6A902504B227}.Release|Any CPU.Build.0 = Release|Any CPU 60 | EndGlobalSection 61 | GlobalSection(SolutionProperties) = preSolution 62 | HideSolutionNode = FALSE 63 | EndGlobalSection 64 | GlobalSection(NestedProjects) = preSolution 65 | {5169FE1E-64A2-4BF0-9DA0-2480107FD444} = {171C9B08-8431-4E4C-A343-CA8D277A2419} 66 | {4EB5ED1E-87BA-4E7A-AE4E-CDAA8923483A} = {FED78C85-8EE1-4C8E-9758-44CAF0070C10} 67 | {C5F387EC-B6A4-4A5C-8C35-9411CE71B35A} = {FED78C85-8EE1-4C8E-9758-44CAF0070C10} 68 | {CBDE7CC3-DC74-44CE-86E7-6A902504B227} = {FED78C85-8EE1-4C8E-9758-44CAF0070C10} 69 | EndGlobalSection 70 | GlobalSection(ExtensibilityGlobals) = postSolution 71 | SolutionGuid = {48382009-29CD-4CBF-89FA-DF1D15D7B8FE} 72 | EndGlobalSection 73 | EndGlobal 74 | -------------------------------------------------------------------------------- /IdentityServer4.Contrib.AspNetCore.Testing.sln.DotSettings: -------------------------------------------------------------------------------- 1 |  2 | DO_NOT_SHOW 3 | Field, Property, Event, Method 4 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> 5 | <Policy Inspect="True" Prefix="" Suffix="" Style="aaBb" /> -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 CleanCode-Labs 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # IdentityServer4.Contrib.AspNetCore.Testing 2 | 3 | ## DEPRECATION NOTICE 4 | 5 | This package has been deprecated. Feel free to clone it and maintain it, if needed. The package has been ported to support `Duende.IdentityServer`. The repository can be found [here](https://github.com/alsami/alsami.Duende.IdentityServer.AspNetCore.Testing). 6 | 7 | ## Infos 8 | 9 | [![Build Status](https://travis-ci.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing.svg?branch=master)](https://travis-ci.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing) 10 | [![codecov](https://codecov.io/gh/alsami/IdentityServer4.Contrib.AspNetCore.Testing/branch/master/graph/badge.svg)](https://codecov.io/gh/alsami/IdentityServer4.Contrib.AspNetCore.Testing) 11 | 12 | [![NuGet](https://img.shields.io/nuget/dt/IdentityServer4.Contrib.AspNetCore.Testing.svg)](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing) 13 | [![NuGet](https://img.shields.io/nuget/vpre/IdentityServer4.Contrib.AspNetCore.Testing.svg)](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing) 14 | 15 | This library serves as a testing framework for [IdentityServer4](http://docs.identityserver.io/en/latest/) using [Microsoft.AspNetCore.Mvc.Testing](https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-3.1) and makes it easy to test your web-applications in combination with `IdentityServer4`. 16 | 17 | ## Usage 18 | 19 | This library is supposed to be used within test-projects. Please checkout the [prerequisites](https://docs.microsoft.com/en-us/aspnet/core/test/integration-tests?view=aspnetcore-2.2#test-app-prerequisites) described by Microsoft. 20 | 21 | Check out the [docs](docs/) for more information about the usage! 22 | 23 | ## Installation 24 | 25 | This package is available via nuget. You can install it using Visual-Studio-Nuget-Browser or by using the dotnet-cli for your test-project. 26 | 27 | ```unspecified 28 | dotnet add package IdentityServer4.Contrib.AspNetCore.Testing 29 | ``` 30 | 31 | If you want to add a specific version of this package 32 | 33 | ```unspecified 34 | dotnet add package IdentityServer4.Contrib.AspNetCore.Testing --version 1.0.0 35 | ``` 36 | 37 | For more information please visit the official [dotnet-cli documentation](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet-add-package). -------------------------------------------------------------------------------- /assets/icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/e13ca502ad5c152489c174621a387f73f98160a4/assets/icon.jpg -------------------------------------------------------------------------------- /changelog.md: -------------------------------------------------------------------------------- 1 | # [5.0.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/5.0.0) (2021-12-03) 2 | 3 | ## Breaking changes 4 | 5 | * Fix spelling and rename `CreateWebHostBuider` to `CreateWebHostBuilder`. Thanks to [pgermishuys](https://github.com/pgermishuys) 6 | 7 | ## Chore 8 | 9 | * Update IdentityServer4 to version `4.1.2` 10 | * Update IdentityModel to version `4.6.0` 11 | * Update Microsoft.AspNetCore.Mvc.Testing to version `3.1.21` 12 | * Update Microsoft.Extensions.Hosting to version `3.1.21` 13 | * Update Microsoft.Extensions.Logging to version `3.1.21` 14 | 15 | # [4.2.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/4.2.0) (2021-11-04) 16 | 17 | ## Features 18 | 19 | Allow `ApiResource` and `ApiScope` to differ and only check that an `ApiResource` has corresponding `ApiScope`. Thanks to [@gerrylowe](https://github.com/gerrylowe)! Closes [#5](https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/issues/5) 20 | 21 | # [4.0.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/4.0.0) (2020-06-20) 22 | 23 | ## Features 24 | 25 | * Support for `IdentityServer4` version 4 26 | 27 | # [4.0.0-rc.3](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/4.0.0-rc.3) (2020-06-13) 28 | 29 | ## Features 30 | 31 | * Support for `IdentityServer4` version 4 preview 6 32 | 33 | # [4.0.0-rc.2](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/4.0.0-rc.1) (2020-05-22) 34 | 35 | ## Features 36 | 37 | * Remove class `IdentityServerTestHostCustomContainerBuilder` and add overload to build a host with custom service provider in `IdentityServerTestHostBuilder` class 38 | 39 | # [4.0.0-rc.1](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/4.0.0-rc.1) (2020-05-21) 40 | 41 | ## Features 42 | 43 | * Support for `IdentityServer4` version 4 preview 5 44 | * New class `IdentityServerTestHostCustomContainerBuilder` which extends `IdentityServerTestHostBuilder` and allows hooking in a third-party container while building the host. You can find an `Autofac` sample in the tests. 45 | 46 | # [4.0.0-rc.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/4.0.0-rc.0) (2020-05-10) 47 | 48 | ## Features 49 | 50 | * Support for `IdentityServer4` version 4 preview 3 51 | * New class `IdentityServerTestHostBuilder` which allows defining an `IHostBuilder` 52 | * New class `IdentityServerTestWebHostBuilder` which contains the already defined logic of `IdentityServerHostBuilder` 53 | * New class `IdentityServerHostProxy` which takes an `IHostBuilder` for creation of the `TestServer` 54 | * New class `IdentityServerWebHostProxy` which contains the already defined logic of `IdentityServerProxy` 55 | 56 | ## Breaking changes 57 | 58 | * When defining an `ApiResource` according `ApiScope` is required to be passed along. This is a breaking-change introduced through the update of `IdentityServer4` to version 4. The according builder used validates that api-scopes are present when using api-resources. It will throw an exception if not. 59 | * For more details on the breaking changes of `IdentityServer4` please check out the [changelog](https://github.com/IdentityServer/IdentityServer4/releases/tag/4.0.0-preview.4) ot it. 60 | 61 | ## Deprecation notice 62 | 63 | * `IdentityServerHostBuilder` has been marked as deprecated and will be removed with version 5. Please use `IdentityServerTestHostBuilder` or `IdentityServerTestWebHostBuilder` instead 64 | * `IdentityServerProxy` has been marked as deprecated and will be removed with version 5. Please use `IdentityServerHostProxy` or `IdentityServerWebHostProxy` instead. 65 | 66 | # [3.1.1](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/3.1.1) (2019-12-26) 67 | 68 | ## Chore 69 | 70 | * Update `IdentityServer4` to version `3.1.0` 71 | 72 | # [3.1.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/3.1.0) (2019-12-11) 73 | 74 | ## Breaking changes 75 | 76 | * .NET Core 3.1 is now required 77 | 78 | ## Chore 79 | 80 | * Update dependencies to `3.1.0` 81 | 82 | # [3.0.2](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/3.0.2) (2019-11-12) 83 | 84 | ## Chore 85 | 86 | * Update `IdentityServer4` to 3.0.2 87 | * Update `IdentityModel` to 4.1.0 88 | 89 | # [3.0.1](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/3.0.1) (2019-09-25) 90 | 91 | ## Chore 92 | 93 | * Update `IdentityServer4` to 3.0.1 94 | 95 | # [3.0.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/3.0.0) (2019-09-23) 96 | 97 | ## Breaking changes 98 | 99 | * .NET-Core 3 is now required. 100 | * `IdentityServerWebHostBuilder` has been renamed to `IdentityServerHostBuilder` 101 | 102 | ## Features 103 | 104 | * .NET-Core 3 support! 105 | 106 | # [1.4.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/1.4.0) (2019-07-19) 107 | 108 | ## Chore 109 | 110 | * Update `IdentityServer4` to version 2.5.0 111 | * Update `IdentitxModel` to version 3.10.10 112 | 113 | # [1.3.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/1.3.0) (2019-05-05) 114 | 115 | ## Features 116 | 117 | * Add constructor to `IdentityServerProxy` to directly pass in a `TestServer` 118 | 119 | # [1.2.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/1.2.0) (2019-04-09) 120 | 121 | ## Features 122 | 123 | * Allow passing of an `IdentityServerOptionsBuilder` to use custom options in `IdentityServerWebHostBuilder` 124 | * Allow passing of an `IdentityServerBuilder` to use a custom builder in `IdentityServerWebHostBuilder` 125 | 126 | ## Chore 127 | 128 | * Update Identity.Model to 3.10.7 129 | 130 | ## Features 131 | 132 | * Expose `TestServer` from class `IdentityServerProxy` in order to be able to create a handler manually. 133 | 134 | # [1.1.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/1.1.0) (2019-04-08) 135 | 136 | ## Features 137 | 138 | * Expose `TestServer` from class `IdentityServerProxy` in order to be able to create a handler manually. 139 | 140 | # [1.0.0](https://www.nuget.org/packages/IdentityServer4.Contrib.AspNetCore.Testing/1.0.0) (2019-04-08) 141 | 142 | ## Intial Release 143 | 144 | This is the inital release of the package. This release contains two mandatory classes 145 | 146 | * `IdentityServerWebHostBuilder` 147 | * `IdentityServerProxy` 148 | 149 | ### [IdentityServerWebHostBuilder](https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/blob/master/src/IdentityServer4.Contrib.AspNetCore.Testing/Builder/IdentityServerWebHostBuilder.cs) 150 | 151 | The `IdentityServerWebHostBuilder` class is a fluent-builder that contains the following functions 152 | 153 | * `AddApiResources(params ApiResource[] apiResources)` 154 | * `AddClients(params Client[] clients)` 155 | * `AddIdentityResources(params IdentityResource[] identityResources)` 156 | * `UseApplicationBuilder(Action applicationBuilder)` 157 | * `UseConfigurationBuilder( 158 | Action configurationBuilder)` 159 | * `UseServices( 160 | Action servicesBuilder)` 161 | * `UseLoggingBuilder( 162 | Action loggingBuilder)` 163 | * `UseResourceOwnerPasswordValidator(Type type)` 164 | * `UseResourceOwnerPasswordValidator( 165 | TResourceOwnerPasswordValidator resourceOwnerPasswordValidator) 166 | where TResourceOwnerPasswordValidator : class, IResourceOwnerPasswordValidator` 167 | * `UseProfileService(Type type)` 168 | * `UseProfileService( 169 | TProfileService profileService) where TProfileService : class, IProfileService` 170 | * `CreateWebHostBuilder()` 171 | * `UseWebHostBuilder(IWebHostBuilder webHostBuilder)` 172 | 173 | When calling `CreateWebHostBuilder()` a `WebHostBuilder` is created based on the configuration. With that builder we create the `IdentityServerProxy`. 174 | 175 | ### [IdentityServerProxy](https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/blob/master/src/IdentityServer4.Contrib.AspNetCore.Testing/Services/IdentityServerProxy.cs) 176 | 177 | The `IndentityServerProxy`, as the name says, serves as a proxy for `IdentityServer4` and takes a `WebHostBuilder` as constructor parameter, then creates a `TestServer` from `Microsoft.AspNetCore.Mvc.Testing` 178 | 179 | Following functions available 180 | 181 | * `GetDiscoverResponseAsync()` 182 | * `GetTokenAsync(ClientConfiguration clientConfiguration, string grantType, 183 | IDictionary parameters)` 184 | * `GetClientAccessTokenAsync(ClientConfiguration clientConfiguration, 185 | params string[] scopes)` 186 | * `GetClientAccessTokenAsync(ClientConfiguration clientConfiguration, 187 | IDictionary parameters, params string[] scopes)` 188 | * `GetResourceOwnerPasswordAccessTokenAsync( 189 | ClientConfiguration clientConfiguration, UserLoginConfiguration userLoginConfiguration, 190 | params string[] scopes)` 191 | * `GetResourceOwnerPasswordAccessTokenAsync( 192 | ClientConfiguration clientConfiguration, UserLoginConfiguration userLoginConfiguration, 193 | Dictionary parameters)` 194 | * `GetRefreshTokenAsync(ClientConfiguration clientConfiguration, 195 | string refreshToken, params string[] scopes)` 196 | * `GetRefreshTokenAsync(ClientConfiguration clientConfiguration, 197 | string refreshToken, IDictionary parameters)` 198 | * `GetUserInfoAsync(string accessToken)` 199 | -------------------------------------------------------------------------------- /docs/IdentityServerTestWebHostBuilder.md: -------------------------------------------------------------------------------- 1 | # IdentityServerTestWebHostBuilder (formerly IdentityServerHostBuilder) 2 | 3 | Samples are displayed using `xUnit` but would work same way with other test-frameworks. 4 | 5 | ## Load the discover-document 6 | 7 | ```csharp 8 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 9 | .AddClients(new Client 10 | { 11 | ClientId = "MyClient", 12 | ClientSecrets = new List 13 | { 14 | new Secret("MySecret".Sha256()) 15 | } 16 | }) 17 | .AddApiResources(new ApiResource()) 18 | .CreateWebHostBuilder(); 19 | 20 | var identityServerClient = new IdentityServerProxy(webHostBuilder); 21 | var discoveryResponse = await identityServerClient.GetDiscoverResponseAsync(); 22 | 23 | Assert.NotNull(discoveryResponse); 24 | Assert.False(discoveryResponse.IsError, discoveryResponse.Error); 25 | ``` 26 | 27 | ## Request a token using client-credentials 28 | 29 | ```csharp 30 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 31 | 32 | var client = new Client 33 | { 34 | ClientId = clientConfiguration.Id, 35 | ClientSecrets = new List 36 | { 37 | new Secret(clientConfiguration.Secret.Sha256()) 38 | }, 39 | AllowedScopes = new[] {"api1"}, 40 | AllowedGrantTypes = new[] {GrantType.ClientCredentials}, 41 | AccessTokenType = AccessTokenType.Jwt, 42 | AccessTokenLifetime = 7200 43 | }; 44 | 45 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 46 | .AddClients(client) 47 | .AddApiResources(new ApiResource("api1", "api1name")) 48 | .CreateWebHostBuilder(); 49 | 50 | var identityServerProxy = new IdentityServerProxy(webHostBuilder); 51 | 52 | var tokenResponse = await identityServerProxy.GetClientAccessTokenAsync(clientConfiguration, "api1"); 53 | 54 | Assert.NotNull(tokenResponse); 55 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 56 | ``` 57 | 58 | ## Resource-Owner-Grant with a typed `IResourceOwnerPasswordValidator` 59 | 60 | ```csharp 61 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 62 | 63 | var client = new Client 64 | { 65 | ClientId = clientConfiguration.Id, 66 | ClientSecrets = new List 67 | { 68 | new Secret(clientConfiguration.Secret.Sha256()) 69 | }, 70 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 71 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 72 | AccessTokenType = AccessTokenType.Jwt, 73 | AccessTokenLifetime = 7200, 74 | AllowOfflineAccess = true 75 | }; 76 | 77 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 78 | .AddClients(client) 79 | .AddApiResources(new ApiResource("api1", "api1name")) 80 | .UseResourceOwnerPasswordValidator(typeof(SimpleResourceOwnerPasswordValidator)) 81 | .CreateWebHostBuilder(); 82 | 83 | var identityServerProxy = new IdentityServerProxy(webHostBuilder); 84 | 85 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 86 | new UserLoginConfiguration("user", "password"), 87 | "api1", "offline_access"); 88 | 89 | Assert.NotNull(tokenResponse); 90 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 91 | ``` 92 | 93 | ## Requesting a refresh-token 94 | 95 | ```csharp 96 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 97 | 98 | var client = new Client 99 | { 100 | ClientId = clientConfiguration.Id, 101 | ClientSecrets = new List 102 | { 103 | new Secret(clientConfiguration.Secret.Sha256()) 104 | }, 105 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 106 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 107 | AccessTokenType = AccessTokenType.Jwt, 108 | AccessTokenLifetime = 7200, 109 | AllowOfflineAccess = true 110 | }; 111 | 112 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 113 | .AddClients(client) 114 | .AddApiResources(new ApiResource("api1", "api1name")) 115 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 116 | .CreateWebHostBuilder(); 117 | 118 | var identityServerProxy = new IdentityServerProxy(webHostBuilder); 119 | 120 | var scopes = new[] {"api1", "offline_access"}; 121 | 122 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 123 | new UserLoginConfiguration("user", "password"), 124 | scopes); 125 | 126 | // We are breaking the pattern arrange / act / assert here but we need to make sure token requested successfully first 127 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 128 | 129 | 130 | var refreshTokenResponse = await identityServerProxy 131 | .GetRefreshTokenAsync(clientConfiguration, tokenResponse.RefreshToken, 132 | scopes); 133 | 134 | Assert.NotNull(refreshTokenResponse); 135 | Assert.False(refreshTokenResponse.IsError) 136 | ``` 137 | 138 | ## Requesting user-infos 139 | 140 | ```csharp 141 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 142 | 143 | var client = new Client 144 | { 145 | ClientId = clientConfiguration.Id, 146 | ClientSecrets = new List 147 | { 148 | new Secret(clientConfiguration.Secret.Sha256()) 149 | }, 150 | AllowedScopes = new[] 151 | { 152 | "api1", IdentityServerConstants.StandardScopes.OfflineAccess, 153 | IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile 154 | }, 155 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 156 | AccessTokenType = AccessTokenType.Jwt, 157 | AccessTokenLifetime = 7200, 158 | AllowOfflineAccess = true 159 | }; 160 | 161 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 162 | .AddClients(client) 163 | .AddApiResources(new ApiResource("api1", "api1name")) 164 | .AddIdentityResources(new IdentityResources.OpenId(), new IdentityResources.Profile()) 165 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 166 | .UseProfileService(new SimpleProfileService()) 167 | .CreateWebHostBuilder(); 168 | 169 | var identityServerProxy = new IdentityServerProxy(webHostBuilder); 170 | 171 | var scopes = new[] {"api1", "offline_access", "openid", "profile"}; 172 | 173 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 174 | new UserLoginConfiguration("user", "password"), 175 | scopes); 176 | 177 | // We are breaking the pattern arrange / act / assert here but we need to make sure token requested successfully first 178 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 179 | 180 | 181 | var userInfoResponse = await identityServerProxy 182 | .GetUserInfoAsync(tokenResponse.AccessToken); 183 | 184 | Assert.NotNull(userInfoResponse); 185 | Assert.False(userInfoResponse.IsError); 186 | Assert.NotNull(userInfoResponse.Claims); 187 | ``` 188 | 189 | ## Requesting a token from a custom-configuration 190 | 191 | ```csharp 192 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 193 | 194 | var client = new Client 195 | { 196 | ClientId = clientConfiguration.Id, 197 | ClientSecrets = new List 198 | { 199 | new Secret(clientConfiguration.Secret.Sha256()) 200 | }, 201 | AllowedScopes = new[] 202 | { 203 | "api1", IdentityServerConstants.StandardScopes.OfflineAccess, 204 | IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile 205 | }, 206 | AllowedGrantTypes = new[] {"Custom"}, 207 | AccessTokenType = AccessTokenType.Jwt, 208 | AccessTokenLifetime = 7200, 209 | AllowOfflineAccess = true 210 | }; 211 | 212 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 213 | .AddClients(client) 214 | .AddApiResources(new ApiResource("api1", "api1name")) 215 | .AddIdentityResources(new IdentityResources.OpenId(), new IdentityResources.Profile()) 216 | .UseServices((context, collection) => 217 | collection.AddScoped()) 218 | .CreateWebHostBuilder(); 219 | 220 | var identityServerProxy = new IdentityServerProxy(webHostBuilder); 221 | 222 | var scopes = new[] {"api1", "offline_access", "openid", "profile"}; 223 | 224 | var tokenResponse = await identityServerProxy.GetTokenAsync(clientConfiguration, "Custom", 225 | new Dictionary 226 | { 227 | {"scope", string.Join(" ", scopes)}, 228 | {"username", "user"}, 229 | {"password", "password"}, 230 | }); 231 | 232 | Assert.NotNull(tokenResponse); 233 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 234 | Assert.Equal(7200, tokenResponse.ExpiresIn); 235 | Assert.NotNull(tokenResponse.AccessToken); 236 | Assert.NotNull(tokenResponse.RefreshToken); 237 | ``` 238 | 239 | ## Using an existing `WebHostBuilder` 240 | 241 | ```csharp 242 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 243 | .UseWebHostBuilder(Program.CreateWebHostBuilder(new string[] { })) 244 | .CreateWebHostBuilder(); 245 | 246 | var proxy = new IdentityServerProxy(webHostBuilder); 247 | 248 | var scopes = new[] {"api1", "offline_access", "openid", "profile"}; 249 | 250 | var tokenResponse = await proxy.GetResourceOwnerPasswordAccessTokenAsync( 251 | new ClientConfiguration(Clients.Id, Clients.Secret), 252 | new UserLoginConfiguration("user1", "password1"), scopes); 253 | 254 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 255 | ``` 256 | 257 | For more samples and usages, please also have a look at the [tests](https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/tree/master/test). -------------------------------------------------------------------------------- /global.json: -------------------------------------------------------------------------------- 1 | { 2 | "sdk": { 3 | "version": "3.1.403", 4 | "rollForward": "latestFeature" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /icon.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/e13ca502ad5c152489c174621a387f73f98160a4/icon.jpg -------------------------------------------------------------------------------- /publish-packages.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | cd src 3 | for directory in *; do 4 | if [[ -d ${directory} ]]; then 5 | cd ${directory} 6 | dotnet build -c Release 7 | dotnet pack *.csproj --include-symbols -c Release --output "." 8 | sleep 5 9 | dotnet nuget push -s ${1} -k ${2} "${directory}.${3}.symbols.nupkg" 10 | if [[ ${?} != 0 ]] 11 | then 12 | exit -1 13 | fi 14 | cd .. 15 | fi 16 | done -------------------------------------------------------------------------------- /src/Directory.Build.props: -------------------------------------------------------------------------------- 1 | 2 | 3 | 8.0 4 | true 5 | 2021©Sami Al Khatib 6 | Sami Al Khatib 7 | OpenID Connect and OAuth 2.0 Testing-Framework for ASP.NET Core and IdentityServer4 8 | OAuth2;OAuth 2.0;OpenID Connect;Security;Identity;IdentityServer;IdentityServer4;AspNetCore;AspNetCore Testing;IntegrationTesting;Testing;IdentityServer Proxy; 9 | icon.jpg 10 | https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing 11 | MIT 12 | https://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing/blob/master/changelog.md 13 | git 14 | git://github.com/alsami/IdentityServer4.Contrib.AspNetCore.Testing 15 | 5.0.0 16 | 5.0.0 17 | true 18 | true 19 | S3881 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | all 29 | 30 | 31 | all 32 | 33 | 34 | -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Builder/AbstractIdentityServerHostBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using IdentityServer4.Configuration; 5 | using IdentityServer4.Contrib.AspNetCore.Testing.Misc; 6 | using IdentityServer4.Contrib.AspNetCore.Testing.Sinks; 7 | using IdentityServer4.Models; 8 | using IdentityServer4.Services; 9 | using IdentityServer4.Validation; 10 | using Microsoft.AspNetCore.Builder; 11 | using Microsoft.Extensions.DependencyInjection; 12 | 13 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Builder 14 | { 15 | public abstract class AbstractIdentityServerHostBuilder 16 | where TBuilder : AbstractIdentityServerHostBuilder 17 | { 18 | private readonly List internalApiResources; 19 | private readonly List internalApiScopes; 20 | private readonly List internalClients; 21 | private readonly List internalIdentityResources; 22 | 23 | private Func internalIdentityServerBuilder; 24 | private Action internalIdentityServerOptionsBuilder; 25 | private IProfileService internalProfileService; 26 | private Type internalProfileServiceType; 27 | private IResourceOwnerPasswordValidator internalResourceOwnerPasswordValidator; 28 | private Type internalResourceOwnerPasswordValidatorType; 29 | 30 | protected Action InternalApplicationBuilder; 31 | 32 | 33 | protected AbstractIdentityServerHostBuilder() 34 | { 35 | this.InternalApplicationBuilder = builder => { }; 36 | 37 | this.internalApiResources = new List(); 38 | 39 | this.internalApiScopes = new List(); 40 | 41 | this.internalClients = new List(); 42 | 43 | this.internalIdentityResources = new List(); 44 | 45 | this.internalIdentityServerOptionsBuilder = options => 46 | { 47 | options.Events.RaiseInformationEvents = true; 48 | options.Events.RaiseSuccessEvents = true; 49 | options.Events.RaiseFailureEvents = true; 50 | options.Events.RaiseErrorEvents = true; 51 | }; 52 | } 53 | 54 | public TBuilder AddApiResources(params ApiResource[] apiResources) 55 | { 56 | if (!apiResources?.Any() ?? true) 57 | throw new ArgumentException("ApiResources must not be null or empty", nameof(apiResources)); 58 | 59 | this.internalApiResources.AddRange(apiResources); 60 | 61 | return (TBuilder) this; 62 | } 63 | 64 | public TBuilder AddApiScopes(params ApiScope[] apiScopes) 65 | { 66 | if (!apiScopes?.Any() ?? true) 67 | throw new ArgumentException("ApiScopes must not be null or empty", nameof(apiScopes)); 68 | 69 | this.internalApiScopes.AddRange(apiScopes); 70 | 71 | return (TBuilder) this; 72 | } 73 | 74 | public TBuilder AddClients(params Client[] clients) 75 | { 76 | if (!clients?.Any() ?? true) 77 | throw new ArgumentException("Clients must not be null or empty", nameof(clients)); 78 | 79 | this.internalClients.AddRange(clients); 80 | 81 | return (TBuilder) this; 82 | } 83 | 84 | public TBuilder AddIdentityResources(params IdentityResource[] identityResources) 85 | { 86 | if (!identityResources?.Any() ?? true) 87 | throw new ArgumentException("Clients must not be null or empty", nameof(identityResources)); 88 | 89 | this.internalIdentityResources.AddRange(identityResources); 90 | 91 | return (TBuilder) this; 92 | } 93 | 94 | // ReSharper disable once UnusedMember.Global 95 | public TBuilder UseApplicationBuilder(Action applicationBuilder) 96 | { 97 | this.InternalApplicationBuilder = 98 | applicationBuilder ?? throw new ArgumentNullException(nameof(applicationBuilder)); 99 | 100 | return (TBuilder) this; 101 | } 102 | 103 | public TBuilder UseResourceOwnerPasswordValidator(Type type) 104 | { 105 | if (!typeof(IResourceOwnerPasswordValidator).IsAssignableFrom(type)) 106 | throw new ArgumentException($"Type must be assignable to {nameof(IResourceOwnerPasswordValidator)}", 107 | nameof(type)); 108 | 109 | this.internalResourceOwnerPasswordValidatorType = type; 110 | 111 | return (TBuilder) this; 112 | } 113 | 114 | public TBuilder UseResourceOwnerPasswordValidator( 115 | TResourceOwnerPasswordValidator resourceOwnerPasswordValidator) 116 | where TResourceOwnerPasswordValidator : class, IResourceOwnerPasswordValidator 117 | { 118 | this.internalResourceOwnerPasswordValidator = resourceOwnerPasswordValidator; 119 | 120 | return (TBuilder) this; 121 | } 122 | 123 | public TBuilder UseProfileService(Type type) 124 | { 125 | if (!typeof(IProfileService).IsAssignableFrom(type)) 126 | throw new ArgumentException($"Type must be assignable to {nameof(IProfileService)}", 127 | nameof(type)); 128 | 129 | this.internalProfileServiceType = type; 130 | 131 | return (TBuilder) this; 132 | } 133 | 134 | public TBuilder UseProfileService( 135 | TProfileService profileService) where TProfileService : class, IProfileService 136 | { 137 | this.internalProfileService = profileService; 138 | 139 | return (TBuilder) this; 140 | } 141 | 142 | public TBuilder UseIdentityServerOptionsBuilder( 143 | Action identityServerOptionsBuilder) 144 | { 145 | this.internalIdentityServerOptionsBuilder = identityServerOptionsBuilder ?? 146 | throw new ArgumentNullException( 147 | nameof(identityServerOptionsBuilder), 148 | $"{nameof(identityServerOptionsBuilder)} must not be null!"); 149 | 150 | return (TBuilder) this; 151 | } 152 | 153 | public TBuilder UseIdentityServerBuilder( 154 | Func identityServerBuilder) 155 | { 156 | this.internalIdentityServerBuilder = identityServerBuilder ?? 157 | throw new ArgumentNullException(nameof(identityServerBuilder), 158 | $"{nameof(identityServerBuilder)} must not be null!"); 159 | 160 | return (TBuilder) this; 161 | } 162 | 163 | protected void ConfigureIdentityServerServices(IServiceCollection services) 164 | { 165 | services.AddSingleton(new EventCaptureSink(new IdentityServerEventCaptureStore())); 166 | 167 | if (this.internalResourceOwnerPasswordValidator != null) 168 | services.AddSingleton(sp => this.internalResourceOwnerPasswordValidator); 169 | 170 | if (this.internalResourceOwnerPasswordValidatorType != null) 171 | services.AddSingleton(typeof(IResourceOwnerPasswordValidator), 172 | this.internalResourceOwnerPasswordValidatorType); 173 | 174 | if (this.internalProfileService != null) services.AddSingleton(sp => this.internalProfileService); 175 | 176 | if (this.internalProfileServiceType != null) 177 | services.AddSingleton(typeof(IProfileService), this.internalProfileServiceType); 178 | } 179 | 180 | protected IIdentityServerBuilder CreatePreconfiguredIdentityServerBuilder(IServiceCollection services) 181 | { 182 | if (this.internalIdentityServerBuilder != null) return this.internalIdentityServerBuilder(services); 183 | 184 | return services 185 | .AddIdentityServer(this.internalIdentityServerOptionsBuilder) 186 | .AddDefaultEndpoints() 187 | .AddDefaultSecretParsers() 188 | .AddDeveloperSigningCredential(); 189 | } 190 | 191 | protected void ConfigureIdentityServerResources(IIdentityServerBuilder identityServerBuilder) 192 | { 193 | if (this.internalClients.Any()) identityServerBuilder.AddInMemoryClients(this.internalClients); 194 | 195 | if (this.internalApiResources.Any()) 196 | identityServerBuilder.AddInMemoryApiResources(this.internalApiResources); 197 | 198 | if (this.internalApiScopes.Any()) 199 | identityServerBuilder.AddInMemoryApiScopes(this.internalApiScopes); 200 | 201 | if (this.internalIdentityResources.Any()) 202 | identityServerBuilder.AddInMemoryIdentityResources(this.internalIdentityResources); 203 | } 204 | 205 | protected void Validate() 206 | { 207 | foreach (var apiResource in this.internalApiResources) 208 | { 209 | foreach (var scopeName in apiResource.Scopes) 210 | { 211 | if (!this.internalApiScopes.Any(s => s.Name == scopeName)) 212 | { 213 | throw new InvalidOperationException( 214 | $"Resource {apiResource.Name} contains scope {scopeName} not found in ApiScopes"); 215 | } 216 | } 217 | } 218 | } 219 | } 220 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Builder/IdentityServerHostBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Builder 4 | { 5 | [Obsolete("Please use IdentityServerTestWebHostBuilder or IdentityServerTestHostBuilder. IdentityServerHostBuilder will be removed with version 5!")] 6 | // ReSharper disable once UnusedType.Global 7 | public sealed class IdentityServerHostBuilder : IdentityServerTestWebHostBuilder 8 | { 9 | 10 | } 11 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Builder/IdentityServerTestHostBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IdentityServer4.Contrib.AspNetCore.Testing.Misc; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.AspNetCore.TestHost; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using Microsoft.Extensions.Logging; 10 | 11 | // ReSharper disable UnusedMember.Global 12 | 13 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Builder 14 | { 15 | public sealed class IdentityServerTestHostBuilder : AbstractIdentityServerHostBuilder 16 | { 17 | private Action internalLoggingBuilder; 18 | private Action internalConfigurationBuilder; 19 | private Action internalServicesBuilder; 20 | 21 | public IdentityServerTestHostBuilder() 22 | { 23 | this.internalConfigurationBuilder = (context, configurationBuilder) => { }; 24 | 25 | this.internalServicesBuilder = (builder, services) => { }; 26 | 27 | this.internalLoggingBuilder = (context, loggingBuilder) => 28 | loggingBuilder.AddProvider(new DefaultLoggerProvider()); 29 | } 30 | 31 | public IdentityServerTestHostBuilder UseConfigurationBuilder( 32 | Action configurationBuilder) 33 | { 34 | this.internalConfigurationBuilder = configurationBuilder; 35 | 36 | return this; 37 | } 38 | 39 | public IdentityServerTestHostBuilder UseServices( 40 | Action servicesBuilder) 41 | { 42 | this.internalServicesBuilder = servicesBuilder; 43 | 44 | return this; 45 | } 46 | 47 | public IdentityServerTestHostBuilder UseLoggingBuilder( 48 | Action loggingBuilder) 49 | { 50 | this.internalLoggingBuilder = loggingBuilder ?? 51 | throw new ArgumentNullException(nameof(loggingBuilder), 52 | "loggingBuilder must not be null"); 53 | 54 | return this; 55 | } 56 | 57 | public IHostBuilder CreateHostBuilder() 58 | { 59 | this.Validate(); 60 | 61 | return this.CreateHostBuilderInternal(); 62 | } 63 | 64 | public IHostBuilder CreateHostBuilder( 65 | IServiceProviderFactory serviceProviderFactory, Action containerSetupAction) 66 | { 67 | if (serviceProviderFactory is null) throw new ArgumentNullException(nameof(serviceProviderFactory)); 68 | if (containerSetupAction is null) throw new ArgumentNullException(nameof(containerSetupAction)); 69 | 70 | this.Validate(); 71 | 72 | return this.CreateHostBuilderInternal() 73 | .UseServiceProviderFactory(serviceProviderFactory) 74 | .ConfigureContainer(containerSetupAction); 75 | } 76 | 77 | private IHostBuilder CreateHostBuilderInternal() 78 | { 79 | return new HostBuilder() 80 | .ConfigureLogging(this.internalLoggingBuilder) 81 | .ConfigureWebHost(webHostBuilder => 82 | { 83 | webHostBuilder 84 | .UseTestServer() 85 | .Configure(applicationBuilder => 86 | { 87 | applicationBuilder.UseIdentityServer(); 88 | this.InternalApplicationBuilder(applicationBuilder); 89 | }); 90 | }) 91 | .ConfigureServices((context, services) => 92 | { 93 | this.internalServicesBuilder(context, services); 94 | 95 | this.ConfigureIdentityServerServices(services); 96 | 97 | var identityServerBuilder = this.CreatePreconfiguredIdentityServerBuilder(services); 98 | 99 | this.ConfigureIdentityServerResources(identityServerBuilder); 100 | }) 101 | .ConfigureAppConfiguration(this.internalConfigurationBuilder); 102 | } 103 | } 104 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Builder/IdentityServerTestWebHostBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using IdentityServer4.Contrib.AspNetCore.Testing.Misc; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.AspNetCore.Hosting; 5 | using Microsoft.Extensions.Configuration; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Logging; 8 | 9 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Builder 10 | { 11 | public class IdentityServerTestWebHostBuilder : AbstractIdentityServerHostBuilder 12 | { 13 | private IWebHostBuilder internalHostBuilder; 14 | 15 | private Action internalLoggingBuilder; 16 | private Action internalConfigurationBuilder; 17 | private Action internalServicesBuilder; 18 | 19 | public IdentityServerTestWebHostBuilder() 20 | { 21 | this.internalConfigurationBuilder = (context, configurationBuilder) => { }; 22 | 23 | this.internalServicesBuilder = (builder, services) => { }; 24 | 25 | this.internalLoggingBuilder = (context, loggingBuilder) => 26 | loggingBuilder.AddProvider(new DefaultLoggerProvider()); 27 | } 28 | 29 | public IdentityServerTestWebHostBuilder UseConfigurationBuilder( 30 | Action configurationBuilder) 31 | { 32 | this.internalConfigurationBuilder = configurationBuilder; 33 | 34 | return this; 35 | } 36 | 37 | public IdentityServerTestWebHostBuilder UseServices( 38 | Action servicesBuilder) 39 | { 40 | this.internalServicesBuilder = servicesBuilder; 41 | 42 | return this; 43 | } 44 | 45 | public IdentityServerTestWebHostBuilder UseLoggingBuilder( 46 | Action loggingBuilder) 47 | { 48 | this.internalLoggingBuilder = loggingBuilder ?? 49 | throw new ArgumentNullException(nameof(loggingBuilder), 50 | "loggingBuilder must not be null"); 51 | 52 | return this; 53 | } 54 | 55 | public IdentityServerTestWebHostBuilder UseWebHostBuilder(IWebHostBuilder webHostBuilder) 56 | { 57 | this.internalHostBuilder = webHostBuilder ?? throw new ArgumentNullException(nameof(webHostBuilder)); 58 | 59 | return this; 60 | } 61 | 62 | public IWebHostBuilder CreateWebHostBuilder() 63 | { 64 | if (this.internalHostBuilder != null) return this.internalHostBuilder; 65 | 66 | this.Validate(); 67 | 68 | return new WebHostBuilder() 69 | .ConfigureLogging(this.internalLoggingBuilder) 70 | .Configure(builder => 71 | { 72 | builder.UseIdentityServer(); 73 | this.InternalApplicationBuilder(builder); 74 | }) 75 | .ConfigureServices((context, services) => 76 | { 77 | this.internalServicesBuilder(context, services); 78 | 79 | this.ConfigureIdentityServerServices(services); 80 | 81 | var identityServerBuilder = this.CreatePreconfiguredIdentityServerBuilder(services); 82 | 83 | this.ConfigureIdentityServerResources(identityServerBuilder); 84 | }) 85 | .ConfigureAppConfiguration(this.internalConfigurationBuilder); 86 | } 87 | } 88 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Configuration/ClientConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Configuration 2 | { 3 | public class ClientConfiguration 4 | { 5 | public ClientConfiguration(string id, string secret) 6 | { 7 | this.Id = id; 8 | this.Secret = secret; 9 | } 10 | 11 | public string Id { get; } 12 | 13 | public string Secret { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Configuration/UserLoginConfiguration.cs: -------------------------------------------------------------------------------- 1 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Configuration 2 | { 3 | public class UserLoginConfiguration 4 | { 5 | public UserLoginConfiguration(string username, string password) 6 | { 7 | this.Username = username; 8 | this.Password = password; 9 | } 10 | 11 | public string Username { get; } 12 | 13 | public string Password { get; } 14 | } 15 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/IdentityServer4.Contrib.AspNetCore.Testing.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Misc/DefaultLoggerProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Misc 4 | { 5 | internal class DefaultLoggerProvider : ILoggerProvider 6 | { 7 | private readonly ILoggerFactory loggerFactory; 8 | 9 | public DefaultLoggerProvider(ILoggerFactory loggerFactor = null) 10 | { 11 | this.loggerFactory = loggerFactor ?? new LoggerFactory(); 12 | } 13 | 14 | public ILogger CreateLogger(string categoryName) 15 | { 16 | return this.loggerFactory.CreateLogger(categoryName); 17 | } 18 | 19 | public void Dispose() 20 | { 21 | this.loggerFactory.Dispose(); 22 | } 23 | } 24 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Misc/IdentityServerEventCaptureStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using IdentityServer4.Events; 5 | 6 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Misc 7 | { 8 | internal class IdentityServerEventCaptureStore 9 | { 10 | private readonly List events; 11 | 12 | public IdentityServerEventCaptureStore() 13 | { 14 | this.events = new List(); 15 | } 16 | 17 | public void AddEvent(Event @event) 18 | { 19 | if (@event == null) throw new ArgumentNullException(nameof(@event)); 20 | 21 | this.events.Add(@event); 22 | } 23 | 24 | public Event GetById(int id) 25 | { 26 | return this.events.FirstOrDefault(e => e.Id == id); 27 | } 28 | 29 | public IEnumerable GetEvents() 30 | { 31 | return this.events.AsReadOnly(); 32 | } 33 | 34 | public bool ContainsEventType(EventTypes eventType) 35 | { 36 | var @event = this.events.FirstOrDefault(e => e.EventType == eventType); 37 | return @event != null; 38 | } 39 | 40 | public bool ContainsMessage(string message, StringComparison comparison = StringComparison.OrdinalIgnoreCase) 41 | { 42 | if (string.IsNullOrWhiteSpace(message)) throw new ArgumentNullException(nameof(message)); 43 | 44 | var matchingEvents = this.events.Where(e => string.Compare(e.Message, message, comparison) == 0); 45 | return matchingEvents.Any(); 46 | } 47 | 48 | public bool ContainsMessageStartsWith(string value, 49 | StringComparison comparison = StringComparison.OrdinalIgnoreCase) 50 | { 51 | if (string.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); 52 | 53 | var matchingEvents = this.events.Where(e => e.Message.StartsWith(value, comparison)); 54 | return matchingEvents.Any(); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Services/AbstractIdentityServerProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | using IdentityModel; 6 | using IdentityModel.Client; 7 | using IdentityServer4.Contrib.AspNetCore.Testing.Configuration; 8 | using IdentityServer4.Models; 9 | using Microsoft.AspNetCore.TestHost; 10 | 11 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Services 12 | { 13 | public abstract class AbstractIdentityServerProxy 14 | { 15 | public abstract TestServer IdentityServer { get; } 16 | 17 | public async Task GetDiscoverResponseAsync() 18 | { 19 | using var proxy = this.IdentityServer.CreateHandler(); 20 | using var client = new HttpClient(proxy); 21 | return await client.GetDiscoveryDocumentAsync(this.IdentityServer.BaseAddress.ToString()); 22 | } 23 | 24 | public async Task GetTokenAsync(ClientConfiguration clientConfiguration, string grantType, 25 | IDictionary parameters) 26 | { 27 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 28 | 29 | EnsureDiscoverResponse(discoveryResponse); 30 | 31 | using var proxy = this.IdentityServer.CreateHandler(); 32 | using var client = new HttpClient(proxy); 33 | 34 | return await client.RequestTokenAsync(new TokenRequest 35 | { 36 | Address = discoveryResponse.TokenEndpoint, 37 | ClientId = clientConfiguration.Id, 38 | ClientSecret = clientConfiguration.Secret, 39 | GrantType = grantType, 40 | Parameters = parameters 41 | }); 42 | } 43 | 44 | public async Task GetClientAccessTokenAsync(ClientConfiguration clientConfiguration, 45 | params string[] scopes) 46 | { 47 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 48 | 49 | EnsureDiscoverResponse(discoveryResponse); 50 | 51 | using var proxy = this.IdentityServer.CreateHandler(); 52 | using var client = new HttpClient(proxy); 53 | 54 | return await ExecuteClientAccessTokenRequestAsync(client, discoveryResponse, clientConfiguration, 55 | new Dictionary(), scopes); 56 | } 57 | 58 | public async Task GetClientAccessTokenAsync(ClientConfiguration clientConfiguration, 59 | IDictionary parameters, params string[] scopes) 60 | { 61 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 62 | 63 | EnsureDiscoverResponse(discoveryResponse); 64 | 65 | var usedParameters = parameters ?? new Dictionary(); 66 | 67 | using var proxy = this.IdentityServer.CreateHandler(); 68 | using var client = new HttpClient(proxy); 69 | 70 | return await ExecuteClientAccessTokenRequestAsync(client, discoveryResponse, clientConfiguration, 71 | usedParameters, scopes); 72 | } 73 | 74 | public async Task GetResourceOwnerPasswordAccessTokenAsync( 75 | ClientConfiguration clientConfiguration, UserLoginConfiguration userLoginConfiguration, 76 | params string[] scopes) 77 | { 78 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 79 | 80 | EnsureDiscoverResponse(discoveryResponse); 81 | 82 | using var proxy = this.IdentityServer.CreateHandler(); 83 | using var client = new HttpClient(proxy); 84 | 85 | return await ExecuteResourceOwnerAccessTokenRequestAsync(client, discoveryResponse, 86 | clientConfiguration, userLoginConfiguration, new Dictionary(), scopes); 87 | } 88 | 89 | public async Task GetResourceOwnerPasswordAccessTokenAsync( 90 | ClientConfiguration clientConfiguration, UserLoginConfiguration userLoginConfiguration, 91 | Dictionary parameters) 92 | { 93 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 94 | 95 | EnsureDiscoverResponse(discoveryResponse); 96 | 97 | using var proxy = this.IdentityServer.CreateHandler(); 98 | using var client = new HttpClient(proxy); 99 | 100 | return await ExecuteResourceOwnerAccessTokenRequestAsync(client, discoveryResponse, 101 | clientConfiguration, userLoginConfiguration, parameters, Array.Empty()); 102 | } 103 | 104 | public async Task GetRefreshTokenAsync(ClientConfiguration clientConfiguration, 105 | string refreshToken, params string[] scopes) 106 | { 107 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 108 | 109 | EnsureDiscoverResponse(discoveryResponse); 110 | 111 | using var proxy = this.IdentityServer.CreateHandler(); 112 | using var client = new HttpClient(proxy); 113 | 114 | return await ExecuteRefreshAccessTokenRequestAsync(client, discoveryResponse, clientConfiguration, 115 | refreshToken, new Dictionary(), scopes); 116 | } 117 | 118 | public async Task GetRefreshTokenAsync(ClientConfiguration clientConfiguration, 119 | string refreshToken, IDictionary parameters) 120 | { 121 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 122 | 123 | EnsureDiscoverResponse(discoveryResponse); 124 | 125 | using var proxy = this.IdentityServer.CreateHandler(); 126 | using var client = new HttpClient(proxy); 127 | 128 | return await ExecuteRefreshAccessTokenRequestAsync(client, discoveryResponse, clientConfiguration, 129 | refreshToken, parameters, Array.Empty()); 130 | } 131 | 132 | public async Task GetUserInfoAsync(string accessToken) 133 | { 134 | var discoveryResponse = await this.GetDiscoverResponseAsync(); 135 | 136 | EnsureDiscoverResponse(discoveryResponse); 137 | 138 | using var proxy = this.IdentityServer.CreateHandler(); 139 | using var client = new HttpClient(proxy); 140 | 141 | return await client.GetUserInfoAsync(new UserInfoRequest 142 | { 143 | Address = discoveryResponse.UserInfoEndpoint, 144 | Token = accessToken 145 | }); 146 | } 147 | 148 | private static Task ExecuteResourceOwnerAccessTokenRequestAsync(HttpMessageInvoker client, 149 | DiscoveryDocumentResponse discoveryResponse, 150 | ClientConfiguration clientConfiguration, UserLoginConfiguration userLoginConfiguration, 151 | IDictionary parameters, IEnumerable scopes) 152 | => client.RequestPasswordTokenAsync(new PasswordTokenRequest 153 | { 154 | UserName = userLoginConfiguration.Username, 155 | Password = userLoginConfiguration.Password, 156 | ClientId = clientConfiguration.Id, 157 | ClientSecret = clientConfiguration.Secret, 158 | Address = discoveryResponse.TokenEndpoint, 159 | GrantType = GrantType.ResourceOwnerPassword, 160 | Scope = string.Join(" ", scopes), 161 | Parameters = parameters 162 | }); 163 | 164 | private static Task ExecuteRefreshAccessTokenRequestAsync(HttpMessageInvoker client, 165 | DiscoveryDocumentResponse discoveryResponse, 166 | ClientConfiguration clientConfiguration, string refreshToken, IDictionary parameters, 167 | IEnumerable scopes) 168 | => client.RequestRefreshTokenAsync(new RefreshTokenRequest 169 | { 170 | RefreshToken = refreshToken, 171 | ClientId = clientConfiguration.Id, 172 | ClientSecret = clientConfiguration.Secret, 173 | Address = discoveryResponse.TokenEndpoint, 174 | GrantType = GrantType.ResourceOwnerPassword, 175 | Scope = string.Join(" ", scopes), 176 | Parameters = parameters 177 | }); 178 | 179 | private static Task ExecuteClientAccessTokenRequestAsync(HttpMessageInvoker client, 180 | DiscoveryDocumentResponse discoveryResponse, 181 | ClientConfiguration clientConfiguration, IDictionary parameters, IEnumerable scopes) 182 | => client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest 183 | { 184 | Address = discoveryResponse.TokenEndpoint, 185 | ClientId = clientConfiguration.Id, 186 | ClientSecret = clientConfiguration.Secret, 187 | GrantType = OidcConstants.GrantTypes.ClientCredentials, 188 | Scope = string.Join(" ", scopes), 189 | Parameters = parameters 190 | }); 191 | 192 | private static void EnsureDiscoverResponse(ProtocolResponse discoveryResponse) 193 | { 194 | if (!discoveryResponse.IsError) return; 195 | 196 | throw new InvalidOperationException( 197 | $"Cannot continue since discover-request has failed with message:\n{discoveryResponse.Error}"); 198 | } 199 | } 200 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Services/IdentityServerHostProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using Microsoft.AspNetCore.TestHost; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Services 7 | { 8 | public sealed class IdentityServerHostProxy : AbstractIdentityServerProxy, IAsyncDisposable 9 | { 10 | private readonly IHost host; 11 | 12 | public IdentityServerHostProxy(IHostBuilder hostBuilder) 13 | { 14 | if (hostBuilder is null) throw new ArgumentNullException(nameof(hostBuilder)); 15 | 16 | this.host = hostBuilder.Start(); 17 | } 18 | 19 | public override TestServer IdentityServer => this.host.GetTestServer(); 20 | 21 | public async ValueTask DisposeAsync() 22 | { 23 | if (this.host is null) return; 24 | 25 | await this.host.StopAsync(); 26 | } 27 | } 28 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Services/IdentityServerProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.TestHost; 4 | 5 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Services 6 | { 7 | [Obsolete("Please use IdentityServerWebHostProxy. IdentityServerProxy will be removed with version 5")] 8 | // ReSharper disable once UnusedType.Global 9 | public sealed class IdentityServerProxy : IdentityServerWebHostProxy 10 | { 11 | public IdentityServerProxy(IWebHostBuilder webHostBuilder) : base(webHostBuilder) 12 | { 13 | } 14 | 15 | public IdentityServerProxy(TestServer identityServer) : base(identityServer) 16 | { 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Services/IdentityServerWebHostProxy.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.AspNetCore.TestHost; 4 | 5 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Services 6 | { 7 | public class IdentityServerWebHostProxy : AbstractIdentityServerProxy 8 | { 9 | public IdentityServerWebHostProxy(IWebHostBuilder webHostBuilder) 10 | { 11 | if (webHostBuilder == null) 12 | throw new ArgumentNullException(nameof(webHostBuilder), 13 | "webHostBuilder must not be null"); 14 | 15 | this.IdentityServer = new TestServer(webHostBuilder); 16 | } 17 | 18 | // ReSharper disable once UnusedMember.Global 19 | public IdentityServerWebHostProxy(TestServer identityServer) 20 | { 21 | this.IdentityServer = identityServer; 22 | } 23 | 24 | public override TestServer IdentityServer { get; } 25 | } 26 | } -------------------------------------------------------------------------------- /src/IdentityServer4.Contrib.AspNetCore.Testing/Sinks/EventCaptureSink.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using IdentityServer4.Contrib.AspNetCore.Testing.Misc; 3 | using IdentityServer4.Events; 4 | using IdentityServer4.Services; 5 | 6 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Sinks 7 | { 8 | internal class EventCaptureSink : IEventSink 9 | { 10 | private readonly IdentityServerEventCaptureStore identityServerEventCaptureStore; 11 | 12 | public EventCaptureSink(IdentityServerEventCaptureStore identityServerEventCaptureStore) 13 | { 14 | this.identityServerEventCaptureStore = identityServerEventCaptureStore; 15 | } 16 | 17 | public Task PersistAsync(Event evt) 18 | { 19 | this.identityServerEventCaptureStore.AddEvent(evt); 20 | return Task.CompletedTask; 21 | } 22 | } 23 | } -------------------------------------------------------------------------------- /test.sh: -------------------------------------------------------------------------------- 1 | function run_tests() { 2 | dotnet test -c Release -p:CoverletOutput="../../coverage/" -p:MergeWith="../../coverage/coverage.json" -p:CollectCoverage=true -p:CoverletOutputFormat=\"json,lcov,opencover\" -p:ThresholdType=line -p:ThresholdStat=total -p:Exclude=\"[IdentityServer4.Api]*,[IdentityServer4.Server]*\" | tee testoutput.txt 3 | return ${?} 4 | } 5 | 6 | function publish_coverage() { 7 | curl https://codecov.io/bash -o codecov.sh 8 | chmod +x codecov.sh 9 | ./codecov.sh 10 | return ${?} 11 | } 12 | 13 | run_tests 14 | publish_coverage 15 | exit ${?} 16 | -------------------------------------------------------------------------------- /test/IdentityServer4.Api/AuthController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace IdentityServer4.Api 5 | { 6 | [Authorize] 7 | [Route("api/[controller]")] 8 | public class AuthController 9 | { 10 | [HttpGet] 11 | public IActionResult TestAuth() 12 | { 13 | return new OkObjectResult(new 14 | { 15 | Message = "This endpoint would not be reachable, if the authentication was not working" 16 | }); 17 | } 18 | } 19 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Api/IdentityServer4.Api.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /test/IdentityServer4.Api/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace IdentityServer4.Api 4 | { 5 | public static class Program 6 | { 7 | public static void Main(string[] args) 8 | { 9 | throw new NotImplementedException("This application is not meant to be run manually!"); 10 | } 11 | } 12 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Api/Startup.cs: -------------------------------------------------------------------------------- 1 | using System.Net.Http; 2 | using Microsoft.AspNetCore.Authentication.JwtBearer; 3 | using Microsoft.AspNetCore.Builder; 4 | using Microsoft.Extensions.DependencyInjection; 5 | 6 | namespace IdentityServer4.Api 7 | { 8 | public class Startup 9 | { 10 | private readonly HttpMessageHandler identityServerMessageHandler; 11 | 12 | public Startup(HttpMessageHandler identityServerMessageHandler) 13 | { 14 | this.identityServerMessageHandler = identityServerMessageHandler; 15 | } 16 | 17 | public void ConfigureServices(IServiceCollection services) 18 | { 19 | services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) 20 | .AddIdentityServerAuthentication(options => 21 | { 22 | options.RequireHttpsMetadata = false; 23 | options.Authority = "http://localhost"; 24 | options.JwtBackChannelHandler = this.identityServerMessageHandler; 25 | }); 26 | services.AddMvc(options => options.EnableEndpointRouting = false); 27 | } 28 | 29 | public void Configure(IApplicationBuilder app) 30 | { 31 | app.UseAuthentication(); 32 | app.UseMvc(); 33 | } 34 | } 35 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/IdentityServer4.Contrib.AspNetCore.Testing.Tests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | false 6 | true 7 | $(NoWarn);S2699 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | all 21 | runtime; build; native; contentfiles; analyzers; buildtransitive 22 | 23 | 24 | runtime; build; native; contentfiles; analyzers; buildtransitive 25 | all 26 | 27 | 28 | all 29 | runtime; build; native; contentfiles; analyzers; buildtransitive 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | Always 42 | 43 | 44 | 45 | 46 | 47 | Always 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/IdentityServerHostProxyTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Threading.Tasks; 3 | using Autofac.Extensions.DependencyInjection; 4 | using IdentityServer4.Contrib.AspNetCore.Testing.Builder; 5 | using IdentityServer4.Contrib.AspNetCore.Testing.Configuration; 6 | using IdentityServer4.Contrib.AspNetCore.Testing.Services; 7 | using IdentityServer4.Models; 8 | using Xunit; 9 | 10 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Tests 11 | { 12 | public class IdentityServerHostProxyTests 13 | { 14 | [Fact] 15 | public async Task GetDiscoverResponseAsync_ValidConfiguration_Succeeds() 16 | { 17 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 18 | 19 | var client = new Client 20 | { 21 | ClientId = clientConfiguration.Id, 22 | ClientSecrets = new List 23 | { 24 | new Secret(clientConfiguration.Secret.Sha256()) 25 | }, 26 | AllowedScopes = new[] {"api1"}, 27 | AllowedGrantTypes = new[] {GrantType.ClientCredentials}, 28 | AccessTokenType = AccessTokenType.Jwt, 29 | AccessTokenLifetime = 7200 30 | }; 31 | 32 | var hostBuilder = new IdentityServerTestHostBuilder() 33 | .AddClients(client) 34 | .AddApiResources(new ApiResource("api1", "api1name")) 35 | .AddApiScopes(new ApiScope("api1")) 36 | .CreateHostBuilder(new AutofacServiceProviderFactory(), ContainerBuilderConfiguration.ConfigureContainer); 37 | 38 | var identityServerProxy = new IdentityServerHostProxy(hostBuilder); 39 | 40 | var response = await identityServerProxy.GetDiscoverResponseAsync(); 41 | 42 | Assert.NotNull(response); 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/IdentityServerTestHostBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Reflection; 5 | using Autofac.Extensions.DependencyInjection; 6 | using IdentityServer4.Contrib.AspNetCore.Testing.Builder; 7 | using IdentityServer4.Contrib.AspNetCore.Testing.Configuration; 8 | using IdentityServer4.Models; 9 | using IdentityServer4.Services; 10 | using IdentityServer4.Testing.Infrastructure.Services; 11 | using IdentityServer4.Testing.Infrastructure.Validators; 12 | using IdentityServer4.Validation; 13 | using Microsoft.Extensions.DependencyInjection; 14 | using Microsoft.Extensions.Hosting; 15 | using Serilog; 16 | using Serilog.Events; 17 | using Serilog.Sinks.SystemConsole.Themes; 18 | using Xunit; 19 | 20 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Tests 21 | { 22 | public class IdentityServerTestHostBuilderTests 23 | { 24 | [Fact] 25 | public void CreateHostBuilder_UseProfileServiceTypedButOfWrongType_ThrowsArgumentException() 26 | { 27 | Assert.Throws(() => new IdentityServerTestHostBuilder().UseProfileService(typeof(ExtensionsGrantValidator))); 28 | } 29 | 30 | [Fact] 31 | public void CreateHostBuilder_UseProfileServiceTypes_Resolveable() 32 | { 33 | var (client, apiResource, apiScope) = CreateTestData(); 34 | 35 | var builder = new IdentityServerTestHostBuilder() 36 | .UseProfileService(typeof(SimpleProfileService)) 37 | .AddClients(client) 38 | .AddApiResources(apiResource) 39 | .AddApiScopes(apiScope) 40 | .CreateHostBuilder(new AutofacServiceProviderFactory(), ContainerBuilderConfiguration.ConfigureContainer); 41 | 42 | var host = builder.Start(); 43 | host.Services.GetRequiredService(); 44 | } 45 | 46 | [Fact] 47 | public void CreateHostBuilder_UseResourceOwnerPasswordValidatorTypedButOfWrongType_ThrowsArgumentException() 48 | { 49 | Assert.Throws(() => new IdentityServerTestHostBuilder() 50 | .UseResourceOwnerPasswordValidator(typeof(ExtensionsGrantValidator))); 51 | } 52 | 53 | [Fact] 54 | public void CreateHostBuilder_UseResourceOwnerPasswordValidatorTyped_Resolveable() 55 | { 56 | var (client, apiResource, apiScope) = CreateTestData(); 57 | 58 | var builder = new IdentityServerTestHostBuilder() 59 | .UseResourceOwnerPasswordValidator(typeof(SimpleResourceOwnerPasswordValidator)) 60 | .AddClients(client) 61 | .AddApiResources(apiResource) 62 | .AddApiScopes(apiScope) 63 | .CreateHostBuilder(new AutofacServiceProviderFactory(), ContainerBuilderConfiguration.ConfigureContainer); 64 | 65 | var host = builder.Start(); 66 | host.Services.GetRequiredService(); 67 | } 68 | 69 | 70 | [Fact] 71 | public void CreateHostBuilder_UseResourceOwnerPasswordValidator_Resolveable() 72 | { 73 | InitializeSerilog(); 74 | 75 | var (client, apiResource, apiScope) = CreateTestData(); 76 | 77 | var builder = new IdentityServerTestHostBuilder() 78 | .UseLoggingBuilder((context, loggingBuilder) => loggingBuilder.AddSerilog()) 79 | .UseResourceOwnerPasswordValidator(typeof(ResourceOwnerValidatorWithDependencies)) 80 | .AddClients(client) 81 | .AddApiResources(apiResource) 82 | .AddApiScopes(apiScope) 83 | .CreateHostBuilder(new AutofacServiceProviderFactory(), ContainerBuilderConfiguration.ConfigureContainer); 84 | 85 | var host = builder.Start(); 86 | host.Services.GetRequiredService(); 87 | } 88 | 89 | [Fact] 90 | public void CreateHostBuilder_ServiceRegisteredWithAutofacContainer_ServiceResolveable() 91 | { 92 | var (client, apiResource, apiScope) = CreateTestData(); 93 | 94 | var builder = new IdentityServerTestHostBuilder() 95 | .AddClients(client) 96 | .AddApiResources(apiResource) 97 | .AddApiScopes(apiScope) 98 | .CreateHostBuilder(new AutofacServiceProviderFactory(), ContainerBuilderConfiguration.ConfigureContainer); 99 | 100 | var host = builder.Start(); 101 | 102 | host.Services.GetRequiredService(); 103 | } 104 | 105 | [Fact] 106 | public void CreateHostBuilder_ValidateScopes() 107 | { 108 | var apiResource = new ApiResource("res1"); 109 | apiResource.Scopes = new List { "scope1", "scope3" }; 110 | // Bad scope specs 111 | Assert.Throws(() => new IdentityServerTestHostBuilder() 112 | .AddApiResources(apiResource) 113 | .AddApiScopes(new ApiScope("scope3")) 114 | .CreateHostBuilder()); 115 | Assert.Throws(() => new IdentityServerTestHostBuilder() 116 | .AddApiResources(apiResource) 117 | .AddApiScopes(new ApiScope("scope1"), new ApiScope("scope2")) 118 | .CreateHostBuilder()); 119 | Assert.Throws(() => new IdentityServerTestHostBuilder() 120 | .AddApiResources(apiResource) 121 | .AddApiScopes(new ApiScope("scope2"), new ApiScope("scope3")) 122 | .CreateHostBuilder()); 123 | // Good scope specs 124 | new IdentityServerTestHostBuilder() 125 | .AddApiResources(apiResource) 126 | .AddApiScopes(new ApiScope("scope1"), new ApiScope("scope2"), new ApiScope("scope3")) 127 | .CreateHostBuilder(); 128 | new IdentityServerTestHostBuilder() 129 | .AddApiResources(apiResource) 130 | .AddApiScopes(new ApiScope("scope1"), new ApiScope("scope3")) 131 | .CreateHostBuilder(); 132 | } 133 | 134 | private static (Client client, ApiResource apiResource, ApiScope apiScope) CreateTestData() 135 | { 136 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 137 | 138 | var client = new Client 139 | { 140 | ClientId = clientConfiguration.Id, 141 | ClientSecrets = new List 142 | { 143 | new Secret(clientConfiguration.Secret.Sha256()) 144 | }, 145 | AllowedScopes = new[] {"api1"}, 146 | AllowedGrantTypes = new[] {GrantType.ClientCredentials}, 147 | AccessTokenType = AccessTokenType.Jwt, 148 | AccessTokenLifetime = 7200 149 | }; 150 | 151 | return (client, new ApiResource("api1", "api1name"), new ApiScope("api1")); 152 | } 153 | 154 | private static void InitializeSerilog() 155 | { 156 | Log.Logger = new LoggerConfiguration() 157 | .Enrich.FromLogContext() 158 | .MinimumLevel.Debug() 159 | .WriteTo.RollingFile(Path.Combine(AppContext.BaseDirectory, "Logs", 160 | $"{Assembly.GetExecutingAssembly().GetName().Name}.log")) 161 | .WriteTo.Console( 162 | outputTemplate: 163 | "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", 164 | theme: AnsiConsoleTheme.Literate, restrictedToMinimumLevel: LogEventLevel.Error) 165 | .CreateLogger(); 166 | } 167 | } 168 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/IdentityServerWebHostBuilderTests.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using IdentityServer4.Contrib.AspNetCore.Testing.Builder; 5 | using IdentityServer4.Services; 6 | using IdentityServer4.Testing.Infrastructure.Services; 7 | using IdentityServer4.Testing.Infrastructure.Validators; 8 | using IdentityServer4.Validation; 9 | using Microsoft.AspNetCore.Hosting; 10 | using Microsoft.Extensions.Configuration; 11 | using Microsoft.Extensions.DependencyInjection; 12 | using Microsoft.Extensions.Logging; 13 | using Serilog; 14 | using Serilog.Events; 15 | using Serilog.Sinks.SystemConsole.Themes; 16 | using Xunit; 17 | 18 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Tests 19 | { 20 | public class IdentityServerWebHostBuilderTests 21 | { 22 | private static void InitializeSerilog() 23 | { 24 | Log.Logger = new LoggerConfiguration() 25 | .Enrich.FromLogContext() 26 | .MinimumLevel.Debug() 27 | .WriteTo.RollingFile(Path.Combine(AppContext.BaseDirectory, "Logs", 28 | $"{Assembly.GetExecutingAssembly().GetName().Name}.log")) 29 | .WriteTo.Console( 30 | outputTemplate: 31 | "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", 32 | theme: AnsiConsoleTheme.Literate, restrictedToMinimumLevel: LogEventLevel.Error) 33 | .CreateLogger(); 34 | } 35 | 36 | [Fact] 37 | public void IdentityServerWebHostBuilder_UseConfigurationBuilder_HasSettings() 38 | { 39 | var webHost = new IdentityServerTestWebHostBuilder() 40 | .UseConfigurationBuilder((context, builder) => 41 | { 42 | context.HostingEnvironment.WebRootPath = AppContext.BaseDirectory; 43 | builder.AddJsonFile(Path.Combine(AppContext.BaseDirectory, "testappsettings.json"), false); 44 | }) 45 | .CreateWebHostBuilder() 46 | .Build(); 47 | 48 | var configuration = webHost.Services.GetRequiredService(); 49 | var hostingEnvironment = 50 | webHost.Services.GetRequiredService(); 51 | 52 | Assert.Equal("PropValue", configuration["Prop"]); 53 | Assert.Equal(AppContext.BaseDirectory, hostingEnvironment.ContentRootPath); 54 | } 55 | 56 | [Fact] 57 | public void IdentityServerWebHostBuilder_UseLoggingBuilder_Serilog_Expect_Logger_And_Provider() 58 | { 59 | InitializeSerilog(); 60 | 61 | var webHost = new IdentityServerTestWebHostBuilder() 62 | .UseLoggingBuilder((context, builder) => builder.AddSerilog()) 63 | .CreateWebHostBuilder() 64 | .UseContentRoot(AppContext.BaseDirectory) 65 | .Build(); 66 | 67 | var logger = webHost 68 | .Services 69 | .GetRequiredService>(); 70 | 71 | var path = Path.Combine(AppContext.BaseDirectory, "Logs", 72 | $"{Assembly.GetExecutingAssembly().GetName().Name}-{DateTime.UtcNow:yyyyMMdd}.log"); 73 | 74 | logger.LogError($"Logging to path {path} works!"); 75 | } 76 | 77 | [Fact] 78 | public void IdentityServerWebHostBuilder_UseProfileService_Resolveable() 79 | { 80 | var webHost = new IdentityServerTestWebHostBuilder() 81 | .UseProfileService(new SimpleProfileService()) 82 | .CreateWebHostBuilder() 83 | .Build(); 84 | 85 | webHost.Services.GetRequiredService(); 86 | } 87 | 88 | [Fact] 89 | public void IdentityServerWebHostBuilder_UseProfileService_Typed_Invalid_Throws() 90 | { 91 | Assert.Throws(() => new IdentityServerTestWebHostBuilder() 92 | .UseProfileService(typeof(ExtensionsGrantValidator))); 93 | } 94 | 95 | [Fact] 96 | public void IdentityServerWebHostBuilder_UseProfileService_Typed_Resolveable() 97 | { 98 | var webHost = new IdentityServerTestWebHostBuilder() 99 | .UseProfileService(typeof(SimpleProfileService)) 100 | .CreateWebHostBuilder() 101 | .Build(); 102 | 103 | webHost.Services.GetRequiredService(); 104 | } 105 | 106 | [Fact] 107 | public void IdentityServerWebHostBuilder_UseResourceOwnerPasswordValidator_Resolveable() 108 | { 109 | var webHost = new IdentityServerTestWebHostBuilder() 110 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 111 | .CreateWebHostBuilder() 112 | .Build(); 113 | 114 | webHost.Services.GetRequiredService(); 115 | } 116 | 117 | [Fact] 118 | public void IdentityServerWebHostBuilder_UseResourceOwnerPasswordValidator_Typed_Invalid_Throws() 119 | { 120 | Assert.Throws(() => new IdentityServerTestWebHostBuilder() 121 | .UseResourceOwnerPasswordValidator(typeof(ExtensionsGrantValidator))); 122 | } 123 | 124 | [Fact] 125 | public void IdentityServerWebHostBuilder_UseResourceOwnerPasswordValidator_Typed_Resolveable() 126 | { 127 | var webHost = new IdentityServerTestWebHostBuilder() 128 | .UseResourceOwnerPasswordValidator(typeof(SimpleResourceOwnerPasswordValidator)) 129 | .CreateWebHostBuilder() 130 | .Build(); 131 | 132 | webHost.Services.GetRequiredService(); 133 | } 134 | 135 | [Fact] 136 | public void IdentityServerWebHostBuilder_UseResourceOwnerPasswordValidator_With_Dependencies_Resolveable() 137 | { 138 | InitializeSerilog(); 139 | 140 | var webHost = new IdentityServerTestWebHostBuilder() 141 | .UseLoggingBuilder((context, builder) => builder.AddSerilog()) 142 | .UseResourceOwnerPasswordValidator(typeof(ResourceOwnerValidatorWithDependencies)) 143 | .CreateWebHostBuilder() 144 | .Build(); 145 | 146 | webHost.Services.GetRequiredService(); 147 | } 148 | } 149 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/IdentityServerWebHostProxyTests.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | using IdentityModel; 5 | using IdentityModel.Client; 6 | using IdentityServer4.Api; 7 | using IdentityServer4.Contrib.AspNetCore.Testing.Builder; 8 | using IdentityServer4.Contrib.AspNetCore.Testing.Configuration; 9 | using IdentityServer4.Contrib.AspNetCore.Testing.Services; 10 | using IdentityServer4.Models; 11 | using IdentityServer4.Server.Models; 12 | using IdentityServer4.Testing.Infrastructure.Services; 13 | using IdentityServer4.Testing.Infrastructure.Validators; 14 | using IdentityServer4.Validation; 15 | using Microsoft.AspNetCore; 16 | using Microsoft.AspNetCore.Hosting; 17 | using Microsoft.AspNetCore.TestHost; 18 | using Microsoft.Extensions.DependencyInjection; 19 | using Xunit; 20 | using IdentityResources = IdentityServer4.Models.IdentityResources; 21 | using Program = IdentityServer4.Server.Program; 22 | 23 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Tests 24 | { 25 | public class IdentityServerWebHostProxyTests 26 | { 27 | [Fact] 28 | public async Task IdentityServerProxy_GetClientCredentialsAsync_Authorize_Api_Succeeds() 29 | { 30 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 31 | 32 | var client = new Client 33 | { 34 | ClientId = clientConfiguration.Id, 35 | ClientSecrets = new List 36 | { 37 | new Secret(clientConfiguration.Secret.Sha256()) 38 | }, 39 | AllowedScopes = new[] {"api1"}, 40 | AllowedGrantTypes = new[] {GrantType.ClientCredentials}, 41 | AccessTokenType = AccessTokenType.Jwt, 42 | AccessTokenLifetime = 7200 43 | }; 44 | 45 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 46 | .AddClients(client) 47 | .AddApiResources(new ApiResource("api1", "api1name")) 48 | .AddApiScopes(new ApiScope("api1")) 49 | .CreateWebHostBuilder(); 50 | 51 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 52 | 53 | var tokenResponse = await identityServerProxy.GetClientAccessTokenAsync(clientConfiguration, "api1"); 54 | 55 | var apiWebHostBuilder = WebHost.CreateDefaultBuilder() 56 | .ConfigureServices(services => 57 | services.AddSingleton(identityServerProxy.IdentityServer.CreateHandler())) 58 | .UseStartup(); 59 | 60 | var apiServer = new TestServer(apiWebHostBuilder); 61 | 62 | var apiClient = apiServer.CreateClient(); 63 | 64 | apiClient.SetBearerToken(tokenResponse.AccessToken); 65 | 66 | var apiResponse = await apiClient.GetAsync("api/auth"); 67 | 68 | Assert.True(apiResponse.IsSuccessStatusCode, "should have been authenticated!"); 69 | } 70 | 71 | [Fact] 72 | public async Task IdentityServerProxy_GetClientCredentialsAsync_Succeeds() 73 | { 74 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 75 | 76 | var client = new Client 77 | { 78 | ClientId = clientConfiguration.Id, 79 | ClientSecrets = new List 80 | { 81 | new Secret(clientConfiguration.Secret.Sha256()) 82 | }, 83 | AllowedScopes = new[] {"api1"}, 84 | AllowedGrantTypes = new[] {GrantType.ClientCredentials}, 85 | AccessTokenType = AccessTokenType.Jwt, 86 | AccessTokenLifetime = 7200 87 | }; 88 | 89 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 90 | .AddClients(client) 91 | .AddApiResources(new ApiResource("api1", "api1name")) 92 | .AddApiScopes(new ApiScope("api1")) 93 | .CreateWebHostBuilder(); 94 | 95 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 96 | 97 | var tokenResponse = await identityServerProxy.GetClientAccessTokenAsync(clientConfiguration, "api1"); 98 | 99 | Assert.NotNull(tokenResponse); 100 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 101 | Assert.NotNull(tokenResponse.AccessToken); 102 | Assert.Equal(7200, tokenResponse.ExpiresIn); 103 | Assert.Equal("Bearer", tokenResponse.TokenType); 104 | } 105 | 106 | [Fact] 107 | public async Task IdentityServerProxy_GetClientCredentialsAsync_WithScope_In_Parameters_Succeeds() 108 | { 109 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 110 | 111 | var client = new Client 112 | { 113 | ClientId = clientConfiguration.Id, 114 | ClientSecrets = new List 115 | { 116 | new Secret(clientConfiguration.Secret.Sha256()) 117 | }, 118 | AllowedScopes = new[] {"api1"}, 119 | AllowedGrantTypes = new[] {GrantType.ClientCredentials}, 120 | AccessTokenType = AccessTokenType.Jwt, 121 | AccessTokenLifetime = 7200 122 | }; 123 | 124 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 125 | .AddClients(client) 126 | .AddApiResources(new ApiResource("api1", "api1name")) 127 | .AddApiScopes(new ApiScope("api1")) 128 | .CreateWebHostBuilder(); 129 | 130 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 131 | 132 | var tokenResponse = await identityServerProxy.GetClientAccessTokenAsync(clientConfiguration, 133 | new Dictionary 134 | { 135 | {"Scope", "api1"} 136 | }); 137 | 138 | Assert.NotNull(tokenResponse); 139 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 140 | Assert.NotNull(tokenResponse.AccessToken); 141 | Assert.Equal(7200, tokenResponse.ExpiresIn); 142 | Assert.Equal("Bearer", tokenResponse.TokenType); 143 | } 144 | 145 | [Fact] 146 | public async Task IdentityServerProxy_GetDiscoverDocumentAsync_Succeeds() 147 | { 148 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 149 | .AddClients(new Client 150 | { 151 | ClientId = "MyClient", 152 | ClientSecrets = new List 153 | { 154 | new Secret("MySecret".Sha256()) 155 | } 156 | }) 157 | .AddApiResources(new ApiResource()) 158 | .AddApiScopes(new ApiScope()) 159 | .CreateWebHostBuilder(); 160 | 161 | var identityServerClient = new IdentityServerWebHostProxy(webHostBuilder); 162 | var discoveryResponse = await identityServerClient.GetDiscoverResponseAsync(); 163 | 164 | Assert.NotNull(discoveryResponse); 165 | Assert.False(discoveryResponse.IsError, discoveryResponse.Error); 166 | } 167 | 168 | [Fact] 169 | public async Task IdentityServerProxy_GetRefreshTokenAsync_Valid_Token_Succeeds() 170 | { 171 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 172 | 173 | var client = new Client 174 | { 175 | ClientId = clientConfiguration.Id, 176 | ClientSecrets = new List 177 | { 178 | new Secret(clientConfiguration.Secret.Sha256()) 179 | }, 180 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 181 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 182 | AccessTokenType = AccessTokenType.Jwt, 183 | AccessTokenLifetime = 7200, 184 | AllowOfflineAccess = true 185 | }; 186 | 187 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 188 | .AddClients(client) 189 | .AddApiResources(new ApiResource("api1", "api1name")) 190 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 191 | .AddApiScopes(new ApiScope("api1")) 192 | .CreateWebHostBuilder(); 193 | 194 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 195 | 196 | var scopes = new[] {"api1", "offline_access"}; 197 | 198 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 199 | new UserLoginConfiguration("user", "password"), 200 | scopes); 201 | 202 | // We are breaking the pattern arrange / act / assert here but we need to make sure token requested successfully first 203 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 204 | 205 | 206 | var refreshTokenResponse = await identityServerProxy 207 | .GetRefreshTokenAsync(clientConfiguration, tokenResponse.RefreshToken, 208 | scopes); 209 | 210 | Assert.NotNull(refreshTokenResponse); 211 | Assert.False(refreshTokenResponse.IsError, 212 | refreshTokenResponse.Error ?? refreshTokenResponse.ErrorDescription); 213 | Assert.NotNull(refreshTokenResponse.AccessToken); 214 | Assert.NotNull(refreshTokenResponse.RefreshToken); 215 | Assert.Equal(7200, refreshTokenResponse.ExpiresIn); 216 | Assert.Equal("Bearer", refreshTokenResponse.TokenType); 217 | } 218 | 219 | [Fact] 220 | public async Task IdentityServerProxy_GetRefreshTokenAsync_WithScope_In_Parameters_Valid_User_Succeeds() 221 | { 222 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 223 | 224 | var client = new Client 225 | { 226 | ClientId = clientConfiguration.Id, 227 | ClientSecrets = new List 228 | { 229 | new Secret(clientConfiguration.Secret.Sha256()) 230 | }, 231 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 232 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 233 | AccessTokenType = AccessTokenType.Jwt, 234 | AccessTokenLifetime = 7200, 235 | AllowOfflineAccess = true 236 | }; 237 | 238 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 239 | .AddClients(client) 240 | .AddApiResources(new ApiResource("api1", "api1name")) 241 | .AddApiScopes(new ApiScope("api1")) 242 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 243 | .CreateWebHostBuilder(); 244 | 245 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 246 | 247 | const string scopes = "api1 offline_access"; 248 | 249 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 250 | new UserLoginConfiguration("user", "password"), 251 | new Dictionary 252 | { 253 | {"Scope", scopes} 254 | }); 255 | 256 | // We are breaking the pattern arrange / act / assert here but we need to make sure token requested successfully first 257 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 258 | 259 | var refreshTokenResponse = await identityServerProxy 260 | .GetRefreshTokenAsync(clientConfiguration, tokenResponse.RefreshToken, 261 | new Dictionary 262 | { 263 | {"Scope", scopes} 264 | }); 265 | 266 | Assert.NotNull(refreshTokenResponse); 267 | Assert.False(refreshTokenResponse.IsError, 268 | refreshTokenResponse.Error ?? refreshTokenResponse.ErrorDescription); 269 | Assert.NotNull(refreshTokenResponse.AccessToken); 270 | Assert.NotNull(refreshTokenResponse.RefreshToken); 271 | Assert.Equal(7200, refreshTokenResponse.ExpiresIn); 272 | Assert.Equal("Bearer", refreshTokenResponse.TokenType); 273 | } 274 | 275 | [Fact] 276 | public async Task IdentityServerProxy_GetResourceOwnerTokenAsync_Custom_WebHost_Succeeds() 277 | { 278 | var host = new IdentityServerTestWebHostBuilder() 279 | .UseWebHostBuilder(Program.CreateWebHostBuilder(new string[] { })) 280 | .CreateWebHostBuilder(); 281 | 282 | var proxy = new IdentityServerWebHostProxy(host); 283 | 284 | var scopes = new[] {"api1", "offline_access", "openid", "profile"}; 285 | 286 | var tokenResponse = await proxy.GetResourceOwnerPasswordAccessTokenAsync( 287 | new ClientConfiguration(Clients.Id, Clients.Secret), 288 | new UserLoginConfiguration("user1", "password1"), scopes); 289 | 290 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 291 | } 292 | 293 | [Fact] 294 | public async Task IdentityServerProxy_GetResourceOwnerTokenAsync_Invalid_User_Succeeds() 295 | { 296 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 297 | 298 | var client = new Client 299 | { 300 | ClientId = clientConfiguration.Id, 301 | ClientSecrets = new List 302 | { 303 | new Secret(clientConfiguration.Secret.Sha256()) 304 | }, 305 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 306 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 307 | AccessTokenType = AccessTokenType.Jwt, 308 | AccessTokenLifetime = 7200, 309 | AllowOfflineAccess = true 310 | }; 311 | 312 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 313 | .AddClients(client) 314 | .AddApiResources(new ApiResource("api1", "api1name")) 315 | .AddApiScopes(new ApiScope()) 316 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 317 | .CreateWebHostBuilder(); 318 | 319 | var identityServerClient = new IdentityServerWebHostProxy(webHostBuilder); 320 | 321 | var tokenResponse = await identityServerClient.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 322 | new UserLoginConfiguration("user", "password1"), 323 | "api1", "offline_access"); 324 | 325 | Assert.NotNull(tokenResponse); 326 | Assert.True(tokenResponse.IsError); 327 | } 328 | 329 | [Fact] 330 | public async Task 331 | IdentityServerProxy_GetResourceOwnerTokenAsync_Valid_User_Custom_IdentityServerBuilder_Succeeds() 332 | { 333 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 334 | 335 | var client = new Client 336 | { 337 | ClientId = clientConfiguration.Id, 338 | ClientSecrets = new List 339 | { 340 | new Secret(clientConfiguration.Secret.Sha256()) 341 | }, 342 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 343 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 344 | AccessTokenType = AccessTokenType.Jwt, 345 | AccessTokenLifetime = 7200, 346 | AllowOfflineAccess = true 347 | }; 348 | 349 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 350 | .AddClients(client) 351 | .AddApiResources(new ApiResource("api1", "api1name")) 352 | .AddApiScopes(new ApiScope("api1")) 353 | .UseResourceOwnerPasswordValidator(typeof(SimpleResourceOwnerPasswordValidator)) 354 | .UseIdentityServerBuilder(services => services 355 | .AddIdentityServer() 356 | .AddDefaultEndpoints() 357 | .AddDeveloperSigningCredential() 358 | ) 359 | .CreateWebHostBuilder(); 360 | 361 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 362 | 363 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 364 | new UserLoginConfiguration("user", "password"), 365 | "api1", "offline_access"); 366 | 367 | Assert.NotNull(tokenResponse); 368 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 369 | Assert.NotNull(tokenResponse.AccessToken); 370 | Assert.NotNull(tokenResponse.RefreshToken); 371 | Assert.Equal(7200, tokenResponse.ExpiresIn); 372 | Assert.Equal("Bearer", tokenResponse.TokenType); 373 | } 374 | 375 | [Fact] 376 | public async Task 377 | IdentityServerProxy_GetResourceOwnerTokenAsync_Valid_User_Custom_IdentityServerBuilderOptions_Token_Endpoint_Disabled_Fails() 378 | { 379 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 380 | 381 | var client = new Client 382 | { 383 | ClientId = clientConfiguration.Id, 384 | ClientSecrets = new List 385 | { 386 | new Secret(clientConfiguration.Secret.Sha256()) 387 | }, 388 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 389 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 390 | AccessTokenType = AccessTokenType.Jwt, 391 | AccessTokenLifetime = 7200, 392 | AllowOfflineAccess = true 393 | }; 394 | 395 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 396 | .AddClients(client) 397 | .AddApiResources(new ApiResource("api1", "api1name")) 398 | .AddApiScopes(new ApiScope("api1")) 399 | .UseResourceOwnerPasswordValidator(typeof(SimpleResourceOwnerPasswordValidator)) 400 | .UseIdentityServerOptionsBuilder(options => options.Endpoints.EnableTokenEndpoint = false) 401 | .CreateWebHostBuilder(); 402 | 403 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 404 | 405 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 406 | new UserLoginConfiguration("user", "password"), 407 | "api1", "offline_access"); 408 | 409 | Assert.NotNull(tokenResponse); 410 | Assert.True(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 411 | } 412 | 413 | [Fact] 414 | public async Task IdentityServerProxy_GetResourceOwnerTokenAsync_Valid_User_Succeeds() 415 | { 416 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 417 | 418 | var client = new Client 419 | { 420 | ClientId = clientConfiguration.Id, 421 | ClientSecrets = new List 422 | { 423 | new Secret(clientConfiguration.Secret.Sha256()) 424 | }, 425 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 426 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 427 | AccessTokenType = AccessTokenType.Jwt, 428 | AccessTokenLifetime = 7200, 429 | AllowOfflineAccess = true 430 | }; 431 | 432 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 433 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 434 | .AddClients(client) 435 | .AddApiResources(new ApiResource("api1", "api1name")) 436 | .AddApiScopes(new ApiScope("api1")) 437 | .CreateWebHostBuilder(); 438 | 439 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 440 | 441 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 442 | new UserLoginConfiguration("user", "password"), 443 | "api1", "offline_access"); 444 | 445 | Assert.NotNull(tokenResponse); 446 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 447 | Assert.NotNull(tokenResponse.AccessToken); 448 | Assert.NotNull(tokenResponse.RefreshToken); 449 | Assert.Equal(7200, tokenResponse.ExpiresIn); 450 | Assert.Equal("Bearer", tokenResponse.TokenType); 451 | } 452 | 453 | [Fact] 454 | public async Task IdentityServerProxy_GetResourceOwnerTokenAsync_Valid_User_Typed_Validator_Succeeds() 455 | { 456 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 457 | 458 | var client = new Client 459 | { 460 | ClientId = clientConfiguration.Id, 461 | ClientSecrets = new List 462 | { 463 | new Secret(clientConfiguration.Secret.Sha256()) 464 | }, 465 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 466 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 467 | AccessTokenType = AccessTokenType.Jwt, 468 | AccessTokenLifetime = 7200, 469 | AllowOfflineAccess = true 470 | }; 471 | 472 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 473 | .AddClients(client) 474 | .AddApiResources(new ApiResource("api1", "api1name")) 475 | .AddApiScopes(new ApiScope("api1")) 476 | .UseResourceOwnerPasswordValidator(typeof(SimpleResourceOwnerPasswordValidator)) 477 | .CreateWebHostBuilder(); 478 | 479 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 480 | 481 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 482 | new UserLoginConfiguration("user", "password"), 483 | "api1", "offline_access"); 484 | 485 | Assert.NotNull(tokenResponse); 486 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 487 | Assert.NotNull(tokenResponse.AccessToken); 488 | Assert.NotNull(tokenResponse.RefreshToken); 489 | Assert.Equal(7200, tokenResponse.ExpiresIn); 490 | Assert.Equal("Bearer", tokenResponse.TokenType); 491 | } 492 | 493 | [Fact] 494 | public async Task IdentityServerProxy_GetResourceOwnerTokenAsync_WithScope_In_Parameters_Valid_User_Succeeds() 495 | { 496 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 497 | 498 | var client = new Client 499 | { 500 | ClientId = clientConfiguration.Id, 501 | ClientSecrets = new List 502 | { 503 | new Secret(clientConfiguration.Secret.Sha256()) 504 | }, 505 | AllowedScopes = new[] {"api1", IdentityServerConstants.StandardScopes.OfflineAccess}, 506 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 507 | AccessTokenType = AccessTokenType.Jwt, 508 | AccessTokenLifetime = 7200, 509 | AllowOfflineAccess = true 510 | }; 511 | 512 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 513 | .AddClients(client) 514 | .AddApiResources(new ApiResource("api1", "api1name")) 515 | .AddApiScopes(new ApiScope("api1")) 516 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 517 | .CreateWebHostBuilder(); 518 | 519 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 520 | 521 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 522 | new UserLoginConfiguration("user", "password"), 523 | new Dictionary 524 | { 525 | {"Scope", "api1 offline_access"} 526 | }); 527 | 528 | Assert.NotNull(tokenResponse); 529 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 530 | Assert.NotNull(tokenResponse.AccessToken); 531 | Assert.NotNull(tokenResponse.RefreshToken); 532 | Assert.Equal(7200, tokenResponse.ExpiresIn); 533 | Assert.Equal("Bearer", tokenResponse.TokenType); 534 | } 535 | 536 | [Fact] 537 | public async Task IdentityServerProxy_GetTokenAsync_Extension_Grant_Valid_User_Succeeds() 538 | { 539 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 540 | 541 | var client = new Client 542 | { 543 | ClientId = clientConfiguration.Id, 544 | ClientSecrets = new List 545 | { 546 | new Secret(clientConfiguration.Secret.Sha256()) 547 | }, 548 | AllowedScopes = new[] 549 | { 550 | "api1", IdentityServerConstants.StandardScopes.OfflineAccess, 551 | IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile 552 | }, 553 | AllowedGrantTypes = new[] {"Custom"}, 554 | AccessTokenType = AccessTokenType.Jwt, 555 | AccessTokenLifetime = 7200, 556 | AllowOfflineAccess = true 557 | }; 558 | 559 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 560 | .AddClients(client) 561 | .AddApiResources(new ApiResource("api1", "api1name")) 562 | .AddApiScopes(new ApiScope("api1")) 563 | .AddIdentityResources(new IdentityResources.OpenId(), new IdentityResources.Profile()) 564 | .UseServices((context, collection) => 565 | collection.AddScoped()) 566 | .CreateWebHostBuilder(); 567 | 568 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 569 | 570 | var scopes = new[] {"api1", "offline_access", "openid", "profile"}; 571 | 572 | var tokenResponse = await identityServerProxy.GetTokenAsync(clientConfiguration, "Custom", 573 | new Dictionary 574 | { 575 | {"scope", string.Join(" ", scopes)}, 576 | {"username", "user"}, 577 | {"password", "password"} 578 | }); 579 | 580 | Assert.NotNull(tokenResponse); 581 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 582 | Assert.Equal(7200, tokenResponse.ExpiresIn); 583 | Assert.NotNull(tokenResponse.AccessToken); 584 | Assert.NotNull(tokenResponse.RefreshToken); 585 | } 586 | 587 | [Fact] 588 | public async Task IdentityServerProxy_GetUserInfoAsync_Valid_Token_Succeeds() 589 | { 590 | var clientConfiguration = new ClientConfiguration("MyClient", "MySecret"); 591 | 592 | var client = new Client 593 | { 594 | ClientId = clientConfiguration.Id, 595 | ClientSecrets = new List 596 | { 597 | new Secret(clientConfiguration.Secret.Sha256()) 598 | }, 599 | AllowedScopes = new[] 600 | { 601 | "api1", IdentityServerConstants.StandardScopes.OfflineAccess, 602 | IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile 603 | }, 604 | AllowedGrantTypes = new[] {GrantType.ClientCredentials, GrantType.ResourceOwnerPassword}, 605 | AccessTokenType = AccessTokenType.Jwt, 606 | AccessTokenLifetime = 7200, 607 | AllowOfflineAccess = true 608 | }; 609 | 610 | var webHostBuilder = new IdentityServerTestWebHostBuilder() 611 | .AddClients(client) 612 | .AddApiResources(new ApiResource("api1", "api1name")) 613 | .AddApiScopes(new ApiScope("api1")) 614 | .AddIdentityResources(new IdentityResources.OpenId(), new IdentityResources.Profile()) 615 | .UseResourceOwnerPasswordValidator(new SimpleResourceOwnerPasswordValidator()) 616 | .UseProfileService(new SimpleProfileService()) 617 | .CreateWebHostBuilder(); 618 | 619 | var identityServerProxy = new IdentityServerWebHostProxy(webHostBuilder); 620 | 621 | var scopes = new[] {"api1", "offline_access", "openid", "profile"}; 622 | 623 | var tokenResponse = await identityServerProxy.GetResourceOwnerPasswordAccessTokenAsync(clientConfiguration, 624 | new UserLoginConfiguration("user", "password"), 625 | scopes); 626 | 627 | // We are breaking the pattern arrange / act / assert here but we need to make sure token requested successfully first 628 | Assert.False(tokenResponse.IsError, tokenResponse.Error ?? tokenResponse.ErrorDescription); 629 | 630 | 631 | var userInfoResponse = await identityServerProxy 632 | .GetUserInfoAsync(tokenResponse.AccessToken); 633 | 634 | Assert.NotNull(userInfoResponse); 635 | Assert.False(userInfoResponse.IsError); 636 | Assert.NotNull(userInfoResponse.Claims); 637 | 638 | var subjectClaim = userInfoResponse.Claims.First(claim => claim.Type == JwtClaimTypes.Subject); 639 | Assert.NotNull(subjectClaim); 640 | Assert.Equal("user", subjectClaim.Value); 641 | } 642 | } 643 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/Services/SimpleProfileService.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Linq; 3 | using System.Security.Claims; 4 | using System.Threading.Tasks; 5 | using IdentityModel; 6 | using IdentityServer4.Models; 7 | using IdentityServer4.Services; 8 | 9 | namespace IdentityServer4.Testing.Infrastructure.Services 10 | { 11 | public class SimpleProfileService : IProfileService 12 | 13 | { 14 | public Task GetProfileDataAsync(ProfileDataRequestContext context) 15 | { 16 | var subject = context.Subject.Claims.First(claim => claim.Type == JwtClaimTypes.Subject).Value; 17 | 18 | context.IssuedClaims = new List 19 | { 20 | new Claim(JwtClaimTypes.Subject, subject) 21 | }; 22 | 23 | return Task.CompletedTask; 24 | } 25 | 26 | public Task IsActiveAsync(IsActiveContext context) 27 | { 28 | context.IsActive = true; 29 | return Task.CompletedTask; 30 | } 31 | } 32 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/Shared/ContainerBuilderConfiguration.cs: -------------------------------------------------------------------------------- 1 | using Autofac; 2 | 3 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Tests 4 | { 5 | public static class ContainerBuilderConfiguration 6 | { 7 | public static void ConfigureContainer(ContainerBuilder containerBuilder) => 8 | containerBuilder.RegisterType().AsSelf(); 9 | } 10 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/Shared/Dependency.cs: -------------------------------------------------------------------------------- 1 | namespace IdentityServer4.Contrib.AspNetCore.Testing.Tests 2 | { 3 | public class Dependency 4 | { 5 | } 6 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/Validators/ExtensionsGrantValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Security.Claims; 3 | using System.Threading.Tasks; 4 | using IdentityModel; 5 | using IdentityServer4.Models; 6 | using IdentityServer4.Validation; 7 | 8 | namespace IdentityServer4.Testing.Infrastructure.Validators 9 | { 10 | public class ExtensionsGrantValidator : IExtensionGrantValidator 11 | { 12 | public Task ValidateAsync(ExtensionGrantValidationContext context) 13 | { 14 | if (context.Request.Raw["username"] != "user" || context.Request.Raw["password"] != "password") 15 | { 16 | context.Result = 17 | new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Username or password is wrong!"); 18 | return Task.CompletedTask; 19 | } 20 | 21 | context.Result = new GrantValidationResult("user", "custom", new List 22 | { 23 | new Claim(JwtClaimTypes.Subject, "user") 24 | }); 25 | 26 | return Task.CompletedTask; 27 | } 28 | 29 | public string GrantType => "Custom"; 30 | } 31 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/Validators/ResourceOwnerValidatorWithDependencies.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.Extensions.Logging; 2 | 3 | namespace IdentityServer4.Testing.Infrastructure.Validators 4 | { 5 | public class ResourceOwnerValidatorWithDependencies : SimpleResourceOwnerPasswordValidator 6 | { 7 | // ReSharper disable once PrivateFieldCanBeConvertedToLocalVariable 8 | private readonly ILogger logger; 9 | 10 | public ResourceOwnerValidatorWithDependencies(ILogger logger) 11 | { 12 | this.logger = logger; 13 | } 14 | } 15 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/Validators/SimpleResourceOwnerPasswordValidator.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using System.Security.Claims; 3 | using System.Threading.Tasks; 4 | using IdentityModel; 5 | using IdentityServer4.Models; 6 | using IdentityServer4.Validation; 7 | 8 | namespace IdentityServer4.Testing.Infrastructure.Validators 9 | { 10 | public class SimpleResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator 11 | { 12 | public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) 13 | { 14 | if (context.UserName != "user" || context.Password != "password") 15 | { 16 | context.Result = 17 | new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Username or password is wrong!"); 18 | return Task.CompletedTask; 19 | } 20 | 21 | context.Result = new GrantValidationResult("user", OidcConstants.AuthenticationMethods.Password, 22 | new List 23 | { 24 | new Claim(JwtClaimTypes.Subject, "user") 25 | }); 26 | 27 | return Task.CompletedTask; 28 | } 29 | } 30 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Contrib.AspNetCore.Testing.Tests/testappsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Prop": "PropValue" 3 | } 4 | -------------------------------------------------------------------------------- /test/IdentityServer4.Server/IdentityServer4.Server.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.1 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /test/IdentityServer4.Server/Models/ApiResources.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IdentityServer4.Models; 3 | 4 | namespace IdentityServer4.Server.Models 5 | { 6 | public static class ApiResources 7 | { 8 | public static IEnumerable GetApiResources 9 | => new List 10 | { 11 | new ApiResource("api1", "api1") 12 | }; 13 | } 14 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Server/Models/Clients.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IdentityServer4.Models; 3 | 4 | namespace IdentityServer4.Server.Models 5 | { 6 | public static class Clients 7 | { 8 | public const string Id = "sampleclient"; 9 | public const string Secret = "samplesecret"; 10 | 11 | public static IEnumerable GetClients 12 | => new List 13 | { 14 | new Client 15 | { 16 | ClientId = Id, 17 | ClientSecrets = new List 18 | { 19 | new Secret(Secret.Sha256()) 20 | }, 21 | AllowedScopes = new List 22 | { 23 | "api1", IdentityServerConstants.StandardScopes.OpenId, 24 | IdentityServerConstants.StandardScopes.Profile 25 | }, 26 | AllowedGrantTypes = new List 27 | { 28 | GrantType.ResourceOwnerPassword 29 | }, 30 | AllowOfflineAccess = true, 31 | AccessTokenLifetime = 60 * 60, 32 | RefreshTokenUsage = TokenUsage.OneTimeOnly, 33 | RefreshTokenExpiration = TokenExpiration.Absolute 34 | } 35 | }; 36 | } 37 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Server/Models/IdentityResources.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IdentityServer4.Models; 3 | 4 | namespace IdentityServer4.Server.Models 5 | { 6 | public static class IdentityResources 7 | { 8 | public static IEnumerable GetIdentityResources 9 | => new List 10 | { 11 | new IdentityServer4.Models.IdentityResources.OpenId(), 12 | new IdentityServer4.Models.IdentityResources.Profile() 13 | }; 14 | } 15 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Server/Models/TestUsers.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Generic; 2 | using IdentityServer4.Test; 3 | 4 | namespace IdentityServer4.Server.Models 5 | { 6 | public static class TestUsers 7 | { 8 | public static List GeTestUsers() 9 | { 10 | return new List 11 | { 12 | new TestUser 13 | { 14 | Username = "user1", 15 | Password = "password1", 16 | IsActive = true, 17 | SubjectId = "user1" 18 | } 19 | }; 20 | } 21 | } 22 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | using System.Reflection; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Serilog; 8 | using Serilog.Sinks.SystemConsole.Themes; 9 | 10 | namespace IdentityServer4.Server 11 | { 12 | public static class Program 13 | { 14 | public static Task Main(string[] args) 15 | { 16 | using var host = CreateWebHostBuilder(args).Build(); 17 | 18 | return host.RunAsync(); 19 | } 20 | 21 | public static IWebHostBuilder CreateWebHostBuilder(string[] _) 22 | { 23 | return WebHost.CreateDefaultBuilder() 24 | .UseStartup() 25 | .UseSerilog(ConfigureLogger); 26 | } 27 | 28 | private static void ConfigureLogger(WebHostBuilderContext hostContext, LoggerConfiguration loggerConfiguration) 29 | { 30 | loggerConfiguration.Enrich.FromLogContext() 31 | .MinimumLevel.Debug() 32 | .WriteTo.RollingFile(Path.Combine(AppContext.BaseDirectory, "Logs", 33 | $"{Assembly.GetExecutingAssembly().GetName().Name}.log")) 34 | .WriteTo.Console( 35 | outputTemplate: 36 | "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", 37 | theme: AnsiConsoleTheme.Literate); 38 | } 39 | } 40 | } -------------------------------------------------------------------------------- /test/IdentityServer4.Server/Startup.cs: -------------------------------------------------------------------------------- 1 | using System.Threading.Tasks; 2 | using IdentityServer4.Models; 3 | using IdentityServer4.Server.Models; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.Extensions.DependencyInjection; 6 | using Microsoft.Extensions.Logging; 7 | using IdentityResources = IdentityServer4.Server.Models.IdentityResources; 8 | 9 | namespace IdentityServer4.Server 10 | { 11 | public class Startup 12 | { 13 | public void ConfigureServices(IServiceCollection services) 14 | { 15 | services.AddIdentityServer(options => 16 | { 17 | options.Events.RaiseInformationEvents = true; 18 | options.Events.RaiseSuccessEvents = true; 19 | options.Events.RaiseFailureEvents = true; 20 | options.Events.RaiseErrorEvents = true; 21 | }) 22 | .AddDefaultEndpoints() 23 | .AddDefaultSecretParsers() 24 | .AddDeveloperSigningCredential() 25 | .AddInMemoryCaching() 26 | .AddTestUsers(TestUsers.GeTestUsers()) 27 | .AddInMemoryClients(Clients.GetClients) 28 | .AddInMemoryApiResources(ApiResources.GetApiResources) 29 | .AddInMemoryApiScopes(new[] 30 | { 31 | new ApiScope("api1"), 32 | }) 33 | .AddInMemoryIdentityResources(IdentityResources.GetIdentityResources); 34 | } 35 | 36 | public void Configure(IApplicationBuilder app) 37 | { 38 | app.UseIdentityServer(); 39 | 40 | app.Run(ctx => 41 | { 42 | var logger = ctx.RequestServices.GetRequiredService>(); 43 | logger.LogInformation("Logging!"); 44 | return Task.CompletedTask; 45 | }); 46 | } 47 | } 48 | } --------------------------------------------------------------------------------