Client-side navigation. For example, click Counter then Back to return here.
11 |
Angular CLI integration. In development mode, there's no need to run ng serve. It runs in the background automatically, so your client-side resources are dynamically built on demand and the page refreshes when you modify any file.
12 |
Efficient production builds. In production mode, development-time features are disabled, and your dotnet publish configuration automatically invokes ng build to produce minified, ahead-of-time compiled JavaScript files.
13 |
14 |
The ClientApp subdirectory is a standard Angular CLI application. If you open a command prompt in that directory, you can run any ng command (e.g., ng test), or use npm to install extra packages into it.
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/src/app/user-home/user-home.component.spec.ts:
--------------------------------------------------------------------------------
1 | import { ComponentFixture, TestBed, waitForAsync } from '@angular/core/testing';
2 |
3 | import { UserHomeComponent } from './user-home.component';
4 |
5 | describe('UserHomeComponent', () => {
6 | let component: UserHomeComponent;
7 | let fixture: ComponentFixture;
8 |
9 | beforeEach(waitForAsync(() => {
10 | TestBed.configureTestingModule({
11 | declarations: [ UserHomeComponent ]
12 | })
13 | .compileComponents();
14 | }));
15 |
16 | beforeEach(() => {
17 | fixture = TestBed.createComponent(UserHomeComponent);
18 | component = fixture.componentInstance;
19 | fixture.detectChanges();
20 | });
21 |
22 | it('should create', () => {
23 | expect(component).toBeTruthy();
24 | });
25 | });
26 |
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/src/app/user-home/user-home.component.ts:
--------------------------------------------------------------------------------
1 | import { Component } from '@angular/core';
2 | import { UserService } from '../services/user.service';
3 |
4 | @Component({
5 | selector: 'app-user-home',
6 | templateUrl: './user-home.component.html',
7 | styleUrls: ['./user-home.component.css']
8 | })
9 | export class UserHomeComponent {
10 |
11 | userData: string;
12 |
13 | constructor(private userService: UserService) { }
14 |
15 | fetchUserData() {
16 | this.userService.getUserData().subscribe(
17 | (result: string) => {
18 | this.userData = result;
19 | }
20 | );
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/src/assets/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/AnkitSharma-007/jwt-authentication-authorization-angular-aspnetcore/9ac1a641225b86f27fa80956ec94aa23fc3ca761/ngWithJwt/ClientApp/src/assets/.gitkeep
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/src/environments/environment.prod.ts:
--------------------------------------------------------------------------------
1 | export const environment = {
2 | production: true
3 | };
4 |
--------------------------------------------------------------------------------
/ngWithJwt/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/plugins/zone-error'; // Included with Angular CLI.
16 |
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/src/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | ngWithJwt
6 |
7 |
8 |
9 |
10 |
11 |
12 | Loading...
13 |
14 |
15 |
--------------------------------------------------------------------------------
/ngWithJwt/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 |
--------------------------------------------------------------------------------
/ngWithJwt/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 |
--------------------------------------------------------------------------------
/ngWithJwt/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 | /**
22 | * By default, zone.js will patch all possible macroTask and DomEvents
23 | * user can disable parts of macroTask/DomEvents patch by setting following flags
24 | * because those flags need to be set before `zone.js` being loaded, and webpack
25 | * will put import in the top of bundle, so user need to create a separate file
26 | * in this directory (for example: zone-flags.ts), and put the following flags
27 | * into that file, and then add the following code before importing zone.js.
28 | * import './zone-flags.ts';
29 | *
30 | * The flags allowed in zone-flags.ts are listed here.
31 | *
32 | * The following flags will work for all browsers.
33 | *
34 | * (window as any).__Zone_disable_requestAnimationFrame = true; // disable patch requestAnimationFrame
35 | * (window as any).__Zone_disable_on_property = true; // disable patch onProperty such as onclick
36 | * (window as any).__zone_symbol__UNPATCHED_EVENTS = ['scroll', 'mousemove']; // disable patch specified eventNames
37 | *
38 | * in IE/Edge developer tools, the addEventListener will also be wrapped by zone.js
39 | * with the following flag, it will bypass `zone.js` patch for IE/Edge
40 | *
41 | * (window as any).__Zone_enable_cross_context_check = true;
42 | *
43 | */
44 |
45 | /***************************************************************************************************
46 | * Zone JS is required by default for Angular itself.
47 | */
48 | import 'zone.js'; // Included with Angular CLI.
49 |
50 |
51 | /***************************************************************************************************
52 | * APPLICATION IMPORTS
53 | */
54 |
--------------------------------------------------------------------------------
/ngWithJwt/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 |
--------------------------------------------------------------------------------
/ngWithJwt/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/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 | teardown: { destroyAfterEach: false }
17 | }
18 | );
19 | // Then we find all the tests.
20 | const context = require.context('./', true, /\.spec\.ts$/);
21 | // And load the modules.
22 | context.keys().map(context);
23 |
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/src/tsconfig.app.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "outDir": "../out-tsc/app",
5 | "types": []
6 | },
7 | "files": [
8 | "main.ts",
9 | "polyfills.ts"
10 | ],
11 | "include": [
12 | "src/**/*.d.ts"
13 | ]
14 | }
15 |
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/src/tsconfig.server.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "../tsconfig.json",
3 | "compilerOptions": {
4 | "target": "es2016"
5 | },
6 | "angularCompilerOptions": {
7 | "entryModule": "app/app.server.module#AppServerModule"
8 | }
,
9 | "files": [
10 | "main.ts"
11 | ],
12 | "include": [
13 | "src/**/*.d.ts"
14 | ]
15 | }
16 |
--------------------------------------------------------------------------------
/ngWithJwt/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 |
--------------------------------------------------------------------------------
/ngWithJwt/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 |
--------------------------------------------------------------------------------
/ngWithJwt/ClientApp/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compileOnSave": false,
3 | "compilerOptions": {
4 | "baseUrl": "./",
5 | "module": "es2020",
6 | "outDir": "./dist/out-tsc",
7 | "sourceMap": true,
8 | "declaration": false,
9 | "moduleResolution": "node",
10 | "experimentalDecorators": true,
11 | "target": "es2015",
12 | "typeRoots": [
13 | "node_modules/@types"
14 | ],
15 | "lib": [
16 | "es2017",
17 | "dom"
18 | ]
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/ngWithJwt/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-var-keyword": true,
76 | "object-literal-sort-keys": false,
77 | "one-line": [
78 | true,
79 | "check-open-brace",
80 | "check-catch",
81 | "check-else",
82 | "check-whitespace"
83 | ],
84 | "prefer-const": true,
85 | "quotemark": [
86 | true,
87 | "single"
88 | ],
89 | "radix": true,
90 | "semicolon": [
91 | true,
92 | "always"
93 | ],
94 | "triple-equals": [
95 | true,
96 | "allow-null-check"
97 | ],
98 | "typedef-whitespace": [
99 | true,
100 | {
101 | "call-signature": "nospace",
102 | "index-signature": "nospace",
103 | "parameter": "nospace",
104 | "property-declaration": "nospace",
105 | "variable-declaration": "nospace"
106 | }
107 | ],
108 | "unified-signatures": true,
109 | "variable-name": false,
110 | "whitespace": [
111 | true,
112 | "check-branch",
113 | "check-decl",
114 | "check-operator",
115 | "check-separator",
116 | "check-type"
117 | ],
118 | "no-output-on-prefix": true,
119 | "no-inputs-metadata-property": true,
120 | "no-outputs-metadata-property": true,
121 | "no-host-metadata-property": true,
122 | "no-input-rename": true,
123 | "no-output-rename": true,
124 | "use-lifecycle-interface": true,
125 | "use-pipe-transform-interface": true,
126 | "component-class-suffix": true,
127 | "directive-class-suffix": true
128 | }
129 | }
130 |
--------------------------------------------------------------------------------
/ngWithJwt/Controllers/LoginController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using Microsoft.AspNetCore.Mvc;
3 | using Microsoft.Extensions.Configuration;
4 | using Microsoft.IdentityModel.Tokens;
5 | using ngWithJwt.Models;
6 | using System;
7 | using System.Collections.Generic;
8 | using System.IdentityModel.Tokens.Jwt;
9 | using System.Linq;
10 | using System.Security.Claims;
11 | using System.Text;
12 |
13 | namespace ngWithJwt.Controllers
14 | {
15 | [Produces("application/json")]
16 | [Route("api/[controller]")]
17 | public class LoginController : Controller
18 | {
19 | private readonly IConfiguration _config;
20 |
21 | // user details are hardcoded for simplicity. Ideally it should be stored in a database.
22 | private List appUsers = new List
23 | {
24 | new User { FirstName = "Admin", UserName = "admin", Password = "1234", UserType = "Admin" },
25 | new User { FirstName = "Ankit", UserName = "ankit", Password = "1234", UserType = "User" }
26 | };
27 |
28 | public LoginController(IConfiguration config)
29 | {
30 | _config = config;
31 | }
32 |
33 | [HttpPost]
34 | [AllowAnonymous]
35 | public IActionResult Login([FromBody] User login)
36 | {
37 | IActionResult response = Unauthorized();
38 |
39 | User user = AuthenticateUser(login);
40 | if (user != null)
41 | {
42 | var tokenString = GenerateJWT(user);
43 | response = Ok(new
44 | {
45 | token = tokenString,
46 | userDetails = user,
47 | });
48 | }
49 |
50 | return response;
51 | }
52 |
53 | User AuthenticateUser(User loginCredentials)
54 | {
55 | User user = appUsers.SingleOrDefault(x => x.UserName == loginCredentials.UserName && x.Password == loginCredentials.Password);
56 |
57 | return user;
58 | }
59 |
60 | string GenerateJWT(User userInfo)
61 | {
62 | var securityKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Jwt:SecretKey"]));
63 | var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
64 |
65 | var claims = new[]
66 | {
67 | new Claim(JwtRegisteredClaimNames.Sub, userInfo.UserName),
68 | new Claim("firstName", userInfo.FirstName.ToString()),
69 | new Claim("role",userInfo.UserType),
70 | new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
71 | };
72 |
73 | var token = new JwtSecurityToken(
74 | issuer: _config["Jwt:Issuer"],
75 | audience: _config["Jwt:Audience"],
76 | claims: claims,
77 | expires: DateTime.Now.AddMinutes(30),
78 | signingCredentials: credentials
79 | );
80 |
81 | return new JwtSecurityTokenHandler().WriteToken(token);
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/ngWithJwt/Controllers/UserController.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using Microsoft.AspNetCore.Mvc;
3 | using ngWithJwt.Models;
4 |
5 | namespace ngWithJwt.Controllers
6 | {
7 | [Produces("application/json")]
8 | [Route("api/[controller]")]
9 | public class UserController : Controller
10 | {
11 | [HttpGet]
12 | [Route("GetUserData")]
13 | [Authorize(Policy = Policies.User)]
14 | public IActionResult GetUserData()
15 | {
16 | return Ok("This is an normal user");
17 | }
18 |
19 | [HttpGet]
20 | [Route("GetAdminData")]
21 | [Authorize(Policy = Policies.Admin)]
22 | public IActionResult GetAdminData()
23 | {
24 | return Ok("This is an Admin user");
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/ngWithJwt/Models/Policies.cs:
--------------------------------------------------------------------------------
1 | using Microsoft.AspNetCore.Authorization;
2 | using System;
3 |
4 | namespace ngWithJwt.Models
5 | {
6 | public static class Policies
7 | {
8 | public const string Admin = "Admin";
9 | public const string User = "User";
10 |
11 | public static AuthorizationPolicy AdminPolicy()
12 | {
13 | return new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole(Admin).Build();
14 | }
15 |
16 | public static AuthorizationPolicy UserPolicy()
17 | {
18 | return new AuthorizationPolicyBuilder().RequireAuthenticatedUser().RequireRole(User).Build();
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/ngWithJwt/Models/User.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace ngWithJwt.Models
4 | {
5 | public class User
6 | {
7 | public string UserName { get; set; }
8 | public string FirstName { get; set; }
9 | public string Password { get; set; }
10 | public string UserType { get; set; }
11 | }
12 | }
13 |
--------------------------------------------------------------------------------
/ngWithJwt/Pages/Error.cshtml:
--------------------------------------------------------------------------------
1 | @page
2 | @model ErrorModel
3 | @{
4 | ViewData["Title"] = "Error";
5 | }
6 |
7 |
Error.
8 |
An error occurred while processing your request.
9 |
10 | @if (Model.ShowRequestId)
11 | {
12 |
13 | Request ID:@Model.RequestId
14 |
15 | }
16 |
17 |
Development Mode
18 |
19 | Swapping to the Development environment displays detailed information about the error that occurred.
20 |
21 |
22 | The Development environment shouldn't be enabled for deployed applications.
23 | It can result in displaying sensitive information from exceptions to end users.
24 | For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development
25 | and restarting the app.
26 |