Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 062280b

Browse files
add JWT token authentication, working logout too
1 parent e707a04 commit 062280b

File tree

12 files changed

+1432
-1232
lines changed

12 files changed

+1432
-1232
lines changed

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example.csproj‎

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
<None Remove="ClientApp\components\Error.tsx" />
3535
<None Remove="ClientApp\components\Example.tsx" />
3636
<None Remove="ClientApp\components\users\Logout.tsx" />
37+
<None Remove="ClientApp\css\home.css" />
38+
<None Remove="ClientApp\css\loading.css" />
39+
<None Remove="ClientApp\utils\helpers.tsx" />
3740
</ItemGroup>
3841

3942
<ItemGroup>
@@ -47,6 +50,9 @@
4750
<TypeScriptCompile Include="ClientApp\components\users\Login.tsx" />
4851
<TypeScriptCompile Include="ClientApp\components\users\Logout.tsx" />
4952
<TypeScriptCompile Include="ClientApp\components\users\Register.tsx" />
53+
<TypeScriptCompile Include="ClientApp\css\home.css" />
54+
<TypeScriptCompile Include="ClientApp\css\loading.css" />
55+
<TypeScriptCompile Include="ClientApp\utils\helpers.tsx" />
5056
</ItemGroup>
5157

5258
<ItemGroup>

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/ClientApp/components/About.tsx‎

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,12 @@
22

33
export class About extends React.Component<any, any> {
44
public render() {
5-
return <h3>This is our about page.</h3>
6-
};
5+
return (
6+
<div className="about">
7+
<h3>This is our about page.</h3>
8+
<p>Ellipsis loading icon by loading.io</p>
9+
<p>Baby cow image by neko-kuma.deviantart.com</p>
10+
</div>
11+
);
12+
}
713
}

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/ClientApp/components/Home.tsx‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,10 +5,10 @@ import { Grid, Row } from 'react-bootstrap';
55
export class Home extends React.Component<RouteComponentProps<{}>, {}> {
66
public render() {
77
return (
8-
<Grid>
8+
<GridclassName="home">
99
<Row className="text-center">
10-
<h2>Our Products</h2>
11-
<h4>This is an example of setting ASP.NET Core Web API and ReactJS with basic User Authentication. :)</h4>
10+
<h2>Hello!</h2>
11+
<h4>This is an example of setting up an ASP.NET Core Web API and ReactJS with basic User Authentication. :)</h4>
1212
</Row>
1313
</Grid>
1414
);

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/ClientApp/components/NavMenu.tsx‎

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,32 @@
11
import * as React from 'react';
22
import { Link, NavLink } from 'react-router-dom';
33
import { Navbar, NavItem, Nav } from 'react-bootstrap';
4+
import { isLoggedIn, getUser, removeAccessToken } from '../utils/helpers';
5+
6+
export class NavMenu extends React.Component<{}, {}> {
7+
constructor() {
8+
super();
9+
10+
this.logout = this.logout.bind(this);
11+
}
12+
13+
logout() {
14+
removeAccessToken();
15+
window.location.hash = '/';
16+
}
417

5-
export class NavMenu extends React.Component<{}, {}> {
618
render() {
19+
let content = isLoggedIn()
20+
?
21+
<Nav pullRight>
22+
{/*TODO: getUser can go to user profile page :)*/}
23+
<NavItem eventKey={1}>{`${getUser()}`}</NavItem>
24+
<NavItem eventKey={2} onClick={this.logout}>Logout</NavItem>
25+
</Nav>
26+
: <Nav pullRight>
27+
<NavItem eventKey={1} href="/login">Login</NavItem>
28+
<NavItem eventKey={2} href="/register">Register</NavItem>
29+
</Nav>
730
return (
831
<Navbar inverse collapseOnSelect fixedTop>
932
<Navbar.Header>
@@ -17,10 +40,7 @@ export class NavMenu extends React.Component<{}, {}> {
1740
<NavItem eventKey={1} href="/users">Users</NavItem>
1841
<NavItem eventKey={2} href="/about">About</NavItem>
1942
</Nav>
20-
<Nav pullRight>
21-
<NavItem eventKey={1} href="/login">Login</NavItem>
22-
<NavItem eventKey={2} href="/register">Register</NavItem>
23-
</Nav>;
43+
{ content }
2444
</Navbar.Collapse>
2545
</Navbar>
2646
);

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/ClientApp/components/users/Login.tsx‎

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,17 @@
11
import * as React from "react";
2+
import { Redirect } from "react-router-dom";
23
import 'isomorphic-fetch';
34
import { Form, FormGroup, FormControl, ControlLabel, Button, Col, Grid, Row } from 'react-bootstrap';
5+
import { setAccessToken, setUser, isLoggedIn } from '../../utils/helpers';
46

57
export class Login extends React.Component<any, any> {
68
constructor() {
79
super();
810

911
this.state = {
1012
userName: '',
11-
password: ''
13+
password: '',
14+
loggedIn: isLoggedIn()
1215
};
1316

1417
this.handleOnChange = this.handleOnChange.bind(this);
@@ -50,6 +53,9 @@ export class Login extends React.Component<any, any> {
5053
// since it hasn't clue yet; redirect to pages depending of response's status code
5154
checkStatus(res: any): void {
5255
if (res.status >= 200 && res.status < 300) {
56+
setAccessToken(res.access_token);
57+
setUser(this.state.userName);
58+
this.setState({ loggedIn: true });
5359
this.props.history.push('/example');
5460
} else {
5561
let error = new Error(res.statusTest);
@@ -59,6 +65,10 @@ export class Login extends React.Component<any, any> {
5965
}
6066

6167
render() {
68+
if (this.state.loggedIn) {
69+
return <Redirect to="/" />;
70+
}
71+
6272
return (
6373
<Grid>
6474
<Row className="show-grid">
Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
11
/*Import our styles and reference it to the boot.tsx for cleaner code*/
22
@import 'site.css';
3-
@import 'error.css';
3+
@import 'error.css';
4+
@import 'loading.css';
5+
@import 'home.css';

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/Controllers/UsersController.cs‎

Lines changed: 45 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -3,37 +3,45 @@ namespace ASPNETCoreReactJS_Example.Controllers
33
using Data.Models;
44
using Microsoft.AspNetCore.Identity;
55
using Microsoft.AspNetCore.Mvc;
6-
using Microsoft.Extensions.Logging;
6+
using Microsoft.Extensions.Configuration;
7+
using Microsoft.IdentityModel.Tokens;
78
using Models;
89
using Services.Interfaces;
10+
using System;
911
using System.Collections.Generic;
12+
using System.IdentityModel.Tokens.Jwt;
13+
using System.Linq;
14+
using System.Security.Claims;
15+
using System.Text;
1016
using System.Threading.Tasks;
1117

12-
[Route("api/[controller]")]
18+
[Route("api/[controller]/[action]")]
1319
public class UsersController : Controller
1420
{
1521
private readonly UserManager<User> userManager;
1622
private readonly SignInManager<User> signInManager;
23+
private readonly IConfiguration configuration;
1724
private readonly IUserService service;
1825

1926
public UsersController(
2027
UserManager<User> userManager,
2128
SignInManager<User> signInManager,
22-
ILogger<UsersController>logger,
29+
IConfigurationconfiguration,
2330
IUserService service)
2431
{
2532
this.userManager = userManager;
2633
this.signInManager = signInManager;
34+
this.configuration = configuration;
2735
this.service = service;
2836
}
2937

3038
// GET All Users
31-
[HttpGet("[action]")]
39+
[HttpGet]
3240
public IEnumerable<UserViewModel> All()
3341
=> this.service.All();
3442

3543
// POST Register User
36-
[HttpPost("[action]")]
44+
[HttpPost]
3745
public async Task<IActionResult> Register([FromBody]RegisterModel model)
3846
{
3947
if (!ModelState.IsValid)
@@ -58,11 +66,14 @@ public async Task<IActionResult> Register([FromBody]RegisterModel model)
5866

5967
this.service.Add(user);
6068

61-
return this.Ok();
69+
// Automatically sign in user ?
70+
//this.signInManager.SignInAsync(user, false);
71+
72+
return this.Ok(GenerateJwtToken(model.Email, user));
6273
}
6374

6475
// POST Login User
65-
[HttpPost("[action]")]
76+
[HttpPost]
6677
public async Task<IActionResult> Login([FromBody]LoginModel model)
6778
{
6879
if (!ModelState.IsValid)
@@ -78,16 +89,41 @@ public async Task<IActionResult> Login([FromBody]LoginModel model)
7889
return this.BadRequest();
7990
}
8091

81-
return this.Ok();
92+
var appUser = this.userManager.Users.SingleOrDefault(u => u.UserName == model.UserName);
93+
return this.Ok(GenerateJwtToken(model.UserName, appUser));
8294
}
8395

8496
// POST Logout User
85-
[HttpPost("[action]")]
97+
[HttpPost]
8698
public async Task<IActionResult> Logout()
8799
{
88100
await this.signInManager.SignOutAsync();
89101

90102
return this.Ok();
91103
}
104+
105+
private object GenerateJwtToken(string email, IdentityUser user)
106+
{
107+
var claims = new List<Claim>
108+
{
109+
new Claim(JwtRegisteredClaimNames.Sub, email),
110+
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
111+
new Claim(ClaimTypes.NameIdentifier, user.Id)
112+
};
113+
114+
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(configuration["JwtKey"]));
115+
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
116+
var expires = DateTime.Now.AddDays(Convert.ToDouble(configuration["JwtExpireDays"]));
117+
118+
var token = new JwtSecurityToken(
119+
configuration["JwtIssuer"],
120+
configuration["JwtIssuer"],
121+
claims,
122+
expires: expires,
123+
signingCredentials: creds
124+
);
125+
126+
return new JwtSecurityTokenHandler().WriteToken(token);
127+
}
92128
}
93129
}

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/Startup.cs‎

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,20 @@ namespace ASPNETCoreReactJS_Example
44
using Data;
55
using Data.Models;
66
using Extensions;
7+
using Microsoft.AspNetCore.Authentication.JwtBearer;
78
using Microsoft.AspNetCore.Builder;
89
using Microsoft.AspNetCore.Hosting;
910
using Microsoft.AspNetCore.Identity;
1011
using Microsoft.AspNetCore.SpaServices.Webpack;
1112
using Microsoft.EntityFrameworkCore;
1213
using Microsoft.Extensions.Configuration;
1314
using Microsoft.Extensions.DependencyInjection;
15+
using Microsoft.IdentityModel.Tokens;
1416
using Services.Implementations;
1517
using Services.Interfaces;
18+
using System;
19+
using System.IdentityModel.Tokens.Jwt;
20+
using System.Text;
1621

1722
public class Startup
1823
{
@@ -39,9 +44,32 @@ public void ConfigureServices(IServiceCollection services)
3944
.AddEntityFrameworkStores<ExampleDbContext>()
4045
.AddDefaultTokenProviders();
4146

47+
// Add JWT Authentication
48+
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
49+
4250
// Inject Application Services
4351
services.AddTransient<IUserService, UserService>();
4452

53+
services
54+
.AddAuthentication(options =>
55+
{
56+
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
57+
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
58+
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
59+
})
60+
.AddJwtBearer(cfg =>
61+
{
62+
cfg.RequireHttpsMetadata = false;
63+
cfg.SaveToken = true;
64+
cfg.TokenValidationParameters = new TokenValidationParameters
65+
{
66+
ValidIssuer = Configuration["JwtIssuer"],
67+
ValidAudience = Configuration["JwtIssuer"],
68+
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtKey"])),
69+
ClockSkew = TimeSpan.Zero // remove delay of token when expire
70+
};
71+
});
72+
4573
// AutoMapper, of course
4674
services.AddAutoMapper();
4775

@@ -72,6 +100,9 @@ public void Configure(IApplicationBuilder app, IHostingEnvironment env)
72100

73101
app.UseStaticFiles();
74102

103+
// Use Authentication
104+
app.UseAuthentication();
105+
75106
app.UseMvc(routes =>
76107
{
77108
routes.MapRoute(

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/Views/Home/Index.cshtml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
@{
22
ViewData["Title"] = "Home Page";
33
}
4-
<div id="react-app">Loading...</div>
4+
<div id="react-app"><imgclass="loading"src="~/elipsis.svg" /></div>
55

66
@section scripts {
77
<script src="~/dist/main.js" asp-append-version="true"></script>

‎ASPNETCoreReactJS-Example/ASPNETCoreReactJS-Example/appsettings.json‎

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,8 @@
66
"LogLevel": {
77
"Default": "Warning"
88
}
9-
}
9+
},
10+
"JwtKey": "SOME_RANDOM_KEY_DO_NOT_SHARE",
11+
"JwtIssuer": "https://127.0.0.1",
12+
"JwtExpireDays": 30
1013
}

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /