├── .gitattributes
├── .gitignore
├── AspNetCoreIdentity.sln
├── AspNetCoreIdentity
├── .gitignore
├── .vscode
│ ├── launch.json
│ └── tasks.json
├── AspNetCoreIdentity.csproj
├── ClientApp
│ ├── .editorconfig
│ ├── .gitignore
│ ├── README.md
│ ├── angular.json
│ ├── browserslist
│ ├── e2e
│ │ ├── protractor.conf.js
│ │ ├── src
│ │ │ ├── app.e2e-spec.ts
│ │ │ └── app.po.ts
│ │ └── tsconfig.e2e.json
│ ├── package-lock.json
│ ├── package.json
│ ├── src
│ │ ├── app
│ │ │ ├── admin
│ │ │ │ ├── admin.component.css
│ │ │ │ ├── admin.component.html
│ │ │ │ └── admin.component.ts
│ │ │ ├── app.component.css
│ │ │ ├── app.component.html
│ │ │ ├── app.component.ts
│ │ │ ├── app.module.ts
│ │ │ ├── app.server.module.ts
│ │ │ ├── claims
│ │ │ │ ├── access-forbidden
│ │ │ │ │ ├── access-forbidden.component.css
│ │ │ │ │ ├── access-forbidden.component.html
│ │ │ │ │ └── access-forbidden.component.ts
│ │ │ │ ├── claims.component.css
│ │ │ │ ├── claims.component.html
│ │ │ │ └── claims.component.ts
│ │ │ ├── core
│ │ │ │ ├── domain.ts
│ │ │ │ ├── http-interceptors
│ │ │ │ │ ├── auth-interceptor.ts
│ │ │ │ │ └── index.ts
│ │ │ │ ├── openid-connect.service.ts
│ │ │ │ └── state.service.ts
│ │ │ ├── counter
│ │ │ │ ├── counter.component.html
│ │ │ │ ├── counter.component.spec.ts
│ │ │ │ └── counter.component.ts
│ │ │ ├── fetch-data
│ │ │ │ ├── fetch-data.component.html
│ │ │ │ └── fetch-data.component.ts
│ │ │ ├── home
│ │ │ │ ├── home.component.html
│ │ │ │ └── home.component.ts
│ │ │ ├── login
│ │ │ │ ├── login.component.css
│ │ │ │ ├── login.component.html
│ │ │ │ └── login.component.ts
│ │ │ ├── manage-account
│ │ │ │ ├── account-profile.component.css
│ │ │ │ ├── account-profile.component.html
│ │ │ │ ├── account-profile.component.ts
│ │ │ │ ├── manage-account.component.html
│ │ │ │ ├── manage-account.component.ts
│ │ │ │ ├── reset-authenticator.component.css
│ │ │ │ ├── reset-authenticator.component.html
│ │ │ │ ├── reset-authenticator.component.ts
│ │ │ │ ├── setup-authenticator.component.css
│ │ │ │ ├── setup-authenticator.component.html
│ │ │ │ └── setup-authenticator.component.ts
│ │ │ ├── navmenu
│ │ │ │ ├── navmenu.component.css
│ │ │ │ ├── navmenu.component.html
│ │ │ │ └── navmenu.component.ts
│ │ │ ├── password
│ │ │ │ ├── password.component.css
│ │ │ │ ├── password.component.html
│ │ │ │ └── password.component.ts
│ │ │ ├── register
│ │ │ │ ├── register.component.css
│ │ │ │ ├── register.component.html
│ │ │ │ └── register.component.ts
│ │ │ ├── socialapi
│ │ │ │ ├── share.component.css
│ │ │ │ ├── share.component.html
│ │ │ │ └── share.component.ts
│ │ │ └── streaming
│ │ │ │ ├── add
│ │ │ │ ├── add-video.component.css
│ │ │ │ ├── add-video.component.html
│ │ │ │ └── add-video.component.ts
│ │ │ │ ├── register
│ │ │ │ ├── register.component.css
│ │ │ │ ├── register.component.html
│ │ │ │ └── register.component.ts
│ │ │ │ ├── streaming.component.css
│ │ │ │ ├── streaming.component.html
│ │ │ │ └── streaming.component.ts
│ │ ├── assets
│ │ │ └── .gitkeep
│ │ ├── environments
│ │ │ ├── environment.prod.ts
│ │ │ └── environment.ts
│ │ ├── index.html
│ │ ├── karma.conf.js
│ │ ├── main.ts
│ │ ├── polyfills.ts
│ │ ├── styles.css
│ │ ├── test.ts
│ │ ├── tsconfig.app.json
│ │ ├── tsconfig.server.json
│ │ ├── tsconfig.spec.json
│ │ └── tslint.json
│ ├── tsconfig.json
│ └── tslint.json
├── Controllers
│ ├── AccountController.cs
│ ├── ClientAppSettings.cs
│ ├── HomeController.cs
│ ├── ManageController.cs
│ ├── SocialAccountController.cs
│ ├── StreamingController.cs
│ ├── TwoFactorAuthenticationController.cs
│ └── WeatherForecastController.cs
├── Infrastructure
│ ├── AppUserManager.cs
│ ├── AppUserStore.cs
│ ├── AuthMessageSenderOptions.cs
│ ├── DbInitializer.cs
│ ├── EmailSender.cs
│ ├── ExternalProvidersRegistrations.cs
│ ├── Identity.Internals
│ │ ├── Base32.cs
│ │ └── Rfc6238AuthenticationService.cs
│ ├── StreaminCategoryAuthorizeAttribute.cs
│ ├── StreamingCategoryAuthorizationHandler.cs
│ ├── StreamingCategoryPolicyProvider.cs
│ ├── StreamingCategoryRequirement.cs
│ ├── UserCategoryAuthorizationHandler.cs
│ ├── UserRepository.cs
│ └── VideoRepository.cs
├── Models
│ └── AppUser.cs
├── Pages
│ └── _ViewImports.cshtml
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── ViewModels
│ ├── AccountDetailsVM.cs
│ ├── AssociateViewModel.cs
│ ├── AuthenticatorDetailsVM.cs
│ ├── ClaimVM.cs
│ ├── LoginVM.cs
│ ├── RegisterVM.cs
│ ├── ResultVM.cs
│ ├── TwoFactorLoginVM.cs
│ ├── TwoFactorRecoveryCodeLoginVM.cs
│ ├── UpdatePasswordVM.cs
│ ├── UserStateVM.cs
│ ├── VefiryAuthenticatorVM.cs
│ └── VideoVM.cs
├── WeatherForecast.cs
├── appsettings.Development.json
├── appsettings.json
├── libman.json
└── wwwroot
│ ├── content
│ ├── ng2-toastr.min.css
│ └── ng2-toastr.min.js
│ ├── css
│ └── styles.css
│ ├── favicon.ico
│ ├── images
│ ├── 403-forbidden.png
│ ├── dropbox.png
│ ├── facebook.png
│ ├── github.png
│ ├── google.png
│ ├── linkedin.png
│ ├── microsoft.png
│ └── twitter.png
│ └── js
│ ├── oidc-client.js
│ └── qrcode.min.js
├── IdentityServer
├── Config.cs
├── Controllers
│ ├── AccountController.cs
│ ├── ConsentController.cs
│ ├── GrantsController.cs
│ └── HomeController.cs
├── Data
│ ├── ApplicationDbContext.cs
│ └── instructions.md
├── DatabaseInitializer.cs
├── Extensions
│ └── Extensions.cs
├── Filters
│ └── SecurityHeadersAttribute.cs
├── IdentityServer.csproj
├── Models
│ ├── Account
│ │ ├── AccountOptions.cs
│ │ ├── ExternalProvider.cs
│ │ ├── LoggedOutViewModel.cs
│ │ ├── LoginInputModel.cs
│ │ ├── LoginViewModel.cs
│ │ ├── LogoutInputModel.cs
│ │ ├── LogoutViewModel.cs
│ │ ├── RedirectViewModel.cs
│ │ └── RegisterVM.cs
│ ├── Consent
│ │ ├── ConsentInputModel.cs
│ │ ├── ConsentOptions.cs
│ │ ├── ConsentViewModel.cs
│ │ ├── ProcessConsentResult.cs
│ │ └── ScopeViewModel.cs
│ ├── ErrorViewModel.cs
│ └── Grants
│ │ ├── GrantViewModel.cs
│ │ └── GrantsViewModel.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── Startup.cs
├── Views
│ ├── Account
│ │ ├── LoggedOut.cshtml
│ │ ├── Login.cshtml
│ │ ├── Logout.cshtml
│ │ └── Register.cshtml
│ ├── Consent
│ │ └── Index.cshtml
│ ├── Grants
│ │ └── Index.cshtml
│ ├── Home
│ │ └── Index.cshtml
│ ├── Shared
│ │ ├── Error.cshtml
│ │ ├── Redirect.cshtml
│ │ ├── _Layout.cshtml
│ │ ├── _ScopeListItem.cshtml
│ │ └── _ValidationSummary.cshtml
│ ├── _ViewImports.cshtml
│ └── _ViewStart.cshtml
├── appsettings.Development.json
├── appsettings.json
├── tempkey.rsa
└── wwwroot
│ ├── css
│ ├── site.css
│ ├── site.less
│ └── site.min.css
│ ├── favicon.ico
│ ├── icon.jpg
│ ├── icon.png
│ ├── js
│ ├── signin-redirect.js
│ └── signout-redirect.js
│ └── lib
│ ├── bootstrap
│ ├── css
│ │ ├── bootstrap.css
│ │ ├── bootstrap.css.map
│ │ └── bootstrap.min.css
│ ├── fonts
│ │ ├── glyphicons-halflings-regular.eot
│ │ ├── glyphicons-halflings-regular.svg
│ │ ├── glyphicons-halflings-regular.ttf
│ │ ├── glyphicons-halflings-regular.woff
│ │ └── glyphicons-halflings-regular.woff2
│ └── js
│ │ ├── bootstrap.js
│ │ └── bootstrap.min.js
│ └── jquery
│ ├── jquery.js
│ ├── jquery.min.js
│ └── jquery.min.map
├── LICENSE
├── README.md
├── SocialNetwork.API
├── Controllers
│ └── ContactsController.cs
├── Program.cs
├── Properties
│ └── launchSettings.json
├── SocialNetwork.API.csproj
├── Startup.cs
├── appsettings.Development.json
└── appsettings.json
└── appveyor.yml
/.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 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27428.2015
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNetCoreIdentity", "AspNetCoreIdentity\AspNetCoreIdentity.csproj", "{1F8BFE1B-F23D-4E93-B12C-393976AF5371}"
7 | EndProject
8 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer", "IdentityServer\IdentityServer.csproj", "{5178703E-2DE1-4543-B220-CEFECB113CBE}"
9 | EndProject
10 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SocialNetwork.API", "SocialNetwork.API\SocialNetwork.API.csproj", "{BF77D4DE-700A-4CFD-B902-C0B4DA2C83B5}"
11 | EndProject
12 | Global
13 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
14 | Debug|Any CPU = Debug|Any CPU
15 | Release|Any CPU = Release|Any CPU
16 | EndGlobalSection
17 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
18 | {1F8BFE1B-F23D-4E93-B12C-393976AF5371}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
19 | {1F8BFE1B-F23D-4E93-B12C-393976AF5371}.Debug|Any CPU.Build.0 = Debug|Any CPU
20 | {1F8BFE1B-F23D-4E93-B12C-393976AF5371}.Release|Any CPU.ActiveCfg = Release|Any CPU
21 | {1F8BFE1B-F23D-4E93-B12C-393976AF5371}.Release|Any CPU.Build.0 = Release|Any CPU
22 | {5178703E-2DE1-4543-B220-CEFECB113CBE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
23 | {5178703E-2DE1-4543-B220-CEFECB113CBE}.Debug|Any CPU.Build.0 = Debug|Any CPU
24 | {5178703E-2DE1-4543-B220-CEFECB113CBE}.Release|Any CPU.ActiveCfg = Release|Any CPU
25 | {5178703E-2DE1-4543-B220-CEFECB113CBE}.Release|Any CPU.Build.0 = Release|Any CPU
26 | {BF77D4DE-700A-4CFD-B902-C0B4DA2C83B5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
27 | {BF77D4DE-700A-4CFD-B902-C0B4DA2C83B5}.Debug|Any CPU.Build.0 = Debug|Any CPU
28 | {BF77D4DE-700A-4CFD-B902-C0B4DA2C83B5}.Release|Any CPU.ActiveCfg = Release|Any CPU
29 | {BF77D4DE-700A-4CFD-B902-C0B4DA2C83B5}.Release|Any CPU.Build.0 = Release|Any CPU
30 | EndGlobalSection
31 | GlobalSection(SolutionProperties) = preSolution
32 | HideSolutionNode = FALSE
33 | EndGlobalSection
34 | GlobalSection(ExtensibilityGlobals) = postSolution
35 | SolutionGuid = {8F217B3F-B5C6-4409-867D-31CE31CEDEBF}
36 | EndGlobalSection
37 | EndGlobal
38 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | {
2 | // Use IntelliSense to find out which attributes exist for C# debugging
3 | // Use hover for the description of the existing attributes
4 | // For further information visit https://github.com/OmniSharp/omnisharp-vscode/blob/master/debugger-launchjson.md
5 | "version": "0.2.0",
6 | "configurations": [
7 | {
8 | "name": ".NET Core Launch (web)",
9 | "type": "coreclr",
10 | "request": "launch",
11 | "preLaunchTask": "build",
12 | // If you have changed target frameworks, make sure to update the program path.
13 | "program": "${workspaceFolder}/bin/Debug/netcoreapp2.0/AspNetCoreIdentity.dll",
14 | "args": [],
15 | "cwd": "${workspaceFolder}",
16 | "stopAtEntry": false,
17 | "internalConsoleOptions": "openOnSessionStart",
18 | "launchBrowser": {
19 | "enabled": true,
20 | "args": "${auto-detect-url}",
21 | "windows": {
22 | "command": "cmd.exe",
23 | "args": "/C start ${auto-detect-url}"
24 | },
25 | "osx": {
26 | "command": "open"
27 | },
28 | "linux": {
29 | "command": "xdg-open"
30 | }
31 | },
32 | "env": {
33 | "ASPNETCORE_ENVIRONMENT": "Development"
34 | },
35 | "sourceFileMap": {
36 | "/Views": "${workspaceFolder}/Views"
37 | }
38 | },
39 | {
40 | "name": ".NET Core Attach",
41 | "type": "coreclr",
42 | "request": "attach",
43 | "processId": "${command:pickProcess}"
44 | }
45 | ,]
46 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "label": "build",
6 | "command": "dotnet",
7 | "type": "process",
8 | "args": [
9 | "build",
10 | "${workspaceFolder}/AspNetCoreIdentity.csproj"
11 | ],
12 | "problemMatcher": "$msCompile"
13 | }
14 | ]
15 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/.editorconfig:
--------------------------------------------------------------------------------
1 | # Editor configuration, see http://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 | [*.md]
12 | max_line_length = off
13 | trim_trailing_whitespace = false
14 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/.gitignore:
--------------------------------------------------------------------------------
1 | # See http://help.github.com/ignore-files/ for more about ignoring files.
2 |
3 | # compiled output
4 | /dist
5 | /dist-server
6 | /tmp
7 | /out-tsc
8 |
9 | # dependencies
10 | /node_modules
11 |
12 | # IDEs and editors
13 | /.idea
14 | .project
15 | .classpath
16 | .c9/
17 | *.launch
18 | .settings/
19 | *.sublime-workspace
20 |
21 | # IDE - VSCode
22 | .vscode/*
23 | !.vscode/settings.json
24 | !.vscode/tasks.json
25 | !.vscode/launch.json
26 | !.vscode/extensions.json
27 |
28 | # misc
29 | /.sass-cache
30 | /connect.lock
31 | /coverage
32 | /libpeerconnection.log
33 | npm-debug.log
34 | yarn-error.log
35 | testem.log
36 | /typings
37 |
38 | # System Files
39 | .DS_Store
40 | Thumbs.db
41 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/README.md:
--------------------------------------------------------------------------------
1 | # AspNetCoreIdentity
2 |
3 | This project was generated with [Angular CLI](https://github.com/angular/angular-cli) version 6.0.0.
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 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/browserslist:
--------------------------------------------------------------------------------
1 | # This file is currently used by autoprefixer to adjust CSS to support the below specified browsers
2 | # For additional information regarding the format and rule options, please see:
3 | # https://github.com/browserslist/browserslist#queries
4 | # For IE 9-11 support, please uncomment the last line of the file and adjust as needed
5 | > 0.5%
6 | last 2 versions
7 | Firefox ESR
8 | not dead
9 | # IE 9-11
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/e2e/protractor.conf.js:
--------------------------------------------------------------------------------
1 | // Protractor configuration file, see link for more information
2 | // https://github.com/angular/protractor/blob/master/lib/config.ts
3 |
4 | const { SpecReporter } = require("jasmine-spec-reporter");
5 |
6 | exports.config = {
7 | allScriptsTimeout: 11000,
8 | specs: ["./src/**/*.e2e-spec.ts"],
9 | capabilities: {
10 | browserName: "chrome"
11 | },
12 | directConnect: true,
13 | baseUrl: "http://localhost:4200/",
14 | framework: "jasmine",
15 | jasmineNodeOpts: {
16 | showColors: true,
17 | defaultTimeoutInterval: 30000,
18 | print: function() {}
19 | },
20 | onPrepare() {
21 | require("ts-node").register({
22 | project: require("path").join(__dirname, "./tsconfig.e2e.json")
23 | });
24 | jasmine
25 | .getEnv()
26 | .addReporter(new SpecReporter({ spec: { displayStacktrace: true } }));
27 | }
28 | };
29 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/e2e/src/app.e2e-spec.ts:
--------------------------------------------------------------------------------
1 | import { AppPage } from './app.po';
2 |
3 | describe('App', () => {
4 | let page: AppPage;
5 |
6 | beforeEach(() => {
7 | page = new AppPage();
8 | });
9 |
10 | it('should display welcome message', () => {
11 | page.navigateTo();
12 | expect(page.getMainHeading()).toEqual('Hello, world!');
13 | });
14 | });
15 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/e2e/src/app.po.ts:
--------------------------------------------------------------------------------
1 | import { browser, by, element } from 'protractor';
2 |
3 | export class AppPage {
4 | navigateTo() {
5 | return browser.get('/');
6 | }
7 |
8 | getMainHeading() {
9 | return element(by.css('app-root h1')).getText();
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/e2e/tsconfig.e2e.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "module": "commonjs",
6 | "target": "es5",
7 | "types": [
8 | "jasmine",
9 | "jasminewd2",
10 | "node"
11 | ]
12 | }
13 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "AspNetCoreIdentity",
3 | "version": "0.0.0",
4 | "scripts": {
5 | "ng": "ng",
6 | "start": "ng serve",
7 | "build": "ng build",
8 | "build:ssr": "ng run AspNetCoreIdentity:server:dev",
9 | "test": "ng test",
10 | "lint": "ng lint",
11 | "e2e": "ng e2e"
12 | },
13 | "private": true,
14 | "dependencies": {
15 | "@angular/animations": "8.0.0",
16 | "@angular/common": "8.0.0",
17 | "@angular/compiler": "8.0.0",
18 | "@angular/core": "8.0.0",
19 | "@angular/forms": "8.0.0",
20 | "@angular/platform-browser": "8.0.0",
21 | "@angular/platform-browser-dynamic": "8.0.0",
22 | "@angular/platform-server": "8.0.0",
23 | "@angular/router": "8.0.0",
24 | "@nguniversal/module-map-ngfactory-loader": "8.0.0-rc.1",
25 | "aspnet-prerendering": "^3.0.1",
26 | "bootstrap": "3.4.1",
27 | "core-js": "^2.6.5",
28 | "jquery": "3.4.1",
29 | "oidc-client": "^1.9.0",
30 | "popper.js": "^1.14.3",
31 | "rxjs": "^6.4.0",
32 | "zone.js": "~0.9.1"
33 | },
34 | "devDependencies": {
35 | "@angular-devkit/build-angular": "^0.800.6",
36 | "@angular/cli": "8.0.6",
37 | "@angular/compiler-cli": "8.0.0",
38 | "@angular/language-service": "8.0.0",
39 | "@types/jasmine": "~3.3.9",
40 | "@types/jasminewd2": "~2.0.6",
41 | "@types/node": "~11.10.5",
42 | "codelyzer": "^5.0.1",
43 | "jasmine-core": "~3.3.0",
44 | "jasmine-spec-reporter": "~4.2.1",
45 | "karma": "^4.0.0",
46 | "karma-chrome-launcher": "~2.2.0",
47 | "karma-coverage-istanbul-reporter": "~2.0.5",
48 | "karma-jasmine": "~2.0.1",
49 | "karma-jasmine-html-reporter": "^1.4.0",
50 | "typescript": "3.4.5"
51 | },
52 | "optionalDependencies": {
53 | "node-sass": "^4.9.3",
54 | "protractor": "~5.4.0",
55 | "ts-node": "~5.0.1",
56 | "tslint": "~5.9.1"
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/admin/admin.component.css:
--------------------------------------------------------------------------------
1 | .manage-users {
2 | padding: 50px 20px;
3 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/admin/admin.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Manage View
4 |
5 |
6 | {{users.length}} total users
7 |
8 |
9 |
10 |
11 |
Only administrators can access this view
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 | Username
21 | Email
22 | Email confirmed
23 | 2 FA enabled
24 | Lockout Enabled
25 |
26 |
27 |
28 |
29 |
30 |
31 | {{user.userName}}
32 | {{user.email}}
33 | {{user.emailConfirmed}}
34 | {{user.twoFactorEnabled}}
35 | {{user.lockoutEnabled}}
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/admin/admin.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { UserVM } from '../core/domain';
4 |
5 | @Component({
6 | selector: 'admin',
7 | templateUrl: './admin.component.html',
8 | styleUrls: ['./admin.component.css']
9 | })
10 | export class AdminComponent {
11 | public users: UserVM[] = [];
12 |
13 | constructor(public http: HttpClient, @Inject('BASE_URL') public baseUrl: string) {
14 | this.http.get(this.baseUrl + 'api/manage/users').subscribe(result => {
15 | this.users = result;
16 | console.log(this.users);
17 | }, error => console.error(error));
18 | }
19 | }
20 |
21 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/app.component.css:
--------------------------------------------------------------------------------
1 | @media (max-width: 767px) {
2 | /* On small screens, the nav menu spans the full width of the screen. Leave a space for it. */
3 | .body-content {
4 | padding-top: 50px;
5 | }
6 | }
7 |
8 | .navbar-default {
9 | box-shadow: 0 3px 8px 0 rgba(116, 129, 141, 0.1);
10 | border-bottom: 1px solid #d4dadf;
11 | background-color: #FFFFFF;
12 | }
13 |
14 | .navbar-default .navbar-nav > li > a {
15 | color: #3884FE !important;
16 | font-size: 16px;
17 | font-family: Content-font, Roboto, sans-serif;
18 | font-weight: 600;
19 | }
20 |
21 | .container-fluid.content {
22 | margin-top: 50px;
23 | }
24 |
25 | @media (max-width: 768px) {
26 | .navbar-default {
27 | display: none;
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/app.component.html:
--------------------------------------------------------------------------------
1 |
2 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/app.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { Router } from '@angular/router';
3 | import { HttpClient } from '@angular/common/http';
4 | import { StateService } from './core/state.service';
5 | import { Notification } from './core/domain';
6 | import { OpenIdConnectService } from './core/openid-connect.service';
7 |
8 | @Component({
9 | selector: 'app-root',
10 | templateUrl: './app.component.html',
11 | styleUrls: ['./app.component.css']
12 | })
13 | export class AppComponent {
14 |
15 | private notification: Notification = { message: '', type: '' };
16 |
17 | constructor(public http: HttpClient, stateService: StateService, router: Router,
18 | @Inject('BASE_URL') public baseUrl: string, openConnectIdService: OpenIdConnectService) {
19 | this.http.get(this.baseUrl + 'api/account/authenticated').subscribe(result => {
20 | var state = result;
21 | console.log(state);
22 | stateService.setAuthentication(state);
23 | stateService.setDisplayPassword(state.displaySetPassword);
24 | }, error => console.error(error));
25 |
26 | let code = this.getUrlParameter("code");
27 | if (code) {
28 | openConnectIdService.signinRedirectCallback().then(() => {
29 | router.navigate(['/share']);
30 | }).catch((error: any) => {
31 | console.error(error);
32 | });
33 | }
34 |
35 | let message = this.getUrlParameter("message");
36 | if (message) {
37 | let type = this.getUrlParameter("type");
38 | if (!type) {
39 | type = "success";
40 | }
41 |
42 | this.notification.message = message;
43 | this.notification.type = type;
44 | stateService.displayNotification(this.notification);
45 | console.log(message, type);
46 | if (type === "success") {
47 | router.navigate((['/']));
48 | }
49 | }
50 | }
51 |
52 | private getUrlParameter(name: string) {
53 | name = name.replace(/[\[]/, '\\[').replace(/[\]]/, '\\]');
54 | const regex = new RegExp('[\\?&]' + name + '=([^]*)');
55 | const results = regex.exec(location.search);
56 | return results === null ? '' : decodeURIComponent(results[1].replace(/\+/g, ' '));
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/app.module.ts:
--------------------------------------------------------------------------------
1 | import { BrowserModule } from '@angular/platform-browser';
2 | import { NgModule } from '@angular/core';
3 | import { FormsModule } from '@angular/forms';
4 | import { HttpClientModule, HTTP_INTERCEPTORS } from '@angular/common/http';
5 | import { RouterModule } from '@angular/router';
6 |
7 | import { AppComponent } from './app.component';
8 | import { NavMenuComponent } from './navmenu/navmenu.component';
9 | import { HomeComponent } from './home/home.component';
10 | import { ClaimsComponent } from './claims/claims.component';
11 | import { LoginComponent } from './login/login.component';
12 | import { RegisterComponent } from './register/register.component';
13 | import { ManageAccountComponent } from './manage-account/manage-account.component';
14 | import { AccessForbiddenComponent } from './claims/access-forbidden/access-forbidden.component';
15 | import { AdminComponent } from './admin/admin.component';
16 | import { AccountProfileComponent } from './manage-account/account-profile.component';
17 | import { SetupAuthenticatorComponent } from './manage-account/setup-authenticator.component';
18 | import { ResetAuthenticatorComponent } from './manage-account/reset-authenticator.component';
19 | import { StreamingComponent } from './streaming/streaming.component';
20 | import { StreamingRegisterComponent } from './streaming/register/register.component';
21 | import { AddVideoComponent } from './streaming/add/add-video.component';
22 | import { CounterComponent } from './counter/counter.component';
23 | import { FetchDataComponent } from './fetch-data/fetch-data.component';
24 |
25 | import { httpInterceptorProviders } from './core/http-interceptors/index';
26 | import { StateService } from './core/state.service';
27 | import { OpenIdConnectService } from './core/openid-connect.service';
28 | import { PasswordComponent } from './password/password.component';
29 | import { SocialApiShareComponent } from './socialapi/share.component';
30 |
31 |
32 | @NgModule({
33 | declarations: [
34 | AppComponent,
35 | ClaimsComponent,
36 | LoginComponent,
37 | RegisterComponent,
38 | AccessForbiddenComponent,
39 | AdminComponent,
40 | ManageAccountComponent,
41 | AccountProfileComponent,
42 | SetupAuthenticatorComponent,
43 | ResetAuthenticatorComponent,
44 | NavMenuComponent,
45 | HomeComponent,
46 | StreamingComponent,
47 | StreamingRegisterComponent,
48 | AddVideoComponent,
49 | SocialApiShareComponent,
50 | PasswordComponent,
51 | CounterComponent,
52 | FetchDataComponent
53 | ],
54 | imports: [
55 | BrowserModule.withServerTransition({ appId: 'ng-cli-universal' }),
56 | HttpClientModule,
57 | FormsModule,
58 | RouterModule.forRoot([
59 | { path: '', redirectTo: 'home', pathMatch: 'full' },
60 | { path: 'home', component: HomeComponent },
61 | { path: 'login', component: LoginComponent },
62 | { path: 'register', component: RegisterComponent },
63 | { path: 'access-forbidden', component: AccessForbiddenComponent },
64 | { path: 'manage/users', component: AdminComponent },
65 | { path: 'password', component: PasswordComponent },
66 | { path: 'share', component: SocialApiShareComponent },
67 | { path: 'claims', component: ClaimsComponent },
68 | { path: 'videos/:id', component: StreamingComponent },
69 | { path: 'videos', component: StreamingComponent },
70 | { path: 'streaming/register', component: StreamingRegisterComponent },
71 | { path: 'streaming/videos/add', component: AddVideoComponent },
72 | { path: 'manage/account', component: ManageAccountComponent },
73 | { path: 'counter', component: CounterComponent },
74 | { path: 'fetch-data', component: FetchDataComponent },
75 | { path: '**', redirectTo: 'home' }
76 | ])
77 | ],
78 | providers: [
79 | httpInterceptorProviders,
80 | StateService,
81 | OpenIdConnectService
82 | ],
83 | bootstrap: [AppComponent]
84 | })
85 | export class AppModule { }
86 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/app.server.module.ts:
--------------------------------------------------------------------------------
1 | import { NgModule } from '@angular/core';
2 | import { ServerModule } from '@angular/platform-server';
3 | import { ModuleMapLoaderModule } from '@nguniversal/module-map-ngfactory-loader';
4 | import { AppComponent } from './app.component';
5 | import { AppModule } from './app.module';
6 |
7 | @NgModule({
8 | imports: [AppModule, ServerModule, ModuleMapLoaderModule],
9 | bootstrap: [AppComponent]
10 | })
11 | export class AppServerModule { }
12 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/claims/access-forbidden/access-forbidden.component.css:
--------------------------------------------------------------------------------
1 | .center {text-align: center; margin-left: auto; margin-right: auto; margin-bottom: auto; margin-top: auto;}
2 |
3 | .forbidden {
4 | padding-bottom: 70px;
5 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/claims/access-forbidden/access-forbidden.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
Access Forbidden
5 |
6 | Error 403
7 |
8 |
9 |
10 |
You are not authorized to access this resource
11 |
12 |
13 |
14 |
15 |
16 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/claims/access-forbidden/access-forbidden.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'access-forbidden',
5 | templateUrl: './access-forbidden.component.html',
6 | styleUrls: ['./access-forbidden.component.css']
7 | })
8 | export class AccessForbiddenComponent {
9 | }
10 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/claims/claims.component.css:
--------------------------------------------------------------------------------
1 | .claims {
2 | word-wrap: break-word;
3 | }
4 |
5 | .claimType {
6 | color: rgb(56, 132, 254);
7 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/claims/claims.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
User account claims
5 |
6 |
7 |
8 |
9 |
10 | {{userName}}
11 |
12 |
13 |
14 |
15 |
16 |
17 | Type:
18 | {{claim.type}}
19 | Value:
20 | {{claim.value}}
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/claims/claims.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { ClaimsVM, UserClaims } from '../core/domain';
4 |
5 | @Component({
6 | selector: 'claims',
7 | templateUrl: './claims.component.html',
8 | styleUrls: ['./claims.component.css']
9 | })
10 | export class ClaimsComponent {
11 | public claims: ClaimsVM[] = [];
12 | public userName: string = '';
13 |
14 | constructor(public http: HttpClient,
15 | @Inject('BASE_URL') public baseUrl: string) {
16 | this.http.get(this.baseUrl + 'api/account/claims').subscribe(result => {
17 | var claimsResult = result;
18 | this.claims = claimsResult.claims;
19 | this.userName = claimsResult.userName;
20 | }, error => console.error(error));
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/core/domain.ts:
--------------------------------------------------------------------------------
1 | export interface UserVM {
2 | id: string;
3 | email: string;
4 | emailConfirmed: boolean;
5 | userName: string;
6 | lockoutEnabled: boolean;
7 | twoFactorEnabled: boolean;
8 | }
9 |
10 | export interface ClaimsVM {
11 | type: string;
12 | value: string;
13 | }
14 |
15 | export interface UserClaims {
16 | claims: ClaimsVM[];
17 | userName: string;
18 | }
19 |
20 | export interface LoginVM {
21 | username: string;
22 | password: string;
23 | }
24 |
25 | export interface RegisterVM {
26 | userName: string;
27 | email: string;
28 | password: string;
29 | confirmPassword: string;
30 | startFreeTrial: boolean;
31 | isAdmin: boolean;
32 | }
33 |
34 | export interface IContact {
35 | id: string;
36 | name: string;
37 | username: string;
38 | email: string;
39 | }
40 |
41 | export interface StreamingCategoryVM {
42 | category: string;
43 | value: number;
44 | registered: boolean;
45 | }
46 |
47 | export interface VideoVM {
48 | url: string;
49 | title: string;
50 | description: string;
51 | category: string;
52 | }
53 |
54 | export interface UserState {
55 | username: string;
56 | isAuthenticated: boolean;
57 | authenticationMethod: string;
58 | }
59 |
60 | export interface Notification {
61 | message: string;
62 | type: string;
63 | }
64 |
65 | export interface AccountDetailsVM {
66 | username: string;
67 | email: string;
68 | emailConfirmed: boolean;
69 | phoneNumber: string;
70 | externalLogins: string[];
71 | twoFactorEnabled: boolean;
72 | hasAuthenticator: boolean;
73 | twoFactorClientRemembered: boolean;
74 | recoveryCodesLeft: number[];
75 | }
76 |
77 | export interface AuthenticatorDetailsVM {
78 | sharedKey: string;
79 | authenticatorUri: string;
80 | }
81 |
82 | export interface ResultVM {
83 | status: StatusEnum;
84 | message: string;
85 | data: any;
86 | }
87 |
88 | export enum StatusEnum {
89 | Success = 1,
90 | Error = 2
91 | }
92 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/core/http-interceptors/auth-interceptor.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { HttpEvent, HttpInterceptor, HttpHandler, HttpRequest } from '@angular/common/http';
3 | import { Observable, throwError } from 'rxjs';
4 | import { catchError } from 'rxjs/operators';
5 | import { Router } from '@angular/router';
6 | import { StateService } from '../state.service';
7 |
8 | @Injectable()
9 | export class AuthInterceptor implements HttpInterceptor {
10 | constructor(public stateService: StateService, public router: Router) { }
11 |
12 | intercept(request: HttpRequest, next: HttpHandler): Observable> {
13 | return next.handle(request).pipe(catchError(err => {
14 | console.log(this.router.url);
15 | if (this.router.url == "/share")
16 | return Observable.throw(err);
17 |
18 | if ((err.status === 401) && (window.location.href.match(/\?/g) || []).length < 2) {
19 | this.stateService.setAuthentication({ username: '', isAuthenticated: false, authenticationMethod: '' });
20 | this.router.navigate(['/login']);
21 | }
22 | else if ((err.status === 403) && (window.location.href.match(/\?/g) || []).length < 2) {
23 | this.router.navigate(['/access-forbidden']);
24 | }
25 |
26 | const error = err.error.message || err.statusText;
27 | return throwError(error);
28 | }))
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/core/http-interceptors/index.ts:
--------------------------------------------------------------------------------
1 | /* "Barrel" of Http Interceptors */
2 | import { HTTP_INTERCEPTORS } from '@angular/common/http';
3 |
4 | import { AuthInterceptor } from './auth-interceptor';
5 |
6 |
7 | /** Http interceptor providers in outside-in order */
8 | export const httpInterceptorProviders = [
9 | { provide: HTTP_INTERCEPTORS, useClass: AuthInterceptor, multi: true }
10 | ];
11 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/core/openid-connect.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 |
3 | declare var Oidc : any;
4 |
5 | @Injectable()
6 | export class OpenIdConnectService {
7 |
8 | config = {
9 | authority: "http://localhost:5005",
10 | client_id: "AspNetCoreIdentity",
11 | redirect_uri: "http://localhost:5000",
12 | response_type: "code",
13 | scope: "openid profile SocialAPI",
14 | post_logout_redirect_uri: "http://localhost:5000",
15 | };
16 | userManager : any;
17 |
18 | constructor() {
19 | this.userManager = new Oidc.UserManager(this.config);
20 | }
21 |
22 | public getUser() {
23 | return this.userManager.getUser();
24 | }
25 |
26 | public login() {
27 | return this.userManager.signinRedirect();;
28 | }
29 |
30 | public signinRedirectCallback() {
31 | return new Oidc.UserManager({ response_mode: "query" }).signinRedirectCallback();
32 | }
33 |
34 | public logout() {
35 | this.userManager.signoutRedirect();
36 | }
37 |
38 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/core/state.service.ts:
--------------------------------------------------------------------------------
1 | import { Injectable } from '@angular/core';
2 | import { UserState, Notification } from './domain';
3 |
4 | @Injectable()
5 | export class StateService {
6 |
7 | public userState: UserState = { username: '', isAuthenticated: false, authenticationMethod: '' };
8 | public notification: Notification = { message: '', type: '' };
9 | public displaySetPassword: boolean = false;
10 |
11 | /**
12 | * setAuthentication
13 | */
14 | public setAuthentication(state: UserState) {
15 | this.userState = state;
16 | }
17 |
18 | public displayNotification(notify: Notification) {
19 | this.notification.message = notify.message;
20 | this.notification.type = notify.type;
21 |
22 | setTimeout(() => {
23 | this.notification.message = '';
24 | this.notification.type = '';
25 | }, 8000);
26 | }
27 |
28 | public setDisplayPassword(display: boolean) {
29 | this.displaySetPassword = display;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/counter/counter.component.html:
--------------------------------------------------------------------------------
1 | Counter
2 |
3 | This is a simple example of an Angular component.
4 |
5 | Current count: {{ currentCount }}
6 |
7 | Increment
8 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/counter/counter.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { async, ComponentFixture, TestBed } from '@angular/core/testing';
2 |
3 | import { CounterComponent } from './counter.component';
4 |
5 | describe('CounterComponent', () => {
6 | let component: CounterComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(async(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ CounterComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(CounterComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should display a title', async(() => {
23 | const titleText = fixture.nativeElement.querySelector('h1').textContent;
24 | expect(titleText).toEqual('Counter');
25 | }));
26 |
27 | it('should start with count 0, then increments by 1 when clicked', async(() => {
28 | const countElement = fixture.nativeElement.querySelector('strong');
29 | expect(countElement.textContent).toEqual('0');
30 |
31 | const incrementButton = fixture.nativeElement.querySelector('button');
32 | incrementButton.click();
33 | fixture.detectChanges();
34 | expect(countElement.textContent).toEqual('1');
35 | }));
36 | });
37 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/counter/counter.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-counter-component',
5 | templateUrl: './counter.component.html'
6 | })
7 | export class CounterComponent {
8 | public currentCount = 0;
9 |
10 | public incrementCounter() {
11 | this.currentCount++;
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/fetch-data/fetch-data.component.html:
--------------------------------------------------------------------------------
1 | Weather forecast
2 |
3 | This component demonstrates fetching data from the server.
4 |
5 | Loading...
6 |
7 |
8 |
9 |
10 | Date
11 | Temp. (C)
12 | Temp. (F)
13 | Summary
14 |
15 |
16 |
17 |
18 | {{ forecast.date }}
19 | {{ forecast.temperatureC }}
20 | {{ forecast.temperatureF }}
21 | {{ forecast.summary }}
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/fetch-data/fetch-data.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 |
4 | @Component({
5 | selector: 'app-fetch-data',
6 | templateUrl: './fetch-data.component.html'
7 | })
8 | export class FetchDataComponent {
9 | public forecasts: WeatherForecast[];
10 |
11 | constructor(http: HttpClient, @Inject('BASE_URL') baseUrl: string) {
12 | http.get(baseUrl + 'weatherforecast').subscribe(result => {
13 | this.forecasts = result;
14 | }, error => console.error(error));
15 | }
16 | }
17 |
18 | interface WeatherForecast {
19 | date: string;
20 | temperatureC: number;
21 | temperatureF: number;
22 | summary: string;
23 | }
24 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/home/home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 |
3 | @Component({
4 | selector: 'app-home',
5 | templateUrl: './home.component.html',
6 | })
7 | export class HomeComponent {
8 | }
9 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/login/login.component.css:
--------------------------------------------------------------------------------
1 | .login-form {
2 | padding-top: 90px;
3 | }
4 |
5 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/login/login.component.html:
--------------------------------------------------------------------------------
1 |
59 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/login/login.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, OnInit } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { Router } from '@angular/router';
4 | import { StateService } from '../core/state.service';
5 | import { LoginVM, ResultVM, StatusEnum } from '../core/domain';
6 |
7 | @Component({
8 | selector: 'login',
9 | templateUrl: './login.component.html',
10 | styleUrls: ['./login.component.css']
11 | })
12 | export class LoginComponent implements OnInit {
13 | public user: LoginVM = { username: '', password: '' }
14 | public errors: string = '';
15 | public socialProviders: string[] = [];
16 | public requires2FA: boolean = false;
17 | public twoFaCode: string = '';
18 | public useRecoveryCode: boolean = false;
19 | public rememberMachine: boolean = false;
20 |
21 | constructor(public http: HttpClient,
22 | @Inject('BASE_URL') public baseUrl: string,
23 | public router: Router, public stateService: StateService) {
24 | }
25 |
26 | ngOnInit() {
27 | this.http.get(this.baseUrl + 'socialaccount/providers')
28 | .subscribe(result => {
29 | this.socialProviders = result;
30 | console.log(this.socialProviders);
31 | });
32 | }
33 |
34 | onKeydown(event: any) {
35 | if (event.key === "Enter") {
36 | this.login();
37 | }
38 | }
39 |
40 | twoFaPlaceholder() {
41 | return this.useRecoveryCode ? 'Enter recovery code' : '6-digit code';
42 | }
43 |
44 | login() {
45 | this.errors = '';
46 | console.log(this.user);
47 |
48 | if (this.requires2FA) {
49 |
50 | let data = {};
51 | let uri = '';
52 |
53 | if (this.useRecoveryCode) {
54 | data = {
55 | recoveryCode: this.twoFaCode
56 | };
57 | uri = 'api/twoFactorAuthentication/loginWithRecovery';
58 | } else {
59 | data = {
60 | twoFactorCode: this.twoFaCode,
61 | rememberMachine: this.rememberMachine
62 | };
63 | uri = 'api/twoFactorAuthentication/login';
64 | }
65 |
66 | this.http.post(this.baseUrl + uri, data).subscribe(result => {
67 | let loginResult = result;
68 | if (loginResult.status === StatusEnum.Success) {
69 | this.stateService.setAuthentication({
70 | isAuthenticated: true,
71 | username: this.user.username,
72 | authenticationMethod: ''
73 | });
74 | this.router.navigate(['/home']);
75 | } else if (loginResult.status === StatusEnum.Error) {
76 | this.errors = loginResult.data.toString();
77 | }
78 |
79 | },
80 | error => console.error(error));
81 | } else {
82 | this.http.post(this.baseUrl + 'api/account/login', this.user).subscribe(result => {
83 | let loginResult = result;
84 | if (loginResult.status === StatusEnum.Success) {
85 | if (loginResult.data.requires2FA) {
86 | this.requires2FA = true;
87 | this.stateService.displayNotification({ message: loginResult.message, type: "success" });
88 | return;
89 | }
90 | this.stateService.setAuthentication({
91 | isAuthenticated: true,
92 | username: this.user.username,
93 | authenticationMethod: ''
94 | });
95 | this.router.navigate(['/home']);
96 | } else if (loginResult.status === StatusEnum.Error) {
97 | this.errors = loginResult.data.toString();
98 | }
99 |
100 | },
101 | error => console.error(error));
102 | }
103 | }
104 | }
105 |
106 |
107 |
108 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/account-profile.component.css:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/account-profile.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | Basic info
4 |
5 |
6 | Username
7 | Email
8 | 2FA Enabled
9 | Has Authenticator
10 | 2FA Remember
11 |
12 |
13 |
14 |
15 | {{accountDetails.username}}
16 | {{accountDetails.email}}
17 | {{accountDetails.twoFactorEnabled}}
18 | {{accountDetails.hasAuthenticator}}
19 | {{accountDetails.twoFactorClientRemembered}}
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | External providers
28 |
29 |
30 | #
31 | Provider
32 |
33 |
34 |
35 |
36 |
37 | {{i+1}}
38 | {{provider}}
39 |
40 |
41 |
42 |
43 |
44 |
45 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/account-profile.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Input } from '@angular/core';
2 | import { AccountDetailsVM } from '../core/domain';
3 |
4 | @Component({
5 | selector: 'account-profile',
6 | templateUrl: './account-profile.component.html',
7 | styleUrls: ['./account-profile.component.css']
8 | })
9 | export class AccountProfileComponent {
10 |
11 | @Input() accountDetails: AccountDetailsVM;
12 |
13 | }
14 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/manage-account.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
6 |
7 |
19 |
20 |
21 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/manage-account.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { StateService } from '../core/state.service';
4 | import { AccountDetailsVM } from '../core/domain';
5 |
6 | @Component({
7 | selector: 'manage-account',
8 | templateUrl: './manage-account.component.html',
9 | })
10 | export class ManageAccountComponent {
11 |
12 | public accountDetails: AccountDetailsVM = {};
13 |
14 | constructor(public http: HttpClient, @Inject('BASE_URL') public baseUrl: string,
15 | public stateService: StateService) {
16 | this.http.get(this.baseUrl + 'api/twoFactorAuthentication/details').subscribe(result => {
17 | this.accountDetails = result;
18 | console.log(this.accountDetails);
19 | }, error => console.error(error));
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/reset-authenticator.component.css:
--------------------------------------------------------------------------------
1 | #genQrCode {
2 | margin: 20px auto;
3 | padding: 20px;
4 | text-align: center;
5 | background: #ffffff;
6 | }
7 |
8 | .validVerificationCode {
9 | margin-right: 5px;
10 | color: #3884FE;
11 | cursor: pointer;
12 | font-weight: 600;
13 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/reset-authenticator.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | If you reset your authenticator key your authenticator app will not work until you reconfigure it.
6 |
7 |
8 | This process disables 2FA until you verify your authenticator app.
9 | If you do not complete your authenticator app configuration you may lose access to your account.
10 |
11 |
12 |
Reset authenticator
13 |
14 |
15 |
16 |
17 |
18 | Your authenticator app key has been reset successfully
19 |
20 |
21 | 2FA has been disabled until you verify your authenticator app.
22 | If you do not complete your authenticator app configuration you may lose access to your account.
23 |
24 |
25 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/reset-authenticator.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, Input } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { StateService } from '../core/state.service';
4 | import { ResultVM, StatusEnum, AccountDetailsVM } from '../core/domain';
5 |
6 | @Component({
7 | selector: 'reset-authenticator',
8 | templateUrl: './reset-authenticator.component.html',
9 | styleUrls: ['./reset-authenticator.component.css']
10 | })
11 | export class ResetAuthenticatorComponent {
12 |
13 | @Input() accountDetails: AccountDetailsVM;
14 | public authenticatorNeedsSetup: boolean = false;
15 |
16 | constructor(public http: HttpClient, @Inject('BASE_URL') public baseUrl: string,
17 | public stateService: StateService) {
18 | }
19 |
20 | resetAuthenticator() {
21 | this.http.post(this.baseUrl + 'api/twoFactorAuthentication/resetAuthenticator', {}).subscribe(result => {
22 |
23 | let resetAuthenticatorResult = result;
24 |
25 | if (resetAuthenticatorResult.status === StatusEnum.Success) {
26 | this.stateService.displayNotification({ message: resetAuthenticatorResult.message, type: "success" });
27 | this.accountDetails.twoFactorEnabled = false;
28 | this.authenticatorNeedsSetup = true;
29 | }
30 | },
31 | error => console.error(error));
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/setup-authenticator.component.css:
--------------------------------------------------------------------------------
1 | #genQrCode {
2 | margin: 20px auto;
3 | padding: 20px;
4 | text-align: center;
5 | background: #ffffff;
6 | }
7 |
8 | .authenticatorKey {
9 | text-align: center;
10 | font-size: 20px;
11 | color: #8a6d3b;
12 | }
13 |
14 | .validVerificationCode {
15 | margin-right: 5px;
16 | color: #3884FE;
17 | cursor: pointer;
18 | font-weight: 600;
19 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/manage-account/setup-authenticator.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 | {{(accountDetails.hasAuthenticator && !accountDetails.twoFactorEnabled) === true ? "Enable 2FA" : "Setup authenticator"}}
4 |
5 |
6 |
7 | Reset recovery codes
8 |
9 |
10 | Disable 2FA
11 |
12 |
13 |
Scan the QR Code or enter the following key into your two factor authenticator app. Spaces and casing do not matter
14 |
{{authenticatorDetails.sharedKey}}
15 |
Generating...
16 |
17 | Once you have scanned the QR code or input the key above, your two factor authentication app will provide you with a unique code. Enter the code in the confirmation box below
18 |
19 | Verify
20 |
21 |
24 |
25 |
26 | Show possible verification codes
27 |
28 |
29 |
0">
30 |
Valid 6-digit verification codes
31 | {{validVerificationCode}}
32 |
33 |
34 | 0">
35 |
Copy and save your recovery codes
36 |
37 | {{recoveryCode}}
38 |
39 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/navmenu/navmenu.component.css:
--------------------------------------------------------------------------------
1 | li .glyphicon {
2 | margin-right: 10px;
3 | }
4 |
5 | .navbar-inverse .navbar-nav > li > a {
6 | color: #5C6975;
7 | }
8 |
9 | /* Highlighting rules for nav menu items */
10 | .navbar-inverse .navbar-nav > li > a:hover,
11 | li.link-active a,
12 | li.link-active a:hover,
13 | li.link-active a:focus {
14 | background: rgb(230, 236, 241);
15 | }
16 |
17 | /* Keep the nav menu independent of scrolling and on top of other items */
18 | .main-nav {
19 | position: fixed;
20 | top: 0;
21 | left: 0;
22 | right: 0;
23 | z-index: 1;
24 | }
25 |
26 | .logout {
27 | cursor: pointer;
28 | }
29 |
30 | .navbar-inverse {
31 | background: #F5F7F9;
32 | border-right: 1px solid #E6ECF1;
33 | }
34 |
35 | @media (min-width: 768px) {
36 | /* On small screens, convert the nav menu to a vertical sidebar */
37 | .main-nav {
38 | height: 100%;
39 | width: calc(25% - 20px);
40 | }
41 |
42 | .navbar {
43 | border-radius: 0px;
44 | border-width: 0px;
45 | height: 100%;
46 | }
47 |
48 | .navbar-header {
49 | float: none;
50 | }
51 |
52 | .navbar-collapse {
53 | border-top: 1px solid #444;
54 | padding: 0px;
55 | }
56 |
57 | .navbar ul {
58 | float: none;
59 | }
60 |
61 | .navbar li {
62 | float: none;
63 | font-size: 15px;
64 | margin: 6px;
65 | }
66 |
67 | .navbar li a {
68 | padding: 10px 16px;
69 | border-radius: 4px;
70 | }
71 |
72 | .navbar a {
73 | /* If a menu item's text is too long, truncate it */
74 | width: 100%;
75 | white-space: nowrap;
76 | overflow: hidden;
77 | text-overflow: ellipsis;
78 | }
79 | }
80 |
81 | @media (max-width: 768px) {
82 | /* On small screens, convert the nav menu to a vertical sidebar */
83 | .main-nav {
84 | z-index: 5;
85 | }
86 |
87 | .navbar-inverse {
88 | box-shadow: 0 3px 8px 0 rgba(116, 129, 141, 0.1);
89 | border-bottom: 1px solid #d4dadf;
90 | background-color: #FFFFFF;
91 | }
92 |
93 | .navbar-inverse .navbar-toggle {
94 | background-color: #3884FE;
95 | }
96 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/navmenu/navmenu.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { StateService } from '../core/state.service';
3 | import { Router } from '@angular/router';
4 | import { HttpClient } from '@angular/common/http';
5 |
6 | @Component({
7 | selector: 'nav-menu',
8 | templateUrl: './navmenu.component.html',
9 | styleUrls: ['./navmenu.component.css']
10 | })
11 | export class NavMenuComponent {
12 | constructor(public stateService: StateService, public router: Router,
13 | public http: HttpClient,
14 | @Inject('BASE_URL') public baseUrl: string) { }
15 |
16 | logout() {
17 | this.http.post(this.baseUrl + 'api/account/signout', {}).subscribe(result => {
18 | this.stateService.setAuthentication({ username: '', isAuthenticated: false, authenticationMethod: '' });
19 | this.router.navigate(['/home']);
20 | }, error => console.error(error));
21 |
22 | }
23 |
24 | closeMenu() {
25 | if (window.innerWidth < 768) {
26 | let navButton = document.getElementById('navButton');
27 | if (navButton) {
28 | navButton.click();
29 | }
30 | }
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/password/password.component.css:
--------------------------------------------------------------------------------
1 | .password {
2 | padding-top: 90px;
3 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/password/password.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Set password
7 |
8 |
9 |
35 |
36 |
37 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/password/password.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, OnInit } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { Router } from '@angular/router';
4 | import { ResultVM, StatusEnum } from '../core/domain';
5 | import { StateService } from '../core/state.service';
6 |
7 | @Component({
8 | selector: 'password',
9 | templateUrl: './password.component.html',
10 | styleUrls: ['./password.component.css']
11 | })
12 | export class PasswordComponent implements OnInit {
13 |
14 | public errors: string = '';
15 | public password: string = '';
16 | public confirmPassword: string = '';
17 |
18 | constructor(public http: HttpClient,
19 | @Inject('BASE_URL') public baseUrl: string,
20 | public router: Router, public stateService: StateService) {
21 | }
22 |
23 | ngOnInit() {
24 |
25 | }
26 |
27 | onKeydown(event: any) {
28 | if (event.key === "Enter") {
29 | this.setPassword();
30 | }
31 | }
32 |
33 | setPassword() {
34 | this.errors = '';
35 |
36 | let updatePassword = {
37 | password: this.password,
38 | confirmPassword: this.confirmPassword
39 | };
40 |
41 |
42 | this.http.post(this.baseUrl + 'api/account/managePassword', updatePassword).subscribe(result => {
43 | let setPasswordResult = result;
44 |
45 | if (setPasswordResult.status === StatusEnum.Success) {
46 | this.stateService.displayNotification({ message: setPasswordResult.message, type: "success" });
47 | this.stateService.setDisplayPassword(false);
48 | this.router.navigate(['/']);
49 | } else if (setPasswordResult.status === StatusEnum.Error) {
50 | this.errors = setPasswordResult.data.toString();
51 | }
52 | },
53 | error => console.error(error));
54 | }
55 | }
56 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/register/register.component.css:
--------------------------------------------------------------------------------
1 | .registration-form {
2 | padding-top: 90px;
3 | }
4 |
5 | label.trial {
6 | color: #5C6975;
7 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/socialapi/share.component.css:
--------------------------------------------------------------------------------
1 | .share {
2 | padding-top: 90px;
3 | }
4 |
5 | .message {
6 | padding: 14px;
7 | }
8 |
9 | .contact {
10 | color: #4189C7;
11 | font-size: large;
12 | }
13 |
14 | .grant-error {
15 | color: #d9534f;
16 | }
17 |
18 | .grant-success {
19 | text-align: center;
20 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/socialapi/share.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Share with Social API
6 |
7 |
8 |
9 |
10 | It looks like you are not logged in with Social API. Press the Log In button to continue and get access to Social API.
11 |
12 |
25 |
26 |
27 |
28 |
29 | Congratulations! You have successfully connected with Social API!
30 |
31 |
32 | It seems though you haven't granted access to the Social API
33 |
34 |
35 |
36 |
37 | {{contact.name}} {{contact.username}}
38 |
39 |
40 | ASP.NET Core Identity Series has been shared with your contacts
41 |
42 |
43 |
56 |
57 |
58 |
59 |
60 |
61 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/socialapi/share.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { HttpClient, HttpHeaders } from '@angular/common/http';
3 | import { Router } from '@angular/router';
4 | import { IContact } from '../core/domain';
5 | import { StateService } from '../core/state.service';
6 | import { OpenIdConnectService } from '../core/openid-connect.service';
7 |
8 | @Component({
9 | selector: 'social-api-share',
10 | templateUrl: './share.component.html',
11 | styleUrls: ['./share.component.css']
12 | })
13 | export class SocialApiShareComponent {
14 |
15 | public socialLoggedIn: any;
16 | public contacts: IContact[] = [];
17 | public socialApiAccessDenied : boolean = false;
18 |
19 | constructor(public http: HttpClient,
20 | public openConnectIdService: OpenIdConnectService,
21 | public router: Router, public stateService: StateService) {
22 | openConnectIdService.getUser().then((user: any) => {
23 | if (user) {
24 | console.log("User logged in", user.profile);
25 | console.log(user);
26 | this.socialLoggedIn = true;
27 | const headers = new HttpHeaders({'Authorization':`Bearer ${user.access_token}`});
28 | const socialApiContactsURI = "http://localhost:5010/api/contacts";
29 |
30 | this.http.get(socialApiContactsURI, {headers: headers}).subscribe(result => {
31 | this.contacts = result;
32 |
33 | }, error => {
34 | if (error.status === 401) {
35 | this.socialApiAccessDenied = true;
36 | }
37 | });
38 | }
39 |
40 | });
41 | }
42 |
43 | login() {
44 | this.openConnectIdService.login();
45 | }
46 |
47 | logout() {
48 | this.openConnectIdService.logout();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/add/add-video.component.css:
--------------------------------------------------------------------------------
1 | label.trial {
2 | color: whitesmoke;
3 | }
4 |
5 | select.selectpicker {
6 | width: 100%;
7 | padding-right: 25px;
8 | z-index: 1;
9 | color: #333;
10 | background-color: #fff;
11 | border-color: #ccc;
12 | height: 45px;
13 | border: 1px solid #ddd;
14 | font-size: 16px;
15 | -webkit-transition: all 0.1s linear;
16 | -moz-transition: all 0.1s linear;
17 | transition: all 0.1s linear;
18 | border-color: #ccc;
19 | border-radius: 4px;
20 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/add/add-video.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Add YouTube video to registered category
4 |
5 |
6 |
7 |
8 |
9 | Add New Video
10 |
11 |
12 |
45 |
46 |
47 |
48 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/add/add-video.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { HttpClient, HttpHeaders } from '@angular/common/http';
3 | import { VideoVM, StreamingCategoryVM } from '../../core/domain';
4 | import { Router } from '@angular/router';
5 |
6 | @Component({
7 | selector: 'add-video',
8 | templateUrl: './add-video.component.html',
9 | styleUrls: ['./add-video.component.css']
10 | })
11 | export class AddVideoComponent {
12 | public categories: StreamingCategoryVM[] = [];
13 | public newVideo: VideoVM = { title: '', category: '', description: '', url: '' };
14 |
15 | constructor(public http: HttpClient, @Inject('BASE_URL') public baseUrl: string,
16 | private router: Router) {
17 | this.http.get(this.baseUrl + 'api/streaming/videos/register').subscribe(result => {
18 | this.categories = result;
19 | this.newVideo.category = this.categories[0].category;
20 | }, error => console.error(error));
21 | }
22 |
23 | addVideo() {
24 | let categoryId = this.categories.find(cat => cat.category === this.newVideo.category).value;
25 | console.log(this.newVideo);
26 | var postData = {
27 | title: this.newVideo.title,
28 | category: categoryId,
29 | description: this.newVideo.description,
30 | url: this.newVideo.url
31 | };
32 | this.http.post(this.baseUrl + 'api/streaming/videos/add', postData).subscribe(result => {
33 | this.router.navigate(['videos', this.newVideo.category]);
34 | }, error => console.error(error));
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/register/register.component.css:
--------------------------------------------------------------------------------
1 |
2 | .list-group-item.active {
3 | background-color: #3884FE !important;
4 | border-color: #3884FE !important;
5 | }
6 |
7 | .registered, .list-group-item .registered {
8 | background-color: #f5f5f5;
9 | color: green;
10 | }
11 |
12 | button.save {
13 | text-align: center;
14 | }
15 |
16 | button.add {
17 | text-align: center;
18 | background: #eee !important;
19 | color: #232222;
20 | }
21 |
22 | .streaming-category {
23 | cursor: pointer;
24 | }
25 |
26 | .category-button {
27 | padding: 0px 10px !important;
28 | }
29 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/register/register.component.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
Streaming Video Categories
4 |
5 |
6 |
7 |
19 |
20 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/register/register.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject, ViewContainerRef } from '@angular/core';
2 | import { HttpClient, HttpRequest, HttpHeaders } from '@angular/common/http';
3 | import { Router } from '@angular/router';
4 | import { StreamingCategoryVM } from '../../core/domain';
5 | import { StateService } from 'src/app/core/state.service';
6 |
7 | @Component({
8 | selector: 'streaming-register',
9 | templateUrl: './register.component.html',
10 | styleUrls: ['./register.component.css']
11 | })
12 | export class StreamingRegisterComponent {
13 | public categories: StreamingCategoryVM[] = [];
14 | public checkedAll: boolean = false;
15 | public displayVideoForm: boolean = false;
16 |
17 | constructor(public http: HttpClient, @Inject('BASE_URL') public baseUrl: string,
18 | private router: Router, public vcr: ViewContainerRef,
19 | public stateService: StateService) {
20 | this.http.get(this.baseUrl + 'api/streaming/videos/register').subscribe(result => {
21 | this.categories = result;
22 | console.log(this.categories);
23 | }, error => console.error(error));
24 |
25 | }
26 |
27 | toggleCategories($event: any) {
28 | var check = $event.target.checked;
29 |
30 | this.categories.forEach(c => c.registered = check);
31 |
32 | this.update();
33 | }
34 |
35 | toggleCategory(category: StreamingCategoryVM) {
36 | category.registered = !category.registered;
37 |
38 | if(!category.registered) {
39 | this.checkedAll = false;
40 | }
41 |
42 | this.update();
43 | }
44 |
45 | update() {
46 | var categories = this.categories.filter(c => c.registered === true).map(c => c.category);
47 | const headers = new HttpHeaders({'Content-Type':'application/json; charset=utf-8'});
48 |
49 | this.http.post(this.baseUrl + 'api/streaming/videos/register',
50 | JSON.stringify(categories), { headers: headers }).subscribe(result => {
51 | this.stateService.displayNotification({ message: 'Categories updated', type: "success" });
52 | }, error => console.error(error));
53 | }
54 |
55 | viewCategory(event: any, category: string) {
56 | event.stopPropagation();
57 | this.router.navigate(['/videos', category]);
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/streaming.component.css:
--------------------------------------------------------------------------------
1 | .video {
2 | padding-bottom: 20px;
3 | padding-top: 10px;
4 | }
5 |
6 | .video-title {
7 | color:whitesmoke;
8 | }
9 |
10 | .video-description {
11 | color: #3884FE;
12 | }
13 |
14 | .video-category {
15 | font-style: italic;
16 | color: brown;
17 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/streaming.component.html:
--------------------------------------------------------------------------------
1 |
2 |
Sample videos available in our Streaming Service
3 |
4 |
5 |
Want the full experience? Click
6 | here and register for specific Streaming Categories!
7 |
8 | 0">
9 |
You are subscribed to the {{category}} of our Streaming Service
10 |
11 |
12 | {{videos.length}} total videos
13 |
14 |
15 |
16 |
{{video.title}}
17 |
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/app/streaming/streaming.component.ts:
--------------------------------------------------------------------------------
1 | import { Component, Inject } from '@angular/core';
2 | import { HttpClient } from '@angular/common/http';
3 | import { DomSanitizer } from '@angular/platform-browser';
4 | import { ActivatedRoute } from '@angular/router';
5 | import { VideoVM } from '../core/domain';
6 |
7 | @Component({
8 | selector: 'streaming',
9 | templateUrl: './streaming.component.html',
10 | styleUrls: ['./streaming.component.css']
11 | })
12 | export class StreamingComponent {
13 | public videos: VideoVM[] = [];
14 | public category: string = '';
15 | private sub: any;
16 |
17 | constructor(public http: HttpClient, public sanitizer: DomSanitizer,
18 | @Inject('BASE_URL') public baseUrl: string, private route: ActivatedRoute) { }
19 |
20 | ngOnInit() {
21 | this.sub = this.route.params.subscribe(params => {
22 | this.category = params['id'] || '';
23 | var route = this.category.length === 0 ? 'videos' : this.category
24 | this.http.get(this.baseUrl + `api/streaming/${route}`).subscribe(result => {
25 | this.videos = result;
26 | console.log(this.videos);
27 | }, error => console.error(error));
28 | });
29 | }
30 |
31 | ngOnDestroy() {
32 | this.sub.unsubscribe();
33 | }
34 |
35 | sanitizeUrl(url: string) {
36 | return this.sanitizer.bypassSecurityTrustResourceUrl(url);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/ClientApp/src/assets/.gitkeep
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/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 | * In development mode, to ignore zone related error stack frames such as
11 | * `zone.run`, `zoneDelegate.invokeTask` for easier debugging, you can
12 | * import the following file, but please comment it out in production mode
13 | * because it will have performance impact when throw error
14 | */
15 | // import 'zone.js/dist/zone-error'; // Included with Angular CLI.
16 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | chsakell's blog - Identity Series
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | Loading...
18 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/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'),
20 | reports: ['html', 'lcovonly'],
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 | });
31 | };
32 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/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 | export function getBaseUrl() {
8 | return document.getElementsByTagName('base')[0].href;
9 | }
10 |
11 | const providers = [
12 | { provide: 'BASE_URL', useFactory: getBaseUrl, deps: [] }
13 | ];
14 |
15 | if (environment.production) {
16 | enableProdMode();
17 | }
18 |
19 | platformBrowserDynamic(providers).bootstrapModule(AppModule)
20 | .catch(err => console.log(err));
21 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/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.ts';
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 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/styles.css:
--------------------------------------------------------------------------------
1 | /* You can add global styles to this file, and also import other style files */
2 |
3 | /* Provide sufficient contrast against white background */
4 | a {
5 | color: #0366d6;
6 | }
7 |
8 | code {
9 | color: #e01a76;
10 | }
11 |
12 | .btn-primary {
13 | color: #fff;
14 | background-color: #1b6ec2;
15 | border-color: #1861ac;
16 | }
17 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/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: any;
11 |
12 | // First, initialize the Angular testing environment.
13 | getTestBed().initTestEnvironment(
14 | BrowserDynamicTestingModule,
15 | platformBrowserDynamicTesting()
16 | );
17 | // Then we find all the tests.
18 | const context = require.context('./', true, /\.spec\.ts$/);
19 | // And load the modules.
20 | context.keys().map(context);
21 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "exclude": [
8 | "src/test.ts",
9 | "**/*.spec.ts"
10 | ]
11 | }
12 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/tsconfig.server.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "module": "commonjs"
5 | },
6 | "angularCompilerOptions": {
7 | "entryModule": "app/app.server.module#AppServerModule"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/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 | "test.ts",
12 | "polyfills.ts"
13 | ],
14 | "include": [
15 | "**/*.spec.ts",
16 | "**/*.d.ts"
17 | ]
18 | }
19 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/src/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tslint.json",
3 | "rules": {
4 | "directive-selector": [
5 | true,
6 | "attribute",
7 | "app",
8 | "camelCase"
9 | ],
10 | "component-selector": [
11 | true,
12 | "element",
13 | "app",
14 | "kebab-case"
15 | ]
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "module": "esnext",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "moduleResolution": "node",
10 | "emitDecoratorMetadata": true,
11 | "experimentalDecorators": true,
12 | "target": "es2015",
13 | "typeRoots": [
14 | "node_modules/@types"
15 | ],
16 | "lib": [
17 | "es2017",
18 | "dom"
19 | ]
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ClientApp/tslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "rulesDirectory": [
3 | "node_modules/codelyzer"
4 | ],
5 | "rules": {
6 | "arrow-return-shorthand": true,
7 | "callable-types": true,
8 | "class-name": true,
9 | "comment-format": [
10 | true,
11 | "check-space"
12 | ],
13 | "curly": true,
14 | "deprecation": {
15 | "severity": "warn"
16 | },
17 | "eofline": true,
18 | "forin": true,
19 | "import-blacklist": [
20 | true,
21 | "rxjs/Rx"
22 | ],
23 | "import-spacing": true,
24 | "indent": [
25 | true,
26 | "spaces"
27 | ],
28 | "interface-over-type-literal": true,
29 | "label-position": true,
30 | "max-line-length": [
31 | true,
32 | 140
33 | ],
34 | "member-access": false,
35 | "member-ordering": [
36 | true,
37 | {
38 | "order": [
39 | "static-field",
40 | "instance-field",
41 | "static-method",
42 | "instance-method"
43 | ]
44 | }
45 | ],
46 | "no-arg": true,
47 | "no-bitwise": true,
48 | "no-console": [
49 | true,
50 | "debug",
51 | "info",
52 | "time",
53 | "timeEnd",
54 | "trace"
55 | ],
56 | "no-construct": true,
57 | "no-debugger": true,
58 | "no-duplicate-super": true,
59 | "no-empty": false,
60 | "no-empty-interface": true,
61 | "no-eval": true,
62 | "no-inferrable-types": [
63 | true,
64 | "ignore-params"
65 | ],
66 | "no-misused-new": true,
67 | "no-non-null-assertion": true,
68 | "no-shadowed-variable": true,
69 | "no-string-literal": false,
70 | "no-string-throw": true,
71 | "no-switch-case-fall-through": true,
72 | "no-trailing-whitespace": true,
73 | "no-unnecessary-initializer": true,
74 | "no-unused-expression": true,
75 | "no-use-before-declare": true,
76 | "no-var-keyword": true,
77 | "object-literal-sort-keys": false,
78 | "one-line": [
79 | true,
80 | "check-open-brace",
81 | "check-catch",
82 | "check-else",
83 | "check-whitespace"
84 | ],
85 | "prefer-const": true,
86 | "quotemark": [
87 | true,
88 | "single"
89 | ],
90 | "radix": true,
91 | "semicolon": [
92 | true,
93 | "always"
94 | ],
95 | "triple-equals": [
96 | true,
97 | "allow-null-check"
98 | ],
99 | "typedef-whitespace": [
100 | true,
101 | {
102 | "call-signature": "nospace",
103 | "index-signature": "nospace",
104 | "parameter": "nospace",
105 | "property-declaration": "nospace",
106 | "variable-declaration": "nospace"
107 | }
108 | ],
109 | "unified-signatures": true,
110 | "variable-name": false,
111 | "whitespace": [
112 | true,
113 | "check-branch",
114 | "check-decl",
115 | "check-operator",
116 | "check-separator",
117 | "check-type"
118 | ],
119 | "no-output-on-prefix": true,
120 | "no-inputs-metadata-property": true,
121 | "no-outputs-metadata-property": true,
122 | "no-host-metadata-property": true,
123 | "no-input-rename": true,
124 | "no-output-rename": true,
125 | "use-lifecycle-interface": true,
126 | "use-pipe-transform-interface": true,
127 | "component-class-suffix": true,
128 | "directive-class-suffix": true
129 | }
130 | }
131 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Controllers/ClientAppSettings.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 |
3 | namespace AspNetCoreIdentity.Controllers
4 | {
5 | public class ClientAppSettings : Controller
6 | {
7 | public IActionResult Index()
8 | {
9 | var settings = new
10 | {
11 | stsServer = "http://localhost:5005",
12 | redirect_url = "http://localhost:5000",
13 | client_id = "angularclient",
14 | response_type = "code",
15 | scope = "openid profile SocialAPI",
16 | post_logout_redirect_uri = "http://localhost:5000",
17 | start_checksession = true,
18 | silent_renew = true,
19 | post_login_route = "/home",
20 | forbidden_route = "/forbidden",
21 | unauthorized_route = "/unauthorized",
22 | log_console_warning_active = true,
23 | log_console_debug_active = true,
24 | max_id_token_iat_offset_allowed_in_seconds = 10,
25 | apiServer = "http://localhost:5010"
26 | };
27 |
28 | return Ok(settings);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using System.Diagnostics;
2 | using AspNetCoreIdentity.Infrastructure;
3 | using Microsoft.AspNetCore.Mvc;
4 |
5 |
6 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Controllers/ManageController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.AspNetCore.Authorization;
3 | using Microsoft.AspNetCore.Identity;
4 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
5 | using Microsoft.AspNetCore.Mvc;
6 |
7 | namespace AspNetCoreIdentity.Controllers {
8 | [Route ("api/[controller]/[action]")]
9 | [Authorize(Policy = "AdminOnly")]
10 | public class ManageController : Controller {
11 | private readonly UserManager _userManager;
12 | private readonly RoleManager _roleManager;
13 | private readonly IdentityDbContext _context;
14 |
15 | public ManageController (UserManager userManager,
16 | RoleManager roleManager, IdentityDbContext context) {
17 | _userManager = userManager;
18 | _roleManager = roleManager;
19 | _context = context;
20 | }
21 |
22 | [HttpGet]
23 | [Authorize(Policy = "AdminOnly")]
24 | public async Task Users () {
25 | return Ok(_context.Users);
26 | }
27 | }
28 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Controllers/WeatherForecastController.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.Extensions.Logging;
7 |
8 | namespace AspNetCoreIdentity.Controllers
9 | {
10 | [ApiController]
11 | [Route("[controller]")]
12 | public class WeatherForecastController : ControllerBase
13 | {
14 | private static readonly string[] Summaries = new[]
15 | {
16 | "Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
17 | };
18 |
19 | private readonly ILogger _logger;
20 |
21 | public WeatherForecastController(ILogger logger)
22 | {
23 | _logger = logger;
24 | }
25 |
26 | [HttpGet]
27 | public IEnumerable Get()
28 | {
29 | var rng = new Random();
30 | return Enumerable.Range(1, 5).Select(index => new WeatherForecast
31 | {
32 | Date = DateTime.Now.AddDays(index),
33 | TemperatureC = rng.Next(-20, 55),
34 | Summary = Summaries[rng.Next(Summaries.Length)]
35 | })
36 | .ToArray();
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/AuthMessageSenderOptions.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetCoreIdentity.Infrastructure
2 | {
3 | public class AuthMessageSenderOptions
4 | {
5 | public string SendGridUser { get; set; }
6 | public string SendGridKey { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/DbInitializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Security.Claims;
3 | using System.Threading.Tasks;
4 | using Microsoft.AspNetCore.Identity;
5 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
6 |
7 | namespace AspNetCoreIdentity.Infrastructure {
8 | public class DbInitializer : IDbInitializer {
9 | private readonly IdentityDbContext _context;
10 | private readonly RoleManager _roleManager;
11 |
12 | public DbInitializer (
13 | IdentityDbContext context,
14 | RoleManager roleManager) {
15 | _context = context;
16 | _roleManager = roleManager;
17 | }
18 |
19 | //This example just creates an Administrator role and one Admin users
20 | public async Task Initialize () {
21 | //create database schema if none exists
22 | _context.Database.EnsureCreated ();
23 |
24 | //If there is already an Administrator role, abort
25 | var adminRoleExists = await _roleManager.RoleExistsAsync("Admin");
26 |
27 | if (!adminRoleExists) {
28 | //Create the Admin Role
29 | var adminRole = new IdentityRole ("Admin");
30 | var result = await _roleManager.CreateAsync (adminRole);
31 |
32 | if (result.Succeeded) {
33 | // Add the Trial claim
34 | var foreverTrialClaim = new Claim ("Trial", DateTime.Now.AddYears(1).ToString());
35 | await _roleManager.AddClaimAsync (adminRole, foreverTrialClaim);
36 | }
37 | }
38 | }
39 |
40 | }
41 |
42 | public interface IDbInitializer {
43 | Task Initialize ();
44 | }
45 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/EmailSender.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using Microsoft.Extensions.Options;
3 | using SendGrid;
4 | using SendGrid.Helpers.Mail;
5 |
6 | namespace AspNetCoreIdentity.Infrastructure
7 | {
8 | public class EmailSender : IEmailSender
9 | {
10 | public EmailSender(IOptions optionsAccessor)
11 | {
12 | Options = optionsAccessor.Value;
13 | }
14 |
15 | public AuthMessageSenderOptions Options { get; } //set only via Secret Manager
16 |
17 | public Task SendEmailAsync(string email, string subject, string message)
18 | {
19 | return Execute(Options.SendGridKey, subject, message, email);
20 | }
21 |
22 | public Task Execute(string apiKey, string subject, string message, string email)
23 | {
24 | var client = new SendGridClient(apiKey);
25 | var msg = new SendGridMessage()
26 | {
27 | From = new EmailAddress("aspnetidentity@chsakell.com", "ASP.NET Core Identity"),
28 | Subject = subject,
29 | PlainTextContent = message,
30 | HtmlContent = message
31 | };
32 | msg.AddTo(new EmailAddress(email));
33 |
34 | // Disable click tracking.
35 | // See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
36 | msg.SetClickTracking(false, false);
37 |
38 | return client.SendEmailAsync(msg);
39 | }
40 | }
41 |
42 | public interface IEmailSender
43 | {
44 | Task SendEmailAsync(string email, string subject, string message);
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/Identity.Internals/Base32.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 |
7 | namespace AspNetCoreIdentity.Infrastructure.Identity.Internals
8 | {
9 | internal static class Base32
10 | {
11 | private static readonly string _base32Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ234567";
12 |
13 | public static string ToBase32(byte[] input)
14 | {
15 | if (input == null)
16 | {
17 | throw new ArgumentNullException(nameof(input));
18 | }
19 |
20 | StringBuilder sb = new StringBuilder();
21 | for (int offset = 0; offset < input.Length;)
22 | {
23 | byte a, b, c, d, e, f, g, h;
24 | int numCharsToOutput = GetNextGroup(input, ref offset, out a, out b, out c, out d, out e, out f, out g, out h);
25 |
26 | sb.Append((numCharsToOutput >= 1) ? _base32Chars[a] : '=');
27 | sb.Append((numCharsToOutput >= 2) ? _base32Chars[b] : '=');
28 | sb.Append((numCharsToOutput >= 3) ? _base32Chars[c] : '=');
29 | sb.Append((numCharsToOutput >= 4) ? _base32Chars[d] : '=');
30 | sb.Append((numCharsToOutput >= 5) ? _base32Chars[e] : '=');
31 | sb.Append((numCharsToOutput >= 6) ? _base32Chars[f] : '=');
32 | sb.Append((numCharsToOutput >= 7) ? _base32Chars[g] : '=');
33 | sb.Append((numCharsToOutput >= 8) ? _base32Chars[h] : '=');
34 | }
35 |
36 | return sb.ToString();
37 | }
38 |
39 | public static byte[] FromBase32(string input)
40 | {
41 | if (input == null)
42 | {
43 | throw new ArgumentNullException(nameof(input));
44 | }
45 | input = input.TrimEnd('=').ToUpperInvariant();
46 | if (input.Length == 0)
47 | {
48 | return new byte[0];
49 | }
50 |
51 | var output = new byte[input.Length * 5 / 8];
52 | var bitIndex = 0;
53 | var inputIndex = 0;
54 | var outputBits = 0;
55 | var outputIndex = 0;
56 | while (outputIndex < output.Length)
57 | {
58 | var byteIndex = _base32Chars.IndexOf(input[inputIndex]);
59 | if (byteIndex < 0)
60 | {
61 | throw new FormatException();
62 | }
63 |
64 | var bits = Math.Min(5 - bitIndex, 8 - outputBits);
65 | output[outputIndex] <<= bits;
66 | output[outputIndex] |= (byte)(byteIndex >> (5 - (bitIndex + bits)));
67 |
68 | bitIndex += bits;
69 | if (bitIndex >= 5)
70 | {
71 | inputIndex++;
72 | bitIndex = 0;
73 | }
74 |
75 | outputBits += bits;
76 | if (outputBits >= 8)
77 | {
78 | outputIndex++;
79 | outputBits = 0;
80 | }
81 | }
82 | return output;
83 | }
84 |
85 | // returns the number of bytes that were output
86 | private static int GetNextGroup(byte[] input, ref int offset, out byte a, out byte b, out byte c, out byte d, out byte e, out byte f, out byte g, out byte h)
87 | {
88 | uint b1, b2, b3, b4, b5;
89 |
90 | int retVal;
91 | switch (offset - input.Length)
92 | {
93 | case 1: retVal = 2; break;
94 | case 2: retVal = 4; break;
95 | case 3: retVal = 5; break;
96 | case 4: retVal = 7; break;
97 | default: retVal = 8; break;
98 | }
99 |
100 | b1 = (offset < input.Length) ? input[offset++] : 0U;
101 | b2 = (offset < input.Length) ? input[offset++] : 0U;
102 | b3 = (offset < input.Length) ? input[offset++] : 0U;
103 | b4 = (offset < input.Length) ? input[offset++] : 0U;
104 | b5 = (offset < input.Length) ? input[offset++] : 0U;
105 |
106 | a = (byte)(b1 >> 3);
107 | b = (byte)(((b1 & 0x07) << 2) | (b2 >> 6));
108 | c = (byte)((b2 >> 1) & 0x1f);
109 | d = (byte)(((b2 & 0x01) << 4) | (b3 >> 4));
110 | e = (byte)(((b3 & 0x0f) << 1) | (b4 >> 7));
111 | f = (byte)((b4 >> 2) & 0x1f);
112 | g = (byte)(((b4 & 0x3) << 3) | (b5 >> 5));
113 | h = (byte)(b5 & 0x1f);
114 |
115 | return retVal;
116 | }
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/Identity.Internals/Rfc6238AuthenticationService.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Diagnostics;
4 | using System.Linq;
5 | using System.Net;
6 | using System.Security.Cryptography;
7 | using System.Text;
8 | using System.Threading.Tasks;
9 |
10 | namespace AspNetCoreIdentity.Infrastructure.Identity.Internals
11 | {
12 | internal static class Rfc6238AuthenticationService
13 | {
14 | private static readonly DateTime _unixEpoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
15 | private static readonly TimeSpan _timestep = TimeSpan.FromMinutes(3);
16 | private static readonly Encoding _encoding = new UTF8Encoding(false, true);
17 | private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create();
18 |
19 | // Generates a new 80-bit security token
20 | public static byte[] GenerateRandomKey()
21 | {
22 | byte[] bytes = new byte[20];
23 | _rng.GetBytes(bytes);
24 | return bytes;
25 | }
26 |
27 | internal static int ComputeTotp(HashAlgorithm hashAlgorithm, ulong timestepNumber, string modifier)
28 | {
29 | // # of 0's = length of pin
30 | const int Mod = 1000000;
31 |
32 | // See https://tools.ietf.org/html/rfc4226
33 | // We can add an optional modifier
34 | var timestepAsBytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((long)timestepNumber));
35 | var hash = hashAlgorithm.ComputeHash(ApplyModifier(timestepAsBytes, modifier));
36 |
37 | // Generate DT string
38 | var offset = hash[hash.Length - 1] & 0xf;
39 | Debug.Assert(offset + 4 < hash.Length);
40 | var binaryCode = (hash[offset] & 0x7f) << 24
41 | | (hash[offset + 1] & 0xff) << 16
42 | | (hash[offset + 2] & 0xff) << 8
43 | | (hash[offset + 3] & 0xff);
44 |
45 | return binaryCode % Mod;
46 | }
47 |
48 | private static byte[] ApplyModifier(byte[] input, string modifier)
49 | {
50 | if (String.IsNullOrEmpty(modifier))
51 | {
52 | return input;
53 | }
54 |
55 | var modifierBytes = _encoding.GetBytes(modifier);
56 | var combined = new byte[checked(input.Length + modifierBytes.Length)];
57 | Buffer.BlockCopy(input, 0, combined, 0, input.Length);
58 | Buffer.BlockCopy(modifierBytes, 0, combined, input.Length, modifierBytes.Length);
59 | return combined;
60 | }
61 |
62 | // More info: https://tools.ietf.org/html/rfc6238#section-4
63 | private static ulong GetCurrentTimeStepNumber()
64 | {
65 | var delta = DateTime.UtcNow - _unixEpoch;
66 | return (ulong)(delta.Ticks / _timestep.Ticks);
67 | }
68 |
69 | public static int GenerateCode(byte[] securityToken, string modifier = null)
70 | {
71 | if (securityToken == null)
72 | {
73 | throw new ArgumentNullException(nameof(securityToken));
74 | }
75 |
76 | // Allow a variance of no greater than 9 minutes in either direction
77 | var currentTimeStep = GetCurrentTimeStepNumber();
78 | using (var hashAlgorithm = new HMACSHA1(securityToken))
79 | {
80 | return ComputeTotp(hashAlgorithm, currentTimeStep, modifier);
81 | }
82 | }
83 |
84 | public static bool ValidateCode(byte[] securityToken, int code, string modifier = null)
85 | {
86 | if (securityToken == null)
87 | {
88 | throw new ArgumentNullException(nameof(securityToken));
89 | }
90 |
91 | // Allow a variance of no greater than 9 minutes in either direction
92 | var currentTimeStep = GetCurrentTimeStepNumber();
93 | using (var hashAlgorithm = new HMACSHA1(securityToken))
94 | {
95 | for (var i = -2; i <= 2; i++)
96 | {
97 | var computedTotp = ComputeTotp(hashAlgorithm, (ulong)((long)currentTimeStep + i), modifier);
98 | if (computedTotp == code)
99 | {
100 | return true;
101 | }
102 | }
103 | }
104 |
105 | // No match
106 | return false;
107 | }
108 | }
109 | }
110 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/StreaminCategoryAuthorizeAttribute.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using Microsoft.AspNetCore.Authorization;
3 |
4 | namespace AspNetCoreIdentity.Infrastructure {
5 | public class StreamingCategoryAuthorizeAttribute : AuthorizeAttribute {
6 | const string POLICY_PREFIX = "StreamingCategory_";
7 |
8 | public StreamingCategoryAuthorizeAttribute (StreamingCategory category) => Category = category;
9 |
10 | // Get or set the Category property by manipulating the underlying Policy property
11 | public StreamingCategory Category {
12 | get {
13 | var category = (StreamingCategory) Enum.Parse (typeof (StreamingCategory),
14 | Policy.Substring (POLICY_PREFIX.Length));
15 |
16 | return (StreamingCategory) category;
17 | }
18 | set {
19 | Policy = $"{POLICY_PREFIX}{value.ToString()}";
20 | }
21 | }
22 | }
23 |
24 | public enum StreamingCategory {
25 | ACTION_AND_ADVENTURE = 1,
26 | ACTION_COMEDIES = 2,
27 | ACTION_THRILLERS = 3,
28 | SCI_FI = 4,
29 | ANIMATION = 5,
30 | MUSIC_VIDEOS = 6,
31 | BOXING_MOVIES = 7,
32 | FAMILY_MOVIES = 8
33 | }
34 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/StreamingCategoryAuthorizationHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Identity;
5 |
6 | namespace AspNetCoreIdentity.Infrastructure {
7 | internal class StreamingCategoryAuthorizationHandler : AuthorizationHandler {
8 | private readonly UserManager _userManager;
9 |
10 | public StreamingCategoryAuthorizationHandler (UserManager userManager) {
11 | _userManager = userManager;
12 | }
13 |
14 | protected override Task HandleRequirementAsync (AuthorizationHandlerContext context, StreamingCategoryRequirement requirement) {
15 |
16 | var loggedInUserTask = _userManager.GetUserAsync (context.User);
17 |
18 | loggedInUserTask.Wait ();
19 |
20 | var userClaimsTask = _userManager.GetClaimsAsync (loggedInUserTask.Result);
21 |
22 | userClaimsTask.Wait ();
23 |
24 | var userClaims = userClaimsTask.Result;
25 |
26 | if (userClaims.Any (c => c.Type == requirement.Category)) {
27 | context.Succeed (requirement);
28 | }
29 |
30 | return Task.CompletedTask;
31 | }
32 | }
33 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/StreamingCategoryPolicyProvider.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Threading.Tasks;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.Extensions.Options;
5 |
6 | namespace AspNetCoreIdentity.Infrastructure {
7 | public class StreamingCategoryPolicyProvider : IAuthorizationPolicyProvider {
8 | const string POLICY_PREFIX = "StreamingCategory_";
9 |
10 | public DefaultAuthorizationPolicyProvider FallbackPolicyProvider { get; }
11 |
12 | public StreamingCategoryPolicyProvider (IOptions options) {
13 | // ASP.NET Core only uses one authorization policy provider, so if the custom implementation
14 | // doesn't handle all policies (including default policies, etc.) it should fall back to an
15 | // alternate provider.
16 | //
17 | // In this sample, a default authorization policy provider (constructed with options from the
18 | // dependency injection container) is used if this custom provider isn't able to handle a given
19 | // policy name.
20 | //
21 | // If a custom policy provider is able to handle all expected policy names then, of course, this
22 | // fallback pattern is unnecessary.
23 |
24 | // Claims based authorization
25 | options.Value.AddPolicy ("TrialOnly", policy => {
26 | policy.RequireClaim ("Trial");
27 | });
28 |
29 | // Role based authorization
30 | options.Value.AddPolicy ("AdminOnly", policy => {
31 | policy.RequireRole ("Admin");
32 | });
33 |
34 | options.Value.AddPolicy("AddVideoPolicy", policy =>
35 | policy.Requirements.Add(new UserCategoryRequirement()));
36 |
37 | FallbackPolicyProvider = new DefaultAuthorizationPolicyProvider (options);
38 | }
39 |
40 | public Task GetPolicyAsync (string policyName) {
41 | if (policyName.StartsWith (POLICY_PREFIX, StringComparison.OrdinalIgnoreCase)) {
42 | var category = (StreamingCategory) Enum.Parse (typeof (StreamingCategory),
43 | policyName.Substring (POLICY_PREFIX.Length));
44 |
45 | var policy = new AuthorizationPolicyBuilder ();
46 | policy.AddRequirements(new StreamingCategoryRequirement(category.ToString ()));
47 | return Task.FromResult (policy.Build ());
48 | } else {
49 | // If the policy name doesn't match the format expected by this policy provider,
50 | // try the fallback provider. If no fallback provider is used, this would return
51 | // Task.FromResult(null) instead.
52 | return FallbackPolicyProvider.GetPolicyAsync (policyName);
53 | }
54 | }
55 |
56 | public Task GetDefaultPolicyAsync () => FallbackPolicyProvider.GetDefaultPolicyAsync ();
57 |
58 | public Task GetFallbackPolicyAsync() => FallbackPolicyProvider.GetDefaultPolicyAsync();
59 |
60 | }
61 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/StreamingCategoryRequirement.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 |
3 | namespace AspNetCoreIdentity.Infrastructure
4 | {
5 | internal class StreamingCategoryRequirement: IAuthorizationRequirement
6 | {
7 | public string Category { get; private set; }
8 |
9 | public StreamingCategoryRequirement(string category) { Category = category; }
10 | }
11 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/UserCategoryAuthorizationHandler.cs:
--------------------------------------------------------------------------------
1 | using System.Linq;
2 | using System.Threading.Tasks;
3 | using AspNetCoreIdentity.ViewModels;
4 | using Microsoft.AspNetCore.Authorization;
5 | using Microsoft.AspNetCore.Identity;
6 |
7 | namespace AspNetCoreIdentity.Infrastructure
8 | {
9 | public class UserCategoryAuthorizationHandler :
10 | AuthorizationHandler
11 | {
12 | private readonly UserManager _userManager;
13 |
14 | public UserCategoryAuthorizationHandler (UserManager userManager) {
15 | _userManager = userManager;
16 | }
17 |
18 | protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
19 | UserCategoryRequirement requirement,
20 | VideoVM resource)
21 | {
22 | var loggedInUserTask = _userManager.GetUserAsync (context.User);
23 |
24 | loggedInUserTask.Wait ();
25 |
26 | var userClaimsTask = _userManager.GetClaimsAsync (loggedInUserTask.Result);
27 |
28 | userClaimsTask.Wait ();
29 |
30 | var userClaims = userClaimsTask.Result;
31 |
32 | if (userClaims.Any (c => c.Type == resource.Category.ToString())) {
33 | context.Succeed (requirement);
34 | }
35 |
36 | return Task.CompletedTask;
37 | }
38 | }
39 |
40 | public class UserCategoryRequirement : IAuthorizationRequirement { }
41 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Infrastructure/UserRepository.cs:
--------------------------------------------------------------------------------
1 | using AspNetCoreIdentity.Models;
2 | using System.Collections.Generic;
3 |
4 | namespace AspNetCoreIdentity.Infrastructure
5 | {
6 | public static class UserRepository
7 | {
8 | public static List Users;
9 |
10 | static UserRepository()
11 | {
12 | Users = new List();
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Models/AppUser.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetCoreIdentity.Models
2 | {
3 | public class AppUser
4 | {
5 | public string Id { get; set; }
6 | public string UserName { get; set; }
7 | public string Email { get; set; }
8 | public string NormalizeUserName { get; set; }
9 | public string PasswordHash { get; set; }
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Pages/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @namespace AspNetCoreIdentity.Pages
2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
3 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Program.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 | using AspNetCoreIdentity.Infrastructure;
6 | using Microsoft.AspNetCore;
7 | using Microsoft.AspNetCore.Hosting;
8 | using Microsoft.Extensions.Configuration;
9 | using Microsoft.Extensions.DependencyInjection;
10 | using Microsoft.Extensions.Logging;
11 |
12 | namespace AspNetCoreIdentity
13 | {
14 | public class Program
15 | {
16 | public static async Task Main(string[] args)
17 | {
18 | var host = CreateWebHostBuilder(args).Build();
19 |
20 | using (var scope = host.Services.CreateScope())
21 | {
22 | var services = scope.ServiceProvider;
23 | var config = services.GetService();
24 | var dbInitializer = services.GetService();
25 | var useInMemoryStores = config.GetValue("UseInMemoryStores");
26 | await dbInitializer.Initialize();
27 | }
28 |
29 | host.Run();
30 | }
31 |
32 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
33 | WebHost.CreateDefaultBuilder(args)
34 | .UseStartup();
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5000/",
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 | "AspNetCoreIdentity": {
19 | "commandName": "Project",
20 | "launchBrowser": true,
21 | "environmentVariables": {
22 | "ASPNETCORE_ENVIRONMENT": "Development"
23 | },
24 | "applicationUrl": "http://localhost:5000/"
25 | }
26 | }
27 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/AccountDetailsVM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace AspNetCoreIdentity.ViewModels
7 | {
8 | public class AccountDetailsVM
9 | {
10 | public string Username { get; set; }
11 | public string Email { get; set; }
12 | public bool EmailConfirmed { get; set; }
13 | public string PhoneNumber { get; set; }
14 | public List ExternalLogins { get; set; }
15 | public bool TwoFactorEnabled { get; set; }
16 | public bool HasAuthenticator { get; set; }
17 | public bool TwoFactorClientRemembered { get; set; }
18 | public int RecoveryCodesLeft { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/AssociateViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetCoreIdentity.ViewModels
2 | {
3 | public class AssociateViewModel
4 | {
5 | public string Username { get; set; }
6 | public string OriginalEmail { get; set; }
7 | public string AssociateEmail { get; set; }
8 | public bool associateExistingAccount { get; set; }
9 | public string LoginProvider { get; set; }
10 | public string ProviderDisplayName { get; set; }
11 | public string ProviderKey { get; set; }
12 | }
13 | }
14 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/AuthenticatorDetailsVM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Threading.Tasks;
5 |
6 | namespace AspNetCoreIdentity.ViewModels
7 | {
8 | public class AuthenticatorDetailsVM
9 | {
10 | public string SharedKey { get; set; }
11 |
12 | public string AuthenticatorUri { get; set; }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/ClaimVM.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace AspNetCoreIdentity.ViewModels
4 | {
5 | public class ClaimVM
6 | {
7 | public string Type { get; set; }
8 | public string Value { get; set; }
9 | }
10 |
11 | public class UserClaims
12 | {
13 | public IEnumerable Claims { get; set; }
14 | public string UserName { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/LoginVM.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace AspNetCoreIdentity.ViewModels
4 | {
5 | public class LoginVM
6 | {
7 | public string UserName { get; set; }
8 |
9 | [DataType(DataType.Password)]
10 | public string Password { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/RegisterVM.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace AspNetCoreIdentity.ViewModels
4 | {
5 | public class RegisterVM
6 | {
7 | public string UserName { get; set; }
8 |
9 | [DataType(DataType.EmailAddress)]
10 | public string Email { get; set; }
11 |
12 | [DataType(DataType.Password)]
13 | public string Password { get; set; }
14 |
15 | [Compare("Password")]
16 | [DataType(DataType.Password)]
17 | public string ConfirmPassword { get; set; }
18 |
19 | public bool StartFreeTrial {get; set;}
20 |
21 | public bool IsAdmin {get; set;}
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/ResultVM.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetCoreIdentity.ViewModels
2 | {
3 | public class ResultVM
4 | {
5 | public Status Status { get; set; }
6 | public string Message { get; set; }
7 | public object Data { get; set; }
8 | }
9 |
10 | public enum Status
11 | {
12 | Success = 1,
13 | Error = 2
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/TwoFactorLoginVM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace AspNetCoreIdentity.ViewModels
8 | {
9 | public class TwoFactorLoginVM
10 | {
11 | [Required]
12 | [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
13 | [DataType(DataType.Text)]
14 | [Display(Name = "Authenticator code")]
15 | public string TwoFactorCode { get; set; }
16 |
17 | public bool RememberMachine { get; set; }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/TwoFactorRecoveryCodeLoginVM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace AspNetCoreIdentity.ViewModels
8 | {
9 | public class TwoFactorRecoveryCodeLoginVM
10 | {
11 | [Required]
12 | [DataType(DataType.Text)]
13 | [Display(Name = "Recovery Code")]
14 | public string RecoveryCode { get; set; }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/UpdatePasswordVM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace AspNetCoreIdentity.ViewModels
8 | {
9 | public class UpdatePasswordVM
10 | {
11 | [Required(ErrorMessage = "Password is required")]
12 | [DataType(DataType.Password)]
13 | public string Password { get; set; }
14 |
15 | [Required(ErrorMessage = "Confirm Password is required")]
16 | [DataType(DataType.Password)]
17 | [Compare("Password")]
18 | public string ConfirmPassword { get; set; }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/UserStateVM.cs:
--------------------------------------------------------------------------------
1 | namespace AspNetCoreIdentity.ViewModels
2 | {
3 | public class UserStateVM
4 | {
5 | public bool IsAuthenticated { get; set; }
6 | public string Username { get; set; }
7 | public string AuthenticationMethod { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/VefiryAuthenticatorVM.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.ComponentModel.DataAnnotations;
4 | using System.Linq;
5 | using System.Threading.Tasks;
6 |
7 | namespace AspNetCoreIdentity.ViewModels
8 | {
9 | public class VefiryAuthenticatorVM
10 | {
11 | [Required]
12 | [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)]
13 | [DataType(DataType.Text)]
14 | [Display(Name = "Verification Code")]
15 | public string VerificationCode { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/ViewModels/VideoVM.cs:
--------------------------------------------------------------------------------
1 | using AspNetCoreIdentity.Infrastructure;
2 |
3 | namespace AspNetCoreIdentity.ViewModels {
4 | public class VideoVM {
5 | public string Url { get; set; }
6 | public string Title { get; set; }
7 | public string Description { get; set; }
8 | public StreamingCategory Category {get; set;}
9 | }
10 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/WeatherForecast.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace AspNetCoreIdentity
4 | {
5 | public class WeatherForecast
6 | {
7 | public DateTime Date { get; set; }
8 |
9 | public int TemperatureC { get; set; }
10 |
11 | public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
12 |
13 | public string Summary { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "ConnectionStrings": {
3 | "AspNetCoreIdentityDb": "Data Source=.;Database=AspNetCoreIdentityDb;trusted_connection=yes;MultipleActiveResultSets=true;"
4 | },
5 | "Logging": {
6 | "LogLevel": {
7 | "Default": "Warning"
8 | }
9 | },
10 | "InMemoryProvider": true,
11 | "TwoFactorAuthentication:EncryptionEnabled": true
12 | }
13 |
--------------------------------------------------------------------------------
/AspNetCoreIdentity/libman.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "1.0",
3 | "defaultProvider": "cdnjs",
4 | "libraries": []
5 | }
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/403-forbidden.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/403-forbidden.png
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/dropbox.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/dropbox.png
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/facebook.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/facebook.png
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/github.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/github.png
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/google.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/google.png
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/linkedin.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/linkedin.png
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/microsoft.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/microsoft.png
--------------------------------------------------------------------------------
/AspNetCoreIdentity/wwwroot/images/twitter.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/AspNetCoreIdentity/wwwroot/images/twitter.png
--------------------------------------------------------------------------------
/IdentityServer/Config.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using IdentityServer4;
3 | using IdentityServer4.Models;
4 |
5 | namespace IdentityServer
6 | {
7 | public static class Config
8 | {
9 | public static IEnumerable GetIdentityResources()
10 | {
11 | return new List
12 | {
13 | new IdentityResources.OpenId(),
14 | new IdentityResources.Profile(),
15 | };
16 | }
17 |
18 | public static IEnumerable GetClients()
19 | {
20 | return new List
21 | {
22 | new Client
23 | {
24 | ClientId = "AspNetCoreIdentity",
25 | ClientName = "AspNetCoreIdentity Client",
26 | AllowedGrantTypes = GrantTypes.Code,
27 | RequirePkce = true,
28 | RequireClientSecret = false,
29 |
30 | RedirectUris = { "http://localhost:5000" },
31 | PostLogoutRedirectUris = { "http://localhost:5000" },
32 | AllowedCorsOrigins = { "http://localhost:5000" },
33 |
34 | AllowedScopes =
35 | {
36 | IdentityServerConstants.StandardScopes.OpenId,
37 | IdentityServerConstants.StandardScopes.Profile,
38 | "SocialAPI"
39 | }
40 | }
41 | };
42 | }
43 |
44 | public static IEnumerable GetApis()
45 | {
46 | return new List
47 | {
48 | new ApiResource("SocialAPI", "Social Network API")
49 | };
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/IdentityServer/Controllers/GrantsController.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.Linq;
3 | using System.Threading.Tasks;
4 | using IdentityServer.Filters;
5 | using IdentityServer.Models.Grants;
6 | using IdentityServer4.Events;
7 | using IdentityServer4.Extensions;
8 | using IdentityServer4.Services;
9 | using IdentityServer4.Stores;
10 | using Microsoft.AspNetCore.Authorization;
11 | using Microsoft.AspNetCore.Mvc;
12 |
13 | namespace IdentityServer.Controllers
14 | {
15 | ///
16 | /// This sample controller allows a user to revoke grants given to clients
17 | ///
18 | [SecurityHeaders]
19 | [Authorize]
20 | public class GrantsController : Controller
21 | {
22 | private readonly IIdentityServerInteractionService _interaction;
23 | private readonly IClientStore _clients;
24 | private readonly IResourceStore _resources;
25 | private readonly IEventService _events;
26 |
27 | public GrantsController(IIdentityServerInteractionService interaction,
28 | IClientStore clients,
29 | IResourceStore resources,
30 | IEventService events)
31 | {
32 | _interaction = interaction;
33 | _clients = clients;
34 | _resources = resources;
35 | _events = events;
36 | }
37 |
38 | ///
39 | /// Show list of grants
40 | ///
41 | [HttpGet]
42 | public async Task Index()
43 | {
44 | return View("Index", await BuildViewModelAsync());
45 | }
46 |
47 | ///
48 | /// Handle postback to revoke a client
49 | ///
50 | [HttpPost]
51 | [ValidateAntiForgeryToken]
52 | public async Task Revoke(string clientId)
53 | {
54 | await _interaction.RevokeUserConsentAsync(clientId);
55 | await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId));
56 |
57 | return RedirectToAction("Index");
58 | }
59 |
60 | private async Task BuildViewModelAsync()
61 | {
62 | var grants = await _interaction.GetAllUserConsentsAsync();
63 |
64 | var list = new List();
65 | foreach (var grant in grants)
66 | {
67 | var client = await _clients.FindClientByIdAsync(grant.ClientId);
68 | if (client != null)
69 | {
70 | var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes);
71 |
72 | var item = new GrantViewModel()
73 | {
74 | ClientId = client.ClientId,
75 | ClientName = client.ClientName ?? client.ClientId,
76 | ClientLogoUrl = client.LogoUri,
77 | ClientUrl = client.ClientUri,
78 | Created = grant.CreationTime,
79 | Expires = grant.Expiration,
80 | IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(),
81 | ApiGrantNames = resources.ApiResources.Select(x => x.DisplayName ?? x.Name).ToArray()
82 | };
83 |
84 | list.Add(item);
85 | }
86 | }
87 |
88 | return new GrantsViewModel
89 | {
90 | Grants = list
91 | };
92 | }
93 | }
94 | }
--------------------------------------------------------------------------------
/IdentityServer/Controllers/HomeController.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using IdentityServer.Filters;
3 | using IdentityServer.Models;
4 | using IdentityServer4.Services;
5 | using Microsoft.AspNetCore.Authorization;
6 | using Microsoft.AspNetCore.Hosting;
7 | using Microsoft.AspNetCore.Mvc;
8 | using Microsoft.Extensions.Logging;
9 |
10 | namespace IdentityServer.Controllers
11 | {
12 | [SecurityHeaders]
13 | [AllowAnonymous]
14 | public class HomeController : Controller
15 | {
16 | private readonly IIdentityServerInteractionService _interaction;
17 | private readonly IHostingEnvironment _environment;
18 | private readonly ILogger _logger;
19 |
20 | public HomeController(IIdentityServerInteractionService interaction, IHostingEnvironment environment, ILogger logger)
21 | {
22 | _interaction = interaction;
23 | _environment = environment;
24 | _logger = logger;
25 | }
26 |
27 | public IActionResult Index()
28 | {
29 | if (_environment.IsDevelopment())
30 | {
31 | // only show in development
32 | return View();
33 | }
34 |
35 | _logger.LogInformation("Homepage is disabled in production. Returning 404.");
36 | return NotFound();
37 | }
38 |
39 | ///
40 | /// Shows the error page
41 | ///
42 | public async Task Error(string errorId)
43 | {
44 | var vm = new ErrorViewModel();
45 |
46 | // retrieve error details from identityserver
47 | var message = await _interaction.GetErrorContextAsync(errorId);
48 | if (message != null)
49 | {
50 | vm.Error = message;
51 |
52 | if (!_environment.IsDevelopment())
53 | {
54 | // only show in development
55 | message.ErrorDescription = null;
56 | }
57 | }
58 |
59 | return View("Error", vm);
60 | }
61 | }
62 | }
--------------------------------------------------------------------------------
/IdentityServer/Data/ApplicationDbContext.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Identity;
2 | using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
3 | using Microsoft.EntityFrameworkCore;
4 |
5 | namespace IdentityServer.Data
6 | {
7 | public class ApplicationDbContext : IdentityDbContext
8 | {
9 | public ApplicationDbContext(DbContextOptions options)
10 | : base(options)
11 | {
12 | }
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/IdentityServer/Data/instructions.md:
--------------------------------------------------------------------------------
1 | # Migration instructions for IdentityServer project
2 |
3 | * `cd path-to\IdentityServer`
4 | * change `UseInMemoryStores` to `false` in **appsettings.json**
5 |
6 | ## Migrations using terminal
7 |
8 | ### Create migrations
9 | * `dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb`
10 | * `dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb`
11 | * `dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ApplicationDbContext -o Data/Migrations`
12 |
13 | ### Update database
14 | * `dotnet ef database update --context ApplicationDbContext`
15 | * `dotnet ef database update --context PersistedGrantDbContext`
16 | * `dotnet ef database update --context ConfigurationDbContext`
17 |
18 |
19 | ## Migrations using VISUAL STUDIO Package Manager Console
20 |
21 | ### Create migrations
22 | * `Add-Migration InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -Project "IdentityServer" -StartUpProject "IdentityServer" -o Data/Migrations/IdentityServer/PersistedGrantDb`
23 | * `Add-Migration InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -Project "IdentityServer" -StartUpProject "IdentityServer" -o Data/Migrations/IdentityServer/ConfigurationDb`
24 | * `Add-Migration InitialIdentityServerConfigurationDbMigration -c ApplicationDbContext -Project "IdentityServer" -StartUpProject "IdentityServer" -o Data/Migrations`
25 |
26 | ### Update database
27 | * `Update-Database -Context ApplicationDbContext -Project "IdentityServer" -StartupProject "IdentityServer"`
28 | * `Update-Database -Context PersistedGrantDbContext -Project "IdentityServer" -StartupProject "IdentityServer"`
29 | * `Update-Database -Context ConfigurationDbContext -Project "IdentityServer" -StartupProject "IdentityServer"`
30 |
--------------------------------------------------------------------------------
/IdentityServer/DatabaseInitializer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Linq;
3 | using System.Security.Claims;
4 | using IdentityModel;
5 | using IdentityServer.Data;
6 | using IdentityServer4.EntityFramework.DbContexts;
7 | using IdentityServer4.EntityFramework.Mappers;
8 | using Microsoft.AspNetCore.Identity;
9 | using Microsoft.EntityFrameworkCore;
10 | using Microsoft.Extensions.DependencyInjection;
11 |
12 | namespace IdentityServer
13 | {
14 | public class DatabaseInitializer
15 | {
16 | public static void Init(IServiceProvider provider, bool useInMemoryStores)
17 | {
18 | if (!useInMemoryStores)
19 | {
20 | provider.GetRequiredService().Database.Migrate();
21 | provider.GetRequiredService().Database.Migrate();
22 | provider.GetRequiredService().Database.Migrate();
23 | }
24 | InitializeIdentityServer(provider);
25 |
26 | var userManager = provider.GetRequiredService>();
27 | var chsakell = userManager.FindByNameAsync("chsakell").Result;
28 | if (chsakell == null)
29 | {
30 | chsakell = new IdentityUser
31 | {
32 | UserName = "chsakell"
33 | };
34 | var result = userManager.CreateAsync(chsakell, "$AspNetIdentity10$").Result;
35 | if (!result.Succeeded)
36 | {
37 | throw new Exception(result.Errors.First().Description);
38 | }
39 |
40 | chsakell = userManager.FindByNameAsync("chsakell").Result;
41 |
42 | result = userManager.AddClaimsAsync(chsakell, new Claim[]{
43 | new Claim(JwtClaimTypes.Name, "Chris Sakellarios"),
44 | new Claim(JwtClaimTypes.GivenName, "Christos"),
45 | new Claim(JwtClaimTypes.FamilyName, "Sakellarios"),
46 | new Claim(JwtClaimTypes.Email, "chsakellsblog@blog.com"),
47 | new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
48 | new Claim(JwtClaimTypes.WebSite, "https://chsakell.com"),
49 | new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'localhost 10', 'postal_code': 11146, 'country': 'Greece' }",
50 | IdentityServer4.IdentityServerConstants.ClaimValueTypes.Json)
51 | }).Result;
52 |
53 | if (!result.Succeeded)
54 | {
55 | throw new Exception(result.Errors.First().Description);
56 | }
57 | Console.WriteLine("chsakell created");
58 | }
59 | else
60 | {
61 | Console.WriteLine("chsakell already exists");
62 | }
63 | }
64 |
65 | private static void InitializeIdentityServer(IServiceProvider provider)
66 | {
67 | var context = provider.GetRequiredService();
68 | if (!context.Clients.Any())
69 | {
70 | foreach (var client in Config.GetClients())
71 | {
72 | context.Clients.Add(client.ToEntity());
73 | }
74 | context.SaveChanges();
75 | }
76 |
77 | if (!context.IdentityResources.Any())
78 | {
79 | foreach (var resource in Config.GetIdentityResources())
80 | {
81 | context.IdentityResources.Add(resource.ToEntity());
82 | }
83 | context.SaveChanges();
84 | }
85 |
86 | if (!context.ApiResources.Any())
87 | {
88 | foreach (var resource in Config.GetApis())
89 | {
90 | context.ApiResources.Add(resource.ToEntity());
91 | }
92 | context.SaveChanges();
93 | }
94 | }
95 | }
96 | }
97 |
--------------------------------------------------------------------------------
/IdentityServer/Extensions/Extensions.cs:
--------------------------------------------------------------------------------
1 | using System.Threading.Tasks;
2 | using IdentityServer4.Stores;
3 |
4 | namespace IdentityServer.Extensions
5 | {
6 | public static class Extensions
7 | {
8 | ///
9 | /// Determines whether the client is configured to use PKCE.
10 | ///
11 | /// The store.
12 | /// The client identifier.
13 | ///
14 | public static async Task IsPkceClientAsync(this IClientStore store, string client_id)
15 | {
16 | if (!string.IsNullOrWhiteSpace(client_id))
17 | {
18 | var client = await store.FindEnabledClientByIdAsync(client_id);
19 | return client?.RequirePkce == true;
20 | }
21 |
22 | return false;
23 | }
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/IdentityServer/Filters/SecurityHeadersAttribute.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Mvc;
2 | using Microsoft.AspNetCore.Mvc.Filters;
3 |
4 | namespace IdentityServer.Filters
5 | {
6 | public class SecurityHeadersAttribute : ActionFilterAttribute
7 | {
8 | public override void OnResultExecuting(ResultExecutingContext context)
9 | {
10 | var result = context.Result;
11 | if (result is ViewResult)
12 | {
13 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options
14 | if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options"))
15 | {
16 | context.HttpContext.Response.Headers.Add("X-Content-Type-Options", "nosniff");
17 | }
18 |
19 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options
20 | if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options"))
21 | {
22 | context.HttpContext.Response.Headers.Add("X-Frame-Options", "SAMEORIGIN");
23 | }
24 |
25 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy
26 | var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';";
27 | // also consider adding upgrade-insecure-requests once you have HTTPS in place for production
28 | //csp += "upgrade-insecure-requests;";
29 | // also an example if you need client images to be displayed from twitter
30 | // csp += "img-src 'self' https://pbs.twimg.com;";
31 |
32 | // once for standards compliant browsers
33 | if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy"))
34 | {
35 | context.HttpContext.Response.Headers.Add("Content-Security-Policy", csp);
36 | }
37 | // and once again for IE
38 | if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy"))
39 | {
40 | context.HttpContext.Response.Headers.Add("X-Content-Security-Policy", csp);
41 | }
42 |
43 | // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy
44 | var referrer_policy = "no-referrer";
45 | if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy"))
46 | {
47 | context.HttpContext.Response.Headers.Add("Referrer-Policy", referrer_policy);
48 | }
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/IdentityServer/IdentityServer.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 | InProcess
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 | all
17 | runtime; build; native; contentfiles; analyzers; buildtransitive
18 |
19 |
20 |
21 |
22 | all
23 | runtime; build; native; contentfiles; analyzers
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/AccountOptions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace IdentityServer.Models.Account
4 | {
5 | public class AccountOptions
6 | {
7 | public static bool AllowLocalLogin = true;
8 | public static bool AllowRememberLogin = true;
9 | public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30);
10 |
11 | public static bool ShowLogoutPrompt = true;
12 | public static bool AutomaticRedirectAfterSignOut = false;
13 |
14 | // specify the Windows authentication scheme being used
15 | public static readonly string WindowsAuthenticationSchemeName = Microsoft.AspNetCore.Server.IISIntegration.IISDefaults.AuthenticationScheme;
16 | // if user uses windows auth, should we load the groups from windows
17 | public static bool IncludeWindowsGroups = false;
18 |
19 | public static string InvalidCredentialsErrorMessage = "Invalid username or password";
20 |
21 | public static string UserAlreadyExistsErrorMessage = "User already exists";
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/ExternalProvider.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models.Account
2 | {
3 | public class ExternalProvider
4 | {
5 | public string DisplayName { get; set; }
6 | public string AuthenticationScheme { get; set; }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/LoggedOutViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models.Account
2 | {
3 | public class LoggedOutViewModel
4 | {
5 | public string PostLogoutRedirectUri { get; set; }
6 | public string ClientName { get; set; }
7 | public string SignOutIframeUrl { get; set; }
8 |
9 | public bool AutomaticRedirectAfterSignOut { get; set; } = false;
10 |
11 | public string LogoutId { get; set; }
12 | public bool TriggerExternalSignout => ExternalAuthenticationScheme != null;
13 | public string ExternalAuthenticationScheme { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/LoginInputModel.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace IdentityServer.Models.Account
4 | {
5 | public class LoginInputModel
6 | {
7 | [Required]
8 | public string Username { get; set; }
9 |
10 | [Required]
11 | public string Password { get; set; }
12 |
13 | public bool RememberLogin { get; set; }
14 |
15 | public string ReturnUrl { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/LoginViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 |
5 | namespace IdentityServer.Models.Account
6 | {
7 | public class LoginViewModel : LoginInputModel
8 | {
9 | public bool AllowRememberLogin { get; set; } = true;
10 | public bool EnableLocalLogin { get; set; } = true;
11 |
12 | public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty();
13 | public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName));
14 |
15 | public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1;
16 | public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null;
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/LogoutInputModel.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models.Account
2 | {
3 | public class LogoutInputModel
4 | {
5 | public string LogoutId { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/LogoutViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models.Account
2 | {
3 | public class LogoutViewModel : LogoutInputModel
4 | {
5 | public bool ShowLogoutPrompt { get; set; } = true;
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/RedirectViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models
2 | {
3 | public class RedirectViewModel
4 | {
5 | public string RedirectUrl { get; set; }
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Account/RegisterVM.cs:
--------------------------------------------------------------------------------
1 | using System.ComponentModel.DataAnnotations;
2 |
3 | namespace IdentityServer.Models.Account
4 | {
5 | public class RegisterVM
6 | {
7 | public string Username { get; set; }
8 |
9 | [DataType(DataType.EmailAddress)]
10 | public string Email { get; set; }
11 |
12 | [DataType(DataType.Password)]
13 | public string Password { get; set; }
14 |
15 | [Compare("Password")]
16 | [DataType(DataType.Password)]
17 | public string ConfirmPassword { get; set; }
18 |
19 | public string ReturnUrl { get; set; }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Consent/ConsentInputModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace IdentityServer.Models.Consent
4 | {
5 | public class ConsentInputModel
6 | {
7 | public string Button { get; set; }
8 | public IEnumerable ScopesConsented { get; set; }
9 | public bool RememberConsent { get; set; }
10 | public string ReturnUrl { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Consent/ConsentOptions.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models.Consent
2 | {
3 | public class ConsentOptions
4 | {
5 | public static bool EnableOfflineAccess = true;
6 | public static string OfflineAccessDisplayName = "Offline Access";
7 | public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline";
8 |
9 | public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission";
10 | public static readonly string InvalidSelectionErrorMessage = "Invalid selection";
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Consent/ConsentViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace IdentityServer.Models.Consent
4 | {
5 | public class ConsentViewModel : ConsentInputModel
6 | {
7 | public string ClientName { get; set; }
8 | public string ClientUrl { get; set; }
9 | public string ClientLogoUrl { get; set; }
10 | public bool AllowRememberConsent { get; set; }
11 |
12 | public IEnumerable IdentityScopes { get; set; }
13 | public IEnumerable ResourceScopes { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Consent/ProcessConsentResult.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models.Consent
2 | {
3 | public class ProcessConsentResult
4 | {
5 | public bool IsRedirect => RedirectUri != null;
6 | public string RedirectUri { get; set; }
7 | public string ClientId { get; set; }
8 |
9 | public bool ShowView => ViewModel != null;
10 | public ConsentViewModel ViewModel { get; set; }
11 |
12 | public bool HasValidationError => ValidationError != null;
13 | public string ValidationError { get; set; }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Consent/ScopeViewModel.cs:
--------------------------------------------------------------------------------
1 | namespace IdentityServer.Models.Consent
2 | {
3 | public class ScopeViewModel
4 | {
5 | public string Name { get; set; }
6 | public string DisplayName { get; set; }
7 | public string Description { get; set; }
8 | public bool Emphasize { get; set; }
9 | public bool Required { get; set; }
10 | public bool Checked { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/IdentityServer/Models/ErrorViewModel.cs:
--------------------------------------------------------------------------------
1 | using IdentityServer4.Models;
2 |
3 | namespace IdentityServer.Models
4 | {
5 | public class ErrorViewModel
6 | {
7 | public ErrorMessage Error { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Grants/GrantViewModel.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 |
4 | namespace IdentityServer.Models.Grants
5 | {
6 | public class GrantViewModel
7 | {
8 | public string ClientId { get; set; }
9 | public string ClientName { get; set; }
10 | public string ClientUrl { get; set; }
11 | public string ClientLogoUrl { get; set; }
12 | public DateTime Created { get; set; }
13 | public DateTime? Expires { get; set; }
14 | public IEnumerable IdentityGrantNames { get; set; }
15 | public IEnumerable ApiGrantNames { get; set; }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/IdentityServer/Models/Grants/GrantsViewModel.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace IdentityServer.Models.Grants
4 | {
5 | public class GrantsViewModel
6 | {
7 | public IEnumerable Grants { get; set; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/IdentityServer/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.Extensions.DependencyInjection;
5 |
6 | namespace IdentityServer
7 | {
8 | public class Program
9 | {
10 | public static void Main(string[] args)
11 | {
12 | var host = CreateWebHostBuilder(args).Build();
13 |
14 | using (var scope = host.Services.CreateScope())
15 | {
16 | var services = scope.ServiceProvider;
17 | var config = services.GetService();
18 | var useInMemoryStores = config.GetValue("UseInMemoryStores");
19 | DatabaseInitializer.Init(services, useInMemoryStores);
20 | }
21 |
22 | host.Run();
23 | }
24 |
25 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
26 | WebHost.CreateDefaultBuilder(args)
27 | .UseStartup();
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/IdentityServer/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5005",
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 | "IdentityServer": {
19 | "commandName": "Project",
20 | "environmentVariables": {
21 | "ASPNETCORE_ENVIRONMENT": "Development"
22 | },
23 | "applicationUrl": "http://localhost:5005"
24 | }
25 | }
26 | }
--------------------------------------------------------------------------------
/IdentityServer/Views/Account/LoggedOut.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.Account.LoggedOutViewModel
2 |
3 | @{
4 | // set this so the layout rendering sees an anonymous user
5 | ViewData["signed-out"] = true;
6 | }
7 |
8 |
27 |
28 | @section scripts
29 | {
30 | @if (Model.AutomaticRedirectAfterSignOut)
31 | {
32 |
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Account/Login.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.Account.LoginViewModel
2 |
3 | @{
4 | var dictionary = new Dictionary { {"ReturnUrl", Model.ReturnUrl }};
5 | }
6 |
7 |
8 |
11 |
12 |
13 |
14 |
15 |
16 | @if (Model.EnableLocalLogin)
17 | {
18 |
19 |
20 |
21 |
Local Login
22 |
23 |
60 |
61 |
62 | }
63 |
64 | @if (Model.VisibleExternalProviders.Any())
65 | {
66 |
67 |
68 |
69 |
External Login
70 |
71 |
87 |
88 |
89 | }
90 |
91 | @if (!Model.EnableLocalLogin && !Model.VisibleExternalProviders.Any())
92 | {
93 |
94 | Invalid login request
95 | There are no login schemes configured for this client.
96 |
97 | }
98 |
99 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Account/Logout.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.Account.LogoutViewModel
2 |
3 |
4 |
7 |
8 |
9 |
10 |
Would you like to logout of IdentityServer?
11 |
19 |
20 |
21 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Account/Register.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.Account.RegisterVM
2 |
3 |
4 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Local Login
17 |
18 |
48 |
49 |
50 |
51 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Consent/Index.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.Consent.ConsentViewModel
2 |
3 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Grants/Index.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.Grants.GrantsViewModel
2 |
3 |
4 |
12 |
13 | @if (Model.Grants.Any() == false)
14 | {
15 |
16 |
17 |
18 | You have not given access to any applications
19 |
20 |
21 |
22 | }
23 | else
24 | {
25 | foreach (var grant in Model.Grants)
26 | {
27 |
28 |
29 | @if (grant.ClientLogoUrl != null)
30 | {
31 |
32 | }
33 |
34 |
35 |
@grant.ClientName
36 |
37 | Created: @grant.Created.ToString("yyyy-MM-dd")
38 |
39 | @if (grant.Expires.HasValue)
40 | {
41 |
42 | Expires: @grant.Expires.Value.ToString("yyyy-MM-dd")
43 |
44 | }
45 | @if (grant.IdentityGrantNames.Any())
46 | {
47 |
48 |
Identity Grants
49 |
50 | @foreach (var name in grant.IdentityGrantNames)
51 | {
52 | @name
53 | }
54 |
55 |
56 | }
57 | @if (grant.ApiGrantNames.Any())
58 | {
59 |
60 |
API Grants
61 |
62 | @foreach (var name in grant.ApiGrantNames)
63 | {
64 | @name
65 | }
66 |
67 |
68 | }
69 |
70 |
71 |
75 |
76 |
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Home/Index.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | var version = typeof(IdentityServer4.Hosting.IdentityServerMiddleware).Assembly.GetName().Version.ToString();
3 | }
4 |
5 |
6 |
15 |
16 |
17 |
18 |
19 | IdentityServer publishes a
20 | discovery document
21 | where you can find metadata and links to all the endpoints, key material, etc.
22 |
23 |
24 |
25 |
26 | Click here to manage your stored grants.
27 |
28 |
29 |
30 |
39 |
40 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Shared/Error.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.ErrorViewModel
2 |
3 | @{
4 | var error = Model?.Error?.Error;
5 | var errorDescription = Model?.Error?.ErrorDescription;
6 | var request_id = Model?.Error?.RequestId;
7 | }
8 |
9 |
10 |
13 |
14 |
15 |
16 |
17 | Sorry, there was an error
18 |
19 | @if (error != null)
20 | {
21 |
22 |
23 | : @error
24 |
25 |
26 |
27 | if (errorDescription != null)
28 | {
29 |
@errorDescription
30 | }
31 | }
32 |
33 |
34 | @if (request_id != null)
35 | {
36 |
Request Id: @request_id
37 | }
38 |
39 |
40 |
41 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Shared/Redirect.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.RedirectViewModel
2 |
3 | You are now being returned to the application.
4 | Once complete, you may close this tab
5 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Shared/_Layout.cshtml:
--------------------------------------------------------------------------------
1 | @using IdentityServer4.Extensions
2 | @{
3 | string name = null;
4 | if (!true.Equals(ViewData["signed-out"]))
5 | {
6 | name = Context.User?.GetDisplayName();
7 | }
8 | }
9 |
10 |
11 |
12 |
13 |
14 |
15 | IdentityServer4
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
38 |
39 | @if (!string.IsNullOrWhiteSpace(name))
40 | {
41 |
42 |
43 | @name
44 |
47 |
48 |
49 | }
50 |
51 |
52 |
53 |
54 | @RenderBody()
55 |
56 |
57 |
58 |
59 | @RenderSection("scripts", required: false)
60 |
61 |
62 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Shared/_ScopeListItem.cshtml:
--------------------------------------------------------------------------------
1 | @model IdentityServer.Models.Consent.ScopeViewModel
2 |
3 |
4 |
5 |
12 | @if (Model.Required)
13 | {
14 |
17 | }
18 | @Model.DisplayName
19 | @if (Model.Emphasize)
20 | {
21 |
22 | }
23 |
24 | @if (Model.Required)
25 | {
26 | (required)
27 | }
28 | @if (Model.Description != null)
29 | {
30 |
31 | @Model.Description
32 |
33 | }
34 |
--------------------------------------------------------------------------------
/IdentityServer/Views/Shared/_ValidationSummary.cshtml:
--------------------------------------------------------------------------------
1 | @if (ViewContext.ModelState.IsValid == false)
2 | {
3 |
7 | }
--------------------------------------------------------------------------------
/IdentityServer/Views/_ViewImports.cshtml:
--------------------------------------------------------------------------------
1 | @using IdentityServer
2 | @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers
3 |
--------------------------------------------------------------------------------
/IdentityServer/Views/_ViewStart.cshtml:
--------------------------------------------------------------------------------
1 | @{
2 | Layout = "_Layout";
3 | }
4 |
--------------------------------------------------------------------------------
/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 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "ConnectionStrings": {
8 | "IdentityServerConnection": "Data Source=.;Database=IdentityServerDb;trusted_connection=yes;MultipleActiveResultSets=true;"
9 | },
10 | "UseInMemoryStores": true
11 | }
12 |
13 |
--------------------------------------------------------------------------------
/IdentityServer/tempkey.rsa:
--------------------------------------------------------------------------------
1 | {"KeyId":"cbd3483398a40cf777e490cd2244deb3","Parameters":{"D":"nRvkt+n9Jawzff57Gi98FkV8ph1fuL2YFM/HVoexu0GkVDUwL6RfakDWXzBH/PmtOOEpBp0oayZlmgmd4Hu9+SLBEhNVY58zoJR6c8MTj8nA52iJyB3zvTbkK/VNbRxTnylmV/co8do8rDiUC+3QjxcydQSAB34nZ52F4WTDw5z5PrdydhNomu/3kPeUOjhJUZ+jmRTB0xP+5ODYJOboKQBELWEqsXEq/wkUYws86qxL09JhDnIHLetr88LLMcoYSTkgn1Oz9I6/Rfe8+SizQgWpeFXuCn1KqsckaAPSZa45mw7cOYY5DSXQXZqjk9V8Zna83M1DbgPeSe3gARgoxQ==","DP":"sTGWLMK/0ioAXZR43jlDygtONUbM6Ckcvd8q3S7mpmiaNhVBo6KVBU2oFaQ2/t1D/cnBR4SnQNjMJlC6ctTLEBo9voCm2MNOXasfU8BjVWDfrKdePFjBmHpytA7V6PpuiGsZGfHkkCq++p8NQ0y8SwVBZecciAq3ec3DOy6sdfE=","DQ":"xTdgnNoSpXs7M++vPNXHZ3VUEs0okEI2ktuMzvdM8EgIlcvNP3DOF4+8RI0iPKMBJr/6xU6fs8RGmiCGOha48SCwTua7WnrkgsaEbnte+s+eaAGePRZMnOhN1j20Jz6RkcpAA87W9wZ8Gi9XrFnq1ImIfZhKhPkYguVUPckiv5c=","Exponent":"AQAB","InverseQ":"lFPtEbpQedFAC7OE5qqNX7gO1LIGdJXopa8KQqC1CdAmevEDeXBlZT//c/lj+zIe8oiinqPPGBw+08KCfhnA8NtxIQhfIptuEVA45O17U508adBjMwfFgXB54qHm9OVewYwMNY/keavm+Qj6PjdJEemk/5uk6Y6XtycVYeEpWjo=","Modulus":"uUBSCgtKENYM4QQb4L9D9XNXILc7FI2NULCgm9vfYq2mPV3dgXXYuU4rYN6W9ZDs3XZR/ja5BSFPBBwFl44ZMsFyxyt2nFX6s8S1YY4hS5ybqoC9eI7WU6CgqqVUiKfXjtQp2KBmVEGi6GjnrbqcIngoCQ9BQsW+hYdvE0T4JhK5GzPCXbR9yle+nVW/SVx2SKdlecuVTaU2Qa4ionF6RrwA2yJ/6TNikQgEQE2LEVMVMrvAWsbFxrsM8M57tbWe+8IWac2KkZlhQhXXQmoccRkDbKb7d+bA/kcAia8GU8U3twp4EP7TJHBi5/julRlW9Rq5Id926sKGT23PxMXjIQ==","P":"zATIVLzhnmTBm7Vn4ydDel1xKSXlqGEGZmGwnGoWVOkC6JMM4o0U8KYAX+gqeHs1n9m2dkIN6cOu9+PwVB5boL96Q7UvW0F68vtYU53zErFVcTCWIGnqtIgOUD44BUExs58A/oCWQ1ogenfZlWY34FgivDch/2KMRYmchMalUHs=","Q":"6HNsVuHFoKiMgyfTdBPtcS0MdGDLAlQhHg0jcvf733HLJP7XCLvx8zfjU8JRfa7WqLBl5zV7HCdd0abD/SGljDvN4y7uMUPA4G9EeMEszZMLtNfiySwxbPWhZesLSfe5wxGlgLE20eyvv+PxMSDM2wILWYCIRI/f7+5abdTvnhM="}}
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/css/site.css:
--------------------------------------------------------------------------------
1 | body {
2 | margin-top: 65px;
3 | }
4 | .navbar-header {
5 | position: relative;
6 | top: -4px;
7 | }
8 | .navbar-brand > .icon-banner {
9 | position: relative;
10 | top: -2px;
11 | display: inline;
12 | }
13 | .icon {
14 | position: relative;
15 | top: -10px;
16 | }
17 | .logged-out iframe {
18 | display: none;
19 | width: 0;
20 | height: 0;
21 | }
22 | .page-consent .client-logo {
23 | float: left;
24 | }
25 | .page-consent .client-logo img {
26 | width: 80px;
27 | height: 80px;
28 | }
29 | .page-consent .consent-buttons {
30 | margin-top: 25px;
31 | }
32 | .page-consent .consent-form .consent-scopecheck {
33 | display: inline-block;
34 | margin-right: 5px;
35 | }
36 | .page-consent .consent-form .consent-description {
37 | margin-left: 25px;
38 | }
39 | .page-consent .consent-form .consent-description label {
40 | font-weight: normal;
41 | }
42 | .page-consent .consent-form .consent-remember {
43 | padding-left: 16px;
44 | }
45 | .grants .page-header {
46 | margin-bottom: 10px;
47 | }
48 | .grants .grant {
49 | margin-top: 20px;
50 | padding-bottom: 20px;
51 | border-bottom: 1px solid lightgray;
52 | }
53 | .grants .grant img {
54 | width: 100px;
55 | height: 100px;
56 | }
57 | .grants .grant .clientname {
58 | font-size: 140%;
59 | font-weight: bold;
60 | }
61 | .grants .grant .granttype {
62 | font-size: 120%;
63 | font-weight: bold;
64 | }
65 | .grants .grant .created {
66 | font-size: 120%;
67 | font-weight: bold;
68 | }
69 | .grants .grant .expires {
70 | font-size: 120%;
71 | font-weight: bold;
72 | }
73 | .grants .grant li {
74 | list-style-type: none;
75 | display: inline;
76 | }
77 | .grants .grant li:after {
78 | content: ', ';
79 | }
80 | .grants .grant li:last-child:after {
81 | content: '';
82 | }
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/css/site.less:
--------------------------------------------------------------------------------
1 | body {
2 | margin-top: 65px;
3 | }
4 |
5 | .navbar-header {
6 | position: relative;
7 | top: -4px;
8 | }
9 |
10 | .navbar-brand > .icon-banner {
11 | position: relative;
12 | top: -2px;
13 | display: inline;
14 | }
15 |
16 | .icon {
17 | position: relative;
18 | top: -10px;
19 | }
20 |
21 | .logged-out iframe {
22 | display: none;
23 | width: 0;
24 | height: 0;
25 | }
26 |
27 | .page-consent {
28 | .client-logo {
29 | float: left;
30 |
31 | img {
32 | width: 80px;
33 | height: 80px;
34 | }
35 | }
36 |
37 | .consent-buttons {
38 | margin-top: 25px;
39 | }
40 |
41 | .consent-form {
42 | .consent-scopecheck {
43 | display: inline-block;
44 | margin-right: 5px;
45 | }
46 |
47 | .consent-scopecheck[disabled] {
48 | //visibility:hidden;
49 | }
50 |
51 | .consent-description {
52 | margin-left: 25px;
53 |
54 | label {
55 | font-weight: normal;
56 | }
57 | }
58 |
59 | .consent-remember {
60 | padding-left: 16px;
61 | }
62 | }
63 | }
64 |
65 | .grants {
66 | .page-header {
67 | margin-bottom: 10px;
68 | }
69 |
70 | .grant {
71 | margin-top: 20px;
72 | padding-bottom: 20px;
73 | border-bottom: 1px solid lightgray;
74 |
75 | img {
76 | width: 100px;
77 | height: 100px;
78 | }
79 |
80 | .clientname {
81 | font-size: 140%;
82 | font-weight: bold;
83 | }
84 |
85 | .granttype {
86 | font-size: 120%;
87 | font-weight: bold;
88 | }
89 |
90 | .created {
91 | font-size: 120%;
92 | font-weight: bold;
93 | }
94 |
95 | .expires {
96 | font-size: 120%;
97 | font-weight: bold;
98 | }
99 |
100 | li {
101 | list-style-type: none;
102 | display: inline;
103 |
104 | &:after {
105 | content: ', ';
106 | }
107 |
108 | &:last-child:after {
109 | content: '';
110 | }
111 |
112 | .displayname {
113 | }
114 | }
115 | }
116 | }
117 |
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/css/site.min.css:
--------------------------------------------------------------------------------
1 | body{margin-top:65px;}.navbar-header{position:relative;top:-4px;}.navbar-brand>.icon-banner{position:relative;top:-2px;display:inline;}.icon{position:relative;top:-10px;}.logged-out iframe{display:none;width:0;height:0;}.page-consent .client-logo{float:left;}.page-consent .client-logo img{width:80px;height:80px;}.page-consent .consent-buttons{margin-top:25px;}.page-consent .consent-form .consent-scopecheck{display:inline-block;margin-right:5px;}.page-consent .consent-form .consent-description{margin-left:25px;}.page-consent .consent-form .consent-description label{font-weight:normal;}.page-consent .consent-form .consent-remember{padding-left:16px;}.grants .page-header{margin-bottom:10px;}.grants .grant{margin-top:20px;padding-bottom:20px;border-bottom:1px solid #d3d3d3;}.grants .grant img{width:100px;height:100px;}.grants .grant .clientname{font-size:140%;font-weight:bold;}.grants .grant .granttype{font-size:120%;font-weight:bold;}.grants .grant .created{font-size:120%;font-weight:bold;}.grants .grant .expires{font-size:120%;font-weight:bold;}.grants .grant li{list-style-type:none;display:inline;}.grants .grant li:after{content:', ';}.grants .grant li:last-child:after{content:'';}
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/favicon.ico:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/IdentityServer/wwwroot/favicon.ico
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/icon.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/IdentityServer/wwwroot/icon.jpg
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/IdentityServer/wwwroot/icon.png
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/js/signin-redirect.js:
--------------------------------------------------------------------------------
1 | window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url");
2 |
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/js/signout-redirect.js:
--------------------------------------------------------------------------------
1 | window.addEventListener("load", function () {
2 | var a = document.querySelector("a.PostLogoutRedirectUri");
3 | if (a) {
4 | window.location = a.href;
5 | }
6 | });
7 |
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.eot
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.ttf
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff
--------------------------------------------------------------------------------
/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/chsakell/aspnet-core-identity/882cc2329f46086ed3d55e349d041e975b4d2e7c/IdentityServer/wwwroot/lib/bootstrap/fonts/glyphicons-halflings-regular.woff2
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2018 Christos Sakellarios
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/SocialNetwork.API/Controllers/ContactsController.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using Microsoft.AspNetCore.Authorization;
4 | using Microsoft.AspNetCore.Mvc;
5 |
6 | namespace SocialNetwork.API.Controllers
7 | {
8 | [Route("api/[controller]")]
9 | [ApiController]
10 | public class ContactsController : ControllerBase
11 | {
12 | // GET api/values
13 | [HttpGet]
14 | [Authorize]
15 | public ActionResult> Get()
16 | {
17 | return new List
18 | {
19 | new Contact
20 | {
21 | Name = "Francesca Fenton",
22 | Username = "Fenton25",
23 | Email = "francesca@example.com"
24 | },
25 | new Contact {
26 | Name = "Pierce North",
27 | Username = "Pierce",
28 | Email = "pierce@example.com"
29 | },
30 | new Contact {
31 | Name = "Marta Grimes",
32 | Username = "GrimesX",
33 | Email = "marta@example.com"
34 | },
35 | new Contact{
36 | Name = "Margie Kearney",
37 | Username = "Kearney20",
38 | Email = "margie@example.com"
39 | }
40 | };
41 | }
42 | }
43 |
44 | public class Contact
45 | {
46 | public Guid Id { get; set; }
47 | public string Name { get; set; }
48 | public string Username { get; set; }
49 | public string Email { get; set; }
50 |
51 | public Contact()
52 | {
53 | Id = Guid.NewGuid();
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/SocialNetwork.API/Program.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore;
2 | using Microsoft.AspNetCore.Hosting;
3 |
4 | namespace SocialNetwork.API
5 | {
6 | public class Program
7 | {
8 | public static void Main(string[] args)
9 | {
10 | CreateWebHostBuilder(args).Build().Run();
11 | }
12 |
13 | public static IWebHostBuilder CreateWebHostBuilder(string[] args) =>
14 | WebHost.CreateDefaultBuilder(args)
15 | .UseStartup();
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/SocialNetwork.API/Properties/launchSettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "iisSettings": {
3 | "windowsAuthentication": false,
4 | "anonymousAuthentication": true,
5 | "iisExpress": {
6 | "applicationUrl": "http://localhost:5010/",
7 | "sslPort": 0
8 | }
9 | },
10 | "$schema": "http://json.schemastore.org/launchsettings.json",
11 | "profiles": {
12 | "IIS Express": {
13 | "commandName": "IISExpress",
14 | "launchBrowser": true,
15 | "launchUrl": "api/contacts",
16 | "environmentVariables": {
17 | "ASPNETCORE_ENVIRONMENT": "Development"
18 | }
19 | },
20 | "SocialNetwork.API": {
21 | "commandName": "Project",
22 | "launchBrowser": true,
23 | "launchUrl": "api/contacts",
24 | "environmentVariables": {
25 | "ASPNETCORE_ENVIRONMENT": "Development"
26 | },
27 | "applicationUrl": "http://localhost:5010"
28 | }
29 | }
30 | }
--------------------------------------------------------------------------------
/SocialNetwork.API/SocialNetwork.API.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | netcoreapp3.0
5 | InProcess
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
--------------------------------------------------------------------------------
/SocialNetwork.API/Startup.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Builder;
2 | using Microsoft.AspNetCore.Hosting;
3 | using Microsoft.AspNetCore.Mvc;
4 | using Microsoft.Extensions.Configuration;
5 | using Microsoft.Extensions.DependencyInjection;
6 | using Microsoft.Extensions.Hosting;
7 |
8 | namespace SocialNetwork.API
9 | {
10 | public class Startup
11 | {
12 | public Startup(IConfiguration configuration)
13 | {
14 | Configuration = configuration;
15 | }
16 |
17 | public IConfiguration Configuration { get; }
18 |
19 | // This method gets called by the runtime. Use this method to add services to the container.
20 | public void ConfigureServices(IServiceCollection services)
21 | {
22 | services.AddMvc(options => options.EnableEndpointRouting = false);
23 |
24 | services.AddAuthorization();
25 |
26 | services.AddAuthentication("Bearer")
27 | .AddJwtBearer("Bearer", options =>
28 | {
29 | options.Authority = "http://localhost:5005";
30 | options.RequireHttpsMetadata = false;
31 |
32 | options.Audience = "SocialAPI";
33 | });
34 |
35 |
36 | services.AddCors(options =>
37 | {
38 | // this defines a CORS policy called "default"
39 | options.AddPolicy("default", policy =>
40 | {
41 | policy.WithOrigins("http://localhost:5000")
42 | .AllowAnyHeader()
43 | .AllowAnyMethod();
44 | });
45 | });
46 | }
47 |
48 | // This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
49 | public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
50 | {
51 | if (env.IsDevelopment())
52 | {
53 | app.UseDeveloperExceptionPage();
54 | }
55 |
56 | app.UseCors("default");
57 | app.UseAuthentication();
58 | app.UseMvc();
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/SocialNetwork.API/appsettings.Development.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Debug",
5 | "System": "Information",
6 | "Microsoft": "Information"
7 | }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/SocialNetwork.API/appsettings.json:
--------------------------------------------------------------------------------
1 | {
2 | "Logging": {
3 | "LogLevel": {
4 | "Default": "Warning"
5 | }
6 | },
7 | "AllowedHosts": "*"
8 | }
9 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | version: '2.0.{build}'
2 | image: Visual Studio 2019
3 | branches:
4 | only:
5 | - master
6 | clone_depth: 1
7 | init:
8 | # Good practise, because Windows
9 | - git config --global core.autocrlf true
10 |
11 | # Install scripts. (runs after repo cloning)
12 | install:
13 | # Get the latest stable version of Node.js or io.js
14 | - cd AspNetCoreIdentity/ClientApp
15 | # install npm modules
16 | - ps: Install-Product node $env:nodejs_version
17 | - npm install
18 | before_build:
19 | - cd..
20 | # Display minimal restore text
21 | - cmd: dotnet restore --verbosity m
22 | build_script:
23 | # output will be in ./AspNetCoreIdentity/bin/Release/netcoreapp2.0/publish/
24 | - cmd: dotnet publish -c Release
25 |
--------------------------------------------------------------------------------