First commit - init service with swagger, user controller + auth controller
This commit is contained in:
parent
a12c36de6a
commit
629e9c31f7
6
.gitignore
vendored
6
.gitignore
vendored
@ -48,3 +48,9 @@ Thumbs.db
|
||||
*.mov
|
||||
*.wmv
|
||||
|
||||
.vs
|
||||
bin
|
||||
obj
|
||||
*.user
|
||||
ManagerService/bin
|
||||
ManagerService/obj
|
||||
|
||||
35
Manager.Framework/Business/ProfileLogic.cs
Normal file
35
Manager.Framework/Business/ProfileLogic.cs
Normal file
@ -0,0 +1,35 @@
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Manager.Framework.Business
|
||||
{
|
||||
public class ProfileLogic
|
||||
{
|
||||
private readonly ILogger<ProfileLogic> _logger;
|
||||
|
||||
public ProfileLogic(ILogger<ProfileLogic> logger)
|
||||
: base()
|
||||
{
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public bool Authenticate(string email, string password)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(email))
|
||||
{
|
||||
_logger.LogError($"Authenticate error: No e-mail provided");
|
||||
throw new UnauthorizedAccessException("Authentication error");
|
||||
}
|
||||
if (string.IsNullOrEmpty(password))
|
||||
{
|
||||
_logger.LogError($"Authenticate error: No password provided");
|
||||
throw new UnauthorizedAccessException("Authentication error");
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
11
Manager.Framework/Manager.Framework.csproj
Normal file
11
Manager.Framework/Manager.Framework.csproj
Normal file
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="5.0.0" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
29
Manager.Framework/Models/Exceptions/RequestException.cs
Normal file
29
Manager.Framework/Models/Exceptions/RequestException.cs
Normal file
@ -0,0 +1,29 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
using System.Text.Json;
|
||||
|
||||
namespace Manager.Framework.Models
|
||||
{
|
||||
[System.Serializable]
|
||||
public class RequestException : System.Exception
|
||||
{
|
||||
public int StatusCode { get; set; }
|
||||
|
||||
public object Payload { get; set; }
|
||||
|
||||
protected RequestException() { }
|
||||
|
||||
public RequestException(int statusCode) : this() { StatusCode = statusCode; }
|
||||
public RequestException(int statusCode, string message) : base(message) { StatusCode = statusCode; }
|
||||
public RequestException(int statusCode, string message, System.Exception inner) : base(message, inner) { StatusCode = statusCode; }
|
||||
protected RequestException(
|
||||
System.Runtime.Serialization.SerializationInfo info,
|
||||
System.Runtime.Serialization.StreamingContext context) : base(info, context) { }
|
||||
|
||||
public string GetJson()
|
||||
{
|
||||
return JsonSerializer.Serialize(new { StatusCode, Message, Payload });
|
||||
}
|
||||
}
|
||||
}
|
||||
8
Manager.Interfaces/DTO/LoginDTO.cs
Normal file
8
Manager.Interfaces/DTO/LoginDTO.cs
Normal file
@ -0,0 +1,8 @@
|
||||
namespace Manager.Interfaces.DTO
|
||||
{
|
||||
public class LoginDTO
|
||||
{
|
||||
public string Email { get; set; }
|
||||
public string Password { get; set; }
|
||||
}
|
||||
}
|
||||
14
Manager.Interfaces/DTO/SwaggerTokenRequest.cs
Normal file
14
Manager.Interfaces/DTO/SwaggerTokenRequest.cs
Normal file
@ -0,0 +1,14 @@
|
||||
namespace Manager.Interfaces.DTO
|
||||
{
|
||||
/// <summary>
|
||||
/// Swagger test client authentication data
|
||||
/// </summary>
|
||||
public class SwaggerTokenRequest
|
||||
{
|
||||
public string grant_type { get; set; }
|
||||
public string username { get; set; }
|
||||
public string password { get; set; }
|
||||
public string client_id { get; set; }
|
||||
public string client_secret { get; set; }
|
||||
}
|
||||
}
|
||||
14
Manager.Interfaces/DTO/TokenDTO.cs
Normal file
14
Manager.Interfaces/DTO/TokenDTO.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
|
||||
namespace Manager.Interfaces.DTO
|
||||
{
|
||||
public class TokenDTO
|
||||
{
|
||||
public string access_token { get; set; }
|
||||
public string refresh_token { get; set; }
|
||||
public string scope { get; set; }
|
||||
public string token_type { get; set; }
|
||||
public int expires_in { get; set; }
|
||||
public DateTimeOffset expiration { get; set; }
|
||||
}
|
||||
}
|
||||
14
Manager.Interfaces/DTO/UserDetailDTO.cs
Normal file
14
Manager.Interfaces/DTO/UserDetailDTO.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Manager.Interfaces.DTO
|
||||
{
|
||||
public class UserDetailDTO
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
}
|
||||
}
|
||||
11
Manager.Interfaces/Manager.Interfaces.csproj
Normal file
11
Manager.Interfaces/Manager.Interfaces.csproj
Normal file
@ -0,0 +1,11 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="MongoDB.Bson" Version="2.12.1" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
12
Manager.Interfaces/Models/Policy.cs
Normal file
12
Manager.Interfaces/Models/Policy.cs
Normal file
@ -0,0 +1,12 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Manager.Interfaces.Models
|
||||
{
|
||||
public class Policy
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string[] Claims { get; set; }
|
||||
}
|
||||
}
|
||||
18
Manager.Interfaces/Models/TokensSettings.cs
Normal file
18
Manager.Interfaces/Models/TokensSettings.cs
Normal file
@ -0,0 +1,18 @@
|
||||
namespace Manager.Interfaces.Models
|
||||
{
|
||||
public class TokensSettings
|
||||
{
|
||||
/// <summary>
|
||||
/// Application secret for tokens generation
|
||||
/// </summary>
|
||||
public string Secret { get; set; }
|
||||
/// <summary>
|
||||
/// Access token expiration in minutes
|
||||
/// </summary>
|
||||
public int AccessTokenExpiration { get; set; } = 30;
|
||||
/// <summary>
|
||||
/// Refresh token expiration in minutes
|
||||
/// </summary>
|
||||
public int RefreshTokenExpiration { get; set; } = 4 * 60;
|
||||
}
|
||||
}
|
||||
53
Manager.Interfaces/Models/User.cs
Normal file
53
Manager.Interfaces/Models/User.cs
Normal file
@ -0,0 +1,53 @@
|
||||
using Manager.Interfaces.DTO;
|
||||
using MongoDB.Bson.Serialization.Attributes;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Manager.Interfaces.Models
|
||||
{
|
||||
/// <summary>
|
||||
/// User Information
|
||||
/// </summary>
|
||||
public class User
|
||||
{
|
||||
[BsonId]
|
||||
[BsonRepresentation(MongoDB.Bson.BsonType.ObjectId)]
|
||||
public string Id { get; set; }
|
||||
|
||||
[BsonElement("Email")]
|
||||
[BsonRequired]
|
||||
public string Email { get; set; } // UNIQUE !..
|
||||
|
||||
[BsonElement("Password")]
|
||||
[BsonRequired]
|
||||
public string Password { get; set; }
|
||||
|
||||
[BsonElement("FirstName")]
|
||||
[BsonRequired]
|
||||
public string FirstName { get; set; }
|
||||
|
||||
[BsonElement("LastName")]
|
||||
[BsonRequired]
|
||||
public string LastName { get; set; }
|
||||
|
||||
[BsonElement("Token")]
|
||||
[BsonRequired]
|
||||
public string Token { get; set; }
|
||||
|
||||
[BsonElement("DateCreation")]
|
||||
public DateTime DateCreation { get; set; }
|
||||
|
||||
public UserDetailDTO ToDTO()
|
||||
{
|
||||
return new UserDetailDTO()
|
||||
{
|
||||
Id = Id,
|
||||
Email = Email,
|
||||
FirstName = FirstName,
|
||||
LastName = LastName,
|
||||
};
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
37
ManagerService.sln
Normal file
37
ManagerService.sln
Normal file
@ -0,0 +1,37 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.30907.101
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ManagerService", "ManagerService\ManagerService.csproj", "{042E0BC4-8DCF-4EEC-8420-C71AA85D4D99}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Manager.Framework", "Manager.Framework\Manager.Framework.csproj", "{B9548312-650F-4538-85B8-8447F8BB2BD3}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Manager.Interfaces", "Manager.Interfaces\Manager.Interfaces.csproj", "{0B2EDCA0-C813-4EAA-9215-E219AE884F7D}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{042E0BC4-8DCF-4EEC-8420-C71AA85D4D99}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{042E0BC4-8DCF-4EEC-8420-C71AA85D4D99}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{042E0BC4-8DCF-4EEC-8420-C71AA85D4D99}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{042E0BC4-8DCF-4EEC-8420-C71AA85D4D99}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B9548312-650F-4538-85B8-8447F8BB2BD3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B9548312-650F-4538-85B8-8447F8BB2BD3}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B9548312-650F-4538-85B8-8447F8BB2BD3}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B9548312-650F-4538-85B8-8447F8BB2BD3}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0B2EDCA0-C813-4EAA-9215-E219AE884F7D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0B2EDCA0-C813-4EAA-9215-E219AE884F7D}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0B2EDCA0-C813-4EAA-9215-E219AE884F7D}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0B2EDCA0-C813-4EAA-9215-E219AE884F7D}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B478AB21-859A-4B1E-BF99-77B01959805A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
104
ManagerService/Controllers/AuthenticationController.cs
Normal file
104
ManagerService/Controllers/AuthenticationController.cs
Normal file
@ -0,0 +1,104 @@
|
||||
using Manager.Interfaces.DTO;
|
||||
using Manager.Services;
|
||||
using ManagerService.Service.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Net.Http.Headers;
|
||||
using NSwag.Annotations;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ManagerService.Service.Controllers
|
||||
{
|
||||
/// <summary>
|
||||
/// Authentication controller
|
||||
/// </summary>
|
||||
[ApiController, Route("api/[controller]")]
|
||||
[Authorize]
|
||||
[OpenApiTag("Authentication", Description = "Authentication management")]
|
||||
public class AuthenticationController : ControllerBase
|
||||
{
|
||||
private readonly ILogger<AuthenticationController> _logger;
|
||||
private readonly TokensService _tokensService;
|
||||
private readonly UserDatabaseService _UserDatabaseService;
|
||||
|
||||
public AuthenticationController(ILogger<AuthenticationController> logger, TokensService tokensService, UserDatabaseService UserDatabaseService)
|
||||
{
|
||||
_logger = logger;
|
||||
_tokensService = tokensService;
|
||||
_UserDatabaseService = UserDatabaseService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate (business)
|
||||
/// </summary>
|
||||
/// <param name="email">user email</param>
|
||||
/// <param name="password">user password</param>
|
||||
/// <returns>Token descriptor</returns>
|
||||
private ObjectResult Authenticate(string email, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
// For TEST
|
||||
email = "test@email.be";
|
||||
password = "kljqsdkljqsd";
|
||||
var token = _tokensService.Authenticate(email.ToLower(), password);
|
||||
|
||||
// Set user token ?
|
||||
var user = _UserDatabaseService.GetByEmail(email.ToLower());
|
||||
|
||||
if (user == null)
|
||||
throw new KeyNotFoundException("User not found");
|
||||
|
||||
return new OkObjectResult(token);
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger?.LogError(ex, $"Authentication error for user '{email}': unauthorized access");
|
||||
return Unauthorized(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, $"Authenticate error for user '{email}'");
|
||||
return Problem($"Authenticate error for user '{email}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate with form parameters (used by Swagger test client)
|
||||
/// </summary>
|
||||
/// <param name="tokenRequest">Swagger token request</param>
|
||||
/// <returns>Token descriptor</returns>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("Token")]
|
||||
[Consumes("application/x-www-form-urlencoded")]
|
||||
[ProducesResponseType(typeof(TokenDTO), (int) HttpStatusCode.OK)]
|
||||
[ProducesResponseType(typeof(string), (int) HttpStatusCode.Unauthorized)]
|
||||
[ProducesResponseType(typeof(string), (int) HttpStatusCode.InternalServerError)]
|
||||
public ObjectResult AuthenticateWithForm([FromForm] SwaggerTokenRequest tokenRequest)
|
||||
{
|
||||
return Authenticate(tokenRequest.username, tokenRequest.password);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate with Json parameters (used by most clients)
|
||||
/// </summary>
|
||||
/// <param name="login">Login DTO</param>
|
||||
/// <returns>Token descriptor</returns>
|
||||
[AllowAnonymous]
|
||||
[HttpPost("Authenticate")]
|
||||
[Consumes("application/json")]
|
||||
[ProducesResponseType(typeof(TokenDTO), (int)HttpStatusCode.OK)]
|
||||
[ProducesResponseType(typeof(string), (int)HttpStatusCode.Unauthorized)]
|
||||
[ProducesResponseType(typeof(string), (int)HttpStatusCode.InternalServerError)]
|
||||
public ObjectResult AuthenticateWithJson([FromBody] LoginDTO login)
|
||||
{
|
||||
return Authenticate(login.Email.ToLower(), login.Password);
|
||||
}
|
||||
}
|
||||
}
|
||||
205
ManagerService/Controllers/UserController.cs
Normal file
205
ManagerService/Controllers/UserController.cs
Normal file
@ -0,0 +1,205 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Manager.Interfaces.DTO;
|
||||
using Manager.Interfaces.Models;
|
||||
using Manager.Services;
|
||||
using ManagerService.Service.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace ManagerService.Controllers
|
||||
{
|
||||
[Authorize] // TODO Add ROLES (Roles = "Admin")
|
||||
[Route("[controller]")]
|
||||
[ApiController]
|
||||
public class UserController : ControllerBase
|
||||
{
|
||||
private UserDatabaseService _userService;
|
||||
private TokensService _tokenService;
|
||||
private readonly ILogger<UserController> _logger;
|
||||
|
||||
public UserController(ILogger<UserController> logger, UserDatabaseService userService, TokensService tokenService)
|
||||
{
|
||||
_logger = logger;
|
||||
_userService = userService;
|
||||
_tokenService = tokenService;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a list of user
|
||||
/// </summary>
|
||||
[ProducesResponseType(typeof(List<User>), 200)]
|
||||
[ProducesResponseType(typeof(string), 500)]
|
||||
[HttpGet]
|
||||
public ObjectResult Get()
|
||||
{
|
||||
try
|
||||
{
|
||||
List<User> users = _userService.GetAll();
|
||||
|
||||
return new OkObjectResult(users);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Get a specific user
|
||||
/// </summary>
|
||||
/// <param name="id">id user</param>
|
||||
[ProducesResponseType(typeof(UserDetailDTO), 200)]
|
||||
[ProducesResponseType(typeof(string), 404)]
|
||||
[ProducesResponseType(typeof(string), 500)]
|
||||
[HttpGet("{id}")]
|
||||
public ObjectResult Get(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
User user = _userService.GetById(id);
|
||||
|
||||
if (user == null)
|
||||
throw new KeyNotFoundException("This user was not found");
|
||||
|
||||
return new OkObjectResult(user.ToDTO());
|
||||
}
|
||||
catch (KeyNotFoundException ex)
|
||||
{
|
||||
return new NotFoundObjectResult(ex.Message) {};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Create an user
|
||||
/// </summary>
|
||||
/// <param name="newUser">New user info</param>
|
||||
[AllowAnonymous]
|
||||
[ProducesResponseType(typeof(UserDetailDTO), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(typeof(string), 409)]
|
||||
[ProducesResponseType(typeof(string), 500)]
|
||||
[HttpPost]
|
||||
public ObjectResult CreateUser([FromBody] User newUser)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (newUser == null)
|
||||
throw new ArgumentNullException("User param is null");
|
||||
|
||||
newUser.Token = _tokenService.GenerateToken(newUser.Email).ToString();
|
||||
newUser.DateCreation = DateTime.Now;
|
||||
|
||||
List<User> users = _userService.GetAll();
|
||||
|
||||
if (users.Select(u => u.Email).Contains(newUser.Email))
|
||||
throw new InvalidOperationException("This Email is already used");
|
||||
|
||||
User userCreated = _userService.Create(newUser);
|
||||
|
||||
return new OkObjectResult(userCreated.ToDTO());
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
return new BadRequestObjectResult(ex.Message) {};
|
||||
}
|
||||
catch (InvalidOperationException ex)
|
||||
{
|
||||
return new ConflictObjectResult(ex.Message) {};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Update an user
|
||||
/// </summary>
|
||||
/// <param name="updatedUser">User to update</param>
|
||||
[ProducesResponseType(typeof(UserDetailDTO), 200)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(typeof(string), 404)]
|
||||
[ProducesResponseType(typeof(string), 500)]
|
||||
[HttpPut]
|
||||
public ObjectResult UpdateUser([FromBody] User updatedUser)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (updatedUser == null)
|
||||
throw new ArgumentNullException("User param is null");
|
||||
|
||||
User user = _userService.GetById(updatedUser.Id);
|
||||
|
||||
if (user == null)
|
||||
throw new KeyNotFoundException("User does not exist");
|
||||
|
||||
User userModified = _userService.Update(updatedUser.Id, updatedUser);
|
||||
|
||||
return new OkObjectResult(userModified.ToDTO());
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
return new BadRequestObjectResult(ex.Message) {};
|
||||
}
|
||||
catch (KeyNotFoundException ex)
|
||||
{
|
||||
return new NotFoundObjectResult(ex.Message) {};
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Delete an user
|
||||
/// </summary>
|
||||
/// <param name="id">Id of user to delete</param>
|
||||
[ProducesResponseType(typeof(string), 202)]
|
||||
[ProducesResponseType(typeof(string), 400)]
|
||||
[ProducesResponseType(typeof(string), 404)]
|
||||
[ProducesResponseType(typeof(string), 500)]
|
||||
[HttpDelete("{id}")]
|
||||
public ObjectResult DeleteUser(string id)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (id == null)
|
||||
throw new ArgumentNullException("User param is null");
|
||||
|
||||
User user = _userService.GetById(id);
|
||||
|
||||
if (user == null)
|
||||
throw new KeyNotFoundException("User does not exist");
|
||||
|
||||
_userService.Remove(id);
|
||||
|
||||
return new ObjectResult("The user has been deleted") { StatusCode = 202 };
|
||||
|
||||
}
|
||||
catch (ArgumentNullException ex)
|
||||
{
|
||||
return new BadRequestObjectResult(ex.Message) { };
|
||||
}
|
||||
catch (KeyNotFoundException ex)
|
||||
{
|
||||
return new NotFoundObjectResult(ex.Message) { };
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
return new ObjectResult(ex.Message) { StatusCode = 500 };
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
ManagerService/ManagerService.csproj
Normal file
21
ManagerService/ManagerService.csproj
Normal file
@ -0,0 +1,21 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="2.1.2" />
|
||||
<PackageReference Include="Microsoft.IdentityModel.Tokens" Version="6.10.0" />
|
||||
<PackageReference Include="MongoDB.Driver" Version="2.12.1" />
|
||||
<PackageReference Include="NSwag.AspNetCore" Version="13.10.8" />
|
||||
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.10.0" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Manager.Framework\Manager.Framework.csproj" />
|
||||
<ProjectReference Include="..\Manager.Interfaces\Manager.Interfaces.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
|
||||
</Project>
|
||||
26
ManagerService/Program.cs
Normal file
26
ManagerService/Program.cs
Normal file
@ -0,0 +1,26 @@
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ManagerService
|
||||
{
|
||||
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>();
|
||||
});
|
||||
}
|
||||
}
|
||||
31
ManagerService/Properties/launchSettings.json
Normal file
31
ManagerService/Properties/launchSettings.json
Normal file
@ -0,0 +1,31 @@
|
||||
{
|
||||
"iisSettings": {
|
||||
"windowsAuthentication": false,
|
||||
"anonymousAuthentication": true,
|
||||
"iisExpress": {
|
||||
"applicationUrl": "http://localhost:50185",
|
||||
"sslPort": 44339
|
||||
}
|
||||
},
|
||||
"$schema": "http://json.schemastore.org/launchsettings.json",
|
||||
"profiles": {
|
||||
"IIS Express": {
|
||||
"commandName": "IISExpress",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "swagger",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_URLS": "http://*:5000/",
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
}
|
||||
},
|
||||
"ManagerService": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"launchUrl": "weatherforecast",
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "https://localhost:5001;http://localhost:5000"
|
||||
}
|
||||
}
|
||||
}
|
||||
60
ManagerService/Security.cs
Normal file
60
ManagerService/Security.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Manager.Interfaces.Models;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ManagerService.Service
|
||||
{
|
||||
internal static class Security
|
||||
{
|
||||
public const string Scope = "Manager-api";
|
||||
|
||||
/// <summary>
|
||||
/// Permissions
|
||||
/// </summary>
|
||||
private class Permissions
|
||||
{
|
||||
/// <summary>
|
||||
/// Admin access
|
||||
/// </summary>
|
||||
public const string Admin = "Manager.admin";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Custom claims types
|
||||
/// </summary>
|
||||
public class ClaimTypes
|
||||
{
|
||||
public const string Permission = "Permission";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Permissions for each type of profile
|
||||
/// </summary>
|
||||
public static readonly Dictionary<System.Type, string[]> ProfilesConfiguration = new Dictionary<System.Type, string[]>()
|
||||
{
|
||||
// An admin has access to everything
|
||||
//{ typeof(AdminProfile), new[] { Permissions.Admin} },
|
||||
};
|
||||
|
||||
/// <summary>
|
||||
/// Policies names
|
||||
/// </summary>
|
||||
public class Policies
|
||||
{
|
||||
/// <summary>
|
||||
/// Administration
|
||||
/// </summary>
|
||||
public const string Admin = "Manager.Administration";
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Policies
|
||||
/// </summary>
|
||||
public static readonly Policy[] PoliciesConfiguration = new[]
|
||||
{
|
||||
new Policy() { Name = Policies.Admin, Claims = new[] { Permissions.Admin} }
|
||||
};
|
||||
}
|
||||
}
|
||||
140
ManagerService/Services/TokensService.cs
Normal file
140
ManagerService/Services/TokensService.cs
Normal file
@ -0,0 +1,140 @@
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Options;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using System.Security.Cryptography;
|
||||
using Manager.Interfaces.Models;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using Manager.Framework.Business;
|
||||
using Manager.Interfaces.DTO;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
|
||||
namespace ManagerService.Service.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Tokens service
|
||||
/// </summary>
|
||||
public class TokensService
|
||||
{
|
||||
private readonly ILogger<TokensService> _logger;
|
||||
private readonly TokensSettings _tokenSettings;
|
||||
private readonly ProfileLogic _profileLogic;
|
||||
|
||||
private readonly SigningCredentials _signingCredentials;
|
||||
|
||||
/// <summary>
|
||||
/// Constructor
|
||||
/// </summary>
|
||||
/// <param name="logger">Logger</param>
|
||||
/// <param name="tokenSettings">Tokens settings</param>
|
||||
/// <param name="context">Database context</param>
|
||||
/// <param name="profileLogic">Profile logic</param>
|
||||
/// <param name="emailClient">Email client</param>
|
||||
public TokensService(ILogger<TokensService> logger, IOptions<TokensSettings> tokenSettings, ProfileLogic profileLogic)
|
||||
{
|
||||
_logger = logger;
|
||||
_tokenSettings = tokenSettings.Value;
|
||||
_profileLogic = profileLogic;
|
||||
|
||||
var key = Encoding.UTF8.GetBytes(_tokenSettings.Secret);
|
||||
_signingCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Authenticate
|
||||
/// </summary>
|
||||
/// <param name="email">Email</param>
|
||||
/// <param name="password">Password</param>
|
||||
/// <returns>Token DTO in case of success</returns>
|
||||
public TokenDTO Authenticate(string email, string password)
|
||||
{
|
||||
try
|
||||
{
|
||||
var claims = new List<System.Security.Claims.Claim>();
|
||||
var expiration = DateTime.UtcNow.AddMinutes(_tokenSettings.AccessTokenExpiration);
|
||||
|
||||
// Todo nothing good here..
|
||||
var profile = _profileLogic.Authenticate(email, password);
|
||||
|
||||
claims.Add(new Claim(ClaimTypes.Email, email));
|
||||
|
||||
// TODO: add refresh token support
|
||||
|
||||
var tokenHandler = new JwtSecurityTokenHandler();
|
||||
var tokenDescriptor = new SecurityTokenDescriptor()
|
||||
{
|
||||
Subject = new ClaimsIdentity(claims),
|
||||
Expires = expiration,
|
||||
SigningCredentials = _signingCredentials
|
||||
};
|
||||
var token = tokenHandler.CreateToken(tokenDescriptor);
|
||||
|
||||
return new TokenDTO()
|
||||
{
|
||||
access_token = tokenHandler.WriteToken(token),
|
||||
expires_in = _tokenSettings.AccessTokenExpiration * 60,
|
||||
expiration = new DateTimeOffset(token.ValidTo),
|
||||
token_type = "Bearer",
|
||||
scope = Security.Scope
|
||||
};
|
||||
}
|
||||
catch (UnauthorizedAccessException ex)
|
||||
{
|
||||
_logger?.LogError(ex, $"Authenticate error for user '{email}': unauthorized access");
|
||||
throw;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
_logger?.LogError(ex, $"Authenticate error for user '{email}': {ex.Message}");
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
public object GenerateToken(string username)
|
||||
{
|
||||
var secretKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("WVD[&vAwis9=#883bM$FRc0Mw8h")); // Put the secret in a file or something
|
||||
|
||||
var claims = new Claim[] {
|
||||
new Claim(ClaimTypes.Name, username),
|
||||
new Claim(JwtRegisteredClaimNames.Email, "john.doe@blinkingcaret.com"),
|
||||
new Claim(ClaimTypes.Role, "Admin")
|
||||
};
|
||||
|
||||
var token = new JwtSecurityToken(
|
||||
issuer: "Manager App",
|
||||
audience: "Manager client",
|
||||
claims: claims,
|
||||
notBefore: DateTime.Now,
|
||||
expires: DateTime.Now.AddDays(28),
|
||||
signingCredentials: new SigningCredentials(secretKey, SecurityAlgorithms.HmacSha256)
|
||||
);
|
||||
|
||||
string jwtToken = new JwtSecurityTokenHandler().WriteToken(token);
|
||||
|
||||
return jwtToken;
|
||||
}
|
||||
|
||||
public static string GenerateSHA256String(string inputString)
|
||||
{
|
||||
SHA256 sha256 = SHA256Managed.Create();
|
||||
byte[] bytes = Encoding.UTF8.GetBytes(inputString);
|
||||
byte[] hash = sha256.ComputeHash(bytes);
|
||||
return GetStringFromHash(hash);
|
||||
}
|
||||
|
||||
public static string GetStringFromHash(byte[] hash)
|
||||
{
|
||||
StringBuilder result = new StringBuilder();
|
||||
for (int i = 0; i < hash.Length; i++)
|
||||
{
|
||||
result.Append(hash[i].ToString("X2"));
|
||||
}
|
||||
return result.ToString();
|
||||
}
|
||||
}
|
||||
}
|
||||
59
ManagerService/Services/UserDatabaseService.cs
Normal file
59
ManagerService/Services/UserDatabaseService.cs
Normal file
@ -0,0 +1,59 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Manager.Interfaces.Models;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using MongoDB.Driver;
|
||||
|
||||
namespace Manager.Services
|
||||
{
|
||||
public class UserDatabaseService
|
||||
{
|
||||
private readonly IMongoCollection<User> _Users;
|
||||
|
||||
public UserDatabaseService(IConfiguration config)
|
||||
{
|
||||
var client = new MongoClient(config.GetConnectionString("TabletDb"));
|
||||
var database = client.GetDatabase("TabletDb");
|
||||
_Users = database.GetCollection<User>("Users");
|
||||
}
|
||||
public List<User> GetAll()
|
||||
{
|
||||
return _Users.Find(m => true).ToList();
|
||||
}
|
||||
|
||||
public User GetByEmail(string email)
|
||||
{
|
||||
return _Users.Find<User>(m => m.Email == email).FirstOrDefault();
|
||||
}
|
||||
|
||||
public User GetById(string id)
|
||||
{
|
||||
return _Users.Find<User>(m => m.Id == id).FirstOrDefault();
|
||||
}
|
||||
|
||||
public bool IsExist(string id)
|
||||
{
|
||||
return _Users.Find<User>(d => d.Id == id).FirstOrDefault() != null ? true : false;
|
||||
}
|
||||
|
||||
public User Create(User user)
|
||||
{
|
||||
_Users.InsertOne(user);
|
||||
return user;
|
||||
}
|
||||
|
||||
public User Update(string id, User userIn)
|
||||
{
|
||||
_Users.ReplaceOne(user => user.Id == id, userIn);
|
||||
return userIn;
|
||||
}
|
||||
|
||||
public void Remove(string id)
|
||||
{
|
||||
_Users.DeleteOne(user => user.Id == id);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
185
ManagerService/Startup.cs
Normal file
185
ManagerService/Startup.cs
Normal file
@ -0,0 +1,185 @@
|
||||
using Manager.Framework.Business;
|
||||
using Manager.Framework.Models;
|
||||
using Manager.Interfaces.Models;
|
||||
using Manager.Services;
|
||||
using ManagerService.Service;
|
||||
using ManagerService.Service.Services;
|
||||
using Microsoft.AspNetCore.Authentication.JwtBearer;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.AspNetCore.Diagnostics;
|
||||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Microsoft.AspNetCore.HttpsPolicy;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.Extensions.Configuration;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using NSwag;
|
||||
using NSwag.Generation.AspNetCore;
|
||||
using NSwag.Generation.Processors.Security;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Text.Json.Serialization;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace ManagerService
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public Startup(IConfiguration configuration)
|
||||
{
|
||||
Configuration = configuration;
|
||||
}
|
||||
|
||||
public IConfiguration Configuration { get; }
|
||||
|
||||
// This method gets called by the runtime. Use this method to add services to the container.
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
// Swagger
|
||||
services.AddControllers()
|
||||
.AddJsonOptions(options =>
|
||||
{
|
||||
options.JsonSerializerOptions.Converters.Add(new JsonStringEnumConverter());
|
||||
});
|
||||
|
||||
services.AddOpenApiDocument(config =>
|
||||
{
|
||||
ConfigureSwagger(config);
|
||||
});
|
||||
|
||||
services.AddCors(o => o.AddPolicy("AllowAll", builder =>
|
||||
{
|
||||
builder.AllowAnyOrigin()
|
||||
.AllowAnyMethod()
|
||||
.AllowAnyHeader();
|
||||
}));
|
||||
|
||||
// Authentication
|
||||
|
||||
var tokensConfiguration = Configuration.GetSection("Tokens");
|
||||
var tokenSettings = tokensConfiguration.Get<TokensSettings>();
|
||||
|
||||
services.Configure<TokensSettings>(tokensConfiguration);
|
||||
|
||||
foreach (var policy in Security.PoliciesConfiguration)
|
||||
services.AddAuthorization(options =>
|
||||
{
|
||||
options.AddPolicy(policy.Name, policyAdmin =>
|
||||
{
|
||||
foreach (var claim in policy.Claims)
|
||||
policyAdmin.RequireClaim(Security.ClaimTypes.Permission, claim);
|
||||
});
|
||||
});
|
||||
|
||||
services
|
||||
.AddAuthentication(x =>
|
||||
{
|
||||
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
x.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
|
||||
})
|
||||
.AddJwtBearer(x =>
|
||||
{
|
||||
x.RequireHttpsMetadata = false;
|
||||
x.SaveToken = true;
|
||||
x.TokenValidationParameters = new TokenValidationParameters
|
||||
{
|
||||
ValidateIssuerSigningKey = true,
|
||||
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(tokenSettings.Secret)),
|
||||
ValidateIssuer = false,
|
||||
ValidateAudience = false,
|
||||
RequireExpirationTime = false,
|
||||
ValidateLifetime = true
|
||||
};
|
||||
});
|
||||
|
||||
services.AddScoped<TokensService>();
|
||||
services.AddScoped(typeof(ProfileLogic));
|
||||
|
||||
services.AddScoped<UserDatabaseService>();
|
||||
}
|
||||
|
||||
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
|
||||
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
|
||||
{
|
||||
if (env.IsDevelopment())
|
||||
{
|
||||
//app.UseDeveloperExceptionPage();
|
||||
app.UseExceptionHandler(HandleError);
|
||||
}
|
||||
|
||||
app.UseHttpsRedirection();
|
||||
|
||||
app.UseRouting();
|
||||
app.UseAuthentication();
|
||||
app.UseAuthorization();
|
||||
|
||||
app.UseEndpoints(endpoints =>
|
||||
{
|
||||
endpoints.MapControllers();
|
||||
});
|
||||
|
||||
app.UseOpenApi();
|
||||
app.UseSwaggerUi3(configure =>
|
||||
{
|
||||
configure.OperationsSorter = "alpha";
|
||||
configure.TagsSorter = "alpha";
|
||||
});
|
||||
}
|
||||
|
||||
private void ConfigureSwagger(AspNetCoreOpenApiDocumentGeneratorSettings config)
|
||||
{
|
||||
config.GenerateEnumMappingDescription = true;
|
||||
config.AddSecurity("bearer", Enumerable.Empty<string>(), new OpenApiSecurityScheme
|
||||
{
|
||||
Type = OpenApiSecuritySchemeType.OAuth2,
|
||||
Description = "Manager Authentication",
|
||||
Flow = OpenApiOAuth2Flow.Password,
|
||||
Flows = new OpenApiOAuthFlows()
|
||||
{
|
||||
|
||||
Password = new OpenApiOAuthFlow()
|
||||
{
|
||||
Scopes = new Dictionary<string, string>
|
||||
{
|
||||
{ Security.Scope, "Manager WebAPI" }
|
||||
},
|
||||
TokenUrl = "/api/authentication/Token",
|
||||
AuthorizationUrl = "/authentication/Token",
|
||||
}
|
||||
}
|
||||
});
|
||||
config.OperationProcessors.Add(new AspNetCoreOperationSecurityScopeProcessor("bearer"));
|
||||
|
||||
config.PostProcess = document =>
|
||||
{
|
||||
document.Info.Title = "Manager Service";
|
||||
document.Info.Description = "API description";
|
||||
document.Info.Version = "Version Pre-Alpha";
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
private void HandleError(IApplicationBuilder error)
|
||||
{
|
||||
error.Run(async context =>
|
||||
{
|
||||
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
|
||||
var exception = exceptionHandlerPathFeature?.Error as RequestException;
|
||||
|
||||
if (exception != null)
|
||||
{
|
||||
var json = exception.GetJson();
|
||||
context.Response.ContentType = "application/json";
|
||||
context.Response.StatusCode = exception.StatusCode;
|
||||
await context.Response.WriteAsync(json);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
15
ManagerService/WeatherForecast.cs
Normal file
15
ManagerService/WeatherForecast.cs
Normal file
@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace ManagerService
|
||||
{
|
||||
public class WeatherForecast
|
||||
{
|
||||
public DateTime Date { get; set; }
|
||||
|
||||
public int TemperatureC { get; set; }
|
||||
|
||||
public int TemperatureF => 32 + (int)(TemperatureC / 0.5556);
|
||||
|
||||
public string Summary { get; set; }
|
||||
}
|
||||
}
|
||||
9
ManagerService/appsettings.Development.json
Normal file
9
ManagerService/appsettings.Development.json
Normal file
@ -0,0 +1,9 @@
|
||||
{
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
ManagerService/appsettings.json
Normal file
25
ManagerService/appsettings.json
Normal file
@ -0,0 +1,25 @@
|
||||
{
|
||||
"ConnectionStrings": {
|
||||
"TabletDb": "mongodb://admin:MioTech4ever!@localhost:27017" //TO CHANGE
|
||||
},
|
||||
"Logging": {
|
||||
"LogLevel": {
|
||||
"Default": "Information",
|
||||
"Microsoft": "Warning",
|
||||
"Microsoft.Hosting.Lifetime": "Information"
|
||||
}
|
||||
},
|
||||
"AllowedHosts": "*",
|
||||
"Tokens": {
|
||||
"Secret": "WVD[&vAwis9=#883bM$FRc0Mw8h",
|
||||
"AccessTokenExpiration": 86400,
|
||||
"RefreshTokenExpiration": 518400
|
||||
},
|
||||
"SecuritySettings": {
|
||||
"Secret": "kfexxgohdxeelabz",
|
||||
"Issuer": "Manager",
|
||||
"Audience": "the client of your app",
|
||||
"IdType": "Name",
|
||||
"TokenExpiryInHours": 2
|
||||
}
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user