Tuitio refactoring
parent
e5854ef76b
commit
747b91898d
|
@ -17,7 +17,7 @@
|
|||
<Note>
|
||||
<Version>1.0.1</Version>
|
||||
<Content>
|
||||
◾ Hard changes in token structure. Now the token format is base64 and contains a json with all user data like username, first name, last name, profile picture url, email address and a list of claims that can be configured from the database for each user independently.
|
||||
◾ Big changes in token structure. Now the token format is base64 and contains a json with all user data like username, first name, last name, profile picture url, email address and a list of claims that can be configured from the database for each user independently.
|
||||
◾ The generation and validation mechanism for the token has been rewritten to meet the new token structure.
|
||||
◾ The complexity of user information has grown a lot. All users have now besides the data from token other information such as statuses, failed login attempts, last login date, password change date and security stamp.
|
||||
◾ All tokens are persisted in the database and the active ones are reload at a server failure or in case of a restart.
|
||||
|
@ -60,4 +60,12 @@
|
|||
◾ Added README.md file
|
||||
</Content>
|
||||
</Note>
|
||||
<Note>
|
||||
<Version>2.1.0</Version>
|
||||
<Content>
|
||||
◾ Tuitio refactoring
|
||||
◾ Added account logout method
|
||||
◾ Tuitio performance optimizations
|
||||
</Content>
|
||||
</Note>
|
||||
</ReleaseNotes>
|
|
@ -4,7 +4,7 @@ using MediatR;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Application.Services;
|
||||
using Tuitio.Application.Services.Abstractions;
|
||||
using Tuitio.PublishedLanguage.Constants;
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
|
||||
|
@ -30,15 +30,13 @@ namespace Tuitio.Application.CommandHandlers
|
|||
var loginResult = await _userService.Login(command.UserName, command.Password);
|
||||
if (loginResult == null)
|
||||
{
|
||||
_logger.LogDebug($"Authentication failed for user '{command.UserName}'.");
|
||||
_logger.LogDebug($"Login failed for user '{command.UserName}'.");
|
||||
return Envelope<AccountLoginResult>.Error(EnvelopeStatus.BAD_CREDENTIALS);
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Authentication succeeded for user '{command.UserName}'.");
|
||||
_logger.LogDebug($"Login succeeded for user '{command.UserName}'.");
|
||||
|
||||
var token = new Token(loginResult.Raw, loginResult.Token.ExpiresIn);
|
||||
|
||||
var result = new AccountLoginResult(token);
|
||||
var result = new AccountLoginResult(loginResult.Raw, loginResult.Token.ExpiresIn);
|
||||
return Envelope<AccountLoginResult>.Success(result);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,8 @@ using MediatR;
|
|||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Application.Services;
|
||||
using Tuitio.Application.Services.Abstractions;
|
||||
using Tuitio.PublishedLanguage.Constants;
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
|
||||
namespace Tuitio.Application.CommandHandlers
|
||||
|
@ -30,6 +31,12 @@ namespace Tuitio.Application.CommandHandlers
|
|||
public async Task<Envelope<AccountLogoutResult>> Handle(Command command, CancellationToken cancellationToken)
|
||||
{
|
||||
var logoutResult = await _userService.Logout(command.Token);
|
||||
if (logoutResult == null)
|
||||
{
|
||||
_logger.LogDebug($"Logout failed for token '{command.Token}'.");
|
||||
return Envelope<AccountLogoutResult>.Error(EnvelopeStatus.UNAUTHENTICATED);
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Logout succeeded for user '{logoutResult.UserName}'.");
|
||||
|
||||
var result = _mapper.Map<AccountLogoutResult>(logoutResult);
|
||||
|
|
|
@ -1,46 +1,46 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using AutoMapper;
|
||||
using Tuitio.Application.Services;
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
using MediatR;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Application.Services.Abstractions;
|
||||
using Tuitio.PublishedLanguage.Constants;
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
|
||||
namespace Tuitio.Application.CommandHandlers
|
||||
{
|
||||
public class AuthorizeTokenHandler
|
||||
public class AuthorizationHandler
|
||||
{
|
||||
public record Command(string Token) : IRequest<Envelope<TokenAuthorizationResult>>;
|
||||
public record Command(string Token) : IRequest<Envelope<AuthorizationResult>>;
|
||||
|
||||
public class CommandHandler : IRequestHandler<Command, Envelope<TokenAuthorizationResult>>
|
||||
public class CommandHandler : IRequestHandler<Command, Envelope<AuthorizationResult>>
|
||||
{
|
||||
private readonly IUserService _userService;
|
||||
private readonly IMapper _mapper;
|
||||
private readonly ILogger<AuthorizeTokenHandler> _logger;
|
||||
private readonly ILogger<AuthorizationHandler> _logger;
|
||||
|
||||
public CommandHandler(IUserService userService, IMapper mapper, ILogger<AuthorizeTokenHandler> logger)
|
||||
public CommandHandler(IUserService userService, IMapper mapper, ILogger<AuthorizationHandler> logger)
|
||||
{
|
||||
_userService = userService;
|
||||
_mapper = mapper;
|
||||
_logger = logger;
|
||||
}
|
||||
|
||||
public Task<Envelope<TokenAuthorizationResult>> Handle(Command command, CancellationToken cancellationToken)
|
||||
public Task<Envelope<AuthorizationResult>> Handle(Command command, CancellationToken cancellationToken)
|
||||
{
|
||||
var token = _userService.Authorize(command.Token);
|
||||
if (token == null)
|
||||
{
|
||||
_logger.LogDebug($"Authorization failed for token '{command.Token}'.");
|
||||
var result = Envelope<TokenAuthorizationResult>.Error(EnvelopeStatus.UNAUTHORIZED);
|
||||
var result = Envelope<AuthorizationResult>.Error(EnvelopeStatus.UNAUTHORIZED);
|
||||
return Task.FromResult(result);
|
||||
}
|
||||
|
||||
_logger.LogDebug($"Authorization succeeded for token '{command.Token}'.");
|
||||
var authorizationResult = new TokenAuthorizationResult(_mapper.Map<TokenCore>(token));
|
||||
var envelope = Envelope<TokenAuthorizationResult>.Success(authorizationResult);
|
||||
var authorizationResult = _mapper.Map<AuthorizationResult>(token);
|
||||
var envelope = Envelope<AuthorizationResult>.Success(authorizationResult);
|
||||
return Task.FromResult(envelope);
|
||||
}
|
||||
}
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using Tuitio.Application.Services;
|
||||
using Tuitio.Application.Services.Abstractions;
|
||||
using Tuitio.Application.Stores;
|
||||
using Tuitio.Domain.Abstractions;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
|
||||
namespace Tuitio.Application
|
||||
{
|
||||
|
|
|
@ -12,7 +12,7 @@ namespace Tuitio.Application.Mappings
|
|||
{
|
||||
public MappingProfile()
|
||||
{
|
||||
CreateMap<models.Token, dto.TokenCore>();
|
||||
CreateMap<models.Token, dto.AuthorizationResult>();
|
||||
CreateMap<AppUser, models.Token>()
|
||||
.ForMember(z => z.Claims, src => src.MapFrom(z => ComposeClaims(z.Claims)));
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
using Tuitio.Domain.Entities;
|
||||
using Tuitio.Domain.Models;
|
||||
|
||||
namespace Tuitio.Application.Services
|
||||
namespace Tuitio.Application.Services.Abstractions
|
||||
{
|
||||
internal interface ITokenService
|
||||
{
|
|
@ -1,10 +1,10 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using Tuitio.Domain.Models;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Domain.Models;
|
||||
using Tuitio.Domain.Models.Account;
|
||||
|
||||
namespace Tuitio.Application.Services
|
||||
namespace Tuitio.Application.Services.Abstractions
|
||||
{
|
||||
public interface IUserService
|
||||
{
|
|
@ -5,6 +5,7 @@ using Newtonsoft.Json;
|
|||
using System;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
using Tuitio.Application.Services.Abstractions;
|
||||
using Tuitio.Application.Stores;
|
||||
using Tuitio.Domain.Abstractions;
|
||||
using Tuitio.Domain.Entities;
|
||||
|
|
|
@ -1,14 +1,14 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Application.Services.Abstractions;
|
||||
using Tuitio.Application.Stores;
|
||||
using Tuitio.Domain.Abstractions;
|
||||
using Tuitio.Domain.Entities;
|
||||
using Tuitio.Domain.Models;
|
||||
using Tuitio.Domain.Repositories;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Domain.Models.Account;
|
||||
using Tuitio.Domain.Repositories;
|
||||
|
||||
namespace Tuitio.Application.Services
|
||||
{
|
||||
|
@ -33,6 +33,9 @@ namespace Tuitio.Application.Services
|
|||
{
|
||||
var passwordHash = _hashingService.HashSha256(password);
|
||||
var user = await _userRepository.GetUser(userName, passwordHash);
|
||||
if (user == null)
|
||||
return null;
|
||||
|
||||
var valid = ValidateUser(user);
|
||||
if (!valid)
|
||||
return null;
|
||||
|
@ -41,8 +44,7 @@ namespace Tuitio.Application.Services
|
|||
var raw = _tokenService.GenerateTokenRaw(token);
|
||||
_securityStore.Set(raw, token);
|
||||
|
||||
|
||||
await _userRepository.UpdateUserAfterAuthentication(user, token, raw);
|
||||
await _userRepository.UpdateUserAfterLogin(user, token, raw);
|
||||
|
||||
var result = new LoginResult(token, raw);
|
||||
return result;
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using Tuitio.Domain.Data.DbContexts;
|
||||
using Tuitio.Domain.Entities;
|
||||
using Tuitio.Domain.Models;
|
||||
using Tuitio.Domain.Repositories;
|
||||
using Microsoft.EntityFrameworkCore;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Domain.Data.DbContexts;
|
||||
using Tuitio.Domain.Entities;
|
||||
using Tuitio.Domain.Models;
|
||||
using Tuitio.Domain.Repositories;
|
||||
|
||||
namespace Tuitio.Domain.Data.Repositories
|
||||
{
|
||||
|
@ -28,7 +28,7 @@ namespace Tuitio.Domain.Data.Repositories
|
|||
.FirstOrDefaultAsync(z => z.UserName == userName && z.Password == password);
|
||||
}
|
||||
|
||||
public async Task UpdateUserAfterAuthentication(AppUser user, Token token, string tokenRaw)
|
||||
public async Task UpdateUserAfterLogin(AppUser user, Token token, string tokenRaw)
|
||||
{
|
||||
var userToken = new UserToken()
|
||||
{
|
||||
|
@ -40,23 +40,25 @@ namespace Tuitio.Domain.Data.Repositories
|
|||
};
|
||||
|
||||
await _dbContext.AddAsync(userToken);
|
||||
|
||||
user.LastLoginDate = DateTime.UtcNow;
|
||||
|
||||
//asta nu trebuie
|
||||
//_dbContext.Update(user);
|
||||
|
||||
await _dbContext.SaveChangesAsync();
|
||||
}
|
||||
|
||||
public Task<UserToken[]> GetActiveTokens()
|
||||
public async Task<UserToken[]> GetActiveTokens()
|
||||
{
|
||||
var currentDate = DateTime.UtcNow;
|
||||
|
||||
// remove expired tokens
|
||||
_dbContext.UserTokens.RemoveRange(_dbContext.UserTokens.Where(z => z.ValidUntil < currentDate));
|
||||
await _dbContext.SaveChangesAsync();
|
||||
|
||||
// retrieve active tokens
|
||||
var query = _dbContext.UserTokens
|
||||
.Where(z => z.ValidFrom <= currentDate && z.ValidUntil >= currentDate);
|
||||
|
||||
// read all tokens, keep the valid ones and remove the expired ones.
|
||||
return query.ToArrayAsync();
|
||||
var tokens = await query.ToArrayAsync();
|
||||
return tokens;
|
||||
}
|
||||
|
||||
public Task RemoveToken(Guid tokenId)
|
||||
|
|
|
@ -10,7 +10,7 @@ namespace Tuitio.Domain.Repositories
|
|||
public interface IUserRepository
|
||||
{
|
||||
Task<AppUser> GetUser(string userName, string password);
|
||||
Task UpdateUserAfterAuthentication(AppUser user, Token token, string tokenRaw);
|
||||
Task UpdateUserAfterLogin(AppUser user, Token token, string tokenRaw);
|
||||
Task<UserToken[]> GetActiveTokens();
|
||||
Task RemoveToken(Guid tokenId);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,7 @@ namespace Tuitio.PublishedLanguage.Constants
|
|||
{
|
||||
public const string
|
||||
BAD_CREDENTIALS = "BAD_CREDENTIALS",
|
||||
UNAUTHENTICATED = "UNAUTHENTICATED",
|
||||
UNAUTHORIZED = "UNAUTHORIZED";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,10 +1,25 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Tuitio.PublishedLanguage.Dto
|
||||
{
|
||||
public record AccountLoginResult(Token Token);
|
||||
public record AccountLoginResult(string Token, double ExpiresIn);
|
||||
public record AccountLogoutResult(int UserId, string UserName, DateTime LogoutDate);
|
||||
public record TokenAuthorizationResult(TokenCore TokenCore);
|
||||
public class AuthorizationResult
|
||||
{
|
||||
public Guid TokenId { get; init; }
|
||||
public int UserId { get; init; }
|
||||
public string UserName { get; init; }
|
||||
public string FirstName { get; init; }
|
||||
public string LastName { get; init; }
|
||||
public string Email { get; init; }
|
||||
public string ProfilePictureUrl { get; init; }
|
||||
public string SecurityStamp { get; init; }
|
||||
public string LockStamp { get; init; }
|
||||
public DateTime CreatedAt { get; init; }
|
||||
public double ExpiresIn { get; init; }
|
||||
public Dictionary<string, string> Claims { get; init; }
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
namespace Tuitio.PublishedLanguage.Dto
|
||||
{
|
||||
// move this in result
|
||||
public record Token(string Raw, double ExpiresIn);
|
||||
}
|
|
@ -1,19 +0,0 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Tuitio.PublishedLanguage.Dto
|
||||
{
|
||||
public class TokenCore
|
||||
{
|
||||
public int UserId { get; set; }
|
||||
public string UserName { get; set; }
|
||||
public string FirstName { get; set; }
|
||||
public string LastName { get; set; }
|
||||
public string Email { get; set; }
|
||||
public string ProfilePictureUrl { get; set; }
|
||||
public string SecurityStamp { get; set; }
|
||||
public string LockStamp { get; set; }
|
||||
public Dictionary<string, string> Claims { get; set; }
|
||||
}
|
||||
}
|
|
@ -1,3 +1,7 @@
|
|||
2.0.0 release [2023-01-31 02:17]
|
||||
2.1.0 release [2023-03-07 22:17]
|
||||
◾ Tuitio refactoring
|
||||
◾ Added account logout method
|
||||
|
||||
2.0.0 release [2023-01-31 02:17]
|
||||
◾ Tuitio rebranding
|
||||
◾ Initial release of Tuitio's published language package
|
|
@ -7,7 +7,7 @@
|
|||
<RepositoryUrl>https://lab.code-rove.com/gitea/tudor.stanciu/tuitio</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/ReleaseNotes.txt"))</PackageReleaseNotes>
|
||||
<Version>2.0.0</Version>
|
||||
<Version>2.1.0</Version>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Company>Toodle HomeLab</Company>
|
||||
|
|
|
@ -5,7 +5,8 @@ namespace Tuitio.Wrapper.Constants
|
|||
internal struct ApiRoutes
|
||||
{
|
||||
public const string
|
||||
Authentication = "account/login?UserName={0}&Password={1}",
|
||||
AccountLogin = "account/login?UserName={0}&Password={1}",
|
||||
AccountLogout = "account/logout?Token={0}",
|
||||
Authorization = "connect/authorize?Token={0}";
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
2.0.0 release [2023-01-31 02:17]
|
||||
2.1.0 release [2023-03-07 22:17]
|
||||
◾ Tuitio refactoring
|
||||
◾ Added account logout method
|
||||
|
||||
2.0.0 release [2023-01-31 02:17]
|
||||
◾ Tuitio rebranding
|
||||
◾ Initial release of Tuitio's API wrapper
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
|
||||
namespace Tuitio.Wrapper.Services
|
||||
{
|
||||
public interface ITuitioService
|
||||
{
|
||||
Task<Token> Authenticate(string userName, string password);
|
||||
Task<TokenAuthorizationResult> Authorize(string token);
|
||||
Task<AccountLoginResult> Login(string userName, string password);
|
||||
Task<AuthorizationResult> Authorize(string token);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,13 +1,13 @@
|
|||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
using Tuitio.Wrapper.Constants;
|
||||
using Tuitio.Wrapper.Models;
|
||||
using Netmash.Extensions.Http;
|
||||
using System;
|
||||
using System.Net.Http;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.PublishedLanguage.Dto;
|
||||
using Tuitio.Wrapper.Constants;
|
||||
using Tuitio.Wrapper.Models;
|
||||
|
||||
namespace Tuitio.Wrapper.Services
|
||||
{
|
||||
|
@ -24,17 +24,24 @@ namespace Tuitio.Wrapper.Services
|
|||
_httpClient = httpClient;
|
||||
}
|
||||
|
||||
public async Task<Token> Authenticate(string userName, string password)
|
||||
public async Task<AccountLoginResult> Login(string userName, string password)
|
||||
{
|
||||
var route = string.Format(ApiRoutes.Authentication, userName, password);
|
||||
var result = await _httpClient.ExecutePostRequest<Token>(route);
|
||||
var route = string.Format(ApiRoutes.AccountLogin, userName, password);
|
||||
var result = await _httpClient.ExecutePostRequest<AccountLoginResult>(route);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<TokenAuthorizationResult> Authorize(string token)
|
||||
public async Task<AccountLogoutResult> Logout(string token)
|
||||
{
|
||||
var route = string.Format(ApiRoutes.AccountLogout, token);
|
||||
var result = await _httpClient.ExecutePostRequest<AccountLogoutResult>(route);
|
||||
return result;
|
||||
}
|
||||
|
||||
public async Task<AuthorizationResult> Authorize(string token)
|
||||
{
|
||||
var route = string.Format(ApiRoutes.Authorization, token);
|
||||
var result = await _httpClient.ExecutePostRequest<TokenAuthorizationResult>(route);
|
||||
var result = await _httpClient.ExecutePostRequest<AuthorizationResult>(route);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@
|
|||
<RepositoryUrl>https://lab.code-rove.com/gitea/tudor.stanciu/tuitio</RepositoryUrl>
|
||||
<RepositoryType>Git</RepositoryType>
|
||||
<PackageReleaseNotes>$([System.IO.File]::ReadAllText("$(MSBuildProjectDirectory)/ReleaseNotes.txt"))</PackageReleaseNotes>
|
||||
<Version>2.0.0</Version>
|
||||
<Version>2.1.0</Version>
|
||||
<PackageIcon>logo.png</PackageIcon>
|
||||
<PackageReadmeFile>README.md</PackageReadmeFile>
|
||||
<Company>Toodle HomeLab</Company>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
using MediatR;
|
||||
// Copyright (c) 2020 Tudor Stanciu
|
||||
|
||||
using MediatR;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using System.Threading.Tasks;
|
||||
using Tuitio.Application.CommandHandlers;
|
||||
|
@ -27,12 +29,9 @@ namespace Tuitio.Controllers
|
|||
[HttpPost("logout")]
|
||||
public async Task<IActionResult> Logout([FromQuery] string token)
|
||||
{
|
||||
var result = await _mediator.Send(token);
|
||||
|
||||
if (result != null)
|
||||
var command = new AccountLogoutHandler.Command(token);
|
||||
var result = await _mediator.Send(command);
|
||||
return Ok(result);
|
||||
else
|
||||
return BadRequest();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@ namespace Tuitio.Api.Controllers
|
|||
[HttpPost("authorize")]
|
||||
public async Task<IActionResult> AuthorizeToken([FromQuery] string token)
|
||||
{
|
||||
var command = new AuthorizeTokenHandler.Command(token);
|
||||
var command = new AuthorizationHandler.Command(token);
|
||||
var result = await _mediator.Send(command);
|
||||
return Ok(result);
|
||||
}
|
||||
|
|
|
@ -11,11 +11,8 @@ namespace Tuitio.Api.Controllers
|
|||
[Route("system")]
|
||||
public class SystemController : ControllerBase
|
||||
{
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public SystemController(IMediator mediator)
|
||||
{
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
[AllowAnonymous]
|
||||
|
@ -24,12 +21,5 @@ namespace Tuitio.Api.Controllers
|
|||
{
|
||||
return Ok($"Ping success. System datetime: {DateTime.Now}");
|
||||
}
|
||||
|
||||
/*
|
||||
Methods:
|
||||
/version
|
||||
/burn-token
|
||||
/burn-all-tokens
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue