Skip to content

Commit a4470bd

Browse files
committed
initial commit
0 parents  commit a4470bd

16 files changed

+400
-0
lines changed

.gitignore

+42
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
# Logs
2+
logs
3+
*.log
4+
npm-debug.log*
5+
6+
# Runtime data
7+
pids
8+
*.pid
9+
*.seed
10+
11+
# Directory for instrumented libs generated by jscoverage/JSCover
12+
lib-cov
13+
14+
# Coverage directory used by tools like istanbul
15+
coverage
16+
17+
# nyc test coverage
18+
.nyc_output
19+
20+
# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
21+
.grunt
22+
23+
# node-waf configuration
24+
.lock-wscript
25+
26+
# Compiled binary addons (http://nodejs.org/api/addons.html)
27+
build/Release
28+
29+
# Dependency directories
30+
node_modules
31+
jspm_packages
32+
typings
33+
34+
# Optional npm cache directory
35+
.npm
36+
37+
# Optional REPL history
38+
.node_repl_history
39+
40+
# .NET compiled files
41+
bin
42+
obj

.vscode/launch.json

+32
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
{
2+
// Use IntelliSense to learn about possible attributes.
3+
// Hover to view descriptions of existing attributes.
4+
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
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/netcoreapp3.0/WebApi.dll",
14+
"args": [],
15+
"cwd": "${workspaceFolder}",
16+
"stopAtEntry": false,
17+
"internalConsoleOptions": "openOnSessionStart",
18+
"env": {
19+
"ASPNETCORE_ENVIRONMENT": "Development"
20+
},
21+
"sourceFileMap": {
22+
"/Views": "${workspaceFolder}/Views"
23+
}
24+
},
25+
{
26+
"name": ".NET Core Attach",
27+
"type": "coreclr",
28+
"request": "attach",
29+
"processId": "${command:pickProcess}"
30+
}
31+
]
32+
}

.vscode/tasks.json

+17
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
{
2+
"version": "2.0.0",
3+
"tasks": [
4+
{
5+
"label": "build",
6+
"command": "dotnet",
7+
"type": "process",
8+
"args": [
9+
"build",
10+
"${workspaceFolder}/WebApi.csproj",
11+
"/property:GenerateFullPaths=true",
12+
"/consoleloggerparameters:NoSummary"
13+
],
14+
"problemMatcher": "$msCompile"
15+
}
16+
]
17+
}

Controllers/UsersController.cs

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
using Microsoft.AspNetCore.Mvc;
2+
using Microsoft.AspNetCore.Authorization;
3+
using WebApi.Services;
4+
using WebApi.Models;
5+
using System.Linq;
6+
7+
namespace WebApi.Controllers
8+
{
9+
[Authorize]
10+
[ApiController]
11+
[Route("[controller]")]
12+
public class UsersController : ControllerBase
13+
{
14+
private IUserService _userService;
15+
16+
public UsersController(IUserService userService)
17+
{
18+
_userService = userService;
19+
}
20+
21+
[AllowAnonymous]
22+
[HttpPost("authenticate")]
23+
public IActionResult Authenticate([FromBody]AuthenticateModel model)
24+
{
25+
var user = _userService.Authenticate(model.Username, model.Password);
26+
27+
if (user == null)
28+
return BadRequest(new { message = "Username or password is incorrect" });
29+
30+
return Ok(user);
31+
}
32+
33+
[HttpGet]
34+
public IActionResult GetAll()
35+
{
36+
var users = _userService.GetAll();
37+
return Ok(users);
38+
}
39+
}
40+
}

Entities/User.cs

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
namespace WebApi.Entities
2+
{
3+
public class User
4+
{
5+
public int Id { get; set; }
6+
public string FirstName { get; set; }
7+
public string LastName { get; set; }
8+
public string Username { get; set; }
9+
public string Password { get; set; }
10+
public string Token { get; set; }
11+
}
12+
}

Helpers/AppSettings.cs

+7
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
namespace WebApi.Helpers
2+
{
3+
public class AppSettings
4+
{
5+
public string Secret { get; set; }
6+
}
7+
}

Helpers/ExtensionMethods.cs

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
using WebApi.Entities;
4+
5+
namespace WebApi.Helpers
6+
{
7+
public static class ExtensionMethods
8+
{
9+
public static IEnumerable<User> WithoutPasswords(this IEnumerable<User> users) {
10+
return users.Select(x => x.WithoutPassword());
11+
}
12+
13+
public static User WithoutPassword(this User user) {
14+
user.Password = null;
15+
return user;
16+
}
17+
}
18+
}

LICENSE

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
The MIT License (MIT)
2+
3+
Copyright (c) 2019 Jason Watmore
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.

Models/AuthenticateModel.cs

+13
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
using System.ComponentModel.DataAnnotations;
2+
3+
namespace WebApi.Models
4+
{
5+
public class AuthenticateModel
6+
{
7+
[Required]
8+
public string Username { get; set; }
9+
10+
[Required]
11+
public string Password { get; set; }
12+
}
13+
}

Program.cs

+21
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
using Microsoft.AspNetCore.Hosting;
2+
using Microsoft.Extensions.Hosting;
3+
4+
namespace WebApi
5+
{
6+
public class Program
7+
{
8+
public static void Main(string[] args)
9+
{
10+
CreateHostBuilder(args).Build().Run();
11+
}
12+
13+
public static IHostBuilder CreateHostBuilder(string[] args) =>
14+
Host.CreateDefaultBuilder(args)
15+
.ConfigureWebHostDefaults(webBuilder =>
16+
{
17+
webBuilder.UseStartup<Startup>()
18+
.UseUrls("http://localhost:4000");
19+
});
20+
}
21+
}

README.md

+4
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
# aspnet-core-3-jwt-authentication-api
2+
3+
ASP.NET Core 3.0 - JWT Authentication API
4+

Services/UserService.cs

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.IdentityModel.Tokens.Jwt;
4+
using System.Linq;
5+
using System.Security.Claims;
6+
using System.Text;
7+
using Microsoft.Extensions.Options;
8+
using Microsoft.IdentityModel.Tokens;
9+
using WebApi.Entities;
10+
using WebApi.Helpers;
11+
12+
namespace WebApi.Services
13+
{
14+
public interface IUserService
15+
{
16+
User Authenticate(string username, string password);
17+
IEnumerable<User> GetAll();
18+
}
19+
20+
public class UserService : IUserService
21+
{
22+
// users hardcoded for simplicity, store in a db with hashed passwords in production applications
23+
private List<User> _users = new List<User>
24+
{
25+
new User { Id = 1, FirstName = "Test", LastName = "User", Username = "test", Password = "test" }
26+
};
27+
28+
private readonly AppSettings _appSettings;
29+
30+
public UserService(IOptions<AppSettings> appSettings)
31+
{
32+
_appSettings = appSettings.Value;
33+
}
34+
35+
public User Authenticate(string username, string password)
36+
{
37+
var user = _users.SingleOrDefault(x => x.Username == username && x.Password == password);
38+
39+
// return null if user not found
40+
if (user == null)
41+
return null;
42+
43+
// authentication successful so generate jwt token
44+
var tokenHandler = new JwtSecurityTokenHandler();
45+
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
46+
var tokenDescriptor = new SecurityTokenDescriptor
47+
{
48+
Subject = new ClaimsIdentity(new Claim[]
49+
{
50+
new Claim(ClaimTypes.Name, user.Id.ToString())
51+
}),
52+
Expires = DateTime.UtcNow.AddDays(7),
53+
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
54+
};
55+
var token = tokenHandler.CreateToken(tokenDescriptor);
56+
user.Token = tokenHandler.WriteToken(token);
57+
58+
return user.WithoutPassword();
59+
}
60+
61+
public IEnumerable<User> GetAll()
62+
{
63+
return _users.WithoutPasswords();
64+
}
65+
}
66+
}

Startup.cs

+76
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
using Microsoft.AspNetCore.Builder;
2+
using Microsoft.AspNetCore.Hosting;
3+
using Microsoft.Extensions.Configuration;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WebApi.Helpers;
6+
using WebApi.Services;
7+
using Microsoft.IdentityModel.Tokens;
8+
using System.Text;
9+
using Microsoft.AspNetCore.Authentication.JwtBearer;
10+
11+
namespace WebApi
12+
{
13+
public class Startup
14+
{
15+
public Startup(IConfiguration configuration)
16+
{
17+
Configuration = configuration;
18+
}
19+
20+
public IConfiguration Configuration { get; }
21+
22+
// This method gets called by the runtime. Use this method to add services to the container.
23+
public void ConfigureServices(IServiceCollection services)
24+
{
25+
services.AddCors();
26+
services.AddControllers();
27+
28+
// configure strongly typed settings objects
29+
var appSettingsSection = Configuration.GetSection("AppSettings");
30+
services.Configure<AppSettings>(appSettingsSection);
31+
32+
// configure jwt authentication
33+
var appSettings = appSettingsSection.Get<AppSettings>();
34+
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
35+
services.AddAuthentication(x =>
36+
{
37+
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
38+
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
39+
})
40+
.AddJwtBearer(x =>
41+
{
42+
x.RequireHttpsMetadata = false;
43+
x.SaveToken = true;
44+
x.TokenValidationParameters = new TokenValidationParameters
45+
{
46+
ValidateIssuerSigningKey = true,
47+
IssuerSigningKey = new SymmetricSecurityKey(key),
48+
ValidateIssuer = false,
49+
ValidateAudience = false
50+
};
51+
});
52+
53+
// configure DI for application services
54+
services.AddScoped<IUserService, UserService>();
55+
}
56+
57+
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
58+
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
59+
{
60+
app.UseRouting();
61+
62+
// global cors policy
63+
app.UseCors(x => x
64+
.AllowAnyOrigin()
65+
.AllowAnyMethod()
66+
.AllowAnyHeader());
67+
68+
app.UseAuthentication();
69+
app.UseAuthorization();
70+
71+
app.UseEndpoints(endpoints => {
72+
endpoints.MapControllers();
73+
});
74+
}
75+
}
76+
}

0 commit comments

Comments
 (0)