├── .github
├── FUNDING.yml
├── ISSUE_TEMPLATE
│ ├── bug-report--english-only-.md
│ ├── feature-request--english-only-.md
│ └── questions--english-only-.md
├── hooks
│ └── commit-msg
└── workflows
│ ├── publish-package.yml
│ └── pull-request.yml
├── .gitignore
├── .releaserc
├── CHANGELOG.md
├── LICENSE
├── NetDevPack.Identity.sln
├── README.md
├── assets
└── IconNuget.png
└── src
├── NetDevPack.Identity
├── Authorization
│ ├── CustomAuthorizationValidation.cs
│ ├── CustomAuthorizeAttribute.cs
│ └── RequerimentClaimFilter.cs
├── Data
│ └── NetDevPackAppDbContext.cs
├── Interfaces
│ └── IJwtBuilder.cs
├── Jwt
│ ├── Abstractions.cs
│ ├── AppJwtSettings.cs
│ ├── Extensions.cs
│ ├── JwtBuilder.cs
│ ├── JwtBuilderInject.cs
│ ├── Model
│ │ ├── RefreshTokenValidation.cs
│ │ ├── UserClaim.cs
│ │ ├── UserResponse.cs
│ │ └── UserToken.cs
│ └── RefreshTokenType.cs
├── JwtBuilderExtensions.cs
├── Model
│ ├── LoginUser.cs
│ └── RegisterUser.cs
├── NetDevPack.Identity.csproj
└── User
│ ├── Abstractions.cs
│ ├── AspNetUser.cs
│ ├── ClaimsPrincipalExtensions.cs
│ └── IAspNetUser.cs
└── Samples
└── AspNetCore.Jwt.Sample
├── AspNetCore.Jwt.Sample.csproj
├── Config
├── CustomIdentityAndKeyConfig.cs
├── CustomIdentityConfig.cs
├── DefaultIdentityConfig.cs
├── DependencyInjectionConfig.cs
└── SwaggerConfig.cs
├── Controllers
├── AuthController.cs
├── AuthCustomConfigAndKeyController.cs
├── AuthCustomConfigController.cs
├── MainController.cs
└── SampleController.cs
├── Program.cs
├── Properties
└── launchSettings.json
├── RunMigration.txt
├── appsettings.Development.json
└── appsettings.json
/.github/FUNDING.yml:
--------------------------------------------------------------------------------
1 |
2 | # These are supported funding model platforms
3 |
4 | patreon: eduardopires
5 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug-report--english-only-.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Bug report (english only)
3 | about: Create a report to help us improve
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Describe the bug**
11 | A clear and concise description of what the bug is.
12 |
13 | **To Reproduce**
14 | Steps to reproduce the behavior:
15 | 1. Go to '...'
16 | 2. Click on '....'
17 | 3. Scroll down to '....'
18 | 4. See error
19 |
20 | **Expected behavior**
21 | A clear and concise description of what you expected to happen.
22 |
23 | **Screenshots**
24 | If applicable, add screenshots to help explain your problem.
25 |
26 | **Desktop (please complete the following information):**
27 | - OS: [e.g. iOS]
28 | - Browser [e.g. chrome, safari]
29 | - Version [e.g. 22]
30 |
31 | **Smartphone (please complete the following information):**
32 | - Device: [e.g. iPhone6]
33 | - OS: [e.g. iOS8.1]
34 | - Browser [e.g. stock browser, safari]
35 | - Version [e.g. 22]
36 |
37 | **Additional context**
38 | Add any other context about the problem here.
39 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature-request--english-only-.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Feature request (english only)
3 | about: Suggest an idea for this project
4 | title: ''
5 | labels: ''
6 | assignees: ''
7 |
8 | ---
9 |
10 | **Is your feature request related to a problem? Please describe.**
11 | A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
12 |
13 | **Describe the solution you'd like**
14 | A clear and concise description of what you want to happen.
15 |
16 | **Describe alternatives you've considered**
17 | A clear and concise description of any alternative solutions or features you've considered.
18 |
19 | **Additional context**
20 | Add any other context or screenshots about the feature request here.
21 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/questions--english-only-.md:
--------------------------------------------------------------------------------
1 | ---
2 | name: Questions (english only)
3 | about: Describe your question in as much detail as possible.
4 | title: ''
5 | labels: question
6 | assignees: ''
7 |
8 | ---
9 |
10 |
11 |
--------------------------------------------------------------------------------
/.github/hooks/commit-msg:
--------------------------------------------------------------------------------
1 | #!/bin/sh
2 | #
3 | # An example hook script to check the commit log message.
4 | # Called by "git commit" with one argument, the name of the file
5 | # that has the commit message. The hook should exit with non-zero
6 | # status after issuing an appropriate message if it wants to stop the
7 | # commit. The hook is allowed to edit the commit message file.
8 | #
9 | # To enable this hook, rename this file to "commit-msg".
10 |
11 | # Uncomment the below to add a Signed-off-by line to the message.
12 | # Doing this in a hook is a bad idea in general, but the prepare-commit-msg
13 | # hook is more suited to it.
14 | #
15 | # SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
16 | # grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
17 |
18 | # This example catches duplicate Signed-off-by lines.
19 |
20 | test "" = "$(grep '^Signed-off-by: ' "$1" |
21 | sort | uniq -c | sed -e '/^[ ]*1[ ]/d')" || {
22 | echo >&2 Duplicate Signed-off-by lines.
23 | exit 1
24 | }
25 | if ! head -1 "$1" | grep -qE "^(feat|fix|ci|chore|docs|test|style|refactor|chk)(\(.+?\))?(\!)?: .{1,}$"; then
26 | echo "Aborting commit. Your commit message is invalid. See some examples below:" >&2
27 | echo "feat(logging): added logs for failed signups" >&2
28 | echo "fix(homepage): fixed image gallery" >&2
29 | echo "test(homepage): updated tests" >&2
30 | echo "docs(readme): added new logging table information" >&2
31 | echo "For more information check https://www.conventionalcommits.org/en/v1.0.0/ for more details" >&2
32 | exit 1
33 | fi
34 | if ! head -1 "$1" | grep -qE "^.{1,150}$"; then
35 | echo "Aborting commit. Your commit message is too long." >&2
36 | exit 1
37 | fi
--------------------------------------------------------------------------------
/.github/workflows/publish-package.yml:
--------------------------------------------------------------------------------
1 | name: Master - Publish packages
2 |
3 | on:
4 | push:
5 | branches: [ master ]
6 |
7 | env:
8 | CURRENT_REPO_URL: https://github.com/${{ github.repository }}
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 |
14 | steps:
15 | - name: Checkout repository
16 | uses: actions/checkout@v3
17 |
18 | - name: Setup .NET 6
19 | uses: actions/setup-dotnet@v3
20 | with:
21 | dotnet-version: 6.0.x
22 |
23 | - name: Setup .NET 7
24 | uses: actions/setup-dotnet@v3
25 | with:
26 | dotnet-version: 7.0.x
27 |
28 | - name: Semantic Release
29 | id: semantic
30 | uses: cycjimmy/semantic-release-action@v2
31 | with:
32 | semantic_version: 18.0.1
33 | extra_plugins: |
34 | @semantic-release/changelog
35 | @semantic-release/github
36 | @semantic-release/git
37 | env:
38 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
39 |
40 | - name: Generate Package
41 | run: dotnet pack -c Release -o out -p:PackageVersion=${{ steps.semantic.outputs.new_release_version }} -p:RepositoryUrl=${{env.CURRENT_REPO_URL}}
42 |
43 | - name: Publish the package to nuget.org
44 | run: dotnet nuget push ./out/*.nupkg --skip-duplicate -n -d -k ${{ secrets.NUGET_AUTH_TOKEN}} -s https://api.nuget.org/v3/index.json
45 |
--------------------------------------------------------------------------------
/.github/workflows/pull-request.yml:
--------------------------------------------------------------------------------
1 | name: Pull Request Analisys
2 |
3 | on:
4 | pull_request:
5 | branches: [ master ]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 |
11 | steps:
12 | - name: Checkout repository
13 | uses: actions/checkout@v3
14 |
15 | - name: Setup .NET 6
16 | uses: actions/setup-dotnet@v3
17 | with:
18 | dotnet-version: 6.0.x
19 |
20 | - name: Setup .NET 7
21 | uses: actions/setup-dotnet@v3
22 | with:
23 | dotnet-version: 7.0.x
24 |
25 | - name: Restore dependencies
26 | run: dotnet restore
27 |
28 | - name: Build
29 | run: dotnet build --no-restore
30 |
31 | - name: Test
32 | run: dotnet test --no-build
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | PublishProfiles
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 | [Xx]64/
21 | [Xx]86/
22 | [Bb]uild/
23 | bld/
24 | [Bb]in/
25 | [Oo]bj/
26 |
27 | # Visual Studio 2015 cache/options directory
28 | .vs/
29 | # Uncomment if you have tasks that create the project's static files in wwwroot
30 | #wwwroot/
31 |
32 | # MSTest test Results
33 | [Tt]est[Rr]esult*/
34 | [Bb]uild[Ll]og.*
35 |
36 | # NUNIT
37 | *.VisualState.xml
38 | TestResult.xml
39 |
40 | # Build Results of an ATL Project
41 | [Dd]ebugPS/
42 | [Rr]eleasePS/
43 | dlldata.c
44 |
45 | # DNX
46 | project.lock.json
47 | artifacts/
48 |
49 | *_i.c
50 | *_p.c
51 | *_i.h
52 | *.ilk
53 | *.meta
54 | *.obj
55 | *.pch
56 | *.pdb
57 | *.pgc
58 | *.pgd
59 | *.rsp
60 | *.sbr
61 | *.tlb
62 | *.tli
63 | *.tlh
64 | *.tmp
65 | *.tmp_proj
66 | *.log
67 | *.vspscc
68 | *.vssscc
69 | .builds
70 | *.pidb
71 | *.svclog
72 | *.scc
73 |
74 | # Chutzpah Test files
75 | _Chutzpah*
76 |
77 | # Visual C++ cache files
78 | ipch/
79 | *.aps
80 | *.ncb
81 | *.opendb
82 | *.opensdf
83 | *.sdf
84 | *.cachefile
85 | *.VC.db
86 |
87 | # Visual Studio profiler
88 | *.psess
89 | *.vsp
90 | *.vspx
91 | *.sap
92 |
93 | # TFS 2012 Local Workspace
94 | $tf/
95 |
96 | # Guidance Automation Toolkit
97 | *.gpState
98 |
99 | # ReSharper is a .NET coding add-in
100 | _ReSharper*/
101 | *.[Rr]e[Ss]harper
102 | *.DotSettings.user
103 |
104 | # JustCode is a .NET coding add-in
105 | .JustCode
106 |
107 | # TeamCity is a build add-in
108 | _TeamCity*
109 |
110 | # DotCover is a Code Coverage Tool
111 | *.dotCover
112 |
113 | # NCrunch
114 | _NCrunch_*
115 | .*crunch*.local.xml
116 | nCrunchTemp_*
117 |
118 | # MightyMoose
119 | *.mm.*
120 | AutoTest.Net/
121 |
122 | # Web workbench (sass)
123 | .sass-cache/
124 |
125 | # Installshield output folder
126 | [Ee]xpress/
127 |
128 | # DocProject is a documentation generator add-in
129 | DocProject/buildhelp/
130 | DocProject/Help/*.HxT
131 | DocProject/Help/*.HxC
132 | DocProject/Help/*.hhc
133 | DocProject/Help/*.hhk
134 | DocProject/Help/*.hhp
135 | DocProject/Help/Html2
136 | DocProject/Help/html
137 |
138 | # Click-Once directory
139 | publish/
140 |
141 | # Publish Web Output
142 | *.[Pp]ublish.xml
143 | *.azurePubxml
144 |
145 | # TODO: Un-comment the next line if you do not want to checkin
146 | # your web deploy settings because they may include unencrypted
147 | # passwords
148 | #*.pubxml
149 | *.publishproj
150 |
151 | # NuGet Packages
152 | *.nupkg
153 | # The packages folder can be ignored because of Package Restore
154 | **/packages/*
155 | # except build/, which is used as an MSBuild target.
156 | !**/packages/build/
157 | # Uncomment if necessary however generally it will be regenerated when needed
158 | #!**/packages/repositories.config
159 | # NuGet v3's project.json files produces more ignoreable files
160 | *.nuget.props
161 | *.nuget.targets
162 |
163 | # Microsoft Azure Build Output
164 | csx/
165 | *.build.csdef
166 |
167 | # Microsoft Azure Emulator
168 | ecf/
169 | rcf/
170 |
171 | # Windows Store app package directory
172 | AppPackages/
173 | BundleArtifacts/
174 |
175 | # Visual Studio cache files
176 | # files ending in .cache can be ignored
177 | *.[Cc]ache
178 | # but keep track of directories ending in .cache
179 | !*.[Cc]ache/
180 |
181 | # Others
182 | ClientBin/
183 | [Ss]tyle[Cc]op.*
184 | ~$*
185 | *~
186 | *.dbmdl
187 | *.dbproj.schemaview
188 | *.pfx
189 | *.publishsettings
190 | node_modules/
191 | orleans.codegen.cs
192 |
193 | # RIA/Silverlight projects
194 | Generated_Code/
195 |
196 | # Backup & report files from converting an old project file
197 | # to a newer Visual Studio version. Backup files are not needed,
198 | # because we have git ;-)
199 | _UpgradeReport_Files/
200 | Backup*/
201 | UpgradeLog*.XML
202 | UpgradeLog*.htm
203 |
204 | # SQL Server files
205 | *.mdf
206 | *.ldf
207 |
208 | # Business Intelligence projects
209 | *.rdl.data
210 | *.bim.layout
211 | *.bim_*.settings
212 |
213 | # Microsoft Fakes
214 | FakesAssemblies/
215 |
216 | # GhostDoc plugin setting file
217 | *.GhostDoc.xml
218 |
219 | # Node.js Tools for Visual Studio
220 | .ntvs_analysis.dat
221 |
222 | # Visual Studio 6 build log
223 | *.plg
224 |
225 | # Visual Studio 6 workspace options file
226 | *.opt
227 |
228 | # Visual Studio LightSwitch build output
229 | **/*.HTMLClient/GeneratedArtifacts
230 | **/*.DesktopClient/GeneratedArtifacts
231 | **/*.DesktopClient/ModelManifest.xml
232 | **/*.Server/GeneratedArtifacts
233 | **/*.Server/ModelManifest.xml
234 | _Pvt_Extensions
235 |
236 | # LightSwitch generated files
237 | GeneratedArtifacts/
238 | ModelManifest.xml
239 |
240 | # Paket dependency manager
241 | .paket/paket.exe
242 |
243 | # FAKE - F# Make
244 | .fake/
245 | /ASP.NET Core/03 - WebAPI/Projetos/01 MinhaAPICompleta 3.1/API/MinhaAPICompleta.sln.DotSettings.zip
246 | /ASP.NET Core/04 - Enterprise Applications/Projetos/Instrutor/NerdStoreEnterprise/ComunicacaoFilaIdentidadeCliente.rar
247 |
--------------------------------------------------------------------------------
/.releaserc:
--------------------------------------------------------------------------------
1 | {
2 | "branches": ["master"],
3 |
4 | "plugins": [
5 | "@semantic-release/commit-analyzer",
6 | "@semantic-release/release-notes-generator",
7 | "@semantic-release/changelog",
8 | "@semantic-release/github",
9 | "@semantic-release/git"
10 | ]
11 | }
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## [7.0.1](https://github.com/NetDevPack/Security.Identity/compare/v7.0.0...v7.0.1) (2022-11-17)
2 |
3 |
4 | ### Bug Fixes
5 |
6 | * sample ([297960f](https://github.com/NetDevPack/Security.Identity/commit/297960f05634f9ae825502fc267104d143ed9a24))
7 |
8 | # [7.0.0](https://github.com/NetDevPack/Security.Identity/compare/v6.1.7...v7.0.0) (2022-11-17)
9 |
10 |
11 | ### Features
12 |
13 | * net 7 ([e355161](https://github.com/NetDevPack/Security.Identity/commit/e355161c8066299efea364c78dd56c9908b074a0))
14 | * net7 ([062deca](https://github.com/NetDevPack/Security.Identity/commit/062decae7ab872abad55b4d1af39b6332bb5c8be))
15 |
16 |
17 | ### BREAKING CHANGES
18 |
19 | * dotnet 7
20 |
21 | ## [6.1.7](https://github.com/NetDevPack/Security.Identity/compare/v6.1.6...v6.1.7) (2022-04-18)
22 |
23 |
24 | ### Bug Fixes
25 |
26 | * declarative extensions ([6c726bb](https://github.com/NetDevPack/Security.Identity/commit/6c726bb0173a69b8872bf1c1114ef60ac0475629))
27 | * options at extension method ([afe8269](https://github.com/NetDevPack/Security.Identity/commit/afe82691e6c19a7742701add856fcd6250a56552))
28 |
29 | ## [6.1.6](https://github.com/NetDevPack/Security.Identity/compare/v6.1.5...v6.1.6) (2022-04-17)
30 |
31 |
32 | ### Bug Fixes
33 |
34 | * AddIdentity instead AddIdentityCore ([5383cae](https://github.com/NetDevPack/Security.Identity/commit/5383caebdaab8fdb94fc408449f7b257535bc91f))
35 |
36 | ## [6.1.5](https://github.com/NetDevPack/Security.Identity/compare/v6.1.4...v6.1.5) (2022-04-17)
37 |
38 |
39 | ### Bug Fixes
40 |
41 | * symetric validation ([6e0dea3](https://github.com/NetDevPack/Security.Identity/commit/6e0dea3c38d7ff71411a6ae0b87fd6fd9e995cf5))
42 |
43 | ## [6.1.4](https://github.com/NetDevPack/Security.Identity/compare/v6.1.3...v6.1.4) (2022-04-17)
44 |
45 |
46 | ### Bug Fixes
47 |
48 | * configuration settings ([a6050c0](https://github.com/NetDevPack/Security.Identity/commit/a6050c0f0e97c4cc2bbc7b3ebb69e8df71cad020))
49 | * latest package versions ([5f7426c](https://github.com/NetDevPack/Security.Identity/commit/5f7426ce86c6f1540560ac6e2eb66154ea576e17))
50 |
51 | ## [6.1.3](https://github.com/NetDevPack/Security.Identity/compare/v6.1.2...v6.1.3) (2022-04-17)
52 |
53 |
54 | ### Bug Fixes
55 |
56 | * Issuer default value from HttpContext ([9fd3425](https://github.com/NetDevPack/Security.Identity/commit/9fd3425e5cc1f4597d4f94330367b9c001dfb6a2))
57 |
58 | ## [6.1.2](https://github.com/NetDevPack/Security.Identity/compare/v6.1.1...v6.1.2) (2022-04-17)
59 |
60 |
61 | ### Bug Fixes
62 |
63 | * Remove unused dependencies ([bf68ee3](https://github.com/NetDevPack/Security.Identity/commit/bf68ee3dae54a31370151c1b2662d0e1634a6cbe))
64 |
65 | ## [6.1.1](https://github.com/NetDevPack/Security.Identity/compare/v6.1.0...v6.1.1) (2022-04-17)
66 |
67 |
68 | ### Bug Fixes
69 |
70 | * latest packages ([0e43313](https://github.com/NetDevPack/Security.Identity/commit/0e43313f4034ff168df5c8e7313f704844b1a23e))
71 |
72 | # [6.1.0](https://github.com/NetDevPack/Security.Identity/compare/v6.0.1...v6.1.0) (2022-04-17)
73 |
74 |
75 | ### Bug Fixes
76 |
77 | * message lenght ([ec6a841](https://github.com/NetDevPack/Security.Identity/commit/ec6a841fa44331f3f9b5777d7f17a2a07bef958b))
78 |
79 |
80 | ### Features
81 |
82 | * **security:** Now it uses NetDevPack.Security.Jwt ([9427db1](https://github.com/NetDevPack/Security.Identity/commit/9427db111753a8830226e4be1023ed565e006c89))
83 |
84 | ## [6.0.1](https://github.com/NetDevPack/Security.Identity/compare/v6.0.0...v6.0.1) (2022-02-27)
85 |
86 |
87 | ### Bug Fixes
88 |
89 | * force v6 ([c5ec778](https://github.com/NetDevPack/Security.Identity/commit/c5ec778bac196c69cffb1d94c6a6af2c7fc7d666))
90 |
91 | # [2.0.0](https://github.com/NetDevPack/Security.Identity/compare/v1.2.0...v2.0.0) (2022-02-27)
92 |
93 |
94 | ### Bug Fixes
95 |
96 | * semantic release ([e744c34](https://github.com/NetDevPack/Security.Identity/commit/e744c34e486b6a7176c61dec99378b2d46497600))
97 |
98 |
99 | ### Features
100 |
101 | * Adding .NET 6 support ([e2746fe](https://github.com/NetDevPack/Security.Identity/commit/e2746fe5adf46ef7532f199a7d6b9c350e13ec3f))
102 | * dotnet 6 ([f80c9ce](https://github.com/NetDevPack/Security.Identity/commit/f80c9ce9df6bd1ce5ae83fafc5f1d79063a0469f))
103 | * dotnet 6 ([bd89d96](https://github.com/NetDevPack/Security.Identity/commit/bd89d96dbc48dfc9d25c9dec2abf663f524336ff))
104 |
105 |
106 | ### BREAKING CHANGES
107 |
108 | * dotnet 6 version
109 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2020 NetDevPack
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 |
--------------------------------------------------------------------------------
/NetDevPack.Identity.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 16
4 | VisualStudioVersion = 16.0.30011.22
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "NetDevPack.Identity", "src\NetDevPack.Identity\NetDevPack.Identity.csproj", "{43A11876-765D-4387-BC28-4C9371DDD62D}"
7 | EndProject
8 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Samples", "Samples", "{8A673A3F-D855-4F9B-BA0B-0D2B5DE369EB}"
9 | EndProject
10 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Pack", "Pack", "{7985C809-6CBD-44C2-BEA7-1AD828707F3E}"
11 | EndProject
12 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCore.Jwt.Sample", "src\Samples\AspNetCore.Jwt.Sample\AspNetCore.Jwt.Sample.csproj", "{B5BE907B-0C79-489F-99BD-2AD82FA727E3}"
13 | EndProject
14 | Global
15 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
16 | Debug|Any CPU = Debug|Any CPU
17 | Release|Any CPU = Release|Any CPU
18 | EndGlobalSection
19 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
20 | {43A11876-765D-4387-BC28-4C9371DDD62D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {43A11876-765D-4387-BC28-4C9371DDD62D}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {43A11876-765D-4387-BC28-4C9371DDD62D}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {43A11876-765D-4387-BC28-4C9371DDD62D}.Release|Any CPU.Build.0 = Release|Any CPU
24 | {B5BE907B-0C79-489F-99BD-2AD82FA727E3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
25 | {B5BE907B-0C79-489F-99BD-2AD82FA727E3}.Debug|Any CPU.Build.0 = Debug|Any CPU
26 | {B5BE907B-0C79-489F-99BD-2AD82FA727E3}.Release|Any CPU.ActiveCfg = Release|Any CPU
27 | {B5BE907B-0C79-489F-99BD-2AD82FA727E3}.Release|Any CPU.Build.0 = Release|Any CPU
28 | EndGlobalSection
29 | GlobalSection(SolutionProperties) = preSolution
30 | HideSolutionNode = FALSE
31 | EndGlobalSection
32 | GlobalSection(NestedProjects) = preSolution
33 | {43A11876-765D-4387-BC28-4C9371DDD62D} = {7985C809-6CBD-44C2-BEA7-1AD828707F3E}
34 | {B5BE907B-0C79-489F-99BD-2AD82FA727E3} = {8A673A3F-D855-4F9B-BA0B-0D2B5DE369EB}
35 | EndGlobalSection
36 | GlobalSection(ExtensibilityGlobals) = postSolution
37 | SolutionGuid = {C92C10D5-BEA9-4EC0-BD95-6228657E412D}
38 | EndGlobalSection
39 | EndGlobal
40 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | What is the .NET DevPack.Identity?
4 | =====================
5 | .NET DevPack Identity is a set of common implementations to help you implementing ASP.NET Identity, JWT, claims validation and another facilities
6 |
7 | [](https://www.codacy.com/gh/NetDevPack/NetDevPack.Identity?utm_source=github.com&utm_medium=referral&utm_content=NetDevPack/NetDevPack.Identity&utm_campaign=Badge_Grade)
8 | [](https://ci.appveyor.com/project/EduardoPires/netdevpack-identity)
9 | 
10 | [](LICENSE)
11 |
12 | ## Give a Star! :star:
13 | If you liked the project or if NetDevPack helped you, please give a star ;)
14 |
15 | ## Get Started
16 |
17 | | Package | Version | Popularity |
18 | | ------- | ----- | ----- |
19 | | `NetDevPack.Identity` | [](https://nuget.org/packages/NetDevPack.Identity) | [](https://nuget.org/packages/NetDevPack.Identity) |
20 |
21 |
22 | .NET DevPack.Identity can be installed in your ASP.NET Core application using the Nuget package manager or the `dotnet` CLI.
23 |
24 | ```
25 | dotnet add package NetDevPack.Identity
26 | ```
27 |
28 | If you want to use our IdentityDbContext (ASP.NET Identity standard) you will need to create the Identity tables. Set your connection string in the `appsettings.json` and follow the next steps:
29 |
30 | Add the IdentityDbContext configuration in your `startup.cs`:
31 |
32 | ```csharp
33 | services.AddIdentityEntityFrameworkContextConfiguration(options =>
34 | options.UseSqlServer(configuration.GetConnectionString("DefaultConnection"),
35 | b=>b.MigrationsAssembly("AspNetCore.Jwt.Sample")));
36 | ```
37 |
38 | >**Note:** You must inform the namespace to avoid migration errors
39 | >
40 | >**Note:** You must install the `Microsoft.EntityFrameworkCore.SqlServer` or another provider like `Npgsql.EntityFrameworkCore.PostgreSQL` package to have support from your database. Find the package for your database [here](https://docs.microsoft.com/en-us/ef/core/providers/?tabs=dotnet-core-cli)
41 |
42 | Add the Identity configuration in `ConfigureServices` method of your `startup.cs`:
43 |
44 | ```csharp
45 | services.AddIdentityConfiguration();
46 | ```
47 |
48 | >**Note:** This extension returns an IdentityBuilder to allow you extending the configuration
49 |
50 | Add the Identity configuration in `Configure` method of your `startup.cs`:
51 |
52 | ```csharp
53 | app.UseAuthConfiguration();
54 | ```
55 |
56 | >**Note:** This method need to be set between `app.UseRouting()` and `app.UseEndpoints()`
57 |
58 | Run the command to generate the migration files:
59 |
60 | ```
61 | dotnet ef migrations add Initial --context NetDevPackAppDbContext --project /.csproj
62 | ```
63 |
64 | Run the command to generate the database:
65 |
66 | ```
67 | dotnet ef database update --context NetDevPackAppDbContext --project /.csproj
68 | ```
69 | >**Note:** If are you using your own `IdentityDbContext` you must change the `NetDevPackAppDbContext` value to your context class name in the commands above.
70 |
71 | After execute this steps you will be all set to use the Identity in your Application.
72 |
73 | ### Configuring JWT
74 | If you want to generate JSON Web Tokens in your application you need to add the JWT configuration in `ConfigureServices` method of your `startup.cs`
75 | ```csharp
76 | services.AddJwtConfiguration(Configuration)
77 | .AddNetDevPackIdentity();
78 | ```
79 |
80 | Set your `appsettings.json` file with this values:
81 |
82 | ```json
83 | "AppJwtSettings": {
84 | "Audience": "MyApplication.Name"
85 | }
86 | ```
87 | It's possible to configure some aspects of token
88 |
89 | |Key|Meaning|Default
90 | |--|--|---|
91 | |Expiration| Expiration time (in hours) | 1 |
92 | |Issuer| The party that "created" the token and signed it with its private key. Usually the application Url | Get current root Url from `HttpContext` |
93 | |Audience| API's that should accept the token. E.g your application Main name. | NetDevPack |
94 | |RefreshTokenExpiration | Refresh token expiration (In Days) | 30 |
95 | |RefreshTokenType | `OneTime` or `ReUse` | 30 |
96 | |SecretKey `Deprecated` | Is your key to build JWT. **Read notes**| Do not use it |
97 |
98 | >**Note:** Now we are using [NetDevPack.Security.Jwt](https://github.com/NetDevPack/Security.Jwt) to generate and Store your keys. It generate a RSA 2048 by default. You can check the project for more info.
99 |
100 | ### Generating JWT
101 | You will need to set a single dependency in your Authentication Controller:
102 |
103 | ```csharp
104 | public AuthController(IJwtBuilder jwtBuilder)
105 | {
106 | _jwtBuilder = jwtBuilder;
107 | }
108 | ```
109 |
110 | After user register or login process you can generate a JWT to respond the request. Use our implementation, you just need inform the user email and the dependencies injected in your controller:
111 |
112 | ```csharp
113 | return _jwtBuilder
114 | .WithEmail(email)
115 | .WithRefreshToken()
116 | .BuildToken();
117 | ```
118 |
119 | >**Note:** This builder can return a single string with JWT or a complex object `UserResponse` if you want return more data than a single JWT string.
120 |
121 | #### Adding Claims to your JWT
122 | You can call more methods in `JwtBuilder` to provide more information about the user:
123 |
124 | ```csharp
125 | return _jwtBuilder
126 | .WithEmail(email)
127 | .WithJwtClaims()
128 | .WithUserClaims()
129 | .WithUserRoles()
130 | .WithRefreshToken()
131 | .BuildToken();
132 | ```
133 |
134 | |Method|Meaning|
135 | |--|--|
136 | |WithJwtClaims()| Claims of JWT like `sub`, `jti`, `nbf` and others |
137 | |WithUserClaims()| The user claims registered in `AspNetUserClaims` table|
138 | |WithUserRoles()| The user roles (as claims) registered in `AspNetUserRoles` table |
139 | |BuildToken()| Build and return the JWT as single string |
140 |
141 | If you want return your complex object `UserResponse` you need to change the last method to:
142 |
143 | ```csharp
144 | return _jwtBuilder
145 | .WithEmail(email)
146 | .WithJwtClaims()
147 | .WithUserClaims()
148 | .WithUserRoles()
149 | .WithRefreshToken()
150 | .BuildUserResponse();
151 | ```
152 |
153 | ## Examples
154 | Use the [sample application](https://github.com/NetDevPack/NetDevPack.Identity/tree/master/src/Samples/AspNetCore.Jwt.Sample) to understand how NetDevPack.Identity can be implemented and help you to decrease the complexity of your application and development time.
155 |
156 | ## Compatibility
157 | The **NetDevPack.Identity** was developed to be implemented in **ASP.NET Core**. It support all .NET versions since 3.1.
158 |
159 | ## About
160 | .NET DevPack.Identity was developed by [Eduardo Pires](http://eduardopires.net.br) under the [MIT license](LICENSE).
161 |
162 |
--------------------------------------------------------------------------------
/assets/IconNuget.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/NetDevPack/Security.Identity/c7edc15cfdab1a6dc63872e8bd6d3c99fc4094a7/assets/IconNuget.png
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Authorization/CustomAuthorizationValidation.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using Microsoft.AspNetCore.Http;
3 |
4 | namespace NetDevPack.Identity.Authorization
5 | {
6 | public static class CustomAuthorizationValidation
7 | {
8 | public static bool UserHasValidClaim(HttpContext context, string claimName, string claimValue)
9 | {
10 | return context.User.Identity.IsAuthenticated &&
11 | context.User.Claims.Any(c =>
12 | c.Type == claimName &&
13 | c.Value.Split(',').Select(v => v.Trim()).Contains(claimValue));
14 | }
15 |
16 | }
17 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Authorization/CustomAuthorizeAttribute.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using Microsoft.AspNetCore.Mvc;
3 |
4 | namespace NetDevPack.Identity.Authorization
5 | {
6 | public class CustomAuthorizeAttribute : TypeFilterAttribute
7 | {
8 | public CustomAuthorizeAttribute(string claimName, string claimValue) : base(typeof(RequerimentClaimFilter))
9 | {
10 | Arguments = new object[] { new Claim(claimName, claimValue) };
11 | }
12 | }
13 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Authorization/RequerimentClaimFilter.cs:
--------------------------------------------------------------------------------
1 | using System.Security.Claims;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.AspNetCore.Mvc.Filters;
4 |
5 | namespace NetDevPack.Identity.Authorization
6 | {
7 | internal class RequerimentClaimFilter : IAuthorizationFilter
8 | {
9 | private readonly Claim _claim;
10 |
11 | public RequerimentClaimFilter(Claim claim)
12 | {
13 | _claim = claim;
14 | }
15 |
16 | public void OnAuthorization(AuthorizationFilterContext context)
17 | {
18 | if (!context.HttpContext.User.Identity.IsAuthenticated)
19 | {
20 | context.Result = new StatusCodeResult(401);
21 | return;
22 | }
23 |
24 | if (!CustomAuthorizationValidation.UserHasValidClaim(context.HttpContext, _claim.Type, _claim.Value))
25 | {
26 | context.Result = new StatusCodeResult(403);
27 | }
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Data/NetDevPackAppDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
2 | using Microsoft.EntityFrameworkCore;
3 |
4 | namespace NetDevPack.Identity.Data
5 | {
6 | public class NetDevPackAppDbContext : IdentityDbContext
7 | {
8 | public NetDevPackAppDbContext(DbContextOptions options) : base(options) { }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Interfaces/IJwtBuilder.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using NetDevPack.Identity.Jwt;
3 | using NetDevPack.Identity.Jwt.Model;
4 |
5 | namespace NetDevPack.Identity.Interfaces;
6 |
7 | public interface IJwtBuilder
8 | {
9 | IJwtBuilder WithEmail(string email);
10 | IJwtBuilder WithUsername(string username);
11 | IJwtBuilder WithUserId(string id);
12 | IJwtBuilder WithJwtClaims();
13 | IJwtBuilder WithUserClaims();
14 | IJwtBuilder WithUserRoles();
15 | IJwtBuilder WithRefreshToken();
16 | Task BuildToken();
17 | Task BuildUserResponse();
18 | Task ValidateRefreshToken(string refreshToken);
19 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/Abstractions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Text;
3 | using Microsoft.AspNetCore.Authentication.JwtBearer;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.IdentityModel.Tokens;
7 | using NetDevPack.Security.Jwt.Core.Interfaces;
8 |
9 | namespace NetDevPack.Identity.Jwt
10 | {
11 | public static class Abstractions
12 | {
13 | public static IServiceCollection AddJwtConfiguration(this IServiceCollection services, IConfiguration configuration, string appJwtSettingsKey = "AppJwtSettings")
14 | {
15 | if (services == null) throw new ArgumentException(nameof(services));
16 | if (configuration == null) throw new ArgumentException(nameof(configuration));
17 |
18 | var appSettingsSection = configuration.GetSection(appJwtSettingsKey);
19 | services.Configure(appSettingsSection);
20 |
21 | var appSettings = appSettingsSection.Get();
22 |
23 | if (!string.IsNullOrEmpty(appSettings.SecretKey))
24 | SymetricKeyConfiguration(services, appSettings);
25 | else
26 | {
27 | services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
28 | {
29 | options.RequireHttpsMetadata = true;
30 | options.SaveToken = true;
31 | options.TokenValidationParameters = new TokenValidationParameters
32 | {
33 | ValidateIssuer = true,
34 | ValidateAudience = true,
35 | ValidateLifetime = true,
36 | ValidateIssuerSigningKey = true,
37 | ValidAudience = appSettings.Audience,
38 | ValidIssuer = appSettings.Issuer,
39 | };
40 | });
41 | }
42 |
43 | return services;
44 | }
45 |
46 | private static void SymetricKeyConfiguration(IServiceCollection services, AppJwtSettings appSettings)
47 | {
48 | var key = Encoding.ASCII.GetBytes(appSettings.SecretKey);
49 |
50 | services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
51 | .AddJwtBearer(options =>
52 | {
53 | options.RequireHttpsMetadata = true;
54 | options.SaveToken = true;
55 | options.TokenValidationParameters = new TokenValidationParameters
56 | {
57 | ValidateIssuerSigningKey = true,
58 | IssuerSigningKey = new SymmetricSecurityKey(key),
59 | ValidateIssuer = true,
60 | ValidateAudience = true,
61 | ValidAudience = appSettings.Audience,
62 | ValidIssuer = appSettings.Issuer
63 | };
64 | });
65 | }
66 | }
67 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/AppJwtSettings.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace NetDevPack.Identity.Jwt
5 | {
6 | public class AppJwtSettings
7 | {
8 | [Obsolete("For better security use IJwtBuilder and set null for this field")]
9 | public string SecretKey { get; set; }
10 |
11 | ///
12 | /// Hours
13 | ///
14 | public int Expiration { get; set; } = 1;
15 | ///
16 | /// Days
17 | ///
18 | public int RefreshTokenExpiration { get; set; } = 30;
19 | public string Issuer { get; set; } = "NetDevPack.Identity";
20 | public string Audience { get; set; } = "Api";
21 |
22 | ///
23 | /// One Time => Only the lastest refresh token is valid. Ignore olders refresh token.
24 | /// Better security and best suit for application with only one Frontend
25 | ///
26 | /// ReUse => Accept olders refresh tokens
27 | /// Decrease security but better for scenarios where there are more than one frontend. Like a browser + mobile app
28 | ///
29 | public RefreshTokenType RefreshTokenType { get; set; } = RefreshTokenType.OneTime;
30 |
31 |
32 | }
33 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Security.Claims;
5 | using System.Text;
6 | using Microsoft.IdentityModel.JsonWebTokens;
7 |
8 | namespace NetDevPack.Identity.Jwt
9 | {
10 | internal static class Extensions
11 | {
12 | public static void RemoveRefreshToken(this ICollection claims)
13 | {
14 | var refreshToken = claims.FirstOrDefault(f => f.Type == "LastRefreshToken");
15 | if (refreshToken is not null)
16 | claims.Remove(refreshToken);
17 | }
18 |
19 | public static string GetJwtId(this ClaimsIdentity principal)
20 | {
21 | return principal.FindFirst(JwtRegisteredClaimNames.Jti)?.Value;
22 | }
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/JwtBuilder.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IdentityModel.Tokens.Jwt;
4 | using System.Linq;
5 | using System.Security.Claims;
6 | using System.Text;
7 | using Microsoft.AspNetCore.Identity;
8 | using Microsoft.IdentityModel.Tokens;
9 | using NetDevPack.Identity.Jwt.Model;
10 |
11 | namespace NetDevPack.Identity.Jwt
12 | {
13 | [Obsolete("Inject IJwtBuilder instead")]
14 | public class JwtBuilder where TIdentityUser : IdentityUser where TKey : IEquatable
15 | {
16 | private UserManager _userManager;
17 | private AppJwtSettings _appJwtSettings;
18 | private TIdentityUser _user;
19 | private ICollection _userClaims;
20 | private ICollection _jwtClaims;
21 | private ClaimsIdentity _identityClaims;
22 |
23 | public JwtBuilder WithUserManager(UserManager userManager)
24 | {
25 | _userManager = userManager ?? throw new ArgumentException(nameof(userManager));
26 | return this;
27 | }
28 |
29 | public JwtBuilder WithJwtSettings(AppJwtSettings appJwtSettings)
30 | {
31 | _appJwtSettings = appJwtSettings ?? throw new ArgumentException(nameof(appJwtSettings));
32 | return this;
33 | }
34 |
35 | public JwtBuilder WithEmail(string email)
36 | {
37 | if (string.IsNullOrEmpty(email)) throw new ArgumentException(nameof(email));
38 |
39 | _user = _userManager.FindByEmailAsync(email).Result;
40 | _userClaims = new List();
41 | _jwtClaims = new List();
42 | _identityClaims = new ClaimsIdentity();
43 |
44 | return this;
45 | }
46 |
47 | public JwtBuilder WithJwtClaims()
48 | {
49 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Sub, _user.Id.ToString()));
50 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Email, _user.Email));
51 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
52 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Nbf, ToUnixEpochDate(DateTime.UtcNow).ToString()));
53 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(DateTime.UtcNow).ToString(), ClaimValueTypes.Integer64));
54 |
55 | _identityClaims.AddClaims(_jwtClaims);
56 |
57 | return this;
58 | }
59 |
60 | public JwtBuilder WithUserClaims()
61 | {
62 | _userClaims = _userManager.GetClaimsAsync(_user).Result;
63 | _identityClaims.AddClaims(_userClaims);
64 |
65 | return this;
66 | }
67 |
68 | public JwtBuilder WithUserRoles()
69 | {
70 | var userRoles = _userManager.GetRolesAsync(_user).Result;
71 | userRoles.ToList().ForEach(r => _identityClaims.AddClaim(new Claim("role", r)));
72 |
73 | return this;
74 | }
75 |
76 | public string BuildToken()
77 | {
78 | var tokenHandler = new JwtSecurityTokenHandler();
79 | var key = Encoding.ASCII.GetBytes(_appJwtSettings.SecretKey);
80 | var token = tokenHandler.CreateToken(new SecurityTokenDescriptor
81 | {
82 | Issuer = _appJwtSettings.Issuer,
83 | Audience = _appJwtSettings.Audience,
84 | Subject = _identityClaims,
85 | Expires = DateTime.UtcNow.AddHours(_appJwtSettings.Expiration),
86 | SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key),
87 | SecurityAlgorithms.HmacSha256Signature)
88 | });
89 |
90 | return tokenHandler.WriteToken(token);
91 | }
92 |
93 | public UserResponse BuildUserResponse()
94 | {
95 | var user = new UserResponse
96 | {
97 | AccessToken = BuildToken(),
98 | ExpiresIn = TimeSpan.FromHours(_appJwtSettings.Expiration).TotalSeconds,
99 | UserToken = new UserToken
100 | {
101 | Id = _user.Id,
102 | Email = _user.Email,
103 | Claims = _userClaims.Select(c => new UserClaim { Type = c.Type, Value = c.Value })
104 | }
105 | };
106 |
107 | return user;
108 | }
109 |
110 | private static long ToUnixEpochDate(DateTime date)
111 | => (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero))
112 | .TotalSeconds);
113 | }
114 |
115 | public class JwtBuilder : JwtBuilder where TIdentityUser : IdentityUser { }
116 |
117 | public sealed class JwtBuilder : JwtBuilder { }
118 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/JwtBuilderInject.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IdentityModel.Tokens.Jwt;
4 | using System.Linq;
5 | using System.Security.Claims;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using Microsoft.AspNetCore.Http;
9 | using Microsoft.AspNetCore.Identity;
10 | using Microsoft.Extensions.Options;
11 | using Microsoft.IdentityModel.JsonWebTokens;
12 | using Microsoft.IdentityModel.Tokens;
13 | using NetDevPack.Identity.Interfaces;
14 | using NetDevPack.Identity.Jwt.Model;
15 | using NetDevPack.Identity.User;
16 | using NetDevPack.Security.Jwt.Core.Interfaces;
17 | using JwtRegisteredClaimNames = System.IdentityModel.Tokens.Jwt.JwtRegisteredClaimNames;
18 |
19 |
20 | namespace NetDevPack.Identity.Jwt;
21 |
22 | internal class JwtBuilderInject : IJwtBuilder
23 | where TIdentityUser : IdentityUser
24 | where TKey : IEquatable
25 | {
26 | private readonly UserManager _userManager;
27 | private readonly IOptions _settings;
28 | private readonly IJwtService _jwtService;
29 | private readonly IHttpContextAccessor _httpContext;
30 |
31 | private ICollection _userClaims;
32 | private ICollection _jwtClaims;
33 | private ClaimsIdentity _identityClaims;
34 | private bool _useEmail = false;
35 | private bool _useId = false;
36 | private bool _useUsername = false;
37 |
38 | private string _key;
39 | private string _email;
40 | private string _username;
41 |
42 | private bool _useClaims;
43 | private bool _useRoles;
44 | private bool _useRt;
45 | private bool _useDefaultJwtClaims;
46 | private TIdentityUser _user;
47 |
48 | public JwtBuilderInject(
49 | UserManager userManager,
50 | IOptions settings,
51 | IJwtService jwtService,
52 | IHttpContextAccessor httpContext)
53 | {
54 | _userManager = userManager;
55 | _settings = settings;
56 | _jwtService = jwtService;
57 | _httpContext = httpContext;
58 | _userClaims = new List();
59 | _jwtClaims = new List();
60 | _identityClaims = new ClaimsIdentity();
61 | }
62 |
63 | public IJwtBuilder WithEmail(string email)
64 | {
65 | if (string.IsNullOrEmpty(email)) throw new ArgumentException(nameof(email));
66 |
67 | _useUsername = false;
68 | _useId = false;
69 |
70 | _useEmail = true;
71 | _email = email;
72 |
73 | return this;
74 | }
75 |
76 | public IJwtBuilder WithUserId(string id)
77 | {
78 | _useEmail = false;
79 | _useUsername = false;
80 | _useId = true;
81 | _key = id ?? throw new ArgumentException(nameof(id));
82 |
83 | return this;
84 | }
85 | public IJwtBuilder WithUsername(string username)
86 | {
87 | if (string.IsNullOrEmpty(username)) throw new ArgumentException(nameof(username));
88 |
89 | _useId = false;
90 | _useEmail = false;
91 |
92 | _useUsername = true;
93 | _username = username;
94 |
95 | return this;
96 | }
97 | public IJwtBuilder WithJwtClaims()
98 | {
99 | _useDefaultJwtClaims = true;
100 | return this;
101 | }
102 |
103 | public IJwtBuilder WithUserClaims()
104 | {
105 | _useClaims = true;
106 |
107 | return this;
108 | }
109 |
110 | public IJwtBuilder WithUserRoles()
111 | {
112 | _useRoles = true;
113 | return this;
114 | }
115 |
116 | public IJwtBuilder WithRefreshToken()
117 | {
118 | _useRt = true;
119 | return this;
120 | }
121 |
122 | public async Task BuildRefreshToken()
123 | {
124 | var jti = Guid.NewGuid().ToString();
125 | var key = await GetCurrentKey();
126 | var user = await GetUser();
127 | var claims = new List(2)
128 | {
129 | new(JwtRegisteredClaimNames.Sub, user.Id.ToString()),
130 | new(JwtRegisteredClaimNames.Jti, jti)
131 | };
132 |
133 | var identityClaims = new ClaimsIdentity();
134 | identityClaims.AddClaims(claims);
135 |
136 | var handler = new JwtSecurityTokenHandler();
137 |
138 | var issuer = _settings.Value.Issuer;
139 | if (string.IsNullOrEmpty(issuer))
140 | issuer = $"{_httpContext.HttpContext.Request.Scheme}://{_httpContext.HttpContext.Request.Host}";
141 |
142 | var securityToken = handler.CreateToken(new SecurityTokenDescriptor
143 | {
144 | Issuer = issuer,
145 | Audience = _settings.Value.Audience,
146 | SigningCredentials = key,
147 | Subject = identityClaims,
148 | NotBefore = DateTime.UtcNow,
149 | Expires = DateTime.UtcNow.AddDays(_settings.Value.RefreshTokenExpiration),
150 | TokenType = "rt+jwt"
151 | });
152 | await UpdateLastGeneratedClaim(jti, user);
153 | var encodedJwt = handler.WriteToken(securityToken);
154 | return encodedJwt;
155 | }
156 |
157 | private async Task UpdateLastGeneratedClaim(string jti, TIdentityUser user)
158 | {
159 |
160 | var claims = await _userManager.GetClaimsAsync(user);
161 | var newLastRtClaim = new Claim("LastRefreshToken", jti);
162 |
163 | var claimLastRt = claims.FirstOrDefault(f => f.Type == "LastRefreshToken");
164 | if (claimLastRt != null)
165 | await _userManager.ReplaceClaimAsync(user, claimLastRt, newLastRtClaim);
166 | else
167 | await _userManager.AddClaimAsync(user, newLastRtClaim);
168 | }
169 |
170 | public async Task BuildToken()
171 | {
172 | var tokenHandler = new JwtSecurityTokenHandler();
173 | var key = await GetCurrentKey();
174 | var user = await GetUser();
175 |
176 | if (_useDefaultJwtClaims)
177 | {
178 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString()));
179 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Email, user.Email));
180 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()));
181 | _jwtClaims.Add(new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(DateTime.UtcNow).ToString(), ClaimValueTypes.Integer64));
182 |
183 | _identityClaims.AddClaims(_jwtClaims);
184 | }
185 |
186 | if (_useRoles)
187 | {
188 | var userRoles = await _userManager.GetRolesAsync(user);
189 | userRoles.ToList().ForEach(r => _identityClaims.AddClaim(new Claim("role", r)));
190 | }
191 |
192 | if (_useClaims)
193 | {
194 | _userClaims = await _userManager.GetClaimsAsync(user);
195 | _userClaims.RemoveRefreshToken();
196 | _identityClaims.AddClaims(_userClaims);
197 | }
198 |
199 | var issuer = _settings.Value.Issuer;
200 | if (string.IsNullOrEmpty(issuer))
201 | issuer = $"{_httpContext.HttpContext.Request.Scheme}://{_httpContext.HttpContext.Request.Host}";
202 |
203 | var token = tokenHandler.CreateToken(new SecurityTokenDescriptor
204 | {
205 | Issuer = issuer,
206 | Audience = _settings.Value.Audience,
207 | Subject = _identityClaims,
208 | Expires = DateTime.UtcNow.AddHours(_settings.Value.Expiration),
209 | NotBefore = DateTime.UtcNow,
210 | SigningCredentials = key,
211 | TokenType = "at+jwt"
212 | });
213 |
214 | return tokenHandler.WriteToken(token);
215 | }
216 |
217 |
218 | private async Task GetUser()
219 | {
220 | if (!_useEmail && !_useId && !_useUsername)
221 | throw new ArgumentNullException("User id or email should be provided. You should call WithEmail(email), WithUsername(username) or WithUserId(id).");
222 |
223 | if (_user is not null)
224 | return _user;
225 |
226 | if (_useId)
227 | _user = await _userManager.FindByIdAsync(_key);
228 | else if (_useEmail)
229 | _user = await _userManager.FindByEmailAsync(_email);
230 | else
231 | _user = await _userManager.FindByNameAsync(_username);
232 |
233 | return _user;
234 | }
235 |
236 | private Task GetCurrentKey()
237 | {
238 | if (string.IsNullOrEmpty(_settings.Value.SecretKey))
239 | return _jwtService.GetCurrentSigningCredentials();
240 |
241 | return Task.FromResult(new SigningCredentials(new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_settings.Value.SecretKey)), SecurityAlgorithms.HmacSha256Signature));
242 | }
243 |
244 | public async Task BuildUserResponse()
245 | {
246 | var user = new UserResponse
247 | {
248 | AccessToken = await BuildToken(),
249 | ExpiresIn = TimeSpan.FromHours(_settings.Value.Expiration).TotalSeconds,
250 | UserToken = new UserToken
251 | {
252 | Id = _user.Id,
253 | Email = _user.Email,
254 | Claims = _userClaims.Any() ? _userClaims.Select(c => new UserClaim { Type = c.Type, Value = c.Value }) : null
255 | }
256 | };
257 | if (_useRt)
258 | user.RefreshToken = await BuildRefreshToken();
259 |
260 | return user;
261 | }
262 |
263 | public async Task ValidateRefreshToken(string refreshToken)
264 | {
265 | var handler = new JsonWebTokenHandler();
266 |
267 | var result = handler.ValidateToken(refreshToken, new TokenValidationParameters()
268 | {
269 | RequireSignedTokens = false,
270 | ValidIssuer = _settings.Value.Issuer,
271 | ValidAudience = _settings.Value.Audience,
272 | IssuerSigningKey = await _jwtService.GetCurrentSecurityKey(),
273 | });
274 |
275 | if (!result.IsValid)
276 | return new RefreshTokenValidation(false);
277 |
278 |
279 | var user = await _userManager.FindByIdAsync(result.ClaimsIdentity.GetUserId());
280 | var claims = await _userManager.GetClaimsAsync(user);
281 |
282 | var jti = result.ClaimsIdentity.GetJwtId();
283 |
284 | if (_settings.Value.RefreshTokenType == RefreshTokenType.OneTime)
285 | if (!claims.Any(c => c.Type == "LastRefreshToken" && c.Value == jti))
286 | return new RefreshTokenValidation(false, reason: "Refresh Token already used");
287 |
288 | if (user.LockoutEnabled)
289 | if (user.LockoutEnd < DateTime.Now)
290 | return new RefreshTokenValidation(false, reason: "User blocked");
291 |
292 | return new RefreshTokenValidation(true, result.ClaimsIdentity.GetUserId());
293 | }
294 |
295 | private static long ToUnixEpochDate(DateTime date)
296 | => (long)Math.Round((date.ToUniversalTime() - new DateTimeOffset(1970, 1, 1, 0, 0, 0, TimeSpan.Zero))
297 | .TotalSeconds);
298 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/Model/RefreshTokenValidation.cs:
--------------------------------------------------------------------------------
1 | namespace NetDevPack.Identity.Jwt.Model;
2 |
3 | public class RefreshTokenValidation
4 | {
5 | public RefreshTokenValidation(bool isValid, string userId = null, string reason = null)
6 | {
7 | IsValid = isValid;
8 | UserId = userId;
9 | Reason = reason;
10 | }
11 |
12 | public bool IsValid { get; set; }
13 | public string UserId { get; set; }
14 | public string Reason { get; }
15 |
16 | public static implicit operator bool(RefreshTokenValidation validation) => validation.IsValid;
17 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/Model/UserClaim.cs:
--------------------------------------------------------------------------------
1 | namespace NetDevPack.Identity.Jwt.Model
2 | {
3 | public class UserClaim
4 | {
5 | public string Value { get; set; }
6 | public string Type { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/Model/UserResponse.cs:
--------------------------------------------------------------------------------
1 | namespace NetDevPack.Identity.Jwt.Model
2 | {
3 | public class UserResponse
4 | {
5 | public string AccessToken { get; set; }
6 | public double ExpiresIn { get; set; }
7 | public UserToken UserToken { get; set; }
8 | public string RefreshToken { get; set; }
9 | }
10 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/Model/UserToken.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace NetDevPack.Identity.Jwt.Model
4 | {
5 | public class UserToken
6 | {
7 | public dynamic Id { get; set; }
8 | public string Email { get; set; }
9 | public IEnumerable Claims { get; set; }
10 | }
11 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Jwt/RefreshTokenType.cs:
--------------------------------------------------------------------------------
1 | namespace NetDevPack.Identity.Jwt;
2 |
3 | public enum RefreshTokenType
4 | {
5 | OneTime = 1,
6 | ReUse = 2
7 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/JwtBuilderExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.AspNetCore.Builder;
3 | using Microsoft.AspNetCore.Identity;
4 | using Microsoft.EntityFrameworkCore;
5 | using NetDevPack.Identity.Data;
6 | using NetDevPack.Identity.Interfaces;
7 | using NetDevPack.Identity.Jwt;
8 | using NetDevPack.Security.Jwt.Core;
9 | using NetDevPack.Security.Jwt.Core.Interfaces;
10 |
11 | namespace Microsoft.Extensions.DependencyInjection;
12 |
13 | public static class JwtBuilderExtensions
14 | {
15 | public static IJwksBuilder AddNetDevPackIdentity(this IServiceCollection services, Action options = null)
16 | where TIdentityUser : IdentityUser
17 | where TKey : IEquatable
18 | {
19 | services.AddHttpContextAccessor();
20 | services.AddScoped>();
21 | return services.AddJwksManager(options);
22 | }
23 |
24 | public static IJwksBuilder AddNetDevPackIdentity(this IServiceCollection services, Action options = null)
25 | where TIdentityUser : IdentityUser
26 | {
27 | services.AddHttpContextAccessor();
28 | services.AddScoped>();
29 | return services.AddJwksManager(options);
30 | }
31 |
32 | public static IJwksBuilder AddNetDevPackIdentity(this IServiceCollection services, Action options = null)
33 | {
34 | services.AddHttpContextAccessor();
35 | services.AddScoped>();
36 | return services.AddJwksManager(options);
37 | }
38 |
39 | public static IJwksBuilder AddNetDevPackIdentity(this IJwksBuilder services)
40 | where TIdentityUser : IdentityUser
41 | where TKey : IEquatable
42 | {
43 | services.Services.AddHttpContextAccessor();
44 | services.Services.AddScoped>();
45 | return services;
46 | }
47 |
48 | public static IJwksBuilder AddNetDevPackIdentity(this IJwksBuilder services)
49 | where TIdentityUser : IdentityUser
50 | {
51 | services.Services.AddHttpContextAccessor();
52 | services.Services.AddScoped>();
53 | return services;
54 | }
55 |
56 | public static IJwksBuilder AddNetDevPackIdentity(this IJwksBuilder services)
57 | {
58 | services.Services.AddHttpContextAccessor();
59 | services.Services.AddScoped>();
60 | return services;
61 | }
62 |
63 | public static IdentityBuilder AddIdentityConfiguration(this IServiceCollection services)
64 | {
65 | if (services == null) throw new ArgumentException(nameof(services));
66 |
67 | return services
68 | .AddIdentity()
69 | .AddEntityFrameworkStores()
70 | .AddDefaultTokenProviders();
71 | }
72 |
73 | public static IdentityBuilder AddDefaultIdentity(this IServiceCollection services, Action options = null)
74 | {
75 | if (services == null) throw new ArgumentException(nameof(services));
76 | return services
77 | .AddIdentity()
78 | .AddDefaultTokenProviders();
79 | }
80 |
81 | public static IdentityBuilder AddCustomIdentity(this IServiceCollection services, Action options = null)
82 | where TIdentityUser : IdentityUser
83 | {
84 | if (services == null) throw new ArgumentException(nameof(services));
85 |
86 | return services.AddIdentity(options)
87 | .AddDefaultTokenProviders();
88 | }
89 |
90 | public static IdentityBuilder AddCustomIdentity(this IServiceCollection services, Action options = null)
91 | where TIdentityUser : IdentityUser
92 | where TKey : IEquatable
93 | {
94 | if (services == null) throw new ArgumentException(nameof(services));
95 | return services.AddIdentity>(options)
96 | .AddDefaultTokenProviders();
97 | }
98 |
99 | public static IdentityBuilder AddDefaultRoles(this IdentityBuilder builder)
100 | {
101 | return builder.AddRoles();
102 | }
103 |
104 | public static IdentityBuilder AddCustomRoles(this IdentityBuilder builder)
105 | where TRole : IdentityRole
106 | {
107 | return builder.AddRoles();
108 | }
109 |
110 | public static IdentityBuilder AddCustomRoles(this IdentityBuilder builder)
111 | where TRole : IdentityRole
112 | where TKey : IEquatable
113 | {
114 | return builder.AddRoles();
115 | }
116 |
117 | public static IdentityBuilder AddDefaultEntityFrameworkStores(this IdentityBuilder builder)
118 | {
119 | return builder.AddEntityFrameworkStores();
120 | }
121 |
122 | public static IdentityBuilder AddCustomEntityFrameworkStores(this IdentityBuilder builder) where TContext : DbContext
123 | {
124 | return builder.AddEntityFrameworkStores();
125 | }
126 |
127 | public static IServiceCollection AddIdentityEntityFrameworkContextConfiguration(
128 | this IServiceCollection services, Action options)
129 | {
130 | if (services == null) throw new ArgumentException(nameof(services));
131 | if (options == null) throw new ArgumentException(nameof(options));
132 | return services.AddDbContext(options);
133 | }
134 |
135 | public static IApplicationBuilder UseAuthConfiguration(this IApplicationBuilder app)
136 | {
137 | if (app == null) throw new ArgumentException(nameof(app));
138 |
139 | return app.UseAuthentication()
140 | .UseAuthorization();
141 | }
142 | }
143 |
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Model/LoginUser.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace NetDevPack.Identity.Model
4 | {
5 | public class LoginUser
6 | {
7 | [Required]
8 | [EmailAddress]
9 | public string Email { get; set; }
10 |
11 | [Required]
12 | [StringLength(100, MinimumLength = 6)]
13 | public string Password { get; set; }
14 | }
15 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/Model/RegisterUser.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace NetDevPack.Identity.Model
4 | {
5 | public class RegisterUser
6 | {
7 | [Required]
8 | [EmailAddress]
9 | public string Email { get; set; }
10 |
11 | [Required]
12 | [StringLength(100, MinimumLength = 6)]
13 | public string Password { get; set; }
14 |
15 | [Compare("Password")]
16 | public string ConfirmPassword { get; set; }
17 | }
18 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/NetDevPack.Identity.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net6.0;net7.0
5 | 10.0
6 | NetDevPack.Identity
7 | NetDevPack.Identity
8 | false
9 | Eduardo Pires
10 | desenvolvedor.io
11 | .NET DevPack Identity is a set of common implementations to help you implementing Identity, Jwt, claims validation and another facilities
12 | .NET DevPack Identity is a set of common implementations to help you implementing Identity, Jwt, claims validation and another facilities
13 | https://github.com/EduardoPires/NetDevPack
14 | NetDevPack.Identity
15 | IconNuget.png
16 | https://github.com/EduardoPires/NetDevPack
17 | EduardoPires
18 | Public
19 | MIT
20 | 1.2.0
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 | <_CustomFiles Include="../../.github/hooks/commit-msg" />
36 |
37 |
38 |
39 |
40 |
41 |
42 | True
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/User/Abstractions.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Http;
2 | using Microsoft.Extensions.DependencyInjection;
3 |
4 | namespace NetDevPack.Identity.User
5 | {
6 | public static class Abstractions
7 | {
8 | public static IServiceCollection AddAspNetUserConfiguration(this IServiceCollection services)
9 | {
10 | services.AddSingleton();
11 | services.AddScoped();
12 |
13 | return services;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/User/AspNetUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Security.Claims;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace NetDevPack.Identity.User
7 | {
8 | public class AspNetUser : IAspNetUser
9 | {
10 | private readonly IHttpContextAccessor _accessor;
11 |
12 | public AspNetUser(IHttpContextAccessor accessor)
13 | {
14 | _accessor = accessor;
15 | }
16 |
17 | public string Name => _accessor.HttpContext.User.Identity.Name;
18 |
19 | public Guid GetUserId()
20 | {
21 | return IsAutenticated() ? Guid.Parse(_accessor.HttpContext.User.GetUserId()) : Guid.Empty;
22 | }
23 |
24 | public string GetUserEmail()
25 | {
26 | return IsAutenticated() ? _accessor.HttpContext.User.GetUserEmail() : "";
27 | }
28 |
29 | public bool IsAutenticated()
30 | {
31 | return _accessor.HttpContext.User.Identity.IsAuthenticated;
32 | }
33 |
34 | public bool IsInRole(string role)
35 | {
36 | return _accessor.HttpContext.User.IsInRole(role);
37 | }
38 |
39 | public IEnumerable GetUserClaims()
40 | {
41 | return _accessor.HttpContext.User.Claims;
42 | }
43 |
44 | public HttpContext GetHttpContext()
45 | {
46 | return _accessor.HttpContext;
47 | }
48 | }
49 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/User/ClaimsPrincipalExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IdentityModel.Tokens.Jwt;
3 | using System.Security.Claims;
4 |
5 | namespace NetDevPack.Identity.User
6 | {
7 | public static class ClaimsPrincipalExtensions
8 | {
9 | public static string GetUserId(this ClaimsPrincipal principal)
10 | {
11 | if (principal == null)
12 | {
13 | throw new ArgumentException(nameof(principal));
14 | }
15 |
16 | var claim = principal.FindFirst(JwtRegisteredClaimNames.Sub);
17 | if (claim is null)
18 | claim = principal.FindFirst(ClaimTypes.NameIdentifier);
19 |
20 | return claim?.Value;
21 | }
22 |
23 | public static string GetUserEmail(this ClaimsPrincipal principal)
24 | {
25 | if (principal == null)
26 | {
27 | throw new ArgumentException(nameof(principal));
28 | }
29 | var claim = principal.FindFirst(JwtRegisteredClaimNames.Sub);
30 | if (claim is null)
31 | claim = principal.FindFirst(ClaimTypes.Email);
32 |
33 | return claim?.Value;
34 | }
35 | public static string GetUserId(this ClaimsIdentity principal)
36 | {
37 | if (principal == null)
38 | {
39 | throw new ArgumentException(nameof(principal));
40 | }
41 |
42 | var claim = principal.FindFirst(JwtRegisteredClaimNames.Sub);
43 | if (claim is null)
44 | claim = principal.FindFirst(ClaimTypes.NameIdentifier);
45 |
46 | return claim?.Value;
47 | }
48 |
49 | public static string GetUserEmail(this ClaimsIdentity principal)
50 | {
51 | if (principal == null)
52 | {
53 | throw new ArgumentException(nameof(principal));
54 | }
55 | var claim = principal.FindFirst(JwtRegisteredClaimNames.Sub);
56 | if (claim is null)
57 | claim = principal.FindFirst(ClaimTypes.Email);
58 |
59 | return claim?.Value;
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/src/NetDevPack.Identity/User/IAspNetUser.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Security.Claims;
4 | using Microsoft.AspNetCore.Http;
5 |
6 | namespace NetDevPack.Identity.User
7 | {
8 | public interface IAspNetUser
9 | {
10 | string Name { get; }
11 | Guid GetUserId();
12 | string GetUserEmail();
13 | bool IsAutenticated();
14 | bool IsInRole(string role);
15 | IEnumerable GetUserClaims();
16 | HttpContext GetHttpContext();
17 | }
18 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/AspNetCore.Jwt.Sample.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | net7.0
5 | 71366747-6c85-459a-8d8e-f4a249169d76
6 | Linux
7 | ..\..\..
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | all
18 | runtime; build; native; contentfiles; analyzers; buildtransitive
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Config/CustomIdentityAndKeyConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using NetDevPack.Identity;
7 | using NetDevPack.Identity.Jwt;
8 |
9 | namespace AspNetCore.Jwt.Sample.Config
10 | {
11 | public static class CustomIdentityAndKeyConfig
12 | {
13 | public static void AddCustomIdentityAndKeyConfiguration(this IServiceCollection services, IConfiguration configuration)
14 | {
15 |
16 | // Your own EF Identity configuration - Use when you have another database like postgres
17 | services.AddDbContext(options => options.UseInMemoryDatabase("NetDevPack.Identity"));
18 |
19 | // Your own Identity configuration
20 | services.AddIdentityCore(options =>
21 | {
22 | options.SignIn.RequireConfirmedEmail = false;
23 | options.Lockout.MaxFailedAccessAttempts = 5;
24 | })
25 | .AddRoles()
26 | .AddEntityFrameworkStores()
27 | .AddDefaultTokenProviders();
28 |
29 | // Ours JWT configuration
30 | services.AddJwtConfiguration(configuration).AddNetDevPackIdentity();
31 | }
32 | }
33 |
34 | public class MyIntIdentityUser : IdentityUser
35 | {
36 |
37 | }
38 |
39 | public class MyIntIdentityRoles : IdentityRole
40 | {
41 |
42 | }
43 |
44 | public class MyIntIdentityContext : IdentityDbContext
45 | {
46 | public MyIntIdentityContext(DbContextOptions options) : base(options) { }
47 | }
48 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Config/CustomIdentityConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using NetDevPack.Identity;
7 | using NetDevPack.Identity.Jwt;
8 |
9 | namespace AspNetCore.Jwt.Sample.Config
10 | {
11 | public static class CustomIdentityConfig
12 | {
13 | public static void AddCustomIdentityConfiguration(this IServiceCollection services, IConfiguration configuration)
14 | {
15 | // Your own EF Identity configuration - Use when you have another database like postgres
16 | services.AddDbContext(options => options.UseInMemoryDatabase("NetDevPack.Identity"));
17 |
18 | // Your own Identity configuration
19 | services.AddCustomIdentity(options =>
20 | {
21 | options.SignIn.RequireConfirmedEmail = false;
22 | options.Lockout.MaxFailedAccessAttempts = 5;
23 | })
24 | .AddCustomRoles()
25 | .AddCustomEntityFrameworkStores()
26 | .AddDefaultTokenProviders();
27 |
28 | // Ours JWT configuration
29 | services.AddJwtConfiguration(configuration);
30 | }
31 | }
32 |
33 | public class MyIdentityUser : IdentityUser
34 | {
35 |
36 | }
37 |
38 | public class MyIdentityRoles : IdentityRole
39 | {
40 |
41 | }
42 |
43 | public class MyIdentityContext : IdentityDbContext
44 | {
45 | public MyIdentityContext(DbContextOptions options) : base(options) { }
46 | }
47 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Config/DefaultIdentityConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.EntityFrameworkCore;
2 | using Microsoft.Extensions.Configuration;
3 | using Microsoft.Extensions.DependencyInjection;
4 | using NetDevPack.Identity;
5 | using NetDevPack.Identity.Jwt;
6 |
7 | namespace AspNetCore.Jwt.Sample.Config
8 | {
9 | public static class DefaultIdentityConfig
10 | {
11 | public static void AddDefaultIdentityConfiguration(this IServiceCollection services, IConfiguration configuration)
12 | {
13 | // Default EF Context for Identity (inside of the NetDevPack.Identity)
14 | services.AddIdentityEntityFrameworkContextConfiguration(options => options.UseInMemoryDatabase("NetDevPack.Identity"));
15 |
16 | // Default Identity configuration
17 | services.AddIdentityConfiguration(); // <== This extension returns IdentityBuilder to extends configuration
18 |
19 | // Default JWT configuration
20 | services.AddJwtConfiguration(configuration);
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Config/DependencyInjectionConfig.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.Extensions.DependencyInjection;
2 |
3 | namespace AspNetCore.Jwt.Sample.Config
4 | {
5 | public static class DependencyInjectionConfig
6 | {
7 | public static IServiceCollection AddDependencyInjectionConfiguration(this IServiceCollection services)
8 | {
9 | // Resolve another dependencies here!
10 |
11 | return services;
12 | }
13 | }
14 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Config/SwaggerConfig.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Reflection;
4 | using Microsoft.AspNetCore.Builder;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.OpenApi.Models;
7 |
8 | namespace AspNetCore.Jwt.Sample.Config
9 | {
10 | public static class SwaggerConfig
11 | {
12 | public static void AddSwaggerConfiguration(this IServiceCollection services)
13 | {
14 | services.AddSwaggerGen(c =>
15 | {
16 | c.SwaggerDoc("v1", new OpenApiInfo
17 | {
18 | Title = "NetDevPack Identity Sample API",
19 | Description = "Developed by Eduardo Pires - Owner @ desenvolvedor.io",
20 | Contact = new OpenApiContact { Name = "Eduardo Pires", Email = "contato@eduardopires.net.br" },
21 | License = new OpenApiLicense { Name = "MIT", Url = new Uri("https://opensource.org/licenses/MIT") }
22 | });
23 |
24 | c.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
25 | {
26 | Description = "Input the JWT like: Bearer {your token}",
27 | Name = "Authorization",
28 | Scheme = "Bearer",
29 | BearerFormat = "JWT",
30 | In = ParameterLocation.Header,
31 | Type = SecuritySchemeType.ApiKey
32 | });
33 |
34 | c.AddSecurityRequirement(new OpenApiSecurityRequirement
35 | {
36 | {
37 | new OpenApiSecurityScheme
38 | {
39 | Reference = new OpenApiReference
40 | {
41 | Type = ReferenceType.SecurityScheme,
42 | Id = "Bearer"
43 | }
44 | },
45 | new string[] {}
46 | }
47 | });
48 | });
49 | }
50 |
51 | public static void UseSwaggerConfiguration(this IApplicationBuilder app)
52 | {
53 | app.UseSwagger();
54 | app.UseSwaggerUI(c =>
55 | {
56 | c.SwaggerEndpoint("/swagger/v1/swagger.json", "v1");
57 | });
58 | }
59 | }
60 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Controllers/AuthController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Identity;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.Extensions.Options;
5 | using NetDevPack.Identity.Interfaces;
6 | using NetDevPack.Identity.Jwt;
7 | using NetDevPack.Identity.Jwt.Model;
8 | using NetDevPack.Identity.Model;
9 |
10 | namespace AspNetCore.Jwt.Sample.Controllers
11 | {
12 | [Route("api/account")]
13 | public class AuthController : MainController
14 | {
15 | private readonly SignInManager _signInManager;
16 | private readonly UserManager _userManager;
17 | private readonly IJwtBuilder _jwtBuilder;
18 | private readonly AppJwtSettings _appJwtSettings;
19 |
20 | public AuthController(SignInManager signInManager,
21 | UserManager userManager,
22 | IJwtBuilder jwtBuilder)
23 | {
24 | _signInManager = signInManager;
25 | _userManager = userManager;
26 | _jwtBuilder = jwtBuilder;
27 | }
28 |
29 | [HttpPost("register")]
30 | public async Task Register(RegisterUser registerUser)
31 | {
32 | if (!ModelState.IsValid) return CustomResponse(ModelState);
33 |
34 | var user = new IdentityUser
35 | {
36 | UserName = registerUser.Email,
37 | Email = registerUser.Email,
38 | EmailConfirmed = true
39 | };
40 |
41 | var result = await _userManager.CreateAsync(user, registerUser.Password);
42 |
43 | if (result.Succeeded)
44 | {
45 | return CustomResponse(GetUserResponse(user.Email));
46 | }
47 |
48 | foreach (var error in result.Errors)
49 | {
50 | AddError(error.Description);
51 | }
52 |
53 | return CustomResponse();
54 | }
55 |
56 | [HttpPost("login")]
57 | public async Task Login(LoginUser loginUser)
58 | {
59 | if (!ModelState.IsValid) return CustomResponse(ModelState);
60 |
61 | var result = await _signInManager.PasswordSignInAsync(loginUser.Email, loginUser.Password, false, true);
62 |
63 | if (result.Succeeded)
64 | {
65 | /* ANOTHER OPTIONS */
66 | var userResponse = await GetUserResponse(loginUser.Email);
67 | var jwtUserClaims = await GetJwtWithUserClaims(loginUser.Email);
68 | var jwtNoClaims = await GetJwtWithoutClaims(loginUser.Email);
69 |
70 | var fullJwt = await GetFullJwt(loginUser.Email);
71 | return CustomResponse(fullJwt);
72 | }
73 |
74 | if (result.IsLockedOut)
75 | {
76 | AddError("This user is blocked");
77 | return CustomResponse();
78 | }
79 |
80 | AddError("Incorrect user or password");
81 | return CustomResponse();
82 | }
83 |
84 |
85 | [HttpPost("refresh-token")]
86 | public async Task RefreshToken([FromForm] string refreshToken)
87 | {
88 | var tokenValidation = await _jwtBuilder.ValidateRefreshToken(refreshToken);
89 |
90 | if (!tokenValidation)
91 | {
92 | ModelState.AddModelError("RefreshToken", "Expired token");
93 | return BadRequest(ModelState);
94 | }
95 |
96 | return CustomResponse(await _jwtBuilder
97 | .WithUserId(tokenValidation.UserId)
98 | .WithJwtClaims()
99 | .WithUserClaims()
100 | .WithUserRoles()
101 | .WithRefreshToken()
102 | .BuildToken());
103 |
104 | }
105 | private Task GetUserResponse(string email)
106 | {
107 | return _jwtBuilder
108 | .WithEmail(email)
109 | .WithJwtClaims()
110 | .WithUserClaims()
111 | .WithUserRoles()
112 | .WithRefreshToken()
113 | .BuildUserResponse();
114 | }
115 |
116 | private Task GetFullJwt(string email)
117 | {
118 | return _jwtBuilder
119 | .WithEmail(email)
120 | .WithJwtClaims()
121 | .WithUserClaims()
122 | .WithUserRoles()
123 | .WithRefreshToken()
124 | .BuildToken();
125 | }
126 |
127 | private Task GetJwtWithoutClaims(string email)
128 | {
129 | return _jwtBuilder
130 | .WithEmail(email)
131 | .WithRefreshToken()
132 | .BuildToken();
133 | }
134 |
135 | private Task GetJwtWithUserClaims(string email)
136 | {
137 | return _jwtBuilder
138 | .WithEmail(email)
139 | .WithUserClaims()
140 | .WithRefreshToken()
141 | .BuildToken();
142 | }
143 | }
144 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Controllers/AuthCustomConfigAndKeyController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using AspNetCore.Jwt.Sample.Config;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Identity;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.Extensions.Options;
7 | using NetDevPack.Identity.Interfaces;
8 | using NetDevPack.Identity.Jwt;
9 | using NetDevPack.Identity.Jwt.Model;
10 | using NetDevPack.Identity.Model;
11 | using NetDevPack.Identity.User;
12 |
13 | namespace AspNetCore.Jwt.Sample.Controllers
14 | {
15 | [Route("api/custom-account-key")]
16 | public class AuthCustomConfigAndKeyController : MainController
17 | {
18 | private readonly SignInManager _signInManager;
19 | private readonly UserManager _userManager;
20 | private readonly IJwtBuilder _jwtBuilder;
21 |
22 | public AuthCustomConfigAndKeyController(SignInManager signInManager,
23 | UserManager userManager,
24 | IJwtBuilder jwtBuilder)
25 | {
26 | _signInManager = signInManager;
27 | _userManager = userManager;
28 | _jwtBuilder = jwtBuilder;
29 | }
30 |
31 | [HttpPost("register")]
32 | public async Task Register(RegisterUser registerUser)
33 | {
34 | if (!ModelState.IsValid) return CustomResponse(ModelState);
35 |
36 | var user = new MyIntIdentityUser
37 | {
38 | UserName = registerUser.Email,
39 | Email = registerUser.Email,
40 | EmailConfirmed = true
41 | };
42 |
43 | var result = await _userManager.CreateAsync(user, registerUser.Password);
44 |
45 | if (result.Succeeded)
46 | {
47 | return CustomResponse(await GetUserResponse(user.Email));
48 | }
49 |
50 | foreach (var error in result.Errors)
51 | {
52 | AddError(error.Description);
53 | }
54 |
55 | return CustomResponse();
56 | }
57 |
58 | [HttpPost("login")]
59 | public async Task Login(LoginUser loginUser)
60 | {
61 | if (!ModelState.IsValid) return CustomResponse(ModelState);
62 |
63 | var result = await _signInManager.PasswordSignInAsync(loginUser.Email, loginUser.Password, false, true);
64 |
65 | if (result.Succeeded)
66 | {
67 | /* ANOTHER OPTIONS */
68 | var userResponse = GetUserResponse(loginUser.Email);
69 | var jwtUserClaims = GetJwtWithUserClaims(loginUser.Email);
70 | var jwtNoClaims = GetJwtWithoutClaims(loginUser.Email);
71 |
72 | var fullJwt = GetFullJwt(loginUser.Email);
73 | return CustomResponse(fullJwt);
74 | }
75 |
76 | if (result.IsLockedOut)
77 | {
78 | AddError("This user is blocked");
79 | return CustomResponse();
80 | }
81 |
82 | AddError("Incorrect user or password");
83 | return CustomResponse();
84 | }
85 |
86 | private Task GetUserResponse(string email)
87 | {
88 | return _jwtBuilder
89 | .WithEmail(email)
90 | .WithJwtClaims()
91 | .WithUserClaims()
92 | .WithUserRoles()
93 | .WithRefreshToken()
94 | .BuildUserResponse();
95 | }
96 |
97 | private Task GetFullJwt(string email)
98 | {
99 | return _jwtBuilder
100 | .WithEmail(email)
101 | .WithJwtClaims()
102 | .WithUserClaims()
103 | .WithUserRoles()
104 | .WithRefreshToken()
105 | .BuildToken();
106 | }
107 |
108 | private Task GetJwtWithoutClaims(string email)
109 | {
110 | return _jwtBuilder
111 | .WithEmail(email)
112 | .WithRefreshToken()
113 | .BuildToken();
114 | }
115 |
116 | private Task GetJwtWithUserClaims(string email)
117 | {
118 | return _jwtBuilder
119 | .WithEmail(email)
120 | .WithUserClaims()
121 | .WithRefreshToken()
122 | .BuildToken();
123 | }
124 |
125 | [HttpPost("refresh-token")]
126 | public async Task RefreshToken([FromForm] string refreshToken)
127 | {
128 | var tokenValidation = await _jwtBuilder.ValidateRefreshToken(refreshToken);
129 |
130 | if (!tokenValidation)
131 | {
132 | ModelState.AddModelError("RefreshToken", "Expired token");
133 | return BadRequest(ModelState);
134 | }
135 |
136 | return CustomResponse(await _jwtBuilder
137 | .WithUserId(tokenValidation.UserId)
138 | .WithJwtClaims()
139 | .WithUserClaims()
140 | .WithUserRoles()
141 | .WithRefreshToken()
142 | .BuildUserResponse());
143 |
144 | }
145 | }
146 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Controllers/AuthCustomConfigController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using AspNetCore.Jwt.Sample.Config;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Identity;
5 | using Microsoft.AspNetCore.Mvc;
6 | using Microsoft.Extensions.Options;
7 | using NetDevPack.Identity.Interfaces;
8 | using NetDevPack.Identity.Jwt;
9 | using NetDevPack.Identity.Jwt.Model;
10 | using NetDevPack.Identity.Model;
11 | using NetDevPack.Identity.User;
12 |
13 | namespace AspNetCore.Jwt.Sample.Controllers
14 | {
15 | [Route("api/custom-account")]
16 | public class AuthCustomConfigController : MainController
17 | {
18 | private readonly SignInManager _signInManager;
19 | private readonly UserManager _userManager;
20 | private readonly IJwtBuilder _jwtBuilder;
21 | private readonly AppJwtSettings _appJwtSettings;
22 |
23 | public AuthCustomConfigController(SignInManager signInManager,
24 | UserManager userManager,
25 | IJwtBuilder jwtBuilder)
26 | {
27 | _signInManager = signInManager;
28 | _userManager = userManager;
29 | _jwtBuilder = jwtBuilder;
30 | }
31 |
32 | [HttpPost("register")]
33 | public async Task Register(RegisterUser registerUser)
34 | {
35 | if (!ModelState.IsValid) return CustomResponse(ModelState);
36 |
37 | var user = new MyIdentityUser
38 | {
39 | UserName = registerUser.Email,
40 | Email = registerUser.Email,
41 | EmailConfirmed = true
42 | };
43 |
44 | var result = await _userManager.CreateAsync(user, registerUser.Password);
45 |
46 | if (result.Succeeded)
47 | {
48 | return CustomResponse(GetUserResponse(user.Email));
49 | }
50 |
51 | foreach (var error in result.Errors)
52 | {
53 | AddError(error.Description);
54 | }
55 |
56 | return CustomResponse();
57 | }
58 |
59 | [HttpPost("login")]
60 | public async Task Login(LoginUser loginUser)
61 | {
62 | if (!ModelState.IsValid) return CustomResponse(ModelState);
63 |
64 | var result = await _signInManager.PasswordSignInAsync(loginUser.Email, loginUser.Password, false, true);
65 |
66 | if (result.Succeeded)
67 | {
68 | /* ANOTHER OPTIONS */
69 | var userResponse = await GetUserResponse(loginUser.Email);
70 | var jwtUserClaims = await GetJwtWithUserClaims(loginUser.Email);
71 | var jwtNoClaims = await GetJwtWithoutClaims(loginUser.Email);
72 |
73 | var fullJwt = await GetFullJwt(loginUser.Email);
74 | return CustomResponse(fullJwt);
75 | }
76 |
77 | if (result.IsLockedOut)
78 | {
79 | AddError("This user is blocked");
80 | return CustomResponse();
81 | }
82 |
83 | AddError("Incorrect user or password");
84 | return CustomResponse();
85 | }
86 |
87 |
88 | [HttpPost("refresh-token")]
89 | public async Task RefreshToken([FromForm] string refreshToken)
90 | {
91 | var tokenValidation = await _jwtBuilder.ValidateRefreshToken(refreshToken);
92 |
93 | if (!tokenValidation)
94 | {
95 | ModelState.AddModelError("RefreshToken", "Expired token");
96 | return BadRequest(ModelState);
97 | }
98 |
99 | return CustomResponse(await _jwtBuilder
100 | .WithUserId(tokenValidation.UserId)
101 | .WithJwtClaims()
102 | .WithUserClaims()
103 | .WithUserRoles()
104 | .WithRefreshToken()
105 | .BuildToken());
106 |
107 | }
108 | private Task GetUserResponse(string email)
109 | {
110 | return _jwtBuilder
111 | .WithEmail(email)
112 | .WithJwtClaims()
113 | .WithUserClaims()
114 | .WithUserRoles()
115 | .WithRefreshToken()
116 | .BuildUserResponse();
117 | }
118 |
119 | private Task GetFullJwt(string email)
120 | {
121 | return _jwtBuilder
122 | .WithEmail(email)
123 | .WithJwtClaims()
124 | .WithUserClaims()
125 | .WithUserRoles()
126 | .WithRefreshToken()
127 | .BuildToken();
128 | }
129 |
130 | private Task GetJwtWithoutClaims(string email)
131 | {
132 | return _jwtBuilder
133 | .WithEmail(email)
134 | .WithRefreshToken()
135 | .BuildToken();
136 | }
137 |
138 | private Task GetJwtWithUserClaims(string email)
139 | {
140 | return _jwtBuilder
141 | .WithEmail(email)
142 | .WithUserClaims()
143 | .WithRefreshToken()
144 | .BuildToken();
145 | }
146 | }
147 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Controllers/MainController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using FluentValidation.Results;
4 | using Microsoft.AspNetCore.Mvc;
5 | using Microsoft.AspNetCore.Mvc.ModelBinding;
6 |
7 | namespace AspNetCore.Jwt.Sample.Controllers
8 | {
9 | [ApiController]
10 | public abstract class MainController : ControllerBase
11 | {
12 | private readonly ICollection Errors = new List();
13 |
14 | protected ActionResult CustomResponse(object result = null)
15 | {
16 | if (IsOperationValid())
17 | {
18 | return Ok(result);
19 | }
20 |
21 | return BadRequest(new ValidationProblemDetails(new Dictionary
22 | {
23 | { "Messages", Errors.ToArray() }
24 | }));
25 | }
26 |
27 | protected ActionResult CustomResponse(ModelStateDictionary modelState)
28 | {
29 | var errors = modelState.Values.SelectMany(e => e.Errors);
30 | foreach (var error in errors)
31 | {
32 | AddError(error.ErrorMessage);
33 | }
34 |
35 | return CustomResponse();
36 | }
37 |
38 | protected ActionResult CustomResponse(ValidationResult validationResult)
39 | {
40 | foreach (var error in validationResult.Errors)
41 | {
42 | AddError(error.ErrorMessage);
43 | }
44 |
45 | return CustomResponse();
46 | }
47 |
48 | protected bool IsOperationValid()
49 | {
50 | return !Errors.Any();
51 | }
52 |
53 | protected void AddError(string erro)
54 | {
55 | Errors.Add(erro);
56 | }
57 |
58 | protected void ClearErrors()
59 | {
60 | Errors.Clear();
61 | }
62 | }
63 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Controllers/SampleController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using Microsoft.AspNetCore.Mvc;
3 | using NetDevPack.Identity.Authorization;
4 | using NetDevPack.Identity.User;
5 |
6 | namespace AspNetCore.Jwt.Sample.Controllers
7 | {
8 | [Authorize]
9 | [Route("api/sample")]
10 | public class SampleController : MainController
11 | {
12 | private readonly IAspNetUser _user;
13 |
14 | public SampleController(IAspNetUser user)
15 | {
16 | _user = user;
17 | }
18 |
19 | [HttpGet("read")]
20 | [CustomAuthorize("Sample","Read")]
21 | public IActionResult SampleActionRead()
22 | {
23 | return CustomResponse($"The user {_user.GetUserEmail()} have permission to get this!");
24 | }
25 |
26 | [HttpGet("list")]
27 | [CustomAuthorize("Sample", "List")]
28 | public IActionResult SampleActionList()
29 | {
30 | return CustomResponse($"The user {_user.GetUserEmail()} have permission to get this!");
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Program.cs:
--------------------------------------------------------------------------------
1 | using System.Text.Json.Serialization;
2 | using AspNetCore.Jwt.Sample.Config;
3 | using Microsoft.AspNetCore.Builder;
4 | using Microsoft.Extensions.DependencyInjection;
5 | using Microsoft.Extensions.Hosting;
6 | using NetDevPack.Identity.User;
7 |
8 | var builder = WebApplication.CreateBuilder(args);
9 | builder.Services.AddControllers().AddJsonOptions(o => o.JsonSerializerOptions.DefaultIgnoreCondition = JsonIgnoreCondition.WhenWritingDefault);
10 |
11 | // When you just want the default configuration
12 | //builder.Services.AddDefaultIdentityConfiguration(builder.Configuration);
13 |
14 | // When you have specifics configurations (see inside this method)
15 | //builder.Services.AddCustomIdentityConfiguration(builder.Configuration);
16 |
17 | // When you have specifics configurations (with Key type [see inside this method])
18 |
19 | builder.Services.AddMemoryCache()
20 | .AddCustomIdentityAndKeyConfiguration(builder.Configuration);
21 |
22 | // Setting the interactive AspNetUser (logged in)
23 | builder.Services.AddAspNetUserConfiguration();
24 |
25 | builder.Services.AddSwaggerConfiguration();
26 |
27 | builder.Services.AddDependencyInjectionConfiguration();
28 |
29 | var app = builder.Build();
30 |
31 | if (app.Environment.IsDevelopment())
32 | {
33 | app.UseDeveloperExceptionPage();
34 | }
35 |
36 | app.UseSwaggerConfiguration();
37 |
38 | app.UseHttpsRedirection();
39 |
40 | app.UseRouting();
41 |
42 | // Custom NetDevPack abstraction here!
43 | app.UseAuthConfiguration();
44 |
45 | app.MapControllers();
46 |
47 | app.Run();
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:64309",
7 | "sslPort": 44328
8 | }
9 | },
10 | "$schema": "http://json.schemastore.org/launchsettings.json",
11 | "profiles": {
12 |
13 | "AspNetCore.Jwt.Sample": {
14 | "commandName": "Project",
15 | "launchBrowser": true,
16 | "launchUrl": "swagger",
17 | "environmentVariables": {
18 | "ASPNETCORE_ENVIRONMENT": "Development"
19 | },
20 | "applicationUrl": "https://localhost:6532;http://localhost:6531"
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/RunMigration.txt:
--------------------------------------------------------------------------------
1 | Default configuration:
2 |
3 | dotnet ef migrations add Initial --context NetDevPackAppDbContext --project src\Samples\AspNetCore.Jwt.Sample\AspNetCore.Jwt.Sample.csproj
4 | dotnet ef database update --context NetDevPackAppDbContext --project src\Samples\AspNetCore.Jwt.Sample\AspNetCore.Jwt.Sample.csproj
5 |
6 | Custom configuration:
7 |
8 | dotnet ef migrations add Initial --context MyIdentityContext --project src\Samples\AspNetCore.Jwt.Sample\AspNetCore.Jwt.Sample.csproj
9 | dotnet ef database update --context MyIdentityContext --project src\Samples\AspNetCore.Jwt.Sample\AspNetCore.Jwt.Sample.csproj
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/src/Samples/AspNetCore.Jwt.Sample/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Information",
5 | "Microsoft": "Warning",
6 | "Microsoft.Hosting.Lifetime": "Information"
7 | }
8 | },
9 | "AllowedHosts": "*",
10 | "AppJwtSettings": {
11 | "Issuer": "https://netdevpack.net",
12 | "Audience": "SampleApp"
13 | }
14 | }
15 |
--------------------------------------------------------------------------------