├── .gitattributes ├── .gitignore ├── Angular ├── Angular.csproj ├── angular-app │ ├── .editorconfig │ ├── .gitignore │ ├── README.md │ ├── angular.json │ ├── browserslist │ ├── e2e │ │ ├── protractor.conf.js │ │ ├── src │ │ │ ├── app.e2e-spec.ts │ │ │ └── app.po.ts │ │ └── tsconfig.json │ ├── karma.conf.js │ ├── package-lock.json │ ├── package.json │ ├── src │ │ ├── app │ │ │ ├── app-routing.module.ts │ │ │ ├── app.component.css │ │ │ ├── app.component.html │ │ │ ├── app.component.spec.ts │ │ │ ├── app.component.ts │ │ │ └── app.module.ts │ │ ├── assets │ │ │ └── .gitkeep │ │ ├── environments │ │ │ ├── environment.prod.ts │ │ │ └── environment.ts │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── main.ts │ │ ├── polyfills.ts │ │ ├── styles.css │ │ └── test.ts │ ├── tsconfig.app.json │ ├── tsconfig.json │ ├── tsconfig.spec.json │ └── tslint.json └── package-lock.json ├── Api ├── Api.csproj ├── AuthRequirement │ └── JwtRequirement.cs ├── Controllers │ └── SecretController.cs ├── CustomeAuthenticationHandler.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── appsettings.Development.json └── appsettings.json ├── ApiOne ├── ApiOne.csproj ├── Controllers │ └── SecretController.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── appsettings.Development.json └── appsettings.json ├── ApiTwo ├── ApiTwo.csproj ├── Controllers │ └── HomeController.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── appsettings.Development.json └── appsettings.json ├── Authentication.sln ├── Basics ├── AuthorizationRequirements │ └── CustomRequireClaim.cs ├── Basics.csproj ├── Controllers │ ├── HomeController.cs │ └── OperationsController.cs ├── CustomPolicyProvider │ └── CustomAuthorizationPolicyProvider.cs ├── Pages │ ├── Razor │ │ ├── Index.cshtml │ │ ├── Index.cshtml.cs │ │ ├── Policy.cshtml │ │ ├── Policy.cshtml.cs │ │ ├── Secured.cshtml │ │ └── Secured.cshtml.cs │ └── RazorSecured │ │ ├── Anon.cshtml │ │ ├── Anon.cshtml.cs │ │ ├── Index.cshtml │ │ └── Index.cshtml.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Transformer │ └── ClaimsTransformation.cs ├── Views │ └── Home │ │ ├── Index.cshtml │ │ └── Secret.cshtml ├── appsettings.Development.json └── appsettings.json ├── Client ├── Client.csproj ├── Controllers │ └── HomeController.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ └── Home │ │ ├── Index.cshtml │ │ └── Secret.cshtml ├── appsettings.Development.json └── appsettings.json ├── IdentityExample ├── Controllers │ └── HomeController.cs ├── Data │ └── AppDbContext.cs ├── IdentityExample.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ └── Home │ │ ├── EmailVerification.cshtml │ │ ├── Index.cshtml │ │ ├── Login.cshtml │ │ ├── Register.cshtml │ │ ├── Secret.cshtml │ │ └── VerifyEmail.cshtml ├── appsettings.Development.json └── appsettings.json ├── IdentityServer ├── Configuration.cs ├── Controllers │ ├── AuthController.cs │ ├── ExternalRegisterViewModel.cs │ ├── LoginViewModel.cs │ └── RegisterViewModel.cs ├── Data │ ├── AppDbContext.cs │ └── Migrations │ │ ├── AppMigrations │ │ ├── 20191201210103_init.Designer.cs │ │ ├── 20191201210103_init.cs │ │ └── AppDbContextModelSnapshot.cs │ │ └── IdentityServer │ │ ├── ConfigurationDb │ │ ├── 20191201205411_InitialIdentityServerConfigurationDbMigration.Designer.cs │ │ ├── 20191201205411_InitialIdentityServerConfigurationDbMigration.cs │ │ └── ConfigurationDbContextModelSnapshot.cs │ │ └── PersistedGrantDb │ │ ├── 20191201205312_InitialIdentityServerPersistedGrantDbMigration.Designer.cs │ │ ├── 20191201205312_InitialIdentityServerPersistedGrantDbMigration.cs │ │ └── PersistedGrantDbContextModelSnapshot.cs ├── IdentityServer.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ ├── Auth │ │ ├── ExternalRegister.cshtml │ │ ├── Login.cshtml │ │ └── Register.cshtml │ └── _ViewImports.cshtml ├── appsettings.Development.json ├── appsettings.json ├── db-scripts.txt └── tempkey.rsa ├── JavascriptClient ├── Controllers │ └── HomeController.cs ├── JavascriptClient.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ └── Home │ │ ├── Index.cshtml │ │ └── SignIn.cshtml ├── appsettings.Development.json ├── appsettings.json └── wwwroot │ ├── js-old │ ├── sign-in-callback.js │ └── sign-in.js │ └── main.js ├── MvcClient ├── Controllers │ └── HomeController.cs ├── MvcClient.csproj ├── Program.cs ├── Properties │ └── launchSettings.json ├── Startup.cs ├── Views │ └── Home │ │ ├── Index.cshtml │ │ └── Secret.cshtml ├── appsettings.Development.json └── appsettings.json ├── README.md ├── Server ├── Constants.cs ├── Controllers │ ├── HomeController.cs │ ├── OAuthController.cs │ └── SecretController.cs ├── Program.cs ├── Properties │ └── launchSettings.json ├── Server.csproj ├── Startup.cs ├── Views │ ├── Home │ │ ├── Index.cshtml │ │ └── Secret.cshtml │ └── OAuth │ │ └── Authorize.cshtml ├── appsettings.Development.json └── appsettings.json ├── WpfApp ├── App.config ├── App.xaml ├── App.xaml.cs ├── MainWindow.xaml ├── MainWindow.xaml.cs ├── Properties │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ ├── Resources.resx │ ├── Settings.Designer.cs │ └── Settings.settings ├── WpfApp.csproj ├── WpfEmbeddedBrowser.cs └── packages.config └── flutter_app ├── .gitignore ├── .metadata ├── README.md ├── android ├── .gitignore ├── app │ ├── build.gradle │ └── src │ │ ├── main │ │ ├── AndroidManifest.xml │ │ ├── kotlin │ │ │ └── com │ │ │ │ └── example │ │ │ │ └── flutter_app │ │ │ │ └── MainActivity.kt │ │ └── res │ │ │ ├── drawable │ │ │ └── launch_background.xml │ │ │ ├── mipmap-hdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-mdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxhdpi │ │ │ └── ic_launcher.png │ │ │ ├── mipmap-xxxhdpi │ │ │ └── ic_launcher.png │ │ │ └── values │ │ │ └── styles.xml │ │ └── profile │ │ └── AndroidManifest.xml ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ └── gradle-wrapper.properties └── settings.gradle ├── ios ├── .gitignore ├── Flutter │ ├── AppFrameworkInfo.plist │ ├── Debug.xcconfig │ └── Release.xcconfig ├── Runner.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ └── contents.xcworkspacedata │ └── xcshareddata │ │ └── xcschemes │ │ └── Runner.xcscheme ├── Runner.xcworkspace │ └── contents.xcworkspacedata └── Runner │ ├── AppDelegate.swift │ ├── Assets.xcassets │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-1024x1024@1x.png │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ └── Icon-App-83.5x83.5@2x.png │ └── LaunchImage.imageset │ │ ├── Contents.json │ │ ├── LaunchImage.png │ │ ├── LaunchImage@2x.png │ │ ├── LaunchImage@3x.png │ │ └── README.md │ ├── Base.lproj │ ├── LaunchScreen.storyboard │ └── Main.storyboard │ ├── Info.plist │ └── Runner-Bridging-Header.h ├── lib └── main.dart ├── pubspec.lock ├── pubspec.yaml └── test └── widget_test.dart /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /Angular/Angular.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Exe 5 | netcoreapp3.1 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Angular/angular-app/.editorconfig: -------------------------------------------------------------------------------- 1 | # Editor configuration, see https://editorconfig.org 2 | root = true 3 | 4 | [*] 5 | charset = utf-8 6 | indent_style = space 7 | indent_size = 2 8 | insert_final_newline = true 9 | trim_trailing_whitespace = true 10 | 11 | [*.ts] 12 | quote_type = single 13 | 14 | [*.md] 15 | max_line_length = off 16 | trim_trailing_whitespace = false 17 | -------------------------------------------------------------------------------- /Angular/angular-app/.gitignore: -------------------------------------------------------------------------------- 1 | # See http://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # compiled output 4 | /dist 5 | /tmp 6 | /out-tsc 7 | # Only exists if Bazel was run 8 | /bazel-out 9 | 10 | # dependencies 11 | /node_modules 12 | 13 | # profiling files 14 | chrome-profiler-events*.json 15 | speed-measure-plugin*.json 16 | 17 | # IDEs and editors 18 | /.idea 19 | .project 20 | .classpath 21 | .c9/ 22 | *.launch 23 | .settings/ 24 | *.sublime-workspace 25 | 26 | # IDE - VSCode 27 | .vscode/* 28 | !.vscode/settings.json 29 | !.vscode/tasks.json 30 | !.vscode/launch.json 31 | !.vscode/extensions.json 32 | .history/* 33 | 34 | # misc 35 | /.sass-cache 36 | /connect.lock 37 | /coverage 38 | /libpeerconnection.log 39 | npm-debug.log 40 | yarn-error.log 41 | testem.log 42 | /typings 43 | 44 | # System Files 45 | .DS_Store 46 | Thumbs.db 47 | -------------------------------------------------------------------------------- /Angular/angular-app/README.md: -------------------------------------------------------------------------------- 1 | # AngularApp 2 | 3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 9.1.7. 4 | 5 | ## Development server 6 | 7 | Run `ng serve` for a dev server. Navigate to `http://localhost:4200/`. The app will automatically reload if you change any of the source files. 8 | 9 | ## Code scaffolding 10 | 11 | Run `ng generate component component-name` to generate a new component. You can also use `ng generate directive|pipe|service|class|guard|interface|enum|module`. 12 | 13 | ## Build 14 | 15 | Run `ng build` to build the project. The build artifacts will be stored in the `dist/` directory. Use the `--prod` flag for a production build. 16 | 17 | ## Running unit tests 18 | 19 | Run `ng test` to execute the unit tests via [Karma](https://karma-runner.github.io). 20 | 21 | ## Running end-to-end tests 22 | 23 | Run `ng e2e` to execute the end-to-end tests via [Protractor](http://www.protractortest.org/). 24 | 25 | ## Further help 26 | 27 | To get more help on the Angular CLI use `ng help` or go check out the [Angular CLI README](https://github.com/angular/angular-cli/blob/master/README.md). 28 | -------------------------------------------------------------------------------- /Angular/angular-app/angular.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "./node_modules/@angular/cli/lib/config/schema.json", 3 | "version": 1, 4 | "newProjectRoot": "projects", 5 | "projects": { 6 | "angular-app": { 7 | "projectType": "application", 8 | "schematics": {}, 9 | "root": "", 10 | "sourceRoot": "src", 11 | "prefix": "app", 12 | "architect": { 13 | "build": { 14 | "builder": "@angular-devkit/build-angular:browser", 15 | "options": { 16 | "outputPath": "dist/angular-app", 17 | "index": "src/index.html", 18 | "main": "src/main.ts", 19 | "polyfills": "src/polyfills.ts", 20 | "tsConfig": "tsconfig.app.json", 21 | "aot": true, 22 | "assets": [ 23 | "src/favicon.ico", 24 | "src/assets" 25 | ], 26 | "styles": [ 27 | "src/styles.css" 28 | ], 29 | "scripts": [] 30 | }, 31 | "configurations": { 32 | "production": { 33 | "fileReplacements": [ 34 | { 35 | "replace": "src/environments/environment.ts", 36 | "with": "src/environments/environment.prod.ts" 37 | } 38 | ], 39 | "optimization": true, 40 | "outputHashing": "all", 41 | "sourceMap": false, 42 | "extractCss": true, 43 | "namedChunks": false, 44 | "extractLicenses": true, 45 | "vendorChunk": false, 46 | "buildOptimizer": true, 47 | "budgets": [ 48 | { 49 | "type": "initial", 50 | "maximumWarning": "2mb", 51 | "maximumError": "5mb" 52 | }, 53 | { 54 | "type": "anyComponentStyle", 55 | "maximumWarning": "6kb", 56 | "maximumError": "10kb" 57 | } 58 | ] 59 | } 60 | } 61 | }, 62 | "serve": { 63 | "builder": "@angular-devkit/build-angular:dev-server", 64 | "options": { 65 | "browserTarget": "angular-app:build" 66 | }, 67 | "configurations": { 68 | "production": { 69 | "browserTarget": "angular-app:build:production" 70 | } 71 | } 72 | }, 73 | "extract-i18n": { 74 | "builder": "@angular-devkit/build-angular:extract-i18n", 75 | "options": { 76 | "browserTarget": "angular-app:build" 77 | } 78 | }, 79 | "test": { 80 | "builder": "@angular-devkit/build-angular:karma", 81 | "options": { 82 | "main": "src/test.ts", 83 | "polyfills": "src/polyfills.ts", 84 | "tsConfig": "tsconfig.spec.json", 85 | "karmaConfig": "karma.conf.js", 86 | "assets": [ 87 | "src/favicon.ico", 88 | "src/assets" 89 | ], 90 | "styles": [ 91 | "src/styles.css" 92 | ], 93 | "scripts": [] 94 | } 95 | }, 96 | "lint": { 97 | "builder": "@angular-devkit/build-angular:tslint", 98 | "options": { 99 | "tsConfig": [ 100 | "tsconfig.app.json", 101 | "tsconfig.spec.json", 102 | "e2e/tsconfig.json" 103 | ], 104 | "exclude": [ 105 | "**/node_modules/**" 106 | ] 107 | } 108 | }, 109 | "e2e": { 110 | "builder": "@angular-devkit/build-angular:protractor", 111 | "options": { 112 | "protractorConfig": "e2e/protractor.conf.js", 113 | "devServerTarget": "angular-app:serve" 114 | }, 115 | "configurations": { 116 | "production": { 117 | "devServerTarget": "angular-app:serve:production" 118 | } 119 | } 120 | } 121 | } 122 | }}, 123 | "defaultProject": "angular-app" 124 | } 125 | -------------------------------------------------------------------------------- /Angular/angular-app/browserslist: -------------------------------------------------------------------------------- 1 | # This file is used by the build system to adjust CSS and JS output to support the specified browsers below. 2 | # For additional information regarding the format and rule options, please see: 3 | # https://github.com/browserslist/browserslist#queries 4 | 5 | # You can see what browsers were selected by your queries by running: 6 | # npx browserslist 7 | 8 | > 0.5% 9 | last 2 versions 10 | Firefox ESR 11 | not dead 12 | not IE 9-11 # For IE 9-11 support, remove 'not'. -------------------------------------------------------------------------------- /Angular/angular-app/e2e/protractor.conf.js: -------------------------------------------------------------------------------- 1 | // @ts-check 2 | // Protractor configuration file, see link for more information 3 | // https://github.com/angular/protractor/blob/master/lib/config.ts 4 | 5 | const { SpecReporter } = require('jasmine-spec-reporter'); 6 | 7 | /** 8 | * @type { import("protractor").Config } 9 | */ 10 | exports.config = { 11 | allScriptsTimeout: 11000, 12 | specs: [ 13 | './src/**/*.e2e-spec.ts' 14 | ], 15 | capabilities: { 16 | browserName: 'chrome' 17 | }, 18 | directConnect: true, 19 | baseUrl: 'http://localhost:4200/', 20 | framework: 'jasmine', 21 | jasmineNodeOpts: { 22 | showColors: true, 23 | defaultTimeoutInterval: 30000, 24 | print: function() {} 25 | }, 26 | onPrepare() { 27 | require('ts-node').register({ 28 | project: require('path').join(__dirname, './tsconfig.json') 29 | }); 30 | jasmine.getEnv().addReporter(new SpecReporter({ spec: { displayStacktrace: true } })); 31 | } 32 | }; -------------------------------------------------------------------------------- /Angular/angular-app/e2e/src/app.e2e-spec.ts: -------------------------------------------------------------------------------- 1 | import { AppPage } from './app.po'; 2 | import { browser, logging } from 'protractor'; 3 | 4 | describe('workspace-project App', () => { 5 | let page: AppPage; 6 | 7 | beforeEach(() => { 8 | page = new AppPage(); 9 | }); 10 | 11 | it('should display welcome message', () => { 12 | page.navigateTo(); 13 | expect(page.getTitleText()).toEqual('angular-app app is running!'); 14 | }); 15 | 16 | afterEach(async () => { 17 | // Assert that there are no errors emitted from the browser 18 | const logs = await browser.manage().logs().get(logging.Type.BROWSER); 19 | expect(logs).not.toContain(jasmine.objectContaining({ 20 | level: logging.Level.SEVERE, 21 | } as logging.Entry)); 22 | }); 23 | }); 24 | -------------------------------------------------------------------------------- /Angular/angular-app/e2e/src/app.po.ts: -------------------------------------------------------------------------------- 1 | import { browser, by, element } from 'protractor'; 2 | 3 | export class AppPage { 4 | navigateTo(): Promise { 5 | return browser.get(browser.baseUrl) as Promise; 6 | } 7 | 8 | getTitleText(): Promise { 9 | return element(by.css('app-root .content span')).getText() as Promise; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Angular/angular-app/e2e/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "../tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "../out-tsc/e2e", 5 | "module": "commonjs", 6 | "target": "es5", 7 | "types": [ 8 | "jasmine", 9 | "jasminewd2", 10 | "node" 11 | ] 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Angular/angular-app/karma.conf.js: -------------------------------------------------------------------------------- 1 | // Karma configuration file, see link for more information 2 | // https://karma-runner.github.io/1.0/config/configuration-file.html 3 | 4 | module.exports = function (config) { 5 | config.set({ 6 | basePath: '', 7 | frameworks: ['jasmine', '@angular-devkit/build-angular'], 8 | plugins: [ 9 | require('karma-jasmine'), 10 | require('karma-chrome-launcher'), 11 | require('karma-jasmine-html-reporter'), 12 | require('karma-coverage-istanbul-reporter'), 13 | require('@angular-devkit/build-angular/plugins/karma') 14 | ], 15 | client: { 16 | clearContext: false // leave Jasmine Spec Runner output visible in browser 17 | }, 18 | coverageIstanbulReporter: { 19 | dir: require('path').join(__dirname, './coverage/angular-app'), 20 | reports: ['html', 'lcovonly', 'text-summary'], 21 | fixWebpackSourcePaths: true 22 | }, 23 | reporters: ['progress', 'kjhtml'], 24 | port: 9876, 25 | colors: true, 26 | logLevel: config.LOG_INFO, 27 | autoWatch: true, 28 | browsers: ['Chrome'], 29 | singleRun: false, 30 | restartOnFileChange: true 31 | }); 32 | }; 33 | -------------------------------------------------------------------------------- /Angular/angular-app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "angular-app", 3 | "version": "0.0.0", 4 | "scripts": { 5 | "ng": "ng", 6 | "start": "ng serve", 7 | "build": "ng build", 8 | "test": "ng test", 9 | "lint": "ng lint", 10 | "e2e": "ng e2e" 11 | }, 12 | "private": true, 13 | "dependencies": { 14 | "@angular/animations": "~9.1.9", 15 | "@angular/common": "~9.1.9", 16 | "@angular/compiler": "~9.1.9", 17 | "@angular/core": "~9.1.9", 18 | "@angular/forms": "~9.1.9", 19 | "@angular/platform-browser": "~9.1.9", 20 | "@angular/platform-browser-dynamic": "~9.1.9", 21 | "@angular/router": "~9.1.9", 22 | "angular-auth-oidc-client": "^11.1.1", 23 | "rxjs": "~6.5.4", 24 | "tslib": "^1.10.0", 25 | "zone.js": "~0.10.2" 26 | }, 27 | "devDependencies": { 28 | "@angular-devkit/build-angular": "~0.901.7", 29 | "@angular/cli": "~9.1.7", 30 | "@angular/compiler-cli": "~9.1.9", 31 | "@types/node": "^12.11.1", 32 | "@types/jasmine": "~3.5.0", 33 | "@types/jasminewd2": "~2.0.3", 34 | "codelyzer": "^5.1.2", 35 | "jasmine-core": "~3.5.0", 36 | "jasmine-spec-reporter": "~4.2.1", 37 | "karma": "~5.0.0", 38 | "karma-chrome-launcher": "~3.1.0", 39 | "karma-coverage-istanbul-reporter": "~2.1.0", 40 | "karma-jasmine": "~3.0.1", 41 | "karma-jasmine-html-reporter": "^1.4.2", 42 | "protractor": "~7.0.0", 43 | "ts-node": "~8.3.0", 44 | "tslint": "~6.1.0", 45 | "typescript": "~3.8.3" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Angular/angular-app/src/app/app-routing.module.ts: -------------------------------------------------------------------------------- 1 | import { NgModule } from '@angular/core'; 2 | import { Routes, RouterModule } from '@angular/router'; 3 | 4 | 5 | const routes: Routes = []; 6 | 7 | @NgModule({ 8 | imports: [RouterModule.forRoot(routes)], 9 | exports: [RouterModule] 10 | }) 11 | export class AppRoutingModule { } 12 | -------------------------------------------------------------------------------- /Angular/angular-app/src/app/app.component.css: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nirzaf/Authentication/97cc5e0e01fd9c087cf9f09b6b15b2e207e8bfd9/Angular/angular-app/src/app/app.component.css -------------------------------------------------------------------------------- /Angular/angular-app/src/app/app.component.html: -------------------------------------------------------------------------------- 1 |
2 | 3 | 4 |
5 | -------------------------------------------------------------------------------- /Angular/angular-app/src/app/app.component.spec.ts: -------------------------------------------------------------------------------- 1 | import { TestBed, async } from '@angular/core/testing'; 2 | import { RouterTestingModule } from '@angular/router/testing'; 3 | import { AppComponent } from './app.component'; 4 | 5 | describe('AppComponent', () => { 6 | beforeEach(async(() => { 7 | TestBed.configureTestingModule({ 8 | imports: [ 9 | RouterTestingModule 10 | ], 11 | declarations: [ 12 | AppComponent 13 | ], 14 | }).compileComponents(); 15 | })); 16 | 17 | it('should create the app', () => { 18 | const fixture = TestBed.createComponent(AppComponent); 19 | const app = fixture.componentInstance; 20 | expect(app).toBeTruthy(); 21 | }); 22 | 23 | it(`should have as title 'angular-app'`, () => { 24 | const fixture = TestBed.createComponent(AppComponent); 25 | const app = fixture.componentInstance; 26 | expect(app.title).toEqual('angular-app'); 27 | }); 28 | 29 | it('should render title', () => { 30 | const fixture = TestBed.createComponent(AppComponent); 31 | fixture.detectChanges(); 32 | const compiled = fixture.nativeElement; 33 | expect(compiled.querySelector('.content span').textContent).toContain('angular-app app is running!'); 34 | }); 35 | }); 36 | -------------------------------------------------------------------------------- /Angular/angular-app/src/app/app.component.ts: -------------------------------------------------------------------------------- 1 | import { Component } from '@angular/core'; 2 | import { OidcSecurityService } from 'angular-auth-oidc-client'; 3 | import { HttpClient, HttpHeaders } from '@angular/common/http'; 4 | 5 | @Component({ 6 | selector: 'app-root', 7 | templateUrl: './app.component.html', 8 | styleUrls: ['./app.component.css'] 9 | }) 10 | export class AppComponent { 11 | 12 | constructor( 13 | public oidcSecurityService: OidcSecurityService, 14 | public http: HttpClient) { } 15 | 16 | ngOnInit() { 17 | this.oidcSecurityService 18 | .checkAuth() 19 | .subscribe((auth) => console.log('is authenticated', auth)); 20 | } 21 | 22 | login() { 23 | this.oidcSecurityService.authorize(); 24 | } 25 | 26 | callApi() { 27 | const token = this.oidcSecurityService.getToken(); 28 | 29 | this.http.get("http://localhost:5002/secret", { 30 | headers: new HttpHeaders({ 31 | Authorization: 'Bearer ' + token, 32 | }), 33 | responseType: 'text' 34 | }) 35 | .subscribe((data: any) => { 36 | console.log("api result:", data); 37 | }); 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Angular/angular-app/src/app/app.module.ts: -------------------------------------------------------------------------------- 1 | import { BrowserModule } from '@angular/platform-browser'; 2 | import { APP_INITIALIZER, NgModule } from '@angular/core'; 3 | 4 | import { AppRoutingModule } from './app-routing.module'; 5 | import { AppComponent } from './app.component'; 6 | import { AuthModule, LogLevel, OidcConfigService } from 'angular-auth-oidc-client'; 7 | import { HttpClientModule } from '@angular/common/http'; 8 | 9 | export function configureAuth(oidcConfigService: OidcConfigService) { 10 | return () => 11 | oidcConfigService.withConfig({ 12 | clientId: 'angular', 13 | stsServer: 'http://localhost:5000', 14 | responseType: 'code', 15 | redirectUrl: window.location.origin, 16 | postLogoutRedirectUri: window.location.origin, 17 | scope: 'openid ApiOne', 18 | logLevel: LogLevel.Debug, 19 | }); 20 | } 21 | 22 | @NgModule({ 23 | declarations: [ 24 | AppComponent 25 | ], 26 | imports: [ 27 | BrowserModule, 28 | AppRoutingModule, 29 | AuthModule.forRoot(), 30 | HttpClientModule 31 | ], 32 | providers: [ 33 | OidcConfigService, 34 | { 35 | provide: APP_INITIALIZER, 36 | useFactory: configureAuth, 37 | deps: [OidcConfigService], 38 | multi: true, 39 | }, 40 | ], 41 | bootstrap: [AppComponent] 42 | }) 43 | export class AppModule { } 44 | -------------------------------------------------------------------------------- /Angular/angular-app/src/assets/.gitkeep: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nirzaf/Authentication/97cc5e0e01fd9c087cf9f09b6b15b2e207e8bfd9/Angular/angular-app/src/assets/.gitkeep -------------------------------------------------------------------------------- /Angular/angular-app/src/environments/environment.prod.ts: -------------------------------------------------------------------------------- 1 | export const environment = { 2 | production: true 3 | }; 4 | -------------------------------------------------------------------------------- /Angular/angular-app/src/environments/environment.ts: -------------------------------------------------------------------------------- 1 | // This file can be replaced during build by using the `fileReplacements` array. 2 | // `ng build --prod` replaces `environment.ts` with `environment.prod.ts`. 3 | // The list of file replacements can be found in `angular.json`. 4 | 5 | export const environment = { 6 | production: false 7 | }; 8 | 9 | /* 10 | * For easier debugging in development mode, you can import the following file 11 | * to ignore zone related error stack frames such as `zone.run`, `zoneDelegate.invokeTask`. 12 | * 13 | * This import should be commented out in production mode because it will have a negative impact 14 | * on performance if an error is thrown. 15 | */ 16 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI. 17 | -------------------------------------------------------------------------------- /Angular/angular-app/src/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nirzaf/Authentication/97cc5e0e01fd9c087cf9f09b6b15b2e207e8bfd9/Angular/angular-app/src/favicon.ico -------------------------------------------------------------------------------- /Angular/angular-app/src/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | AngularApp 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /Angular/angular-app/src/main.ts: -------------------------------------------------------------------------------- 1 | import { enableProdMode } from '@angular/core'; 2 | import { platformBrowserDynamic } from '@angular/platform-browser-dynamic'; 3 | 4 | import { AppModule } from './app/app.module'; 5 | import { environment } from './environments/environment'; 6 | 7 | if (environment.production) { 8 | enableProdMode(); 9 | } 10 | 11 | platformBrowserDynamic().bootstrapModule(AppModule) 12 | .catch(err => console.error(err)); 13 | -------------------------------------------------------------------------------- /Angular/angular-app/src/polyfills.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * This file includes polyfills needed by Angular and is loaded before the app. 3 | * You can add your own extra polyfills to this file. 4 | * 5 | * This file is divided into 2 sections: 6 | * 1. Browser polyfills. These are applied before loading ZoneJS and are sorted by browsers. 7 | * 2. Application imports. Files imported after ZoneJS that should be loaded before your main 8 | * file. 9 | * 10 | * The current setup is for so-called "evergreen" browsers; the last versions of browsers that 11 | * automatically update themselves. This includes Safari >= 10, Chrome >= 55 (including Opera), 12 | * Edge >= 13 on the desktop, and iOS 10 and Chrome on mobile. 13 | * 14 | * Learn more in https://angular.io/guide/browser-support 15 | */ 16 | 17 | /*************************************************************************************************** 18 | * BROWSER POLYFILLS 19 | */ 20 | 21 | /** IE10 and IE11 requires the following for NgClass support on SVG elements */ 22 | // import 'classlist.js'; // Run `npm install --save classlist.js`. 23 | 24 | /** 25 | * Web Animations `@angular/platform-browser/animations` 26 | * Only required if AnimationBuilder is used within the application and using IE/Edge or Safari. 27 | * Standard animation support in Angular DOES NOT require any polyfills (as of Angular 6.0). 28 | */ 29 | // import 'web-animations-js'; // Run `npm install --save web-animations-js`. 30 | 31 | /** 32 | * By default, zone.js will patch all possible macroTask and DomEvents 33 | * user can disable parts of macroTask/DomEvents patch by setting following flags 34 | * because those flags need to be set before `zone.js` being loaded, and webpack 35 | * will put import in the top of bundle, so user need to create a separate file 36 | * in this directory (for example: zone-flags.ts), and put the following flags 37 | * into that file, and then add the following code before importing zone.js. 38 | * import './zone-flags'; 39 | * 40 | * The flags allowed in zone-flags.ts are listed here. 41 | * 42 | * The following flags will work for all browsers. 43 | * 44 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame 45 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick 46 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames 47 | * 48 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js 49 | * with the following flag, it will bypass `zone.js` patch for IE/Edge 50 | * 51 | * (window as any).__Zone_enable_cross_context_check = true; 52 | * 53 | */ 54 | 55 | /*************************************************************************************************** 56 | * Zone JS is required by default for Angular itself. 57 | */ 58 | import 'zone.js/dist/zone'; // Included with Angular CLI. 59 | 60 | 61 | /*************************************************************************************************** 62 | * APPLICATION IMPORTS 63 | */ 64 | -------------------------------------------------------------------------------- /Angular/angular-app/src/styles.css: -------------------------------------------------------------------------------- 1 | /* You can add global styles to this file, and also import other style files */ 2 | -------------------------------------------------------------------------------- /Angular/angular-app/src/test.ts: -------------------------------------------------------------------------------- 1 | // This file is required by karma.conf.js and loads recursively all the .spec and framework files 2 | 3 | import 'zone.js/dist/zone-testing'; 4 | import { getTestBed } from '@angular/core/testing'; 5 | import { 6 | BrowserDynamicTestingModule, 7 | platformBrowserDynamicTesting 8 | } from '@angular/platform-browser-dynamic/testing'; 9 | 10 | declare const require: { 11 | context(path: string, deep?: boolean, filter?: RegExp): { 12 | keys(): string[]; 13 | (id: string): T; 14 | }; 15 | }; 16 | 17 | // First, initialize the Angular testing environment. 18 | getTestBed().initTestEnvironment( 19 | BrowserDynamicTestingModule, 20 | platformBrowserDynamicTesting() 21 | ); 22 | // Then we find all the tests. 23 | const context = require.context('./', true, /\.spec\.ts$/); 24 | // And load the modules. 25 | context.keys().map(context); 26 | -------------------------------------------------------------------------------- /Angular/angular-app/tsconfig.app.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/app", 5 | "types": [] 6 | }, 7 | "files": [ 8 | "src/main.ts", 9 | "src/polyfills.ts" 10 | ], 11 | "include": [ 12 | "src/**/*.d.ts" 13 | ] 14 | } 15 | -------------------------------------------------------------------------------- /Angular/angular-app/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": false, 3 | "compilerOptions": { 4 | "baseUrl": "./", 5 | "outDir": "./dist/out-tsc", 6 | "sourceMap": true, 7 | "declaration": false, 8 | "downlevelIteration": true, 9 | "experimentalDecorators": true, 10 | "module": "esnext", 11 | "moduleResolution": "node", 12 | "importHelpers": true, 13 | "target": "es2015", 14 | "lib": [ 15 | "es2018", 16 | "dom" 17 | ] 18 | }, 19 | "angularCompilerOptions": { 20 | "fullTemplateTypeCheck": true, 21 | "strictInjectionParameters": true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Angular/angular-app/tsconfig.spec.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig.json", 3 | "compilerOptions": { 4 | "outDir": "./out-tsc/spec", 5 | "types": [ 6 | "jasmine", 7 | "node" 8 | ] 9 | }, 10 | "files": [ 11 | "src/test.ts", 12 | "src/polyfills.ts" 13 | ], 14 | "include": [ 15 | "src/**/*.spec.ts", 16 | "src/**/*.d.ts" 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /Angular/angular-app/tslint.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "tslint:recommended", 3 | "rules": { 4 | "align": { 5 | "options": [ 6 | "parameters", 7 | "statements" 8 | ] 9 | }, 10 | "array-type": false, 11 | "arrow-return-shorthand": true, 12 | "curly": true, 13 | "deprecation": { 14 | "severity": "warning" 15 | }, 16 | "component-class-suffix": true, 17 | "contextual-lifecycle": true, 18 | "directive-class-suffix": true, 19 | "directive-selector": [ 20 | true, 21 | "attribute", 22 | "app", 23 | "camelCase" 24 | ], 25 | "component-selector": [ 26 | true, 27 | "element", 28 | "app", 29 | "kebab-case" 30 | ], 31 | "eofline": true, 32 | "import-blacklist": [ 33 | true, 34 | "rxjs/Rx" 35 | ], 36 | "import-spacing": true, 37 | "indent": { 38 | "options": [ 39 | "spaces" 40 | ] 41 | }, 42 | "max-classes-per-file": false, 43 | "max-line-length": [ 44 | true, 45 | 140 46 | ], 47 | "member-ordering": [ 48 | true, 49 | { 50 | "order": [ 51 | "static-field", 52 | "instance-field", 53 | "static-method", 54 | "instance-method" 55 | ] 56 | } 57 | ], 58 | "no-console": [ 59 | true, 60 | "debug", 61 | "info", 62 | "time", 63 | "timeEnd", 64 | "trace" 65 | ], 66 | "no-empty": false, 67 | "no-inferrable-types": [ 68 | true, 69 | "ignore-params" 70 | ], 71 | "no-non-null-assertion": true, 72 | "no-redundant-jsdoc": true, 73 | "no-switch-case-fall-through": true, 74 | "no-var-requires": false, 75 | "object-literal-key-quotes": [ 76 | true, 77 | "as-needed" 78 | ], 79 | "quotemark": [ 80 | true, 81 | "single" 82 | ], 83 | "semicolon": { 84 | "options": [ 85 | "always" 86 | ] 87 | }, 88 | "space-before-function-paren": { 89 | "options": { 90 | "anonymous": "never", 91 | "asyncArrow": "always", 92 | "constructor": "never", 93 | "method": "never", 94 | "named": "never" 95 | } 96 | }, 97 | "typedef-whitespace": { 98 | "options": [ 99 | { 100 | "call-signature": "nospace", 101 | "index-signature": "nospace", 102 | "parameter": "nospace", 103 | "property-declaration": "nospace", 104 | "variable-declaration": "nospace" 105 | }, 106 | { 107 | "call-signature": "onespace", 108 | "index-signature": "onespace", 109 | "parameter": "onespace", 110 | "property-declaration": "onespace", 111 | "variable-declaration": "onespace" 112 | } 113 | ] 114 | }, 115 | "variable-name": { 116 | "options": [ 117 | "ban-keywords", 118 | "check-format", 119 | "allow-pascal-case" 120 | ] 121 | }, 122 | "whitespace": { 123 | "options": [ 124 | "check-branch", 125 | "check-decl", 126 | "check-operator", 127 | "check-separator", 128 | "check-type", 129 | "check-typecast" 130 | ] 131 | }, 132 | "no-conflicting-lifecycle": true, 133 | "no-host-metadata-property": true, 134 | "no-input-rename": true, 135 | "no-inputs-metadata-property": true, 136 | "no-output-native": true, 137 | "no-output-on-prefix": true, 138 | "no-output-rename": true, 139 | "no-outputs-metadata-property": true, 140 | "template-banana-in-box": true, 141 | "template-no-negated-async": true, 142 | "use-lifecycle-interface": true, 143 | "use-pipe-transform-interface": true 144 | }, 145 | "rulesDirectory": [ 146 | "codelyzer" 147 | ] 148 | } -------------------------------------------------------------------------------- /Angular/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "requires": true, 3 | "lockfileVersion": 1, 4 | "dependencies": { 5 | "angular-auth-oidc-client": { 6 | "version": "11.1.1", 7 | "resolved": "https://registry.npmjs.org/angular-auth-oidc-client/-/angular-auth-oidc-client-11.1.1.tgz", 8 | "integrity": "sha512-CXKIyjdDCmP/w9p9tSMdyrZ+8sxRQcnwRsqi544GWVQKEoWZqaRNjsdVoOVrqjcHOMYDkHzwt+gROLHg8qnWgw==", 9 | "requires": { 10 | "common-tags": "^1.8.0", 11 | "jsrsasign-reduced": "^8.0.15" 12 | } 13 | }, 14 | "common-tags": { 15 | "version": "1.8.0", 16 | "resolved": "https://registry.npmjs.org/common-tags/-/common-tags-1.8.0.tgz", 17 | "integrity": "sha512-6P6g0uetGpW/sdyUy/iQQCbFF0kWVMSIVSyYz7Zgjcgh8mgw8PQzDNZeyZ5DQ2gM7LBoZPHmnjz8rUthkBG5tw==" 18 | }, 19 | "jsrsasign-reduced": { 20 | "version": "8.0.15", 21 | "resolved": "https://registry.npmjs.org/jsrsasign-reduced/-/jsrsasign-reduced-8.0.15.tgz", 22 | "integrity": "sha512-Ig4W69nXCIUedzOSk3nqJWUr2DmSDENYfsmCqVK33GPETtPcjwREGQc92hV5jcJ6zavMvGD4tjhZ+T7JIiaSLA==" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Api/Api.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Api/AuthRequirement/JwtRequirement.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Http; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace Api.AuthRequirement 7 | { 8 | public class JwtRequirement : IAuthorizationRequirement { } 9 | 10 | public class JwtRequirementHandler : AuthorizationHandler 11 | { 12 | private readonly HttpClient _client; 13 | private readonly HttpContext _httpContext; 14 | 15 | public JwtRequirementHandler( 16 | IHttpClientFactory httpClientFactory, 17 | IHttpContextAccessor httpContextAccessor) 18 | { 19 | _client = httpClientFactory.CreateClient(); 20 | _httpContext = httpContextAccessor.HttpContext; 21 | } 22 | 23 | protected override async Task HandleRequirementAsync( 24 | AuthorizationHandlerContext context, 25 | JwtRequirement requirement) 26 | { 27 | if (_httpContext.Request.Headers.TryGetValue("Authorization", out var authHeader)) 28 | { 29 | var accessToken = authHeader.ToString().Split(' ')[1]; 30 | 31 | var response = await _client 32 | .GetAsync($"https://localhost:44382/oauth/validate?access_token={accessToken}"); 33 | 34 | if(response.StatusCode == System.Net.HttpStatusCode.OK) 35 | { 36 | context.Succeed(requirement); 37 | } 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Api/Controllers/SecretController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace Api.Controllers 5 | { 6 | public class SecretController : Controller 7 | { 8 | [Authorize] 9 | public string Index() 10 | { 11 | return "secret message"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Api/CustomeAuthenticationHandler.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.Extensions.Logging; 3 | using Microsoft.Extensions.Options; 4 | using System.Text.Encodings.Web; 5 | using System.Threading.Tasks; 6 | 7 | namespace Api 8 | { 9 | public class CustomeAuthenticationHandler : AuthenticationHandler 10 | { 11 | public CustomeAuthenticationHandler( 12 | IOptionsMonitor options, 13 | ILoggerFactory logger, 14 | UrlEncoder encoder, 15 | ISystemClock clock) : base(options, logger, encoder, clock) { } 16 | 17 | protected override Task HandleAuthenticateAsync() 18 | { 19 | return Task.FromResult(AuthenticateResult.Fail("Failed Authentication")); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Api/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Api 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Api/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:54609", 7 | "sslPort": 44332 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Api": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Api/Startup.cs: -------------------------------------------------------------------------------- 1 | using Api.AuthRequirement; 2 | using Microsoft.AspNetCore.Authentication; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Builder; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | 9 | namespace Api 10 | { 11 | public class Startup 12 | { 13 | public void ConfigureServices(IServiceCollection services) 14 | { 15 | services.AddAuthentication("DefaultAuth") 16 | .AddScheme("DefaultAuth", null); 17 | 18 | services.AddAuthorization(config => 19 | { 20 | var defaultAuthBuilder = new AuthorizationPolicyBuilder(); 21 | var defaultAuthPolicy = defaultAuthBuilder 22 | .AddRequirements(new JwtRequirement()) 23 | .Build(); 24 | 25 | config.DefaultPolicy = defaultAuthPolicy; 26 | }); 27 | 28 | services.AddScoped(); 29 | 30 | services.AddHttpClient() 31 | .AddHttpContextAccessor(); 32 | 33 | services.AddControllers(); 34 | } 35 | 36 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 37 | { 38 | if (env.IsDevelopment()) 39 | { 40 | app.UseDeveloperExceptionPage(); 41 | } 42 | 43 | app.UseRouting(); 44 | 45 | app.UseAuthentication(); 46 | 47 | app.UseAuthorization(); 48 | 49 | app.UseEndpoints(endpoints => 50 | { 51 | endpoints.MapDefaultControllerRoute(); 52 | }); 53 | } 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /Api/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Api/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /ApiOne/ApiOne.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ApiOne/Controllers/SecretController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System.Linq; 4 | 5 | namespace ApiOne.Controllers 6 | { 7 | public class SecretController : Controller 8 | { 9 | [Route("/secret")] 10 | [Authorize] 11 | public string Index() 12 | { 13 | var claims = User.Claims.ToList(); 14 | return "secret message from ApiOne"; 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /ApiOne/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace ApiOne 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ApiOne/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59871", 7 | "sslPort": 44337 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "ApiOne": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5003;http://localhost:5002", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ApiOne/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Builder; 2 | using Microsoft.AspNetCore.Hosting; 3 | using Microsoft.Extensions.DependencyInjection; 4 | using Microsoft.Extensions.Hosting; 5 | 6 | namespace ApiOne 7 | { 8 | public class Startup 9 | { 10 | public void ConfigureServices(IServiceCollection services) 11 | { 12 | services.AddAuthentication("Bearer") 13 | .AddJwtBearer("Bearer", config => 14 | { 15 | config.Authority = "http://localhost:5000/"; 16 | 17 | config.Audience = "ApiOne"; 18 | 19 | config.RequireHttpsMetadata = false; 20 | }); 21 | 22 | services.AddCors(confg => 23 | confg.AddPolicy("AllowAll", 24 | p => p.AllowAnyOrigin() 25 | .AllowAnyMethod() 26 | .AllowAnyHeader())); 27 | 28 | services.AddControllers(); 29 | } 30 | 31 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 32 | { 33 | if (env.IsDevelopment()) 34 | { 35 | app.UseDeveloperExceptionPage(); 36 | } 37 | 38 | app.UseCors("AllowAll"); 39 | 40 | app.UseRouting(); 41 | 42 | app.UseAuthentication(); 43 | 44 | app.UseAuthorization(); 45 | 46 | app.UseEndpoints(endpoints => 47 | { 48 | endpoints.MapControllers(); 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ApiOne/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ApiOne/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /ApiTwo/ApiTwo.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /ApiTwo/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using IdentityModel.Client; 2 | using Microsoft.AspNetCore.Mvc; 3 | using System.Net.Http; 4 | using System.Threading.Tasks; 5 | 6 | namespace ApiTwo.Controllers 7 | { 8 | public class HomeController : Controller 9 | { 10 | private readonly IHttpClientFactory _httpClientFactory; 11 | 12 | public HomeController(IHttpClientFactory httpClientFactory) 13 | { 14 | _httpClientFactory = httpClientFactory; 15 | } 16 | 17 | [Route("/home")] 18 | public async Task Index() 19 | { 20 | //retrieve access token 21 | var serverClient = _httpClientFactory.CreateClient(); 22 | 23 | var discoveryDocument = await serverClient.GetDiscoveryDocumentAsync("https://localhost:44305/"); 24 | 25 | var tokenResponse = await serverClient.RequestClientCredentialsTokenAsync( 26 | new ClientCredentialsTokenRequest 27 | { 28 | Address = discoveryDocument.TokenEndpoint, 29 | 30 | ClientId = "client_id", 31 | ClientSecret = "client_secret", 32 | 33 | Scope = "ApiOne", 34 | }); 35 | 36 | //retrieve secret data 37 | var apiClient = _httpClientFactory.CreateClient(); 38 | 39 | apiClient.SetBearerToken(tokenResponse.AccessToken); 40 | 41 | var response = await apiClient.GetAsync("https://localhost:44337/secret"); 42 | 43 | var content = await response.Content.ReadAsStringAsync(); 44 | 45 | return Ok(new 46 | { 47 | access_token = tokenResponse.AccessToken, 48 | message = content, 49 | }); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /ApiTwo/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace ApiTwo 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /ApiTwo/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59902", 7 | "sslPort": 44316 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "ApiTwo": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /ApiTwo/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | 11 | namespace ApiTwo 12 | { 13 | public class Startup 14 | { 15 | public void ConfigureServices(IServiceCollection services) 16 | { 17 | services.AddAuthentication("Bearer") 18 | .AddJwtBearer("Bearer", config => { 19 | config.Authority = "https://localhost:44305/"; 20 | 21 | config.Audience = "ApiTwo"; 22 | }); 23 | 24 | services.AddHttpClient(); 25 | 26 | services.AddControllers(); 27 | } 28 | 29 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 30 | { 31 | if (env.IsDevelopment()) 32 | { 33 | app.UseDeveloperExceptionPage(); 34 | } 35 | 36 | app.UseRouting(); 37 | 38 | app.UseAuthentication(); 39 | 40 | app.UseAuthorization(); 41 | 42 | app.UseEndpoints(endpoints => 43 | { 44 | endpoints.MapControllers(); 45 | }); 46 | } 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /ApiTwo/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /ApiTwo/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Basics/AuthorizationRequirements/CustomRequireClaim.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using System.Linq; 3 | using System.Threading.Tasks; 4 | 5 | namespace Basics.AuthorizationRequirements 6 | { 7 | public class CustomRequireClaim : IAuthorizationRequirement 8 | { 9 | public CustomRequireClaim(string claimType) 10 | { 11 | ClaimType = claimType; 12 | } 13 | 14 | public string ClaimType { get; } 15 | } 16 | 17 | public class CustomRequireClaimHandler : AuthorizationHandler 18 | { 19 | protected override Task HandleRequirementAsync( 20 | AuthorizationHandlerContext context, 21 | CustomRequireClaim requirement) 22 | { 23 | var hasClaim = context.User.Claims.Any(x => x.Type == requirement.ClaimType); 24 | if (hasClaim) 25 | { 26 | context.Succeed(requirement); 27 | } 28 | 29 | return Task.CompletedTask; 30 | } 31 | } 32 | 33 | public static class AuthorizationPolicyBuilderExtensions 34 | { 35 | public static AuthorizationPolicyBuilder RequireCustomClaim( 36 | this AuthorizationPolicyBuilder builder, 37 | string claimType) 38 | { 39 | builder.AddRequirements(new CustomRequireClaim(claimType)); 40 | return builder; 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Basics/Basics.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Basics/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Basics.CustomPolicyProvider; 2 | using Microsoft.AspNetCore.Authentication; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.Collections.Generic; 6 | using System.Security.Claims; 7 | using System.Threading.Tasks; 8 | 9 | namespace Basics.Controllers 10 | { 11 | public class HomeController : Controller 12 | { 13 | 14 | public IActionResult Index() 15 | { 16 | return View(); 17 | } 18 | 19 | [Authorize] 20 | public IActionResult Secret() 21 | { 22 | return View(); 23 | } 24 | 25 | [Authorize(Policy = "Claim.DoB")] 26 | public IActionResult SecretPolicy() 27 | { 28 | return View("Secret"); 29 | } 30 | 31 | [Authorize(Roles = "Admin")] 32 | public IActionResult SecretRole() 33 | { 34 | return View("Secret"); 35 | } 36 | 37 | [SecurityLevel(5)] 38 | public IActionResult SecretLevel() 39 | { 40 | return View("Secret"); 41 | } 42 | 43 | [SecurityLevel(10)] 44 | public IActionResult SecretHigherLevel() 45 | { 46 | return View("Secret"); 47 | } 48 | 49 | [AllowAnonymous] 50 | public IActionResult Authenticate() 51 | { 52 | var grandmaClaims = new List() 53 | { 54 | new Claim(ClaimTypes.Name, "Bob"), 55 | new Claim(ClaimTypes.Email, "Bob@fmail.com"), 56 | new Claim(ClaimTypes.DateOfBirth, "11/11/2000"), 57 | new Claim(ClaimTypes.Role, "Admin"), 58 | new Claim(ClaimTypes.Role, "AdminTwo"), 59 | new Claim(DynamicPolicies.SecurityLevel, "7"), 60 | new Claim("Grandma.Says", "Very nice boi."), 61 | }; 62 | 63 | var licenseClaims = new List() 64 | { 65 | new Claim(ClaimTypes.Name, "Bob K Foo"), 66 | new Claim("DrivingLicense", "A+"), 67 | }; 68 | 69 | var grandmaIdentity = new ClaimsIdentity(grandmaClaims, "Grandma Identity"); 70 | var licenseIdentity = new ClaimsIdentity(licenseClaims, "Government"); 71 | 72 | var userPrincipal = new ClaimsPrincipal(new[] { grandmaIdentity, licenseIdentity }); 73 | //----------------------------------------------------------- 74 | HttpContext.SignInAsync(userPrincipal); 75 | 76 | return RedirectToAction("Index"); 77 | } 78 | 79 | public async Task DoStuff( 80 | [FromServices] IAuthorizationService authorizationService) 81 | { 82 | // we are doing stuff here 83 | 84 | var builder = new AuthorizationPolicyBuilder("Schema"); 85 | var customPolicy = builder.RequireClaim("Hello").Build(); 86 | 87 | var authResult = await authorizationService.AuthorizeAsync(User, customPolicy); 88 | 89 | if (authResult.Succeeded) 90 | { 91 | return View("Index"); 92 | } 93 | 94 | return View("Index"); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Basics/Controllers/OperationsController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Authorization.Infrastructure; 3 | using Microsoft.AspNetCore.Mvc; 4 | using System.Threading.Tasks; 5 | 6 | namespace Basics.Controllers 7 | { 8 | public class OperationsController : Controller 9 | { 10 | private readonly IAuthorizationService _authorizationService; 11 | 12 | public OperationsController(IAuthorizationService authorizationService) 13 | { 14 | _authorizationService = authorizationService; 15 | } 16 | 17 | public async Task Open() 18 | { 19 | var cookieJar = new CookieJar(); // get cookie jar from db 20 | await _authorizationService.AuthorizeAsync(User, cookieJar, CookieJarAuthOperations.Open); 21 | return View(); 22 | } 23 | } 24 | 25 | public class CookieJarAuthorizationHandler 26 | : AuthorizationHandler 27 | { 28 | protected override Task HandleRequirementAsync( 29 | AuthorizationHandlerContext context, 30 | OperationAuthorizationRequirement requirement, 31 | CookieJar cookieJar) 32 | { 33 | if (requirement.Name == CookieJarOperations.Look) 34 | { 35 | if (context.User.Identity.IsAuthenticated) 36 | { 37 | context.Succeed(requirement); 38 | } 39 | } 40 | else if (requirement.Name == CookieJarOperations.ComeNear) 41 | { 42 | if (context.User.HasClaim("Friend", "Good")) 43 | { 44 | context.Succeed(requirement); 45 | } 46 | } 47 | 48 | return Task.CompletedTask; 49 | } 50 | } 51 | 52 | public static class CookieJarAuthOperations 53 | { 54 | public static OperationAuthorizationRequirement Open = new OperationAuthorizationRequirement 55 | { 56 | Name = CookieJarOperations.Open 57 | }; 58 | } 59 | 60 | public static class CookieJarOperations 61 | { 62 | public static string Open = "Open"; 63 | public static string TakeCookie = "TakeCookie"; 64 | public static string ComeNear = "ComeNear"; 65 | public static string Look = "Look"; 66 | } 67 | 68 | public class CookieJar 69 | { 70 | public string Name { get; set; } 71 | } 72 | } -------------------------------------------------------------------------------- /Basics/CustomPolicyProvider/CustomAuthorizationPolicyProvider.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.Extensions.Options; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | 8 | namespace Basics.CustomPolicyProvider 9 | { 10 | public class SecurityLevelAttribute : AuthorizeAttribute 11 | { 12 | public SecurityLevelAttribute(int level) 13 | { 14 | Policy = $"{DynamicPolicies.SecurityLevel}.{level}"; 15 | } 16 | } 17 | 18 | // {type} 19 | public static class DynamicPolicies 20 | { 21 | public static IEnumerable Get() 22 | { 23 | yield return SecurityLevel; 24 | yield return Rank; 25 | } 26 | 27 | public const string SecurityLevel = "SecurityLevel"; 28 | public const string Rank = "Rank"; 29 | } 30 | 31 | public static class DynamicAuthorizationPilicyFactory 32 | { 33 | public static AuthorizationPolicy Create(string policyName) 34 | { 35 | var parts = policyName.Split('.'); 36 | var type = parts.First(); 37 | var value = parts.Last(); 38 | 39 | switch (type) 40 | { 41 | case DynamicPolicies.Rank: 42 | return new AuthorizationPolicyBuilder() 43 | .RequireClaim("Rank", value) 44 | .Build(); 45 | case DynamicPolicies.SecurityLevel: 46 | return new AuthorizationPolicyBuilder() 47 | .AddRequirements(new SecurityLevelRequirement(Convert.ToInt32(value))) 48 | .Build(); 49 | default: 50 | return null; 51 | } 52 | } 53 | } 54 | 55 | public class SecurityLevelRequirement : IAuthorizationRequirement 56 | { 57 | public int Level { get; } 58 | public SecurityLevelRequirement(int level) 59 | { 60 | Level = level; 61 | } 62 | } 63 | 64 | public class SecurityLevelHandler : AuthorizationHandler 65 | { 66 | protected override Task HandleRequirementAsync( 67 | AuthorizationHandlerContext context, 68 | SecurityLevelRequirement requirement) 69 | { 70 | var claimValue = Convert.ToInt32(context.User.Claims 71 | .FirstOrDefault(x => x.Type == DynamicPolicies.SecurityLevel) 72 | ?.Value ?? "0"); 73 | 74 | if (requirement.Level <= claimValue) 75 | { 76 | context.Succeed(requirement); 77 | } 78 | return Task.CompletedTask; 79 | } 80 | } 81 | 82 | public class CustomAuthorizationPolicyProvider 83 | : DefaultAuthorizationPolicyProvider 84 | { 85 | public CustomAuthorizationPolicyProvider(IOptions options) : base(options) 86 | { 87 | } 88 | 89 | public override Task GetPolicyAsync(string policyName) 90 | { 91 | foreach (var customPolicy in DynamicPolicies.Get()) 92 | { 93 | if (policyName.StartsWith(customPolicy)) 94 | { 95 | var policy = DynamicAuthorizationPilicyFactory.Create(policyName); 96 | 97 | return Task.FromResult(policy); 98 | } 99 | } 100 | 101 | return base.GetPolicyAsync(policyName); 102 | } 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /Basics/Pages/Razor/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Basics.Pages.Razor.IndexModel 3 | @{ 4 | } 5 | 6 |

Index page, anone can visit this inside public folder.

-------------------------------------------------------------------------------- /Basics/Pages/Razor/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Basics.Pages.Razor 9 | { 10 | public class IndexModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Basics/Pages/Razor/Policy.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Basics.Pages.Razor.PolicyModel 3 | @{ 4 | } 5 | 6 |

Secured by a policy Admin

-------------------------------------------------------------------------------- /Basics/Pages/Razor/Policy.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Basics.Pages.Razor 9 | { 10 | public class PolicyModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Basics/Pages/Razor/Secured.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Basics.Pages.Razor.SecuredModel 3 | @{ 4 | } 5 | 6 |

This is a secured page.

-------------------------------------------------------------------------------- /Basics/Pages/Razor/Secured.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Basics.Pages.Razor 9 | { 10 | public class SecuredModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Basics/Pages/RazorSecured/Anon.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Basics.Pages.RazorSecured.AnonModel 3 | @{ 4 | } 5 | -------------------------------------------------------------------------------- /Basics/Pages/RazorSecured/Anon.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Basics.Pages.RazorSecured 9 | { 10 | public class AnonModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Basics/Pages/RazorSecured/Index.cshtml: -------------------------------------------------------------------------------- 1 | @page 2 | @model Basics.Pages.RazorSecured.IndexModel 3 | @{ 4 | } 5 | -------------------------------------------------------------------------------- /Basics/Pages/RazorSecured/Index.cshtml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Mvc; 6 | using Microsoft.AspNetCore.Mvc.RazorPages; 7 | 8 | namespace Basics.Pages.RazorSecured 9 | { 10 | public class IndexModel : PageModel 11 | { 12 | public void OnGet() 13 | { 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Basics/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Basics 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Basics/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:50690", 7 | "sslPort": 44315 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Basics": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Basics/Startup.cs: -------------------------------------------------------------------------------- 1 | using Basics.AuthorizationRequirements; 2 | using Basics.Controllers; 3 | using Basics.CustomPolicyProvider; 4 | using Basics.Transformer; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Builder; 8 | using Microsoft.AspNetCore.Hosting; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Hosting; 11 | using System.Security.Claims; 12 | 13 | namespace Basics 14 | { 15 | public class Startup 16 | { 17 | public void ConfigureServices(IServiceCollection services) 18 | { 19 | services.AddAuthentication("CookieAuth") 20 | .AddCookie("CookieAuth", config => 21 | { 22 | config.Cookie.Name = "Grandmas.Cookie"; 23 | config.LoginPath = "/Home/Authenticate"; 24 | }); 25 | 26 | services.AddAuthorization(config => 27 | { 28 | //var defaultAuthBuilder = new AuthorizationPolicyBuilder(); 29 | //var defaultAuthPolicy = defaultAuthBuilder 30 | // .RequireAuthenticatedUser() 31 | // .RequireClaim(ClaimTypes.DateOfBirth) 32 | // .Build(); 33 | 34 | //config.DefaultPolicy = defaultAuthPolicy; 35 | 36 | 37 | //config.AddPolicy("Claim.DoB", policyBuilder => 38 | //{ 39 | // policyBuilder.RequireClaim(ClaimTypes.DateOfBirth); 40 | //}); 41 | 42 | config.AddPolicy("Admin", policyBuilder => policyBuilder.RequireClaim(ClaimTypes.Role, "Admin")); 43 | 44 | config.AddPolicy("Claim.DoB", policyBuilder => 45 | { 46 | policyBuilder.RequireCustomClaim(ClaimTypes.DateOfBirth); 47 | }); 48 | }); 49 | 50 | services.AddSingleton(); 51 | services.AddScoped(); 52 | services.AddScoped(); 53 | services.AddScoped(); 54 | services.AddScoped(); 55 | 56 | services.AddControllersWithViews(config => 57 | { 58 | var defaultAuthBuilder = new AuthorizationPolicyBuilder(); 59 | var defaultAuthPolicy = defaultAuthBuilder 60 | .RequireAuthenticatedUser() 61 | .Build(); 62 | 63 | // global authorization filter 64 | //config.Filters.Add(new AuthorizeFilter(defaultAuthPolicy)); 65 | }); 66 | 67 | services.AddRazorPages() 68 | .AddRazorPagesOptions(config => 69 | { 70 | config.Conventions.AuthorizePage("/Razor/Secured"); 71 | config.Conventions.AuthorizePage("/Razor/Policy", "Admin"); 72 | config.Conventions.AuthorizeFolder("/RazorSecured"); 73 | config.Conventions.AllowAnonymousToPage("/RazorSecured/Anon"); 74 | }); 75 | } 76 | 77 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 78 | { 79 | if (env.IsDevelopment()) 80 | { 81 | app.UseDeveloperExceptionPage(); 82 | } 83 | 84 | app.UseRouting(); 85 | 86 | // who are you? 87 | app.UseAuthentication(); 88 | 89 | // are you allowed? 90 | app.UseAuthorization(); 91 | 92 | app.UseEndpoints(endpoints => 93 | { 94 | endpoints.MapDefaultControllerRoute(); 95 | endpoints.MapRazorPages(); 96 | }); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /Basics/Transformer/ClaimsTransformation.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using System.Linq; 3 | using System.Security.Claims; 4 | using System.Threading.Tasks; 5 | 6 | namespace Basics.Transformer 7 | { 8 | public class ClaimsTransformation : IClaimsTransformation 9 | { 10 | public Task TransformAsync(ClaimsPrincipal principal) 11 | { 12 | var hasFriendClaim = principal.Claims.Any(x => x.Type == "Friend"); 13 | 14 | if (!hasFriendClaim) 15 | { 16 | ((ClaimsIdentity)principal.Identity).AddClaim(new Claim("Friend", "Bad")); 17 | } 18 | 19 | return Task.FromResult(principal); 20 | } 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Basics/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 

Home Page

2 | 3 | @* Simple access quick checks *@ 4 | @if (User.Identity.IsAuthenticated) 5 | { 6 |

User is authenticated

7 | } 8 | else 9 | { 10 |

User is NOT authenticated

11 | } 12 | 13 | 14 | @* Big guns reusable functions *@ 15 | @using Microsoft.AspNetCore.Authorization 16 | @inject IAuthorizationService authorizationService 17 | 18 | @if ((await authorizationService.AuthorizeAsync(User, "Claim.DoB")).Succeeded) 19 | { 20 |

User has DoB Claim

21 | } -------------------------------------------------------------------------------- /Basics/Views/Home/Secret.cshtml: -------------------------------------------------------------------------------- 1 | 

Secret Page

-------------------------------------------------------------------------------- /Basics/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Basics/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /Client/Client.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Client/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using Microsoft.AspNetCore.Authorization; 3 | using Microsoft.AspNetCore.Mvc; 4 | using Newtonsoft.Json; 5 | using System; 6 | using System.Collections.Generic; 7 | using System.Net.Http; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Client.Controllers 12 | { 13 | public class HomeController : Controller 14 | { 15 | private readonly IHttpClientFactory _httpClientFactory; 16 | 17 | public HomeController(IHttpClientFactory httpClientFactory) 18 | { 19 | _httpClientFactory = httpClientFactory; 20 | } 21 | 22 | public IActionResult Index() 23 | { 24 | return View(); 25 | } 26 | 27 | [Authorize] 28 | public async Task Secret() 29 | { 30 | var serverResponse = await AccessTokenRefreshWrapper( 31 | () => SecuredGetRequest("https://localhost:44382/secret/index")); 32 | 33 | var apiResponse = await AccessTokenRefreshWrapper( 34 | () => SecuredGetRequest("https://localhost:44332/secret/index")); 35 | 36 | return View(); 37 | } 38 | 39 | private async Task SecuredGetRequest(string url) 40 | { 41 | var token = await HttpContext.GetTokenAsync("access_token"); 42 | var client = _httpClientFactory.CreateClient(); 43 | client.DefaultRequestHeaders.Add("Authorization", $"Bearer {token}"); 44 | return await client.GetAsync(url); 45 | } 46 | 47 | public async Task AccessTokenRefreshWrapper( 48 | Func> initialRequest) 49 | { 50 | var response = await initialRequest(); 51 | 52 | if (response.StatusCode == System.Net.HttpStatusCode.Unauthorized) 53 | { 54 | await RefreshAccessToken(); 55 | response = await initialRequest(); 56 | } 57 | 58 | return response; 59 | } 60 | 61 | private async Task RefreshAccessToken() 62 | { 63 | var refreshToken = await HttpContext.GetTokenAsync("refresh_token"); 64 | 65 | var refreshTokenClient = _httpClientFactory.CreateClient(); 66 | 67 | var requestData = new Dictionary 68 | { 69 | ["grant_type"] = "refresh_token", 70 | ["refresh_token"] = refreshToken 71 | }; 72 | 73 | var request = new HttpRequestMessage(HttpMethod.Post, "https://localhost:44382/oauth/token") 74 | { 75 | Content = new FormUrlEncodedContent(requestData) 76 | }; 77 | 78 | var basicCredentials = "username:password"; 79 | var encodedCredentials = Encoding.UTF8.GetBytes(basicCredentials); 80 | var base64Credentials = Convert.ToBase64String(encodedCredentials); 81 | 82 | request.Headers.Add("Authorization", $"Basic {base64Credentials}"); 83 | 84 | var response = await refreshTokenClient.SendAsync(request); 85 | 86 | var responseString = await response.Content.ReadAsStringAsync(); 87 | var responseData = JsonConvert.DeserializeObject>(responseString); 88 | 89 | var newAccessToken = responseData.GetValueOrDefault("access_token"); 90 | var newRefreshToken = responseData.GetValueOrDefault("refresh_token"); 91 | 92 | var authInfo = await HttpContext.AuthenticateAsync("ClientCookie"); 93 | 94 | authInfo.Properties.UpdateTokenValue("access_token", newAccessToken); 95 | authInfo.Properties.UpdateTokenValue("refresh_token", newRefreshToken); 96 | 97 | await HttpContext.SignInAsync("ClientCookie", authInfo.Principal, authInfo.Properties); 98 | } 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /Client/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Client 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Client/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:49459", 7 | "sslPort": 44342 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Client": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Client/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Security.Claims; 5 | using System.Text; 6 | using System.Text.Json.Serialization; 7 | using System.Threading.Tasks; 8 | using Microsoft.AspNetCore.Authentication.OAuth; 9 | using Microsoft.AspNetCore.Builder; 10 | using Microsoft.AspNetCore.Hosting; 11 | using Microsoft.AspNetCore.Http; 12 | using Microsoft.Extensions.DependencyInjection; 13 | using Microsoft.Extensions.Hosting; 14 | using Newtonsoft.Json; 15 | 16 | namespace Client 17 | { 18 | public class Startup 19 | { 20 | public void ConfigureServices(IServiceCollection services) 21 | { 22 | services.AddAuthentication(config => { 23 | // We check the cookie to confirm that we are authenticated 24 | config.DefaultAuthenticateScheme = "ClientCookie"; 25 | // When we sign in we will deal out a cookie 26 | config.DefaultSignInScheme = "ClientCookie"; 27 | // use this to check if we are allowed to do something. 28 | config.DefaultChallengeScheme = "OurServer"; 29 | }) 30 | .AddCookie("ClientCookie") 31 | .AddOAuth("OurServer", config => { 32 | config.ClientId = "client_id"; 33 | config.ClientSecret = "client_secret"; 34 | config.CallbackPath = "/oauth/callback"; 35 | config.AuthorizationEndpoint = "https://localhost:44382/oauth/authorize"; 36 | config.TokenEndpoint = "https://localhost:44382/oauth/token"; 37 | config.SaveTokens = true; 38 | 39 | config.Events = new OAuthEvents() 40 | { 41 | OnCreatingTicket = context => 42 | { 43 | var accessToken = context.AccessToken; 44 | var base64payload = accessToken.Split('.')[1]; 45 | var bytes = Convert.FromBase64String(base64payload); 46 | var jsonPayload = Encoding.UTF8.GetString(bytes); 47 | var claims = JsonConvert.DeserializeObject>(jsonPayload); 48 | 49 | foreach(var claim in claims) 50 | { 51 | context.Identity.AddClaim(new Claim(claim.Key, claim.Value)); 52 | } 53 | 54 | return Task.CompletedTask; 55 | } 56 | }; 57 | }); 58 | 59 | services.AddHttpClient(); 60 | 61 | services.AddControllersWithViews() 62 | .AddRazorRuntimeCompilation(); 63 | } 64 | 65 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 66 | { 67 | if (env.IsDevelopment()) 68 | { 69 | app.UseDeveloperExceptionPage(); 70 | } 71 | 72 | app.UseRouting(); 73 | 74 | app.UseAuthentication(); 75 | 76 | app.UseAuthorization(); 77 | 78 | app.UseEndpoints(endpoints => 79 | { 80 | endpoints.MapDefaultControllerRoute(); 81 | }); 82 | } 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /Client/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 

Client Home Page

2 | -------------------------------------------------------------------------------- /Client/Views/Home/Secret.cshtml: -------------------------------------------------------------------------------- 1 | 

Client Secret Page

-------------------------------------------------------------------------------- /Client/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Client/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /IdentityExample/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Identity; 3 | using Microsoft.AspNetCore.Mvc; 4 | using NETCore.MailKit.Core; 5 | using System.Threading.Tasks; 6 | 7 | namespace IdentityExample.Controllers 8 | { 9 | public class HomeController : Controller 10 | { 11 | private readonly UserManager _userManager; 12 | private readonly SignInManager _signInManager; 13 | private readonly IEmailService _emailService; 14 | 15 | public HomeController( 16 | UserManager userManager, 17 | SignInManager signInManager, 18 | IEmailService emailService) 19 | { 20 | _userManager = userManager; 21 | _signInManager = signInManager; 22 | _emailService = emailService; 23 | } 24 | 25 | public IActionResult Index() 26 | { 27 | return View(); 28 | } 29 | 30 | [Authorize] 31 | public IActionResult Secret() 32 | { 33 | return View(); 34 | } 35 | public IActionResult Login() 36 | { 37 | return View(); 38 | } 39 | 40 | [HttpPost] 41 | public async Task Login(string username, string password) 42 | { 43 | //login functionality 44 | var user = await _userManager.FindByNameAsync(username); 45 | 46 | if (user != null) 47 | { 48 | //sign in 49 | var signInResult = await _signInManager.PasswordSignInAsync(user, password, false, false); 50 | 51 | if (signInResult.Succeeded) 52 | { 53 | return RedirectToAction("Index"); 54 | } 55 | } 56 | 57 | return RedirectToAction("Index"); 58 | } 59 | 60 | public IActionResult Register() 61 | { 62 | return View(); 63 | } 64 | 65 | [HttpPost] 66 | public async Task Register(string username, string password) 67 | { 68 | //register functionality 69 | 70 | var user = new IdentityUser 71 | { 72 | UserName = username, 73 | Email = "", 74 | }; 75 | 76 | var result = await _userManager.CreateAsync(user, password); 77 | 78 | if (result.Succeeded) 79 | { 80 | //generation of the email token 81 | var code = await _userManager.GenerateEmailConfirmationTokenAsync(user); 82 | 83 | var link = Url.Action(nameof(VerifyEmail), "Home", new { userId = user.Id, code }, Request.Scheme, Request.Host.ToString()); 84 | 85 | await _emailService.SendAsync("test@test.com", "email verify", $"Verify Email", true); 86 | 87 | return RedirectToAction("EmailVerification"); 88 | } 89 | 90 | return RedirectToAction("Index"); 91 | } 92 | 93 | public async Task VerifyEmail(string userId, string code) 94 | { 95 | var user = await _userManager.FindByIdAsync(userId); 96 | 97 | if (user == null) return BadRequest(); 98 | 99 | var result = await _userManager.ConfirmEmailAsync(user, code); 100 | 101 | if (result.Succeeded) 102 | { 103 | return View(); 104 | } 105 | 106 | return BadRequest(); 107 | } 108 | 109 | public IActionResult EmailVerification() => View(); 110 | 111 | public async Task LogOut() 112 | { 113 | await _signInManager.SignOutAsync(); 114 | return RedirectToAction("Index"); 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /IdentityExample/Data/AppDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace IdentityExample.Data 5 | { 6 | // IdentityDbContext contains all the user tables 7 | public class AppDbContext : IdentityDbContext 8 | { 9 | public AppDbContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /IdentityExample/IdentityExample.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /IdentityExample/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace IdentityExample 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /IdentityExample/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:58375", 7 | "sslPort": 0 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IdentityExample": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /IdentityExample/Startup.cs: -------------------------------------------------------------------------------- 1 | using IdentityExample.Data; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using NETCore.MailKit.Extensions; 10 | using NETCore.MailKit.Infrastructure.Internal; 11 | 12 | namespace IdentityExample 13 | { 14 | public class Startup 15 | { 16 | private IConfiguration _config; 17 | 18 | public Startup(IConfiguration config) 19 | { 20 | _config = config; 21 | } 22 | 23 | public void ConfigureServices(IServiceCollection services) 24 | { 25 | services.AddDbContext(config => 26 | { 27 | config.UseInMemoryDatabase("Memory"); 28 | }); 29 | 30 | // AddIdentity registers the services 31 | services.AddIdentity(config => 32 | { 33 | config.Password.RequiredLength = 4; 34 | config.Password.RequireDigit = false; 35 | config.Password.RequireNonAlphanumeric = false; 36 | config.Password.RequireUppercase = false; 37 | config.SignIn.RequireConfirmedEmail = true; 38 | }) 39 | .AddEntityFrameworkStores() 40 | .AddDefaultTokenProviders(); 41 | 42 | services.ConfigureApplicationCookie(config => 43 | { 44 | config.Cookie.Name = "Identity.Cookie"; 45 | config.LoginPath = "/Home/Login"; 46 | }); 47 | 48 | services.AddMailKit(config => config.UseMailKit(_config.GetSection("Email").Get())); 49 | 50 | services.AddControllersWithViews(); 51 | } 52 | 53 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 54 | { 55 | if (env.IsDevelopment()) 56 | { 57 | app.UseDeveloperExceptionPage(); 58 | } 59 | 60 | app.UseRouting(); 61 | 62 | app.UseAuthentication(); 63 | 64 | app.UseAuthorization(); 65 | 66 | app.UseEndpoints(endpoints => 67 | { 68 | endpoints.MapDefaultControllerRoute(); 69 | }); 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /IdentityExample/Views/Home/EmailVerification.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 |

Email has been sent

4 |

Please verify it.

-------------------------------------------------------------------------------- /IdentityExample/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 

Home Page

-------------------------------------------------------------------------------- /IdentityExample/Views/Home/Login.cshtml: -------------------------------------------------------------------------------- 1 | 

Login Page

2 | 3 |
4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /IdentityExample/Views/Home/Register.cshtml: -------------------------------------------------------------------------------- 1 | 

Register Page

2 | 3 |
4 | 5 | 6 | 7 |
8 | -------------------------------------------------------------------------------- /IdentityExample/Views/Home/Secret.cshtml: -------------------------------------------------------------------------------- 1 | 

Secret Page

-------------------------------------------------------------------------------- /IdentityExample/Views/Home/VerifyEmail.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 |

Thank you for verifying your email please login here

4 | Go to Login -------------------------------------------------------------------------------- /IdentityExample/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /IdentityExample/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Email": { 3 | "Server": "127.0.0.1", 4 | "Port": 25, 5 | "SenderName": "Anton", 6 | "SenderEmail": "Testo@testo.com" 7 | }, 8 | "Logging": { 9 | "LogLevel": { 10 | "Default": "Information", 11 | "Microsoft": "Warning", 12 | "Microsoft.Hosting.Lifetime": "Information" 13 | } 14 | }, 15 | "AllowedHosts": "*" 16 | } 17 | -------------------------------------------------------------------------------- /IdentityServer/Controllers/ExternalRegisterViewModel.cs: -------------------------------------------------------------------------------- 1 | namespace IdentityServer.Controllers 2 | { 3 | public class ExternalRegisterViewModel 4 | { 5 | public string Username { get; set; } 6 | public string ReturnUrl { get; set; } 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /IdentityServer/Controllers/LoginViewModel.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication; 2 | using System.Collections.Generic; 3 | 4 | namespace IdentityServer.Controllers 5 | { 6 | public class LoginViewModel 7 | { 8 | public string Username { get; set; } 9 | public string Password { get; set; } 10 | public string ReturnUrl { get; set; } 11 | 12 | public IEnumerable ExternalProviders { get; set; } 13 | } 14 | } -------------------------------------------------------------------------------- /IdentityServer/Controllers/RegisterViewModel.cs: -------------------------------------------------------------------------------- 1 | using System.ComponentModel.DataAnnotations; 2 | 3 | namespace IdentityServer.Controllers 4 | { 5 | public class RegisterViewModel 6 | { 7 | [Required] 8 | public string Username { get; set; } 9 | [Required] 10 | [DataType(DataType.Password)] 11 | public string Password { get; set; } 12 | [Required] 13 | [DataType(DataType.Password)] 14 | [Compare("Password")] 15 | public string ConfirmPassword { get; set; } 16 | public string ReturnUrl { get; set; } 17 | } 18 | } -------------------------------------------------------------------------------- /IdentityServer/Data/AppDbContext.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore; 2 | using Microsoft.EntityFrameworkCore; 3 | 4 | namespace IdentityServer.Data 5 | { 6 | // IdentityDbContext contains all the user tables 7 | public class AppDbContext : IdentityDbContext 8 | { 9 | public AppDbContext(DbContextOptions options) 10 | : base(options) 11 | { 12 | 13 | } 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/20191201205312_InitialIdentityServerPersistedGrantDbMigration.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.EntityFrameworkCore.Migrations; 3 | 4 | namespace IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb 5 | { 6 | public partial class InitialIdentityServerPersistedGrantDbMigration : Migration 7 | { 8 | protected override void Up(MigrationBuilder migrationBuilder) 9 | { 10 | migrationBuilder.CreateTable( 11 | name: "DeviceCodes", 12 | columns: table => new 13 | { 14 | UserCode = table.Column(maxLength: 200, nullable: false), 15 | DeviceCode = table.Column(maxLength: 200, nullable: false), 16 | SubjectId = table.Column(maxLength: 200, nullable: true), 17 | ClientId = table.Column(maxLength: 200, nullable: false), 18 | CreationTime = table.Column(nullable: false), 19 | Expiration = table.Column(nullable: false), 20 | Data = table.Column(maxLength: 50000, nullable: false) 21 | }, 22 | constraints: table => 23 | { 24 | table.PrimaryKey("PK_DeviceCodes", x => x.UserCode); 25 | }); 26 | 27 | migrationBuilder.CreateTable( 28 | name: "PersistedGrants", 29 | columns: table => new 30 | { 31 | Key = table.Column(maxLength: 200, nullable: false), 32 | Type = table.Column(maxLength: 50, nullable: false), 33 | SubjectId = table.Column(maxLength: 200, nullable: true), 34 | ClientId = table.Column(maxLength: 200, nullable: false), 35 | CreationTime = table.Column(nullable: false), 36 | Expiration = table.Column(nullable: true), 37 | Data = table.Column(maxLength: 50000, nullable: false) 38 | }, 39 | constraints: table => 40 | { 41 | table.PrimaryKey("PK_PersistedGrants", x => x.Key); 42 | }); 43 | 44 | migrationBuilder.CreateIndex( 45 | name: "IX_DeviceCodes_DeviceCode", 46 | table: "DeviceCodes", 47 | column: "DeviceCode", 48 | unique: true); 49 | 50 | migrationBuilder.CreateIndex( 51 | name: "IX_DeviceCodes_Expiration", 52 | table: "DeviceCodes", 53 | column: "Expiration"); 54 | 55 | migrationBuilder.CreateIndex( 56 | name: "IX_PersistedGrants_Expiration", 57 | table: "PersistedGrants", 58 | column: "Expiration"); 59 | 60 | migrationBuilder.CreateIndex( 61 | name: "IX_PersistedGrants_SubjectId_ClientId_Type", 62 | table: "PersistedGrants", 63 | columns: new[] { "SubjectId", "ClientId", "Type" }); 64 | } 65 | 66 | protected override void Down(MigrationBuilder migrationBuilder) 67 | { 68 | migrationBuilder.DropTable( 69 | name: "DeviceCodes"); 70 | 71 | migrationBuilder.DropTable( 72 | name: "PersistedGrants"); 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs: -------------------------------------------------------------------------------- 1 | // 2 | using System; 3 | using IdentityServer4.EntityFramework.DbContexts; 4 | using Microsoft.EntityFrameworkCore; 5 | using Microsoft.EntityFrameworkCore.Infrastructure; 6 | using Microsoft.EntityFrameworkCore.Metadata; 7 | using Microsoft.EntityFrameworkCore.Storage.ValueConversion; 8 | 9 | namespace IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb 10 | { 11 | [DbContext(typeof(PersistedGrantDbContext))] 12 | partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot 13 | { 14 | protected override void BuildModel(ModelBuilder modelBuilder) 15 | { 16 | #pragma warning disable 612, 618 17 | modelBuilder 18 | .HasAnnotation("ProductVersion", "3.0.1") 19 | .HasAnnotation("Relational:MaxIdentifierLength", 128) 20 | .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); 21 | 22 | modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.DeviceFlowCodes", b => 23 | { 24 | b.Property("UserCode") 25 | .HasColumnType("nvarchar(200)") 26 | .HasMaxLength(200); 27 | 28 | b.Property("ClientId") 29 | .IsRequired() 30 | .HasColumnType("nvarchar(200)") 31 | .HasMaxLength(200); 32 | 33 | b.Property("CreationTime") 34 | .HasColumnType("datetime2"); 35 | 36 | b.Property("Data") 37 | .IsRequired() 38 | .HasColumnType("nvarchar(max)") 39 | .HasMaxLength(50000); 40 | 41 | b.Property("DeviceCode") 42 | .IsRequired() 43 | .HasColumnType("nvarchar(200)") 44 | .HasMaxLength(200); 45 | 46 | b.Property("Expiration") 47 | .IsRequired() 48 | .HasColumnType("datetime2"); 49 | 50 | b.Property("SubjectId") 51 | .HasColumnType("nvarchar(200)") 52 | .HasMaxLength(200); 53 | 54 | b.HasKey("UserCode"); 55 | 56 | b.HasIndex("DeviceCode") 57 | .IsUnique(); 58 | 59 | b.HasIndex("Expiration"); 60 | 61 | b.ToTable("DeviceCodes"); 62 | }); 63 | 64 | modelBuilder.Entity("IdentityServer4.EntityFramework.Entities.PersistedGrant", b => 65 | { 66 | b.Property("Key") 67 | .HasColumnType("nvarchar(200)") 68 | .HasMaxLength(200); 69 | 70 | b.Property("ClientId") 71 | .IsRequired() 72 | .HasColumnType("nvarchar(200)") 73 | .HasMaxLength(200); 74 | 75 | b.Property("CreationTime") 76 | .HasColumnType("datetime2"); 77 | 78 | b.Property("Data") 79 | .IsRequired() 80 | .HasColumnType("nvarchar(max)") 81 | .HasMaxLength(50000); 82 | 83 | b.Property("Expiration") 84 | .HasColumnType("datetime2"); 85 | 86 | b.Property("SubjectId") 87 | .HasColumnType("nvarchar(200)") 88 | .HasMaxLength(200); 89 | 90 | b.Property("Type") 91 | .IsRequired() 92 | .HasColumnType("nvarchar(50)") 93 | .HasMaxLength(50); 94 | 95 | b.HasKey("Key"); 96 | 97 | b.HasIndex("Expiration"); 98 | 99 | b.HasIndex("SubjectId", "ClientId", "Type"); 100 | 101 | b.ToTable("PersistedGrants"); 102 | }); 103 | #pragma warning restore 612, 618 104 | } 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /IdentityServer/IdentityServer.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | all 15 | runtime; build; native; contentfiles; analyzers; buildtransitive 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /IdentityServer/Program.cs: -------------------------------------------------------------------------------- 1 | using IdentityServer4.EntityFramework.DbContexts; 2 | using IdentityServer4.EntityFramework.Mappers; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.Extensions.DependencyInjection; 7 | using Microsoft.Extensions.Hosting; 8 | using System.Linq; 9 | using System.Security.Claims; 10 | 11 | namespace IdentityServer 12 | { 13 | public class Program 14 | { 15 | public static void Main(string[] args) 16 | { 17 | var host = CreateHostBuilder(args).Build(); 18 | 19 | using (var scope = host.Services.CreateScope()) 20 | { 21 | var userManager = scope.ServiceProvider 22 | .GetRequiredService>(); 23 | 24 | var user = new IdentityUser("bob"); 25 | userManager.CreateAsync(user, "password").GetAwaiter().GetResult(); 26 | userManager.AddClaimAsync(user, new Claim("rc.garndma", "big.cookie")) 27 | .GetAwaiter().GetResult(); 28 | userManager.AddClaimAsync(user, 29 | new Claim("rc.api.garndma", "big.api.cookie")) 30 | .GetAwaiter().GetResult(); 31 | 32 | //scope.ServiceProvider.GetRequiredService() 33 | // .Database.Migrate(); 34 | 35 | //var context = scope.ServiceProvider.GetRequiredService(); 36 | 37 | //context.Database.Migrate(); 38 | 39 | //if (!context.Clients.Any()) 40 | //{ 41 | // foreach (var client in Configuration.GetClients()) 42 | // { 43 | // context.Clients.Add(client.ToEntity()); 44 | // } 45 | // context.SaveChanges(); 46 | //} 47 | 48 | //if (!context.IdentityResources.Any()) 49 | //{ 50 | // foreach (var resource in Configuration.GetIdentityResources()) 51 | // { 52 | // context.IdentityResources.Add(resource.ToEntity()); 53 | // } 54 | // context.SaveChanges(); 55 | //} 56 | 57 | //if (!context.ApiResources.Any()) 58 | //{ 59 | // foreach (var resource in Configuration.GetApis()) 60 | // { 61 | // context.ApiResources.Add(resource.ToEntity()); 62 | // } 63 | // context.SaveChanges(); 64 | //} 65 | } 66 | 67 | host.Run(); 68 | } 69 | 70 | public static IHostBuilder CreateHostBuilder(string[] args) => 71 | Host.CreateDefaultBuilder(args) 72 | .ConfigureWebHostDefaults(webBuilder => 73 | { 74 | webBuilder.UseStartup(); 75 | }); 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /IdentityServer/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:59781", 7 | "sslPort": 44305 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "IdentityServer": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /IdentityServer/Startup.cs: -------------------------------------------------------------------------------- 1 | using IdentityServer.Data; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.AspNetCore.Identity; 5 | using Microsoft.EntityFrameworkCore; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.DependencyInjection; 8 | using Microsoft.Extensions.Hosting; 9 | using System.IO; 10 | using System.Security.Cryptography.X509Certificates; 11 | 12 | namespace IdentityServer 13 | { 14 | public class Startup 15 | { 16 | private readonly IConfiguration _config; 17 | private readonly IWebHostEnvironment _env; 18 | 19 | public Startup(IConfiguration config, IWebHostEnvironment env) 20 | { 21 | _config = config; 22 | _env = env; 23 | } 24 | 25 | public void ConfigureServices(IServiceCollection services) 26 | { 27 | var connectionString = _config.GetConnectionString("DefaultConnection"); 28 | 29 | services.AddDbContext(config => 30 | { 31 | config.UseSqlServer(connectionString); 32 | //config.UseInMemoryDatabase("Memory"); 33 | }); 34 | 35 | // AddIdentity registers the services 36 | services.AddIdentity(config => 37 | { 38 | config.Password.RequiredLength = 4; 39 | config.Password.RequireDigit = false; 40 | config.Password.RequireNonAlphanumeric = false; 41 | config.Password.RequireUppercase = false; 42 | }) 43 | .AddEntityFrameworkStores() 44 | .AddDefaultTokenProviders(); 45 | 46 | services.ConfigureApplicationCookie(config => 47 | { 48 | config.Cookie.Name = "IdentityServer.Cookie"; 49 | config.LoginPath = "/Auth/Login"; 50 | config.LogoutPath = "/Auth/Logout"; 51 | }); 52 | 53 | var assembly = typeof(Startup).Assembly.GetName().Name; 54 | 55 | //var filePath = Path.Combine(_env.ContentRootPath, "is_cert.pfx"); 56 | //var certificate = new X509Certificate2(filePath, "password"); 57 | 58 | services.AddIdentityServer() 59 | .AddAspNetIdentity() 60 | //.AddConfigurationStore(options => 61 | //{ 62 | // options.ConfigureDbContext = b => b.UseSqlServer(connectionString, 63 | // sql => sql.MigrationsAssembly(assembly)); 64 | //}) 65 | //.AddOperationalStore(options => 66 | //{ 67 | // options.ConfigureDbContext = b => b.UseSqlServer(connectionString, 68 | // sql => sql.MigrationsAssembly(assembly)); 69 | //}) 70 | //.AddSigningCredential(certificate); 71 | .AddInMemoryApiResources(Configuration.GetApis()) 72 | .AddInMemoryIdentityResources(Configuration.GetIdentityResources()) 73 | .AddInMemoryClients(Configuration.GetClients()) 74 | .AddDeveloperSigningCredential(); 75 | 76 | services.AddAuthentication() 77 | .AddFacebook(config => { 78 | config.AppId = "3396617443742614"; 79 | config.AppSecret = "secret"; 80 | }); 81 | 82 | services.AddControllersWithViews(); 83 | } 84 | 85 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 86 | { 87 | if (env.IsDevelopment()) 88 | { 89 | app.UseDeveloperExceptionPage(); 90 | } 91 | 92 | app.UseRouting(); 93 | 94 | app.UseIdentityServer(); 95 | 96 | if(_env.IsDevelopment()) 97 | { 98 | app.UseCookiePolicy(new CookiePolicyOptions() 99 | { 100 | MinimumSameSitePolicy = Microsoft.AspNetCore.Http.SameSiteMode.Lax 101 | }); 102 | } 103 | 104 | app.UseEndpoints(endpoints => 105 | { 106 | endpoints.MapDefaultControllerRoute(); 107 | }); 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /IdentityServer/Views/Auth/ExternalRegister.cshtml: -------------------------------------------------------------------------------- 1 | @model ExternalRegisterViewModel 2 | 3 |
4 | 5 |
6 | 7 | 8 | 9 |
10 |
11 | 12 |
13 |
-------------------------------------------------------------------------------- /IdentityServer/Views/Auth/Login.cshtml: -------------------------------------------------------------------------------- 1 | @model LoginViewModel 2 | 3 |

Sign In With

4 |
6 | @foreach (var provider in Model.ExternalProviders) 7 | { 8 | 13 | } 14 |
15 | 16 |
17 | 18 |
19 | 20 | 21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 |
29 |
30 |
31 | Register 33 |
-------------------------------------------------------------------------------- /IdentityServer/Views/Auth/Register.cshtml: -------------------------------------------------------------------------------- 1 | @model RegisterViewModel 2 | 3 |
4 | 5 |
6 | 7 | 8 | 9 |
10 |
11 | 12 | 13 | 14 |
15 |
16 | 17 | 18 | 19 |
20 |
21 | 22 |
23 |
24 | 25 | Back to Login -------------------------------------------------------------------------------- /IdentityServer/Views/_ViewImports.cshtml: -------------------------------------------------------------------------------- 1 | @using IdentityServer.Controllers 2 | @addTagHelper "*, Microsoft.AspNetCore.Mvc.TagHelpers" -------------------------------------------------------------------------------- /IdentityServer/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /IdentityServer/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "ConnectionStrings": { 3 | "DefaultConnection": "Data Source=(localdb)\\MSSQLLocalDB;Initial Catalog=IdentityServer4Tutorial;Integrated Security=True;Connect Timeout=30;Encrypt=False;TrustServerCertificate=False;ApplicationIntent=ReadWrite;MultiSubnetFailover=False" 4 | }, 5 | "Logging": { 6 | "LogLevel": { 7 | "Default": "Information", 8 | "Microsoft": "Warning", 9 | "Microsoft.Hosting.Lifetime": "Information" 10 | } 11 | }, 12 | "AllowedHosts": "*" 13 | } 14 | -------------------------------------------------------------------------------- /IdentityServer/db-scripts.txt: -------------------------------------------------------------------------------- 1 | dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb 2 | dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb 3 | 4 | dotnet ef migrations add -c AppDbContext -o Data/Migrations/AppMigrations 5 | dotnet ef database update -c AppDbContext 6 | 7 | dotnet ef database update -c PersistedGrantDbContext 8 | dotnet ef database update -c ConfigurationDbContext -------------------------------------------------------------------------------- /IdentityServer/tempkey.rsa: -------------------------------------------------------------------------------- 1 | {"KeyId":"MKYnjQ_a1EfAdUkVD7ToVA","Parameters":{"D":"u/GbkcvjWsPNtMymodf6SMuhzGM/f7vEf3pOaZlNmivSoXH6kxiNVPA5bLcsN+ni5Ybq1V0GJsKTwgd9IE+xHkIAoR8EMY7etxADr/azgLTGzuB8nRXL5LrcSeA43j0VHxqSKwYqO3TguVihUmIGC2l+7SOGd5BsZNXRZtbz4NbNOkVE3OXcplK1Rm7nANmBkpE7jScQzA8Vm3RqjA7tobvax11QFcIqk2wfisEiI15liJbKmBKioGLesT/djrVq28qKZWnIWCsBkWdwoSGlYn8pwPMU9UO7FHo4PSh9q+sdfk4CewrYAGl6op4Yj94zxXhqY+67K1PX1bSWLGYIIQ==","DP":"O2vWpsL68mSdSfeK+EeczqhyMVeNgmNwetsdiO7TqBs8uY/rKpeOnuKKxclBYcmleqT3zdajHlCjo02C6aXKMjzBo5UNJcJ/llIrHY7En4pADkTYQSPor8jEASDW0ptmF3r8TA0LR+Wv7u2Xe7yFLu2Paf4KbkS3myyK5WaEYsk=","DQ":"QmXWpltIURiagLtMGoNjpTRFSh+kJsqVp9nnioHShta9+MHC4ICmYngx6DzgIRVMcW7xEcJZYimIFAogNg9GTuVJH/E0/f+ml9a6gQ5W/t0w4MmlRX66dZbLhblvAxBbr2WN2cPR8EWZ9iQiyIEePZEINATNhmFP7xwPfF1BTks=","Exponent":"AQAB","InverseQ":"fsl3OoFlL7k25As4qO0tUx881qyoog2h5KMRlfrMyLrwVlygQSnlmN/dUHDSILu2Y93EBMSS3B76paxh2VNhpw2TZykM+TtrZL+laiU6SE/thtUGUS/Ylgx9y6tsDHIV8EzKxgUYkWTlFOrEElVnu86EEYK3Bu5eSAs+w5JykEs=","Modulus":"01ixZ7ujifVsulyKYKF9Wat/fZRzjkOPG3CL4Iw+Ntxjb6mJUSdTBsV7yYjXjI3yFOqr0loCwfHZb40EE1A7hPNzK3etbAA2SIZa365ZJjv7qJobeCgZjvJnr5UvL+xEBAEzS3vS4u9KmI0+AF243SxtmQxrpkfqTl+cL4wDMmkffGBFlAigufR80NXR5sTejEHAQzjCTPR1518ssNX9OM8t1QHGxGwtp5OUZDSx0DZuCGuvHfEk5eYIEqNfhNEtZnvPmEEqrv6Adweaf4XI2kSrFlBfVjE2DXzeTvy8Kd9wSw6jE8gj+mfo9CcYKpLSqZrxUSetz0RfAnK2sLF1OQ==","P":"4q0Akz8lc69pD6Dx/Yj8k6iDg3QIQRoW+1hHldmu7CKkdbpVY/8vASZZpkvN285HeEIT8xouNRqNjyB1K1e8K7ve3Xg7huEGoNaYs6zTNkhafKVR7P0bS00+bw4gHk/hSYINk01L8yPR0OA1EUkHYfh3FR8epCMigltI7MTW878=","Q":"7rAEcKTkDDOOgMpioOLko/QSO2kqHjqPvX2nSc9YSpvn5Zjl+91w5kTlOlsGh2nFmHIOar0/CrLmDvGN5a2UGoFwiTH16BwJQrQ/hecItXtMoIHyFholRZ+3KEWaQi9qlum0apMECAaL/sszceS2eLkZP43jXvMn7WiF6R819Qc="}} -------------------------------------------------------------------------------- /JavascriptClient/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Mvc; 2 | 3 | namespace JavascriptClient.Controllers 4 | { 5 | public class HomeController : Controller 6 | { 7 | public IActionResult Index() 8 | { 9 | return View(); 10 | } 11 | 12 | public IActionResult SignIn() 13 | { 14 | return View(); 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /JavascriptClient/JavascriptClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /JavascriptClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace JavascriptClient 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /JavascriptClient/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:65505", 7 | "sslPort": 44345 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "JavascriptClient": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /JavascriptClient/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Builder; 6 | using Microsoft.AspNetCore.Hosting; 7 | using Microsoft.AspNetCore.Http; 8 | using Microsoft.Extensions.DependencyInjection; 9 | using Microsoft.Extensions.Hosting; 10 | 11 | namespace JavascriptClient 12 | { 13 | public class Startup 14 | { 15 | public void ConfigureServices(IServiceCollection services) 16 | { 17 | services.AddControllersWithViews() 18 | .AddRazorRuntimeCompilation(); 19 | } 20 | 21 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 22 | { 23 | if (env.IsDevelopment()) 24 | { 25 | app.UseDeveloperExceptionPage(); 26 | } 27 | 28 | app.UseStaticFiles(); 29 | 30 | app.UseRouting(); 31 | 32 | app.UseEndpoints(endpoints => 33 | { 34 | endpoints.MapDefaultControllerRoute(); 35 | }); 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /JavascriptClient/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 

2 | Home Page 3 |

4 | 5 | 6 | 7 | 8 |
9 | 10 |
11 | 12 |
13 | 14 |
15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /JavascriptClient/Views/Home/SignIn.cshtml: -------------------------------------------------------------------------------- 1 |  2 | 3 | -------------------------------------------------------------------------------- /JavascriptClient/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /JavascriptClient/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /JavascriptClient/wwwroot/js-old/sign-in-callback.js: -------------------------------------------------------------------------------- 1 | var extractTokens = function (address) { 2 | var returnValue = address.split('#')[1]; 3 | var values = returnValue.split('&'); 4 | 5 | for (var i = 0; i < values.length; i++) { 6 | var v = values[i]; 7 | var kvPair = v.split('='); 8 | localStorage.setItem(kvPair[0], kvPair[1]); 9 | } 10 | 11 | window.location.href = '/home/index'; 12 | } 13 | 14 | 15 | extractTokens(window.location.href); -------------------------------------------------------------------------------- /JavascriptClient/wwwroot/js-old/sign-in.js: -------------------------------------------------------------------------------- 1 | var createState = function () { 2 | return "SessionValueMakeItABitLongerasdfhjsadoighasdifjdsalkhrfakwelyrosdpiufghasidkgewr"; 3 | }; 4 | 5 | var createNonce = function () { 6 | return "NonceValuedsafliudsayatroiewewryie123"; 7 | }; 8 | 9 | var signIn = function () { 10 | var redirectUri = "https://localhost:44345/Home/SignIn"; 11 | var responseType = "id_token token"; 12 | var scope = "openid ApiOne"; 13 | var authUrl = 14 | "/connect/authorize/callback" + 15 | "?client_id=client_id_js" + 16 | "&redirect_uri=" + encodeURIComponent(redirectUri) + 17 | "&response_type=" + encodeURIComponent(responseType) + 18 | "&scope=" + encodeURIComponent(scope) + 19 | "&nonce="+ createNonce() + 20 | "&state="+ createState(); 21 | 22 | var returnUrl = encodeURIComponent(authUrl); 23 | 24 | window.location.href = "https://localhost:44305/Auth/Login?ReturnUrl=" + returnUrl; 25 | } 26 | 27 | 28 | -------------------------------------------------------------------------------- /JavascriptClient/wwwroot/main.js: -------------------------------------------------------------------------------- 1 | var config = { 2 | userStore: new Oidc.WebStorageStateStore({ store: window.localStorage }), 3 | authority: "https://localhost:44305/", 4 | client_id: "client_id_js", 5 | redirect_uri: "https://localhost:44345/Home/SignIn", 6 | post_logout_redirect_uri: "https://localhost:44345/Home/Index", 7 | response_type: "code", 8 | scope: "openid rc.scope ApiOne ApiTwo" 9 | }; 10 | 11 | var userManager = new Oidc.UserManager(config); 12 | 13 | var signIn = function () { 14 | userManager.signinRedirect(); 15 | }; 16 | 17 | var signOut = function() { 18 | userManager.signoutRedirect(); 19 | }; 20 | 21 | userManager.getUser().then(user => { 22 | console.log("user:", user); 23 | if (user) { 24 | axios.defaults.headers.common["Authorization"] = "Bearer " + user.access_token; 25 | } 26 | }); 27 | 28 | var callApi = function () { 29 | axios.get("https://localhost:44337/secret") 30 | .then(res => { 31 | console.log(res); 32 | }); 33 | }; 34 | 35 | var refreshing = false; 36 | 37 | axios.interceptors.response.use( 38 | function (response) { return response; }, 39 | function (error) { 40 | console.log("axios error:", error.response); 41 | 42 | var axiosConfig = error.response.config; 43 | 44 | //if error response is 401 try to refresh token 45 | if (error.response.status === 401) { 46 | console.log("axios error 401"); 47 | 48 | // if already refreshing don't make another request 49 | if (!refreshing) { 50 | console.log("starting token refresh"); 51 | refreshing = true; 52 | 53 | // do the refresh 54 | return userManager.signinSilent().then(user => { 55 | console.log("new user:", user); 56 | //update the http request and client 57 | axios.defaults.headers.common["Authorization"] = "Bearer " + user.access_token; 58 | axiosConfig.headers["Authorization"] = "Bearer " + user.access_token; 59 | //retry the http request 60 | return axios(axiosConfig); 61 | }); 62 | } 63 | } 64 | 65 | return Promise.reject(error); 66 | }); -------------------------------------------------------------------------------- /MvcClient/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using IdentityModel.Client; 2 | using Microsoft.AspNetCore.Authentication; 3 | using Microsoft.AspNetCore.Authorization; 4 | using Microsoft.AspNetCore.Mvc; 5 | using System.IdentityModel.Tokens.Jwt; 6 | using System.Linq; 7 | using System.Net.Http; 8 | using System.Threading.Tasks; 9 | 10 | namespace MvcClient.Controllers 11 | { 12 | public class HomeController : Controller 13 | { 14 | private readonly IHttpClientFactory _httpClientFactory; 15 | 16 | public HomeController(IHttpClientFactory httpClientFactory) 17 | { 18 | _httpClientFactory = httpClientFactory; 19 | } 20 | 21 | public IActionResult Index() 22 | { 23 | return View(); 24 | } 25 | 26 | [Authorize] 27 | public async Task Secret() 28 | { 29 | var accessToken = await HttpContext.GetTokenAsync("access_token"); 30 | var idToken = await HttpContext.GetTokenAsync("id_token"); 31 | var refreshToken = await HttpContext.GetTokenAsync("refresh_token"); 32 | 33 | var claims = User.Claims.ToList(); 34 | var _accessToken = new JwtSecurityTokenHandler().ReadJwtToken(accessToken); 35 | var _idToken = new JwtSecurityTokenHandler().ReadJwtToken(idToken); 36 | 37 | var result = await GetSecret(accessToken); 38 | 39 | await RefreshAccessToken(); 40 | 41 | return View(); 42 | } 43 | 44 | public IActionResult Logout() 45 | { 46 | return SignOut("Cookie", "oidc"); 47 | } 48 | 49 | public async Task GetSecret(string accessToken) 50 | { 51 | var apiClient = _httpClientFactory.CreateClient(); 52 | 53 | apiClient.SetBearerToken(accessToken); 54 | 55 | var response = await apiClient.GetAsync("https://localhost:44337/secret"); 56 | 57 | var content = await response.Content.ReadAsStringAsync(); 58 | 59 | return content; 60 | } 61 | 62 | private async Task RefreshAccessToken() 63 | { 64 | var serverClient = _httpClientFactory.CreateClient(); 65 | var discoveryDocument = await serverClient.GetDiscoveryDocumentAsync("https://localhost:44305/"); 66 | 67 | var accessToken = await HttpContext.GetTokenAsync("access_token"); 68 | var idToken = await HttpContext.GetTokenAsync("id_token"); 69 | var refreshToken = await HttpContext.GetTokenAsync("refresh_token"); 70 | var refreshTokenClient = _httpClientFactory.CreateClient(); 71 | 72 | var tokenResponse = await refreshTokenClient.RequestRefreshTokenAsync( 73 | new RefreshTokenRequest 74 | { 75 | Address = discoveryDocument.TokenEndpoint, 76 | RefreshToken = refreshToken, 77 | ClientId = "client_id_mvc", 78 | ClientSecret = "client_secret_mvc" 79 | }); 80 | 81 | var authInfo = await HttpContext.AuthenticateAsync("Cookie"); 82 | 83 | authInfo.Properties.UpdateTokenValue("access_token", tokenResponse.AccessToken); 84 | authInfo.Properties.UpdateTokenValue("id_token", tokenResponse.IdentityToken); 85 | authInfo.Properties.UpdateTokenValue("refresh_token", tokenResponse.RefreshToken); 86 | 87 | await HttpContext.SignInAsync("Cookie", authInfo.Principal, authInfo.Properties); 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /MvcClient/MvcClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /MvcClient/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace MvcClient 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /MvcClient/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:49386", 7 | "sslPort": 44322 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "MvcClient": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /MvcClient/Startup.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Authentication; 6 | using Microsoft.AspNetCore.Builder; 7 | using Microsoft.AspNetCore.Hosting; 8 | using Microsoft.AspNetCore.Http; 9 | using Microsoft.Extensions.DependencyInjection; 10 | using Microsoft.Extensions.Hosting; 11 | 12 | namespace MvcClient 13 | { 14 | public class Startup 15 | { 16 | public void ConfigureServices(IServiceCollection services) 17 | { 18 | services.AddAuthentication(config => { 19 | config.DefaultScheme = "Cookie"; 20 | config.DefaultChallengeScheme = "oidc"; 21 | }) 22 | .AddCookie("Cookie") 23 | .AddOpenIdConnect("oidc", config => { 24 | config.Authority = "https://localhost:44305/"; 25 | config.ClientId = "client_id_mvc"; 26 | config.ClientSecret = "client_secret_mvc"; 27 | config.SaveTokens = true; 28 | config.ResponseType = "code"; 29 | config.SignedOutCallbackPath = "/Home/Index"; 30 | 31 | // configure cookie claim mapping 32 | config.ClaimActions.DeleteClaim("amr"); 33 | config.ClaimActions.DeleteClaim("s_hash"); 34 | config.ClaimActions.MapUniqueJsonKey("RawCoding.Grandma", "rc.garndma"); 35 | 36 | // two trips to load claims in to the cookie 37 | // but the id token is smaller ! 38 | config.GetClaimsFromUserInfoEndpoint = true; 39 | 40 | // configure scope 41 | config.Scope.Clear(); 42 | config.Scope.Add("openid"); 43 | config.Scope.Add("rc.scope"); 44 | config.Scope.Add("ApiOne"); 45 | config.Scope.Add("ApiTwo"); 46 | config.Scope.Add("offline_access"); 47 | 48 | }); 49 | 50 | services.AddHttpClient(); 51 | 52 | services.AddControllersWithViews(); 53 | } 54 | 55 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 56 | { 57 | if (env.IsDevelopment()) 58 | { 59 | app.UseDeveloperExceptionPage(); 60 | } 61 | 62 | app.UseRouting(); 63 | 64 | app.UseAuthentication(); 65 | 66 | app.UseAuthorization(); 67 | 68 | app.UseEndpoints(endpoints => 69 | { 70 | endpoints.MapDefaultControllerRoute(); 71 | }); 72 | } 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /MvcClient/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 

Home Page

-------------------------------------------------------------------------------- /MvcClient/Views/Home/Secret.cshtml: -------------------------------------------------------------------------------- 1 | 

Secret Page

-------------------------------------------------------------------------------- /MvcClient/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /MvcClient/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # aspnetcore3 Authentication Tutorial 2 | 3 | This repo is for my Youtube tutorial that can be found here: https://www.youtube.com/playlist?list=PLOeFnOV9YBa7dnrjpOG6lMpcyd7Wn7E8V 4 | -------------------------------------------------------------------------------- /Server/Constants.cs: -------------------------------------------------------------------------------- 1 | namespace Server 2 | { 3 | public static class Constants 4 | { 5 | public const string Issuer = Audiance; 6 | public const string Audiance = "https://localhost:44382/"; 7 | public const string Secret = "not_too_short_secret_otherwise_it_might_error"; 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Server/Controllers/HomeController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IdentityModel.Tokens.Jwt; 3 | using System.Security.Claims; 4 | using System.Text; 5 | using Microsoft.AspNetCore.Authorization; 6 | using Microsoft.AspNetCore.Mvc; 7 | using Microsoft.IdentityModel.Tokens; 8 | 9 | namespace Server.Controllers 10 | { 11 | public class HomeController : Controller 12 | { 13 | public IActionResult Index() 14 | { 15 | return View(); 16 | } 17 | 18 | [Authorize] 19 | public IActionResult Secret() 20 | { 21 | return View(); 22 | } 23 | 24 | public IActionResult Authenticate() 25 | { 26 | var claims = new[] 27 | { 28 | new Claim(JwtRegisteredClaimNames.Sub, "some_id"), 29 | new Claim("granny", "cookie") 30 | }; 31 | 32 | var secretBytes = Encoding.UTF8.GetBytes(Constants.Secret); 33 | var key = new SymmetricSecurityKey(secretBytes); 34 | var algorithm = SecurityAlgorithms.HmacSha256; 35 | 36 | var signingCredentials = new SigningCredentials(key, algorithm); 37 | 38 | var token = new JwtSecurityToken( 39 | Constants.Issuer, 40 | Constants.Audiance, 41 | claims, 42 | DateTime.Now, 43 | DateTime.Now.AddHours(1), 44 | signingCredentials); 45 | 46 | var tokenJson = new JwtSecurityTokenHandler().WriteToken(token); 47 | 48 | return Ok(new { access_token = tokenJson }); 49 | } 50 | 51 | public IActionResult Decode(string part) 52 | { 53 | var bytes = Convert.FromBase64String(part); 54 | return Ok(Encoding.UTF8.GetString(bytes)); 55 | } 56 | } 57 | } -------------------------------------------------------------------------------- /Server/Controllers/OAuthController.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IdentityModel.Tokens.Jwt; 3 | using System.Security.Claims; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Microsoft.AspNetCore.Authorization; 7 | using Microsoft.AspNetCore.Http.Extensions; 8 | using Microsoft.AspNetCore.Mvc; 9 | using Microsoft.IdentityModel.Tokens; 10 | using Newtonsoft.Json; 11 | 12 | namespace Server.Controllers 13 | { 14 | public class OAuthController : Controller 15 | { 16 | [HttpGet] 17 | public IActionResult Authorize( 18 | string responseType, // authorization flow type 19 | string clientId, // client id 20 | string redirectUri, 21 | string scope, // what info I want = email,grandma,tel 22 | string state) // random string generated to confirm that we are going to back to the same client 23 | { 24 | // ?a=foo&b=bar 25 | var query = new QueryBuilder(); 26 | query.Add("redirectUri", redirectUri); 27 | query.Add("state", state); 28 | 29 | return View(model: query.ToString()); 30 | } 31 | 32 | [HttpPost] 33 | public IActionResult Authorize( 34 | string username, 35 | string redirectUri, 36 | string state) 37 | { 38 | const string code = "BABAABABABA"; 39 | var query = new QueryBuilder(); 40 | query.Add("code", code); 41 | query.Add("state", state); 42 | return Redirect($"{redirectUri}{query}"); 43 | } 44 | 45 | public async Task Token( 46 | string grantType, // flow of access_token request 47 | string code, // confirmation of the authentication process 48 | string redirectUri, 49 | string clientId, 50 | string refreshToken) 51 | { 52 | // some mechanism for validating the code 53 | 54 | var claims = new[] 55 | { 56 | new Claim(JwtRegisteredClaimNames.Sub, "some_id"), 57 | new Claim("granny", "cookie") 58 | }; 59 | 60 | var secretBytes = Encoding.UTF8.GetBytes(Constants.Secret); 61 | var key = new SymmetricSecurityKey(secretBytes); 62 | const string algorithm = SecurityAlgorithms.HmacSha256; 63 | 64 | var signingCredentials = new SigningCredentials(key, algorithm); 65 | 66 | var token = new JwtSecurityToken( 67 | Constants.Issuer, 68 | Constants.Audiance, 69 | claims, 70 | DateTime.Now, 71 | grantType == "refresh_token" 72 | ? DateTime.Now.AddMinutes(5) 73 | : DateTime.Now.AddMilliseconds(1), 74 | signingCredentials); 75 | 76 | var accessToken = new JwtSecurityTokenHandler().WriteToken(token); 77 | 78 | var responseObject = new 79 | { 80 | access_token = accessToken, 81 | token_type = "Bearer", 82 | raw_claim = "oauthTutorial", 83 | refresh_token = "RefreshTokenSampleValueSomething77" 84 | }; 85 | 86 | var responseJson = JsonConvert.SerializeObject(responseObject); 87 | var responseBytes = Encoding.UTF8.GetBytes(responseJson); 88 | 89 | await Response.Body.WriteAsync(responseBytes, 0, responseBytes.Length); 90 | 91 | return Redirect(redirectUri); 92 | } 93 | 94 | [Authorize] 95 | public IActionResult Validate() 96 | { 97 | if (HttpContext.Request.Query.TryGetValue("access_token", out var accessToken)) 98 | { 99 | return Ok(); 100 | } 101 | 102 | return BadRequest(); 103 | } 104 | } 105 | } -------------------------------------------------------------------------------- /Server/Controllers/SecretController.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authorization; 2 | using Microsoft.AspNetCore.Mvc; 3 | 4 | namespace Server.Controllers 5 | { 6 | public class SecretController : Controller 7 | { 8 | [Authorize] 9 | public string Index() 10 | { 11 | return "secret message"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Server/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Threading.Tasks; 5 | using Microsoft.AspNetCore.Hosting; 6 | using Microsoft.Extensions.Configuration; 7 | using Microsoft.Extensions.Hosting; 8 | using Microsoft.Extensions.Logging; 9 | 10 | namespace Server 11 | { 12 | public class Program 13 | { 14 | public static void Main(string[] args) 15 | { 16 | CreateHostBuilder(args).Build().Run(); 17 | } 18 | 19 | public static IHostBuilder CreateHostBuilder(string[] args) => 20 | Host.CreateDefaultBuilder(args) 21 | .ConfigureWebHostDefaults(webBuilder => 22 | { 23 | webBuilder.UseStartup(); 24 | }); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Server/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "iisSettings": { 3 | "windowsAuthentication": false, 4 | "anonymousAuthentication": true, 5 | "iisExpress": { 6 | "applicationUrl": "http://localhost:55580", 7 | "sslPort": 44382 8 | } 9 | }, 10 | "profiles": { 11 | "IIS Express": { 12 | "commandName": "IISExpress", 13 | "launchBrowser": true, 14 | "environmentVariables": { 15 | "ASPNETCORE_ENVIRONMENT": "Development" 16 | } 17 | }, 18 | "Server": { 19 | "commandName": "Project", 20 | "launchBrowser": true, 21 | "applicationUrl": "https://localhost:5001;http://localhost:5000", 22 | "environmentVariables": { 23 | "ASPNETCORE_ENVIRONMENT": "Development" 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Server/Server.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netcoreapp3.0 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | -------------------------------------------------------------------------------- /Server/Startup.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.AspNetCore.Authentication.JwtBearer; 2 | using Microsoft.AspNetCore.Builder; 3 | using Microsoft.AspNetCore.Hosting; 4 | using Microsoft.Extensions.DependencyInjection; 5 | using Microsoft.Extensions.Hosting; 6 | using Microsoft.IdentityModel.Tokens; 7 | using System; 8 | using System.Text; 9 | using System.Threading.Tasks; 10 | 11 | namespace Server 12 | { 13 | public class Startup 14 | { 15 | public void ConfigureServices(IServiceCollection services) 16 | { 17 | services.AddAuthentication("OAuth") 18 | .AddJwtBearer("OAuth", config => 19 | { 20 | var secretBytes = Encoding.UTF8.GetBytes(Constants.Secret); 21 | var key = new SymmetricSecurityKey(secretBytes); 22 | 23 | config.Events = new JwtBearerEvents() 24 | { 25 | OnMessageReceived = context => 26 | { 27 | if (context.Request.Query.ContainsKey("access_token")) 28 | { 29 | context.Token = context.Request.Query["access_token"]; 30 | } 31 | 32 | return Task.CompletedTask; 33 | } 34 | }; 35 | 36 | config.TokenValidationParameters = new TokenValidationParameters() 37 | { 38 | ClockSkew = TimeSpan.Zero, 39 | ValidIssuer = Constants.Issuer, 40 | ValidAudience = Constants.Audiance, 41 | IssuerSigningKey = key, 42 | }; 43 | }); 44 | 45 | services.AddControllersWithViews() 46 | .AddRazorRuntimeCompilation(); 47 | } 48 | 49 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env) 50 | { 51 | if (env.IsDevelopment()) 52 | { 53 | app.UseDeveloperExceptionPage(); 54 | } 55 | 56 | app.UseRouting(); 57 | 58 | app.UseAuthentication(); 59 | 60 | app.UseAuthorization(); 61 | 62 | app.UseEndpoints(endpoints => 63 | { 64 | endpoints.MapDefaultControllerRoute(); 65 | }); 66 | } 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /Server/Views/Home/Index.cshtml: -------------------------------------------------------------------------------- 1 | 

Server Home Page

2 | -------------------------------------------------------------------------------- /Server/Views/Home/Secret.cshtml: -------------------------------------------------------------------------------- 1 | 

Server Secret Page

-------------------------------------------------------------------------------- /Server/Views/OAuth/Authorize.cshtml: -------------------------------------------------------------------------------- 1 | @model string 2 | 3 | @{ 4 | var url = $"/OAuth/Authorize{Model}"; 5 | } 6 | 7 | 8 | @Model 9 | 10 |
11 | 12 | 13 |
-------------------------------------------------------------------------------- /Server/appsettings.Development.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Debug", 5 | "System": "Information", 6 | "Microsoft": "Information" 7 | } 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /Server/appsettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "Logging": { 3 | "LogLevel": { 4 | "Default": "Information", 5 | "Microsoft": "Warning", 6 | "Microsoft.Hosting.Lifetime": "Information" 7 | } 8 | }, 9 | "AllowedHosts": "*" 10 | } 11 | -------------------------------------------------------------------------------- /WpfApp/App.config: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /WpfApp/App.xaml: -------------------------------------------------------------------------------- 1 |  6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /WpfApp/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Configuration; 4 | using System.Data; 5 | using System.Linq; 6 | using System.Threading.Tasks; 7 | using System.Windows; 8 | 9 | namespace WpfApp 10 | { 11 | /// 12 | /// Interaction logic for App.xaml 13 | /// 14 | public partial class App : Application 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /WpfApp/MainWindow.xaml: -------------------------------------------------------------------------------- 1 |  9 | 10 |