alternative identity
This commit is contained in:
parent
585ac7c672
commit
85d8e4e6c6
34 changed files with 921 additions and 216 deletions
10
Identity/.vscode/launch.json
vendored
10
Identity/.vscode/launch.json
vendored
|
@ -9,14 +9,12 @@
|
|||
"type": "coreclr",
|
||||
"request": "launch",
|
||||
"preLaunchTask": "build",
|
||||
"program": "${workspaceFolder}/IdentityServer/bin/Debug/netcoreapp3.1/IdentityServer.dll",
|
||||
// If you have changed target frameworks, make sure to update the program path.
|
||||
"program": "${workspaceFolder}/bin/Debug/netcoreapp3.1/WebApi.dll",
|
||||
"args": [],
|
||||
"cwd": "${workspaceFolder}/IdentityServer",
|
||||
"cwd": "${workspaceFolder}",
|
||||
"stopAtEntry": false,
|
||||
"serverReadyAction": {
|
||||
"action": "openExternally",
|
||||
"pattern": "\\bNow listening on:\\s+(https?://\\S+)"
|
||||
},
|
||||
"internalConsoleOptions": "openOnSessionStart",
|
||||
"env": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
|
|
27
Identity/.vscode/tasks.json
vendored
27
Identity/.vscode/tasks.json
vendored
|
@ -7,32 +7,7 @@
|
|||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/IdentityServer/IdentityServer.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "publish",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/IdentityServer/IdentityServer.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
},
|
||||
{
|
||||
"label": "watch",
|
||||
"command": "dotnet",
|
||||
"type": "process",
|
||||
"args": [
|
||||
"watch",
|
||||
"run",
|
||||
"${workspaceFolder}/IdentityServer/IdentityServer.csproj",
|
||||
"${workspaceFolder}/WebApi.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
|
|
134
Identity/Controllers/UsersController.cs
Normal file
134
Identity/Controllers/UsersController.cs
Normal file
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using AutoMapper;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using WebApi.Helpers;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System.Text;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Security.Claims;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using WebApi.Services;
|
||||
using WebApi.Entities;
|
||||
using WebApi.Models.Users;
|
||||
|
||||
namespace WebApi.Controllers
|
||||
{
|
||||
[Authorize]
|
||||
[ApiController]
|
||||
[Route("[controller]")]
|
||||
public class UsersController : ControllerBase
|
||||
{
|
||||
private IUserService _userService;
|
||||
private IMapper _mapper;
|
||||
private readonly AppSettings _appSettings;
|
||||
|
||||
public UsersController(
|
||||
IUserService userService,
|
||||
IMapper mapper,
|
||||
IOptions<AppSettings> appSettings)
|
||||
{
|
||||
_userService = userService;
|
||||
_mapper = mapper;
|
||||
_appSettings = appSettings.Value;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("authenticate")]
|
||||
public IActionResult Authenticate([FromBody]AuthenticateModel model)
|
||||
{
|
||||
var user = _userService.Authenticate(model.Username, model.Password);
|
||||
|
||||
if (user == null)
|
||||
return BadRequest(new { message = "Username or password is incorrect" });
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
|
||||
var tokenDescriptor = new SecurityTokenDescriptor
|
||||
{
|
||||
Subject = new ClaimsIdentity(new Claim[]
|
||||
{
|
||||
new Claim(ClaimTypes.Name, user.Id.ToString())
|
||||
}),
|
||||
Expires = DateTime.UtcNow.AddDays(7),
|
||||
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
|
||||
};
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
var tokenString = tokenHandler.WriteToken(token);
|
||||
|
||||
// return basic user info and authentication token
|
||||
return Ok(new
|
||||
{
|
||||
Id = user.Id,
|
||||
Username = user.Username,
|
||||
FirstName = user.FirstName,
|
||||
LastName = user.LastName,
|
||||
Token = tokenString
|
||||
});
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
[HttpPost("register")]
|
||||
public IActionResult Register([FromBody]RegisterModel model)
|
||||
{
|
||||
// map model to entity
|
||||
var user = _mapper.Map<User>(model);
|
||||
|
||||
try
|
||||
{
|
||||
// create user
|
||||
_userService.Create(user, model.Password);
|
||||
return Ok();
|
||||
}
|
||||
catch (AppException ex)
|
||||
{
|
||||
// return error message if there was an exception
|
||||
return BadRequest(new { message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpGet]
|
||||
public IActionResult GetAll()
|
||||
{
|
||||
var users = _userService.GetAll();
|
||||
var model = _mapper.Map<IList<UserModel>>(users);
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
[HttpGet("{id}")]
|
||||
public IActionResult GetById(int id)
|
||||
{
|
||||
var user = _userService.GetById(id);
|
||||
var model = _mapper.Map<UserModel>(user);
|
||||
return Ok(model);
|
||||
}
|
||||
|
||||
[HttpPut("{id}")]
|
||||
public IActionResult Update(int id, [FromBody]UpdateModel model)
|
||||
{
|
||||
// map model to entity and set id
|
||||
var user = _mapper.Map<User>(model);
|
||||
user.Id = id;
|
||||
|
||||
try
|
||||
{
|
||||
// update user
|
||||
_userService.Update(user, model.Password);
|
||||
return Ok();
|
||||
}
|
||||
catch (AppException ex)
|
||||
{
|
||||
// return error message if there was an exception
|
||||
return BadRequest(new { message = ex.Message });
|
||||
}
|
||||
}
|
||||
|
||||
[HttpDelete("{id}")]
|
||||
public IActionResult Delete(int id)
|
||||
{
|
||||
_userService.Delete(id);
|
||||
return Ok();
|
||||
}
|
||||
}
|
||||
}
|
12
Identity/Entities/User.cs
Normal file
12
Identity/Entities/User.cs
Normal file
|
@ -0,0 +1,12 @@
|
|||
namespace WebApi.Entities
|
||||
{
|
||||
public class User
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Username { get; set; }
|
||||
public byte[] PasswordHash { get; set; }
|
||||
public byte[] PasswordSalt { get; set; }
|
||||
}
|
||||
}
|
19
Identity/Helpers/AppException.cs
Normal file
19
Identity/Helpers/AppException.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Globalization;
|
||||
|
||||
namespace WebApi.Helpers
|
||||
{
|
||||
// Custom exception class for throwing application specific exceptions (e.g. for validation)
|
||||
// that can be caught and handled within the application
|
||||
public class AppException : Exception
|
||||
{
|
||||
public AppException() : base() {}
|
||||
|
||||
public AppException(string message) : base(message) { }
|
||||
|
||||
public AppException(string message, params object[] args)
|
||||
: base(String.Format(CultureInfo.CurrentCulture, message, args))
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
7
Identity/Helpers/AppSettings.cs
Normal file
7
Identity/Helpers/AppSettings.cs
Normal file
|
@ -0,0 +1,7 @@
|
|||
namespace WebApi.Helpers
|
||||
{
|
||||
public class AppSettings
|
||||
{
|
||||
public string Secret { get; set; }
|
||||
}
|
||||
}
|
16
Identity/Helpers/AutoMapperProfile.cs
Normal file
16
Identity/Helpers/AutoMapperProfile.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using AutoMapper;
|
||||
using WebApi.Entities;
|
||||
using WebApi.Models.Users;
|
||||
|
||||
namespace WebApi.Helpers
|
||||
{
|
||||
public class AutoMapperProfile : Profile
|
||||
{
|
||||
public AutoMapperProfile()
|
||||
{
|
||||
CreateMap<User, UserModel>();
|
||||
CreateMap<RegisterModel, User>();
|
||||
CreateMap<UpdateModel, User>();
|
||||
}
|
||||
}
|
||||
}
|
24
Identity/Helpers/DataContext.cs
Normal file
24
Identity/Helpers/DataContext.cs
Normal file
|
@ -0,0 +1,24 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using WebApi.Entities;
|
||||
|
||||
namespace WebApi.Helpers
|
||||
{
|
||||
public class DataContext : DbContext
|
||||
{
|
||||
protected readonly IConfiguration Configuration;
|
||||
|
||||
public DataContext(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
// connect to sql server database
|
||||
options.UseSqlServer(Configuration.GetConnectionString("WebApiDatabase"));
|
||||
}
|
||||
|
||||
public DbSet<User> Users { get; set; }
|
||||
}
|
||||
}
|
16
Identity/Helpers/SqliteDataContext.cs
Normal file
16
Identity/Helpers/SqliteDataContext.cs
Normal file
|
@ -0,0 +1,16 @@
|
|||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
|
||||
namespace WebApi.Helpers
|
||||
{
|
||||
public class SqliteDataContext : DataContext
|
||||
{
|
||||
public SqliteDataContext(IConfiguration configuration) : base(configuration) { }
|
||||
|
||||
protected override void OnConfiguring(DbContextOptionsBuilder options)
|
||||
{
|
||||
// connect to sqlite database
|
||||
options.UseSqlite(Configuration.GetConnectionString("WebApiDatabase"));
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,39 +0,0 @@
|
|||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||
|
||||
|
||||
using IdentityServer4.Models;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace IdentityServer
|
||||
{
|
||||
public static class Config
|
||||
{
|
||||
public static IEnumerable<ApiScope> ApiScopes =>
|
||||
new List<ApiScope>
|
||||
{
|
||||
new ApiScope("seldgemapperApi", "Sledgemapper API")
|
||||
};
|
||||
|
||||
public static IEnumerable<Client> Clients =>
|
||||
new List<Client>
|
||||
{
|
||||
new Client
|
||||
{
|
||||
ClientId = "client",
|
||||
|
||||
// no interactive user, use the clientid/secret for authentication
|
||||
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||
|
||||
// secret for authentication
|
||||
ClientSecrets =
|
||||
{
|
||||
new Secret("secret".Sha256())
|
||||
},
|
||||
|
||||
// scopes that client has access to
|
||||
AllowedScopes = { "seldgemapperApi" }
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="IdentityServer4" Version="4.0.0" />
|
||||
|
||||
<PackageReference Include="Serilog.AspNetCore" Version="3.2.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||
|
||||
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Serilog;
|
||||
using Serilog.Events;
|
||||
using Serilog.Sinks.SystemConsole.Themes;
|
||||
using System;
|
||||
|
||||
namespace IdentityServer
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static int Main(string[] args)
|
||||
{
|
||||
Log.Logger = new LoggerConfiguration()
|
||||
.MinimumLevel.Debug()
|
||||
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
|
||||
.MinimumLevel.Override("System", LogEventLevel.Warning)
|
||||
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
|
||||
.Enrich.FromLogContext()
|
||||
// uncomment to write to Azure diagnostics stream
|
||||
//.WriteTo.File(
|
||||
// @"D:\home\LogFiles\Application\identityserver.txt",
|
||||
// fileSizeLimitBytes: 1_000_000,
|
||||
// rollOnFileSizeLimit: true,
|
||||
// shared: true,
|
||||
// flushToDiskInterval: TimeSpan.FromSeconds(1))
|
||||
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
|
||||
.CreateLogger();
|
||||
|
||||
try
|
||||
{
|
||||
Log.Information("Starting host...");
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
return 0;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Log.Fatal(ex, "Host terminated unexpectedly.");
|
||||
return 1;
|
||||
}
|
||||
finally
|
||||
{
|
||||
Log.CloseAndFlush();
|
||||
}
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.UseSerilog()
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>();
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,12 +0,0 @@
|
|||
{
|
||||
"profiles": {
|
||||
"SelfHost": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001"
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,60 +0,0 @@
|
|||
// Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
|
||||
// Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
|
||||
|
||||
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace IdentityServer
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public IWebHostEnvironment Environment { get; }
|
||||
|
||||
public Startup(IWebHostEnvironment environment)
|
||||
{
|
||||
Environment = environment;
|
||||
}
|
||||
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// uncomment, if you want to add an MVC-based UI
|
||||
//services.AddControllersWithViews();
|
||||
|
||||
var builder = services.AddIdentityServer(options =>
|
||||
{
|
||||
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
|
||||
options.EmitStaticAudienceClaim = true;
|
||||
})
|
||||
// .AddInMemoryIdentityResources(Config.IdentityResources)
|
||||
.AddInMemoryApiScopes(Config.ApiScopes)
|
||||
.AddInMemoryClients(Config.Clients);
|
||||
|
||||
// not recommended for production - you need to store your key material somewhere secure
|
||||
builder.AddDeveloperSigningCredential();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
if (Environment.IsDevelopment())
|
||||
{
|
||||
app.UseDeveloperExceptionPage();
|
||||
}
|
||||
|
||||
// uncomment if you want to add MVC
|
||||
//app.UseStaticFiles();
|
||||
//app.UseRouting();
|
||||
|
||||
app.UseIdentityServer();
|
||||
|
||||
// uncomment, if you want to add MVC
|
||||
//app.UseAuthorization();
|
||||
//app.UseEndpoints(endpoints =>
|
||||
//{
|
||||
// endpoints.MapDefaultControllerRoute();
|
||||
//});
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1 +0,0 @@
|
|||
{"alg":"RS256","d":"u88MEy28-4hRvzgL8MRiihCCqiNRJMkslHMr4NX64Xe_3MIxsfDsp-PF69y8OXYl_wlpWwshosmxBWWiRQNEFyJd6XnuLbOPPbEAvvApCDYEAiVqa9K40pCUPL2ICKk_Ix2qAjDzBAAK8Jvyrl_sth4JSw0IOHrZ77dtljLroII9EZaCcFEaUtFt58zKTg0fphqpZJUPjyAcabtHPCEfJKJFpPulnqOQA_BGhbT3Vpdo-N8gtLbVGzIbe5eOMxXf637STyfBrvkJN9BWQMm46H1XWoDaOD34ZbdbIC84xQn9igLeA1QCLP4C4NNNCkdcqpwd2nE7YoSS9PMOwqR-NQ","dp":"POSsdi7VV0EM843kV59KwLn8hYWTjcG6ACZMK5kKVDJ4tm6CDLJ-1MxCaSQwo2jJ6bRRATNLfmjfePU9qEuQ89oomZ-y0KeppKtwaGbsBPLv3LAjacwSzHY6jHvlEa04BZIHqjQc7MMBzARG68sAc3sbfYGwqGSSEtr01bfMAps","dq":"vGkZsdK_bg71EfDoHBAvknNPcXZ3gIjHxYiSZW8r5ZEawe862wcZcMAzI4PkkNcM9jJxWEWmKZvKORFVJstpdgrpNddblWbw4po_QySe2XBtyh1eplYybmZ7-HWdY-ogkJBWtzUdifGqhyjCtgkxV46pPWse3i_ymVV31Wt6Rak","e":"AQAB","kid":"28F80A128E9BFE8960B690D9A7AB7C83","kty":"RSA","n":"1gLbbP6yT36nnrkn6tBbJyulnhMA13uA8_588b4GBkCTFi4v9R193EJQCg28l6cm-_E93mnlc-_C4-ul1MSoUXehOZxayMzGlMANUVBCRUhazfY8lf3bf0vY1enGOYYQVl-_5w0PhoO5H1zIC7ohYkogivnOIH-QO9RrgwgBwpkm58rsT1pOscFrVKSWObp7_pHksBd3uXFnIDlIdvOGcptyD5YoYteS34Z3GRlgmvdm6Sq4oEwk_zgSWwiDLJtzFQsc4OGVoOBFRO0BxrF6CVrp6nEni6toYLyEHpwqoM56if07RSCihY2CqNFBM_FZa6V1YumC67pdWsjFjitxIQ","p":"3F_djAqfvrj-oyYDC_C1oKUTqDaGp-7OcwxKC86bCiWzeiS71gzbj1i5b4hCfjQgUsJNvpu15ETmc-buKstxjR0AJwVy5K5AjXAh-3BDzCHThVnIeG_NAK3PUtUsgpkfooBLClvnaiahDw55mqrqR2I4huFIkH5cE-s-OYuCq6M","q":"-Jul1jY6mO2F1DMESxaYIAMkvkh-dXn9420_olMs6Eu0-FyDGAdc1kzry0HJ94ZEOk0v8sI-jD8GZUncdQY_jHxGWUYqqQfDhJp4rYgkxUI89Odd40j3l2UDr1rY-ue3PHlY6xf17yGbpCXnS3UUhIaCPMKFmq62LXCi2N2WvGs","qi":"AmReUGiL7X8GwXiv_tjpqFwrGslqFkoIYz0Kq26MCTapDk0O8_qgdE5o5wdI2v87rvEGrGwY8vmmW0Okcfu52xxYxpp28-QPbTnjKCsQbsA7jOV7nejWqeQMTjdSzDd2lY0zvof_xLlHDr9ov2Qigj7ZUwqQ3xzdaAvziDQ0LdE"}
|
21
Identity/LICENSE
Normal file
21
Identity/LICENSE
Normal file
|
@ -0,0 +1,21 @@
|
|||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2019 Jason Watmore
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
53
Identity/Migrations/SqlServerMigrations/20200102103423_InitialCreate.Designer.cs
generated
Normal file
53
Identity/Migrations/SqlServerMigrations/20200102103423_InitialCreate.Designer.cs
generated
Normal file
|
@ -0,0 +1,53 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using WebApi.Helpers;
|
||||
|
||||
namespace WebApi.Migrations.SqlServerMigrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
[Migration("20200102103423_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("WebApi.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace WebApi.Migrations.SqlServerMigrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("SqlServer:Identity", "1, 1"),
|
||||
FirstName = table.Column<string>(nullable: true),
|
||||
LastName = table.Column<string>(nullable: true),
|
||||
Username = table.Column<string>(nullable: true),
|
||||
PasswordHash = table.Column<byte[]>(nullable: true),
|
||||
PasswordSalt = table.Column<byte[]>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,51 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using WebApi.Helpers;
|
||||
|
||||
namespace WebApi.Migrations.SqlServerMigrations
|
||||
{
|
||||
[DbContext(typeof(DataContext))]
|
||||
partial class DataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0")
|
||||
.HasAnnotation("Relational:MaxIdentifierLength", 128)
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
modelBuilder.Entity("WebApi.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int")
|
||||
.HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn);
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("varbinary(max)");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("nvarchar(max)");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
49
Identity/Migrations/SqliteMigrations/20200102102942_InitialCreate.Designer.cs
generated
Normal file
49
Identity/Migrations/SqliteMigrations/20200102102942_InitialCreate.Designer.cs
generated
Normal file
|
@ -0,0 +1,49 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using WebApi.Helpers;
|
||||
|
||||
namespace WebApi.Migrations.SqliteMigrations
|
||||
{
|
||||
[DbContext(typeof(SqliteDataContext))]
|
||||
[Migration("20200102102942_InitialCreate")]
|
||||
partial class InitialCreate
|
||||
{
|
||||
protected override void BuildTargetModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0");
|
||||
|
||||
modelBuilder.Entity("WebApi.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,34 @@
|
|||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
namespace WebApi.Migrations.SqliteMigrations
|
||||
{
|
||||
public partial class InitialCreate : Migration
|
||||
{
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "Users",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(nullable: false)
|
||||
.Annotation("Sqlite:Autoincrement", true),
|
||||
FirstName = table.Column<string>(nullable: true),
|
||||
LastName = table.Column<string>(nullable: true),
|
||||
Username = table.Column<string>(nullable: true),
|
||||
PasswordHash = table.Column<byte[]>(nullable: true),
|
||||
PasswordSalt = table.Column<byte[]>(nullable: true)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PK_Users", x => x.Id);
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "Users");
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
// <auto-generated />
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.EntityFrameworkCore.Infrastructure;
|
||||
using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
|
||||
using WebApi.Helpers;
|
||||
|
||||
namespace WebApi.Migrations.SqliteMigrations
|
||||
{
|
||||
[DbContext(typeof(SqliteDataContext))]
|
||||
partial class SqliteDataContextModelSnapshot : ModelSnapshot
|
||||
{
|
||||
protected override void BuildModel(ModelBuilder modelBuilder)
|
||||
{
|
||||
#pragma warning disable 612, 618
|
||||
modelBuilder
|
||||
.HasAnnotation("ProductVersion", "3.1.0");
|
||||
|
||||
modelBuilder.Entity("WebApi.Entities.User", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("INTEGER");
|
||||
|
||||
b.Property<string>("FirstName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<string>("LastName")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.Property<byte[]>("PasswordHash")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<byte[]>("PasswordSalt")
|
||||
.HasColumnType("BLOB");
|
||||
|
||||
b.Property<string>("Username")
|
||||
.HasColumnType("TEXT");
|
||||
|
||||
b.HasKey("Id");
|
||||
|
||||
b.ToTable("Users");
|
||||
});
|
||||
#pragma warning restore 612, 618
|
||||
}
|
||||
}
|
||||
}
|
13
Identity/Models/Users/AuthenticateModel.cs
Normal file
13
Identity/Models/Users/AuthenticateModel.cs
Normal file
|
@ -0,0 +1,13 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebApi.Models.Users
|
||||
{
|
||||
public class AuthenticateModel
|
||||
{
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
19
Identity/Models/Users/RegisterModel.cs
Normal file
19
Identity/Models/Users/RegisterModel.cs
Normal file
|
@ -0,0 +1,19 @@
|
|||
using System.ComponentModel.DataAnnotations;
|
||||
|
||||
namespace WebApi.Models.Users
|
||||
{
|
||||
public class RegisterModel
|
||||
{
|
||||
[Required]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[Required]
|
||||
public string LastName { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Username { get; set; }
|
||||
|
||||
[Required]
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
10
Identity/Models/Users/UpdateModel.cs
Normal file
10
Identity/Models/Users/UpdateModel.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace WebApi.Models.Users
|
||||
{
|
||||
public class UpdateModel
|
||||
{
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Username { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
10
Identity/Models/Users/UserModel.cs
Normal file
10
Identity/Models/Users/UserModel.cs
Normal file
|
@ -0,0 +1,10 @@
|
|||
namespace WebApi.Models.Users
|
||||
{
|
||||
public class UserModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Username { get; set; }
|
||||
}
|
||||
}
|
21
Identity/Program.cs
Normal file
21
Identity/Program.cs
Normal file
|
@ -0,0 +1,21 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace WebApi
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args)
|
||||
{
|
||||
CreateHostBuilder(args).Build().Run();
|
||||
}
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder =>
|
||||
{
|
||||
webBuilder.UseStartup<Startup>()
|
||||
.UseUrls("http://localhost:4000");
|
||||
});
|
||||
}
|
||||
}
|
10
Identity/Properties/launchSettings.json
Normal file
10
Identity/Properties/launchSettings.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"profiles": {
|
||||
"Development": {
|
||||
"commandName": "Project",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
5
Identity/README.md
Normal file
5
Identity/README.md
Normal file
|
@ -0,0 +1,5 @@
|
|||
# aspnet-core-3-registration-login-api
|
||||
|
||||
ASP.NET Core 3.1 - Simple API for User Management, Authentication and Registration
|
||||
|
||||
For documentation and instructions check out https://jasonwatmore.com/post/2019/10/14/aspnet-core-3-simple-api-for-authentication-registration-and-user-management
|
159
Identity/Services/UserService.cs
Normal file
159
Identity/Services/UserService.cs
Normal file
|
@ -0,0 +1,159 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using WebApi.Entities;
|
||||
using WebApi.Helpers;
|
||||
|
||||
namespace WebApi.Services
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
||||
User Authenticate(string username, string password);
|
||||
IEnumerable<User> GetAll();
|
||||
User GetById(int id);
|
||||
User Create(User user, string password);
|
||||
void Update(User user, string password = null);
|
||||
void Delete(int id);
|
||||
}
|
||||
|
||||
public class UserService : IUserService
|
||||
{
|
||||
private DataContext _context;
|
||||
|
||||
public UserService(DataContext context)
|
||||
{
|
||||
_context = context;
|
||||
}
|
||||
|
||||
public User Authenticate(string username, string password)
|
||||
{
|
||||
if (string.IsNullOrEmpty(username) || string.IsNullOrEmpty(password))
|
||||
return null;
|
||||
|
||||
var user = _context.Users.SingleOrDefault(x => x.Username == username);
|
||||
|
||||
// check if username exists
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
// check if password is correct
|
||||
if (!VerifyPasswordHash(password, user.PasswordHash, user.PasswordSalt))
|
||||
return null;
|
||||
|
||||
// authentication successful
|
||||
return user;
|
||||
}
|
||||
|
||||
public IEnumerable<User> GetAll()
|
||||
{
|
||||
return _context.Users;
|
||||
}
|
||||
|
||||
public User GetById(int id)
|
||||
{
|
||||
return _context.Users.Find(id);
|
||||
}
|
||||
|
||||
public User Create(User user, string password)
|
||||
{
|
||||
// validation
|
||||
if (string.IsNullOrWhiteSpace(password))
|
||||
throw new AppException("Password is required");
|
||||
|
||||
if (_context.Users.Any(x => x.Username == user.Username))
|
||||
throw new AppException("Username \"" + user.Username + "\" is already taken");
|
||||
|
||||
byte[] passwordHash, passwordSalt;
|
||||
CreatePasswordHash(password, out passwordHash, out passwordSalt);
|
||||
|
||||
user.PasswordHash = passwordHash;
|
||||
user.PasswordSalt = passwordSalt;
|
||||
|
||||
_context.Users.Add(user);
|
||||
_context.SaveChanges();
|
||||
|
||||
return user;
|
||||
}
|
||||
|
||||
public void Update(User userParam, string password = null)
|
||||
{
|
||||
var user = _context.Users.Find(userParam.Id);
|
||||
|
||||
if (user == null)
|
||||
throw new AppException("User not found");
|
||||
|
||||
// update username if it has changed
|
||||
if (!string.IsNullOrWhiteSpace(userParam.Username) && userParam.Username != user.Username)
|
||||
{
|
||||
// throw error if the new username is already taken
|
||||
if (_context.Users.Any(x => x.Username == userParam.Username))
|
||||
throw new AppException("Username " + userParam.Username + " is already taken");
|
||||
|
||||
user.Username = userParam.Username;
|
||||
}
|
||||
|
||||
// update user properties if provided
|
||||
if (!string.IsNullOrWhiteSpace(userParam.FirstName))
|
||||
user.FirstName = userParam.FirstName;
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(userParam.LastName))
|
||||
user.LastName = userParam.LastName;
|
||||
|
||||
// update password if provided
|
||||
if (!string.IsNullOrWhiteSpace(password))
|
||||
{
|
||||
byte[] passwordHash, passwordSalt;
|
||||
CreatePasswordHash(password, out passwordHash, out passwordSalt);
|
||||
|
||||
user.PasswordHash = passwordHash;
|
||||
user.PasswordSalt = passwordSalt;
|
||||
}
|
||||
|
||||
_context.Users.Update(user);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
|
||||
public void Delete(int id)
|
||||
{
|
||||
var user = _context.Users.Find(id);
|
||||
if (user != null)
|
||||
{
|
||||
_context.Users.Remove(user);
|
||||
_context.SaveChanges();
|
||||
}
|
||||
}
|
||||
|
||||
// private helper methods
|
||||
|
||||
private static void CreatePasswordHash(string password, out byte[] passwordHash, out byte[] passwordSalt)
|
||||
{
|
||||
if (password == null) throw new ArgumentNullException("password");
|
||||
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
|
||||
|
||||
using (var hmac = new System.Security.Cryptography.HMACSHA512())
|
||||
{
|
||||
passwordSalt = hmac.Key;
|
||||
passwordHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
|
||||
}
|
||||
}
|
||||
|
||||
private static bool VerifyPasswordHash(string password, byte[] storedHash, byte[] storedSalt)
|
||||
{
|
||||
if (password == null) throw new ArgumentNullException("password");
|
||||
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentException("Value cannot be empty or whitespace only string.", "password");
|
||||
if (storedHash.Length != 64) throw new ArgumentException("Invalid length of password hash (64 bytes expected).", "passwordHash");
|
||||
if (storedSalt.Length != 128) throw new ArgumentException("Invalid length of password salt (128 bytes expected).", "passwordHash");
|
||||
|
||||
using (var hmac = new System.Security.Cryptography.HMACSHA512(storedSalt))
|
||||
{
|
||||
var computedHash = hmac.ComputeHash(System.Text.Encoding.UTF8.GetBytes(password));
|
||||
for (int i = 0; i < computedHash.Length; i++)
|
||||
{
|
||||
if (computedHash[i] != storedHash[i]) return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
106
Identity/Startup.cs
Normal file
106
Identity/Startup.cs
Normal file
|
@ -0,0 +1,106 @@
|
|||
using System.Threading.Tasks;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using WebApi.Helpers;
|
||||
using WebApi.Services;
|
||||
using AutoMapper;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using System.Text;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using System;
|
||||
|
||||
namespace WebApi
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
private readonly IWebHostEnvironment _env;
|
||||
private readonly IConfiguration _configuration;
|
||||
|
||||
public Startup(IWebHostEnvironment env, IConfiguration configuration)
|
||||
{
|
||||
_env = env;
|
||||
_configuration = configuration;
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// use sql server db in production and sqlite db in development
|
||||
if (_env.IsProduction())
|
||||
services.AddDbContext<DataContext>();
|
||||
else
|
||||
services.AddDbContext<DataContext, SqliteDataContext>();
|
||||
|
||||
services.AddCors();
|
||||
services.AddControllers();
|
||||
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
|
||||
|
||||
// configure strongly typed settings objects
|
||||
var appSettingsSection = _configuration.GetSection("AppSettings");
|
||||
services.Configure<AppSettings>(appSettingsSection);
|
||||
|
||||
// configure jwt authentication
|
||||
var appSettings = appSettingsSection.Get<AppSettings>();
|
||||
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
|
||||
services.AddAuthentication(x =>
|
||||
{
|
||||
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(x =>
|
||||
{
|
||||
x.Events = new JwtBearerEvents
|
||||
{
|
||||
OnTokenValidated = context =>
|
||||
{
|
||||
var userService = context.HttpContext.RequestServices.GetRequiredService<IUserService>();
|
||||
var userId = int.Parse(context.Principal.Identity.Name);
|
||||
var user = userService.GetById(userId);
|
||||
if (user == null)
|
||||
{
|
||||
// return unauthorized if user no longer exists
|
||||
context.Fail("Unauthorized");
|
||||
}
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
};
|
||||
x.RequireHttpsMetadata = false;
|
||||
x.SaveToken = true;
|
||||
x.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(key),
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false
|
||||
};
|
||||
});
|
||||
|
||||
// configure DI for application services
|
||||
services.AddScoped<IUserService, UserService>();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, DataContext dataContext)
|
||||
{
|
||||
// migrate any database changes on startup (includes initial db creation)
|
||||
dataContext.Database.Migrate();
|
||||
|
||||
app.UseRouting();
|
||||
|
||||
// global cors policy
|
||||
app.UseCors(x => x
|
||||
.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader());
|
||||
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints => endpoints.MapControllers());
|
||||
}
|
||||
}
|
||||
}
|
18
Identity/WebApi.csproj
Normal file
18
Identity/WebApi.csproj
Normal file
|
@ -0,0 +1,18 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="AutoMapper" Version="9.0.0" />
|
||||
<PackageReference Include="AutoMapper.Extensions.Microsoft.DependencyInjection" Version="7.0.0" />
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design" Version="3.1.0">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
<PrivateAssets>all</PrivateAssets>
|
||||
</PackageReference>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Sqlite" Version="3.1.0" />
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.SqlServer" Version="3.1.0" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.6.0" />
|
||||
</ItemGroup>
|
||||
</Project>
|
12
Identity/appsettings.Development.json
Normal file
12
Identity/appsettings.Development.json
Normal file
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"ConnectionStrings": {
|
||||
"WebApiDatabase": "Data Source=LocalDatabase.db"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Debug",
|
||||
"System": "Information",
|
||||
"Microsoft": "Information"
|
||||
}
|
||||
}
|
||||
}
|
16
Identity/appsettings.json
Normal file
16
Identity/appsettings.json
Normal file
|
@ -0,0 +1,16 @@
|
|||
{
|
||||
"AppSettings": {
|
||||
"Secret": "THIS IS USED TO SIGN AND VERIFY JWT TOKENS, REPLACE IT WITH YOUR OWN SECRET, IT CAN BE ANY STRING"
|
||||
},
|
||||
"ConnectionStrings": {
|
||||
"WebApiDatabase": "ENTER PRODUCTION SQL SERVER CONNECTION STRING HERE"
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*"
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue