Add project files.

This commit is contained in:
2023-04-29 09:17:17 +03:00
commit 62178f1f32
493 changed files with 45863 additions and 0 deletions
+4
View File
@@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
</configuration>
@@ -0,0 +1,26 @@
namespace MyOffice.Web.Controllers;
using System.Security.Authentication;
using Core.Extensions;
using IdentityModel;
using Microsoft.AspNetCore.Mvc;
using static IdentityServer4.Models.IdentityResources;
public class BaseApiController : ControllerBase
{
public Guid UserId
{
get
{
var subject = User.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject)?.Value;
if (subject.IsPresent() && Guid.TryParse(subject, out var guid)) return guid;
throw new AuthenticationException("Get UserId failed");
}
}
public ObjectResult ProblemBadRequest(string? detail = null)
{
return Problem(detail, statusCode: StatusCodes.Status400BadRequest);
}
}
+169
View File
@@ -0,0 +1,169 @@
namespace MyOffice.Web.Controllers;
using Models.Auth;
using Identity.Domain;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using System;
using System.ComponentModel.DataAnnotations;
using Microsoft.AspNetCore.Authorization;
using MyOffice.Data.Models.Users;
using Core.Extensions;
using MyOffice.Core.Identity;
using Core;
using Services.Users;
using Services.Users.Domain;
[ApiController]
[Route("api/[controller]")]
public class UserController : BaseApiController
{
private readonly UserManager<ApplicationUser<Guid>> _userManager;
private readonly UserService _userService;
private readonly IEnumerable<IExternalProviderValidator> _externalProviderValidators;
public UserController(
UserManager<ApplicationUser<Guid>> userManager,
UserService userService,
IEnumerable<IExternalProviderValidator> externalProviderValidators
)
{
_userManager = userManager;
_userService = userService;
_externalProviderValidators = externalProviderValidators;
}
[HttpPost("~/api/user/register")]
public async Task<object> Register([FromBody] RegisterModel request)
{
var user = new ApplicationUser<Guid>
{
Id = Guid.NewGuid(),
UserName = request.UserName,
Email = request.UserName
};
var result = await _userManager.CreateAsync(user, request.Password);
return new
{
result.Succeeded,
Errors = result.Errors.Where(x => !x.Code.Equals("DuplicateUserName"))
};
}
[Authorize]
[HttpGet("~/api/user/profile")]
public async Task<object> ProfileGet()
{
var user = await _userManager.GetUserAsync(User);
return user.ToModel(await _userService.GetUserExternals(user.Id));
}
[Authorize]
[HttpPost("~/api/user/profile")]
public async Task<object> ProfileUpdate([FromBody] ProfileModel model)
{
var user = await _userManager.GetUserAsync(User);
user.FirstName = model.FirstName;
user.LastName = model.LastName;
user.FullName = model.FullName.NullIfEmpty() ?? $"{model.FirstName} {model.LastName}";
await _userManager.UpdateAsync(user);
return user.ToModel(await _userService.GetUserExternals(user.Id));
}
[Authorize]
[HttpPost("~/api/user/attach")]
public async Task<object> AttachProvider([FromBody] AttachModel model)
{
var validator = _externalProviderValidators.FirstOrDefault(x => x.Provider.EqualsIgnoreCase(model.Provider));
if (validator == null) return ProblemBadRequest("Provider not supported");
var result = await validator.ValidateAsync(model.Token);
if (!result.IsSuccessed) return ProblemBadRequest("Token not valid");
var execResult = _userService.AddUserExternal(UserId, model.Provider, result.ExternalId!, result.Email!);
switch (execResult.Status)
{
case AddUserExternalStatusEnum.success:
return Ok(new
{
success = true
});
case AddUserExternalStatusEnum.externalid_used:
case AddUserExternalStatusEnum.user_not_valid:
return ProblemBadRequest("Provider already connected");
default:
throw new NotSupportedException(execResult.Status.ToString());
}
}
[Authorize]
[HttpPost("~/api/user/deattach")]
public async Task<object> DeattachProvider([FromBody] DeattachModel model)
{
var exec = await _userService.RemoveUserExternal(UserId, model.Provider);
if (exec.Status == GeneralExecStatus.not_found) return ProblemBadRequest("Provider not connected");
return Ok(new
{
success = true
});
}
}
public class DeattachModel
{
[Required] public string Provider { get; set; } = null!;
}
public class AttachModel
{
[Required] public string Provider { get; set; } = null!;
[Required] public string Token { get; set; } = null!;
}
public class ProfileModel
{
public class ProviderModel
{
public string Provider { get; set; } = null!;
public DateTime CreatedOn { get; set; }
}
public string? Email { get; set; }
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? FullName { get; set; }
public bool? IsEmailConfirmed { get; set; }
public List<ProviderModel>? Providers { get; set; }
}
public static class ProfileModelExtensions
{
public static ProfileModel ToModel(this ApplicationUser<Guid> user, List<UserExternal> userClaims)
{
return new ProfileModel
{
Email = user.Email,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
IsEmailConfirmed = user.IsEmailConfirmed,
Providers = userClaims?.Select(x => new ProfileModel.ProviderModel
{
Provider = x.Provider,
CreatedOn = x.CreatedOn
}).ToList()
};
}
}
@@ -0,0 +1,47 @@
namespace MyOffice.Web.Controllers;
using Models.WeatherForecast;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
[ApiController]
[Route("[controller]")]
public class WeatherForecastController : ControllerBase
{
private static readonly string[] Summaries = new[]
{
"Freezing", "Bracing", "Chilly", "Cool", "Mild", "Warm", "Balmy", "Hot", "Sweltering", "Scorching"
};
private readonly ILogger<WeatherForecastController> _logger;
public WeatherForecastController(ILogger<WeatherForecastController> logger)
{
_logger = logger;
}
[HttpGet("~/api/weatherforecast")]
public IEnumerable<WeatherForecast> Get()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
[HttpGet("~/api/weatherforecast/auth")]
[Authorize]
public IEnumerable<WeatherForecast> GetAuth()
{
return Enumerable.Range(1, 5).Select(index => new WeatherForecast
{
Date = DateTime.Now.AddDays(index),
TemperatureC = Random.Shared.Next(-20, 55),
Summary = Summaries[Random.Shared.Next(Summaries.Length)]
})
.ToArray();
}
}
@@ -0,0 +1,34 @@
namespace MyOffice.Web.Identity.Configure;
using MyOffice.Web.Infrastructure;
using IdentityServer4.AccessTokenValidation;
using Microsoft.Extensions.Options;
public class ConfigureIdentityServerOptions : IConfigureNamedOptions<IdentityServerAuthenticationOptions>
{
readonly GlobalSettings _globalSettings;
readonly ILogger<ConfigureIdentityServerOptions> _logger;
public ConfigureIdentityServerOptions(
GlobalSettings globalSettings,
ILogger<ConfigureIdentityServerOptions> logger
)
{
_globalSettings = globalSettings;
_logger = logger;
}
public void Configure(string? name, IdentityServerAuthenticationOptions options)
{
//_logger.LogError($"Configure: {_globalSettings.Host}");
if (name == IdentityServerAuthenticationDefaults.AuthenticationScheme)
{
options.RequireHttpsMetadata = false;
options.ApiName = IdentityServerConfig.ApiName;
options.Authority = _globalSettings.Host;
}
}
// This won't be called, but is required for the IConfigureNamedOptions interface
public void Configure(IdentityServerAuthenticationOptions options) => Configure(Options.DefaultName, options);
}
@@ -0,0 +1,107 @@
namespace MyOffice.Web.Identity.Configure;
using IdentityModel;
using IdentityServer4;
using IdentityServer4.Models;
public class IdentityServerConfig
{
public static class ClaimConstants
{
public const string Subject = "subject";
public const string Permission = "permission";
}
public static class ScopeConstants
{
public const string Roles = "roles";
}
public const string ApiName = "api";
public const string ApiFriendlyName = "MyOffice.Web API";
public const string AppClientID = "angulartemplate_spa";
public static IEnumerable<IdentityResource> GetIdentityResources()
{
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResources.Phone(),
new IdentityResources.Email(),
new(ScopeConstants.Roles, new List<string> { JwtClaimTypes.Role })
};
}
public static IEnumerable<ApiScope> GetApiScopes()
{
return new List<ApiScope>
{
new(ApiName, ApiFriendlyName)
{
UserClaims =
{
JwtClaimTypes.Name,
JwtClaimTypes.Email,
JwtClaimTypes.PhoneNumber,
JwtClaimTypes.Role,
ClaimConstants.Permission
}
}
};
}
public static IEnumerable<ApiResource> GetApiResources()
{
return new List<ApiResource>
{
new(ApiName)
{
Scopes = { ApiName }
}
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new()
{
ClientId = AppClientID,
ClientSecrets = new List<Secret>
{
new Secret("secret".Sha256())
},
AllowedGrantTypes = new[]
{
GrantType.AuthorizationCode,
GrantType.ClientCredentials,
GrantType.DeviceFlow,
GrantType.ResourceOwnerPassword,
"external"
},
AllowAccessTokensViaBrowser = true,
RequireClientSecret = false,
RedirectUris = new[]
{
"http://localhost:4200/silent-refresh.html",
},
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Phone,
IdentityServerConstants.StandardScopes.Email,
ScopeConstants.Roles,
ApiName
},
AllowOfflineAccess = true,
RefreshTokenExpiration = TokenExpiration.Sliding,
RefreshTokenUsage = TokenUsage.OneTimeOnly
}
};
}
}
@@ -0,0 +1,27 @@
namespace MyOffice.Web.Identity;
using Core.Extensions;
using IdentityServer4.Services;
using Infrastructure;
public class CorsPolicyService : ICorsPolicyService
{
private readonly ILogger<CorsPolicyService> _logger;
private readonly GlobalSettings _globalSettings;
public CorsPolicyService(
ILogger<CorsPolicyService> logger,
GlobalSettings globalSettings
)
{
_logger = logger;
_globalSettings = globalSettings;
}
public Task<bool> IsOriginAllowedAsync(string origin)
{
var result = origin.EqualsIgnoreCase(_globalSettings.Host);
return Task.FromResult(result);
}
}
@@ -0,0 +1,5 @@
namespace MyOffice.Web.Identity.Domain;
public class ApplicationRole
{
}
@@ -0,0 +1,16 @@
namespace MyOffice.Web.Identity.Domain;
public class ApplicationUser<TKey>
{
#pragma warning disable CS8618 // Non-nullable property 'Id' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public TKey Id { get; set; }
#pragma warning restore CS8618 // Non-nullable property 'Id' must contain a non-null value when exiting constructor. Consider declaring the property as nullable.
public string UserName { get; set; } = null!;
public string Email { get; set; } = null!;
public string PasswordHash { get; set; } = null!;
public string? FirstName { get; set; }
public string? LastName { get; set; }
public string? FullName { get; set; }
public bool IsEmailConfirmed { get; set; }
}
@@ -0,0 +1,19 @@
namespace MyOffice.Web.Identity.Domain;
public class ExternalProvidersConfig
{
public class Auth0Config
{
public string? ClientId { get; set; }
public string? Domain { get; set; }
public string? SecretKey { get; set; }
}
public class GoogleConfig
{
public string? ClientId { get; set; }
}
public Auth0Config? Auth0 { get; set; }
public GoogleConfig? Google { get; set; }
}
@@ -0,0 +1,167 @@
namespace MyOffice.Web.Identity;
using MyOffice.Data.Models.Users;
using Domain;
using Data.Repositories.Users;
using Repositories;
using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Validation;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
using System.Security.Claims;
using Core.Extensions;
using Core.Identity;
public class ExtensionGrantValidator : IExtensionGrantValidator
{
private readonly IUserStore<ApplicationUser<Guid>> _userStore;
private readonly ILogger<ExtensionGrantValidator> _logger;
private readonly AppUserManager _appUserManager;
private readonly IUserExternalRepository _userExternalRepository;
private readonly IHttpContextAccessor _httpContextAccessor;
private readonly ExternalProvidersConfig _externalProvidersConfig;
private readonly IEnumerable<IExternalProviderValidator> _externalProviderValidators;
public ExtensionGrantValidator(
ILogger<ExtensionGrantValidator> logger,
IUserStore<ApplicationUser<Guid>> userStore,
AppUserManager appUserManager,
IUserExternalRepository userExternalRepository,
IHttpContextAccessor httpContextAccessor,
IOptions<ExternalProvidersConfig> externalProvidersConfig,
IEnumerable<IExternalProviderValidator> externalProviderValidators
)
{
if (externalProvidersConfig == null)
throw new ArgumentNullException(nameof(externalProvidersConfig));
_userStore = userStore ?? throw new ArgumentNullException(nameof(userStore));
_appUserManager = appUserManager ?? throw new ArgumentNullException(nameof(appUserManager));
_userExternalRepository =
userExternalRepository ?? throw new ArgumentNullException(nameof(userExternalRepository));
_httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor));
_externalProvidersConfig = externalProvidersConfig.Value;
_externalProviderValidators = externalProviderValidators;
_logger = logger ?? throw new ArgumentNullException(nameof(logger));
}
public string GrantType => "external";
public async Task ValidateAsync(ExtensionGrantValidationContext context)
{
var provider = context.Request.Raw["provider"];
if (provider == null)
throw new ArgumentNullException("provider not found");
var validator = _externalProviderValidators.FirstOrDefault(x => x.Provider.EqualsIgnoreCase(provider));
if (validator == null || !validator.IsConfigured)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest,
$"Provider not supported: {provider}");
return;
}
var token = context.Request.Raw["token"];
if (token == null)
throw new ArgumentNullException("token not found");
var result = await validator.ValidateAsync(token);
if (!result.IsSuccessed)
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidRequest, $"Token not valid");
return;
}
context.Result = await ProcessAsync(result.Email!, result.ExternalId!, provider, result.EmailVerified,
result.FullName);
}
private readonly object _lock = new();
private async Task<GrantValidationResult> ProcessAsync(
string email,
string externlaId,
string provider,
bool isEmailConfirmed,
string? fullName
)
{
// connect to exists user
if (_httpContextAccessor.HttpContext?.User?.Identity?.IsAuthenticated == true)
{
return new GrantValidationResult(TokenRequestErrors.InvalidRequest, $"Authentication failed");
}
else
{
var user = await _appUserManager.FindByEmailAsync(email);
if (user == null)
{
if (!isEmailConfirmed)
return new GrantValidationResult(TokenRequestErrors.InvalidRequest, "Email not confirmed");
user = await CreateUser(email, isEmailConfirmed, fullName);
if (user == null) return new GrantValidationResult(TokenRequestErrors.InvalidRequest);
}
var claim = await _userExternalRepository.GetByUserIdAsync(user.Id, provider);
if (claim == null) claim = AddClaim(user, email, externlaId, provider);
return ClaimToGrantValidationResult(claim.UserId, "", "");
}
}
private async Task<ApplicationUser<Guid>?> CreateUser(string email, bool isEmailConfirmed, string? fullName)
{
var user = new ApplicationUser<Guid>
{
Id = Guid.NewGuid(),
UserName = email,
Email = email,
IsEmailConfirmed = isEmailConfirmed,
FullName = fullName
};
var password = "Qq1!_" + Guid.NewGuid();
var result = await _appUserManager.CreateAsync(user, password);
if (!result.Succeeded) return null;
return user;
}
private UserExternal AddClaim(ApplicationUser<Guid> user, string email, string externalId, string provider)
{
lock (_lock)
{
var claim = _userExternalRepository.GetByUserId(user.Id, provider);
if (claim != null) return claim;
claim = new UserExternal
{
UserId = user.Id,
CreatedOn = DateTime.UtcNow,
Provider = provider.ToLower(),
ExternalId = externalId,
Email = email
};
_userExternalRepository.AddUserExternal(claim);
return claim;
}
}
private GrantValidationResult ClaimToGrantValidationResult(Guid userId, string provider, string email)
{
var userIdStr = userId.ToString();
var claims = new List<Claim>
{
new(JwtClaimTypes.Id, userIdStr),
new(JwtClaimTypes.Subject, userIdStr),
new(JwtClaimTypes.Email, email)
};
var grantValidationResult = new GrantValidationResult(userIdStr, provider, claims);
return grantValidationResult;
}
}
@@ -0,0 +1,69 @@
namespace MyOffice.Web.Identity.ExternalProviders;
using Domain;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Protocols;
using Microsoft.IdentityModel.Protocols.OpenIdConnect;
using Microsoft.IdentityModel.Tokens;
using System.IdentityModel.Tokens.Jwt;
using System.Text;
using Core.Extensions;
using Core.Identity;
public class ExternalProviderValidatorAuth0 : IExternalProviderValidator
{
private readonly ExternalProvidersConfig _externalProvidersConfig;
private readonly ILogger<ExternalProviderValidatorAuth0> _logger;
public ExternalProviderValidatorAuth0(
ILogger<ExternalProviderValidatorAuth0> logger,
IOptions<ExternalProvidersConfig> externalProvidersConfig
)
{
_externalProvidersConfig = externalProvidersConfig.Value;
_logger = logger;
Provider = ExternalProvidersConst.PROVIDER_AUTH0;
IsConfigured = _externalProvidersConfig.IsAuth0Configured();
}
public string Provider { get; }
public bool IsConfigured { get; }
public async Task<ExternalProviderValidatorResult> ValidateAsync(string token)
{
var auth0Domain = _externalProvidersConfig.Auth0!.Domain!;
var secretKey = _externalProvidersConfig.Auth0!.SecretKey!;
var securityKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey));
var configurationManager = new ConfigurationManager<OpenIdConnectConfiguration>(
$"{auth0Domain}.well-known/openid-configuration", new OpenIdConnectConfigurationRetriever());
var openIdConfig = await configurationManager.GetConfigurationAsync(CancellationToken.None);
var validations = new TokenValidationParameters
{
ValidIssuer = auth0Domain,
ValidAudiences = new[] { _externalProvidersConfig.Auth0!.ClientId },
IssuerSigningKeys = openIdConfig.SigningKeys,
TokenDecryptionKey = securityKey
};
var tokenHandler = new JwtSecurityTokenHandler();
var user = tokenHandler.ValidateToken(token, validations, out var validatedToken);
if (user.Identity?.IsAuthenticated != true)
{
return ExternalProviderValidatorResult.Failed();
}
var email = user.Claims.GetEmail();
var emailVerified = user.Claims.GetValue("email_verified").AsBool();
var fullName = user.Claims.GetValue("name")?.ToString();
var externalId = user.Claims.GetSID();
if (email == null || externalId == null)
{
return ExternalProviderValidatorResult.Failed();
}
return ExternalProviderValidatorResult.Success(email, emailVerified, externalId, fullName);
}
}
@@ -0,0 +1,36 @@
namespace MyOffice.Web.Identity.ExternalProviders;
using MyOffice.Core.Identity;
using Domain;
using Google.Apis.Auth;
using Microsoft.Extensions.Options;
public class ExternalProviderValidatorGoogle : IExternalProviderValidator
{
private readonly ExternalProvidersConfig _externalProvidersConfig;
public ExternalProviderValidatorGoogle(
IOptions<ExternalProvidersConfig> externalProvidersConfig
)
{
_externalProvidersConfig = externalProvidersConfig.Value;
Provider = ExternalProvidersConst.PROVIDER_GOOGLE;
IsConfigured = _externalProvidersConfig.IsGoogleConfigured();
}
public string Provider { get; }
public bool IsConfigured { get; }
public async Task<ExternalProviderValidatorResult> ValidateAsync(string token)
{
var settings = new GoogleJsonWebSignature.ValidationSettings()
{
Audience = new List<string>() { _externalProvidersConfig.Google!.ClientId! }
};
var result = await GoogleJsonWebSignature.ValidateAsync(token, settings);
if (!result.EmailVerified) return ExternalProviderValidatorResult.Failed();
return ExternalProviderValidatorResult.Success(result.Email, result.EmailVerified, result.Subject, result.Name);
}
}
@@ -0,0 +1,7 @@
namespace MyOffice.Web.Identity.ExternalProviders;
public class ExternalProvidersConst
{
public const string PROVIDER_GOOGLE = "google";
public const string PROVIDER_AUTH0 = "auth0";
}
@@ -0,0 +1,24 @@
using MyOffice.Web.Identity.Domain;
namespace MyOffice.Web.Identity;
using Core.Extensions;
public static class ExternalProvidersConfigExtensions
{
public static bool IsGoogleConfigured(this ExternalProvidersConfig? config)
{
return config != null
&& config.Google != null
&& config.Google.ClientId.IsPresent();
}
public static bool IsAuth0Configured(this ExternalProvidersConfig? config)
{
return config != null
&& config.Auth0 != null
&& config.Auth0.ClientId.IsPresent()
&& config.Auth0.Domain.IsPresent()
&& config.Auth0.SecretKey.IsPresent();
}
}
@@ -0,0 +1,54 @@
namespace MyOffice.Web.Identity;
using System.Collections.Concurrent;
using Core.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Stores;
public class PersistedGrantStore : IPersistedGrantStore
{
private static ConcurrentDictionary<string, PersistedGrant> _store = new ConcurrentDictionary<string, PersistedGrant>();
public Task StoreAsync(PersistedGrant grant)
{
_store.AddOrUpdate(grant.Key, grant, (key, oldValue) => grant);
return Task.CompletedTask;
}
public Task<PersistedGrant> GetAsync(string key)
{
if (!_store.TryGetValue(key, out var grant))
throw new Exception($"Key '{key}' not found");
return Task.FromResult(grant);
}
public Task<IEnumerable<PersistedGrant>> GetAllAsync(PersistedGrantFilter filter)
{
var list = _store.Values.Where(x =>
(filter.ClientId.IsMissing() || filter.ClientId == x.ClientId)
&& (filter.SessionId.IsMissing() || filter.SessionId == x.SessionId)
&& (filter.SubjectId.IsMissing() || filter.SubjectId == x.SubjectId)
&& (filter.Type.IsMissing() || filter.Type == x.Type)
);
return Task.FromResult(list);
}
public Task RemoveAsync(string key)
{
_store.TryRemove(key, out _);
return Task.CompletedTask;
}
public async Task RemoveAllAsync(PersistedGrantFilter filter)
{
var list = await GetAllAsync(filter);
foreach (var item in list)
{
await RemoveAsync(item.Key);
}
}
}
+36
View File
@@ -0,0 +1,36 @@
namespace MyOffice.Web.Identity;
using Data.Repositories.Users;
using IdentityModel;
using IdentityServer4.Models;
using IdentityServer4.Services;
using System.Security.Claims;
public class ProfileService : IProfileService
{
private readonly IUserRepository _userRepository;
public ProfileService(
IUserRepository userRepository
)
{
_userRepository = userRepository;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject);
if (sub != null && Guid.TryParse(sub.Value, out var id))
{
var user = await _userRepository.GetUserAsync(id);
if (user != null)
if (context.RequestedClaimTypes.Contains(JwtClaimTypes.Email))
context.IssuedClaims.Add(new Claim(JwtClaimTypes.Email, user.Email));
}
}
public Task IsActiveAsync(IsActiveContext context)
{
return Task.FromResult(0);
}
}
@@ -0,0 +1,83 @@
namespace MyOffice.Web.Identity.Repositories;
using System.Security.Cryptography;
using Domain;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Options;
public class PasswordHasher : IPasswordHasher<ApplicationUser<Guid>>
{
public string HashPassword(ApplicationUser<Guid> user, string password)
{
byte[] salt;
salt = RandomNumberGenerator.GetBytes(16);
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
var hash = pbkdf2.GetBytes(20);
var hashBytes = new byte[36];
Array.Copy(salt, 0, hashBytes, 0, 16);
Array.Copy(hash, 0, hashBytes, 16, 20);
var passwordHash = Convert.ToBase64String(hashBytes);
return passwordHash;
}
private bool IsValidHash(string password, string passwordHash)
{
/* Fetch the stored value */
/* Extract the bytes */
var hashBytes = Convert.FromBase64String(passwordHash);
/* Get the salt */
var salt = new byte[16];
Array.Copy(hashBytes, 0, salt, 0, 16);
/* Compute the hash on the password the user entered */
var pbkdf2 = new Rfc2898DeriveBytes(password, salt, 100000);
var hash = pbkdf2.GetBytes(20);
/* Compare the results */
for (var i = 0; i < 20; i++)
if (hashBytes[i + 16] != hash[i])
return false;
return true;
}
public PasswordVerificationResult VerifyHashedPassword(
ApplicationUser<Guid> user,
string hashedPassword,
string providedPassword
)
{
if (IsValidHash(providedPassword, hashedPassword)) return PasswordVerificationResult.Success;
return PasswordVerificationResult.Failed;
}
}
public class AppUserManager : UserManager<ApplicationUser<Guid>>
{
public AppUserManager(
IUserStore<ApplicationUser<Guid>> store,
IOptions<IdentityOptions> optionsAccessor,
IPasswordHasher<ApplicationUser<Guid>> passwordHasher,
IEnumerable<IUserValidator<ApplicationUser<Guid>>> userValidators,
IEnumerable<IPasswordValidator<ApplicationUser<Guid>>> passwordValidators,
ILookupNormalizer keyNormalizer,
IdentityErrorDescriber errors,
IServiceProvider services,
ILogger<UserManager<ApplicationUser<Guid>>> logger) :
base(
store,
optionsAccessor,
passwordHasher,
userValidators,
passwordValidators,
keyNormalizer,
errors,
services,
logger
)
{
}
}
@@ -0,0 +1,62 @@
namespace MyOffice.Web.Identity.Repositories;
using Domain;
using Microsoft.AspNetCore.Identity;
public class RoleStore : IRoleStore<ApplicationRole>
{
public void Dispose()
{
}
public Task<IdentityResult> CreateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> UpdateAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<IdentityResult> DeleteAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetRoleIdAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetRoleNameAsync(ApplicationRole role, string roleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedRoleNameAsync(ApplicationRole role, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetNormalizedRoleNameAsync(ApplicationRole role, string normalizedName,
CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<ApplicationRole> FindByIdAsync(string roleId, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<ApplicationRole> FindByNameAsync(string normalizedRoleName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,192 @@
namespace MyOffice.Web.Identity.Repositories;
using Data.Models.Users;
using Data.Repositories.Users;
using Domain;
using Microsoft.AspNetCore.Identity;
public class UserStore :
IUserStore<ApplicationUser<Guid>>,
IUserPasswordStore<ApplicationUser<Guid>>,
IUserEmailStore<ApplicationUser<Guid>>
{
private readonly IUserRepository _userRepository;
public UserStore(
IUserRepository userRepository
)
{
_userRepository = userRepository;
}
public void Dispose()
{
}
public Task<string> GetUserIdAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Id.ToString());
}
public Task<string> GetUserNameAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.UserName);
}
public Task SetUserNameAsync(ApplicationUser<Guid> user, string userName, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetNormalizedUserNameAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetNormalizedUserNameAsync(ApplicationUser<Guid> user, string normalizedName,
CancellationToken cancellationToken)
{
user.UserName = normalizedName;
return Task.FromResult(0);
}
public async Task<IdentityResult> CreateAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
var result = await _userRepository.AddUserAsync(new User
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
IsEmailConfirmed = user.IsEmailConfirmed
});
return result == 1 ? IdentityResult.Success : IdentityResult.Failed();
}
public async Task<IdentityResult> UpdateAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
var userDb = new User
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
IsEmailConfirmed = user.IsEmailConfirmed
};
var result = await _userRepository.UpdateUserAsync(userDb);
return result == 1 ? IdentityResult.Success : IdentityResult.Failed();
}
public Task<IdentityResult> DeleteAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public async Task<ApplicationUser<Guid>> FindByIdAsync(string userId, CancellationToken cancellationToken)
{
var user = await _userRepository.GetUserAsync(Guid.Parse(userId));
if (user == null)
{
#pragma warning disable CS8603 // Possible null reference return.
return null;
#pragma warning restore CS8603 // Possible null reference return.
}
return new ApplicationUser<Guid>
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
IsEmailConfirmed = user.IsEmailConfirmed
};
}
public async Task<ApplicationUser<Guid>> FindByNameAsync(string normalizedUserName,
CancellationToken cancellationToken)
{
var user = await _userRepository.GetByUserUserNameAsync(normalizedUserName);
if (user == null)
{
#pragma warning disable CS8603 // Possible null reference return.
return null;
#pragma warning restore CS8603 // Possible null reference return.
}
return new ApplicationUser<Guid>
{
Id = user.Id,
UserName = user.UserName,
Email = user.Email,
PasswordHash = user.PasswordHash,
FirstName = user.FirstName,
LastName = user.LastName,
FullName = user.FullName,
IsEmailConfirmed = user.IsEmailConfirmed
};
}
public Task SetPasswordHashAsync(ApplicationUser<Guid> user, string passwordHash,
CancellationToken cancellationToken)
{
user.PasswordHash = passwordHash;
return Task.FromResult(0);
}
public Task<string> GetPasswordHashAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.PasswordHash);
}
public Task<bool> HasPasswordAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetEmailAsync(ApplicationUser<Guid> user, string email, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<string> GetEmailAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user.Email);
}
public Task<bool> GetEmailConfirmedAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task SetEmailConfirmedAsync(ApplicationUser<Guid> user, bool confirmed, CancellationToken cancellationToken)
{
throw new NotImplementedException();
}
public Task<ApplicationUser<Guid>> FindByEmailAsync(string normalizedEmail, CancellationToken cancellationToken)
{
return FindByNameAsync(normalizedEmail, cancellationToken);
}
public Task<string?> GetNormalizedEmailAsync(ApplicationUser<Guid> user, CancellationToken cancellationToken)
{
return Task.FromResult(user?.Email);
}
public Task SetNormalizedEmailAsync(ApplicationUser<Guid> user, string normalizedEmail,
CancellationToken cancellationToken)
{
return Task.FromResult(0);
}
}
@@ -0,0 +1,6 @@
namespace MyOffice.Web.Infrastructure;
public class GlobalSettings
{
public string? Host { get; set; }
}
+7
View File
@@ -0,0 +1,7 @@
namespace MyOffice.Web.Models.Auth;
public class LoginModel
{
public string? UserName { get; set; }
public string? Password { get; set; }
}
@@ -0,0 +1,8 @@
namespace MyOffice.Web.Models.Auth;
public class RegisterModel
{
public string UserName { get; set; } = null!;
public string Password { get; set; } = null!;
public string ConfirmPassword { get; set; } = null!;
}
@@ -0,0 +1,12 @@
namespace MyOffice.Web.Models.WeatherForecast;
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; }
}
+54
View File
@@ -0,0 +1,54 @@
<Project Sdk="Microsoft.NET.Sdk.Web">
<PropertyGroup>
<TargetFramework>net6.0</TargetFramework>
<Nullable>enable</Nullable>
<ImplicitUsings>enable</ImplicitUsings>
</PropertyGroup>
<ItemGroup>
<Compile Remove="logs\**" />
<Content Remove="logs\**" />
<EmbeddedResource Remove="logs\**" />
<None Remove="logs\**" />
</ItemGroup>
<ItemGroup>
<Content Update="appsettings.Production.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Production.sample.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Development.json" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Development.sample.json" CopyToPublishDirectory="Never" />
<Content Update="web.Release.config" CopyToPublishDirectory="Never" />
<Content Update="appsettings.Production.json">
<CopyToOutputDirectory>Never</CopyToOutputDirectory>
</Content>
<Content Remove="wwwroot\index.html" />
</ItemGroup>
<ItemGroup>
<None Include="wwwroot\index.html" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Auth0.AuthenticationApi" Version="7.17.4" />
<PackageReference Include="Auth0.Core" Version="7.17.4" />
<PackageReference Include="Auth0.ManagementApi" Version="7.17.4" />
<PackageReference Include="Google.Apis.Auth" Version="1.58.0-beta01" />
<PackageReference Include="IdentityServer4.AccessTokenValidation" Version="3.0.1" />
<PackageReference Include="IdentityServer4.AspNetIdentity" Version="4.1.2" />
<PackageReference Include="Swashbuckle.AspNetCore" Version="6.4.0" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\MyOffice.Core\MyOffice.Core.csproj" />
<ProjectReference Include="..\MyOffice.Data.Models\MyOffice.Data.Models.csproj" />
<ProjectReference Include="..\MyOffice.Data.Repositories\MyOffice.Data.Repositories.csproj" />
<ProjectReference Include="..\MyOffice.DbContext\MyOffice.DbContext.csproj" />
<ProjectReference Include="..\MyOffice.Migration.Postgres\MyOffice.Migrations.Postgres.csproj" />
<ProjectReference Include="..\MyOffice.Migration.Sqlite\MyOffice.Migrations.Sqlite.csproj" />
<ProjectReference Include="..\MyOffice.Services\MyOffice.Services.csproj" />
<ProjectReference Include="..\MyOffice.Shared\MyOffice.Shared.csproj" />
</ItemGroup>
</Project>
+353
View File
@@ -0,0 +1,353 @@
namespace MyOffice.Web;
using System.IO;
using System.Linq;
using Core.Extensions;
using System.Collections.Concurrent;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.FileProviders.Physical;
using Microsoft.Extensions.Logging;
using Microsoft.IdentityModel.Logging;
using Microsoft.AspNetCore.Builder;
using Microsoft.Extensions.Hosting;
using Microsoft.Extensions.Options;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Configuration;
using Identity;
using Identity.Domain;
using Identity.ExternalProviders;
using Identity.Repositories;
using IdentityServer4.AccessTokenValidation;
using IdentityServer4.Configuration;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using Microsoft.IdentityModel.Tokens;
using System.Security.Cryptography;
using Core.Identity;
using Data.Repositories.Users;
using DbContext;
using Services.Users;
using Shared;
using Identity.Configure;
using Infrastructure;
using Microsoft.Extensions.Logging.Console;
using IdentityServer4.Services;
public class Program
{
public static void Main(string[] args)
{
var builder = WebApplication.CreateBuilder(args);
AddServices(builder);
var app = builder.Build();
Configure(app);
app.Run();
}
private static void AddServices(WebApplicationBuilder builder)
{
builder.Services.AddLogging(logging =>
logging.AddSimpleConsole(options =>
{
//options.SingleLine = true;
options.TimestampFormat = "[HH:mm:ss] ";
options.ColorBehavior = LoggerColorBehavior.Enabled;
})
);
//File Logger
builder.Logging.AddFile(builder.Configuration.GetSection("Logging"));
// shared configuration
builder.Configuration.AddConfiguration(SharedConfiguration.CreateConfigurationContainer());
// Database
var databaseProvider = builder.Configuration["DatabaseProvider"];
if (databaseProvider == null)
throw new NullReferenceException($"Configuration DatabaseProvider {databaseProvider}");
var connectionString = builder.Configuration.GetConnectionString(databaseProvider);
if (connectionString == null)
throw new NullReferenceException($"Configuration ConnectionString {databaseProvider}");
var connectionConfiguration = new ConnectionConfiguration(databaseProvider, connectionString);
RepositoryInitializer.Initialize(builder.Services, connectionConfiguration);
// Configurations
builder.Services.Configure<ExternalProvidersConfig>(
builder.Configuration.GetSection("ExternalProviders")
);
InitializeGlobalSettings(builder);
AddIdentityServices(builder);
AddRepositories(builder);
AddBusinessServices(builder);
builder.Services
.AddControllers()
.AddJsonOptions(options =>
{
options.AllowInputFormatterExceptionMessages = builder.Environment.IsDevelopment();
});
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
//builder.Services.AddEndpointsApiExplorer();
//builder.Services.AddSwaggerGen();
builder.Services.AddCors();
}
private static void InitializeGlobalSettings(WebApplicationBuilder builder)
{
builder.Services.Configure<GlobalSettings>(_ => { });
builder.Services.AddSingleton(resolver => resolver.GetRequiredService<IOptions<GlobalSettings>>().Value);
}
private static void AddIdentityServices(WebApplicationBuilder builder)
{
// add identity
builder.Services
.AddIdentity<ApplicationUser<Guid>, ApplicationRole>()
.AddUserStore<UserStore>()
.AddRoleStore<RoleStore>()
.AddUserManager<AppUserManager>()
.AddDefaultTokenProviders();
builder.Services.AddScoped<ICorsPolicyService, CorsPolicyService>();
builder.Services.AddScoped<IPasswordHasher<ApplicationUser<Guid>>, PasswordHasher>();
// Identity Services
builder.Services.AddScoped<IUserStore<ApplicationUser<Guid>>, UserStore>();
builder.Services.AddScoped<IRoleStore<ApplicationRole>, RoleStore>();
// External providers
builder.Services.AddScoped<IExternalProviderValidator, ExternalProviderValidatorAuth0>();
builder.Services.AddScoped<IExternalProviderValidator, ExternalProviderValidatorGoogle>();
// Configure Identity options and password complexity here
builder.Services.Configure<IdentityOptions>(options =>
{
// User settings
options.User.RequireUniqueEmail = true;
// Password settings
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequireLowercase = true;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(30);
options.Lockout.MaxFailedAccessAttempts = 10;
});
// Adds IdentityServer.
builder.Services
.AddIdentityServer(o =>
{
o.UserInteraction = new UserInteractionOptions()
{
LoginUrl = "/authentication/signin",
LoginReturnUrlParameter = "returnUrl"
};
})
.AddSigningCredential(CreateSigningCredential())
.AddPersistedGrantStore<PersistedGrantStore>()
.AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources())
.AddInMemoryApiScopes(IdentityServerConfig.GetApiScopes())
.AddInMemoryApiResources(IdentityServerConfig.GetApiResources())
.AddInMemoryClients(IdentityServerConfig.GetClients())
.AddAspNetIdentity<ApplicationUser<Guid>>()
.AddProfileService<ProfileService>()
.AddExtensionGrantValidator<ExtensionGrantValidator>();
var authentication = builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = IdentityServerAuthenticationDefaults.AuthenticationScheme;
});
var frontEndHost = builder.Configuration.GetValue<string>("FrontEnd:Host");
var isDynamicFrontEndHost = frontEndHost.IsMissing();
if (isDynamicFrontEndHost)
{
builder.Services.ConfigureOptions<ConfigureIdentityServerOptions>();
authentication.AddIdentityServerAuthentication();
}
else
{
authentication.AddIdentityServerAuthentication(options =>
{
options.RequireHttpsMetadata = false;
options.ApiName = IdentityServerConfig.ApiName;
options.Authority = frontEndHost;
});
}
}
private static void AddRepositories(WebApplicationBuilder builder)
{
builder.Services.AddScoped<IUserRepository, UserRepository>();
builder.Services.AddScoped<IUserExternalRepository, UserExternalRepository>();
}
private static void AddBusinessServices(WebApplicationBuilder builder)
{
builder.Services.AddScoped<UserService, UserService>();
}
private static readonly ConcurrentDictionary<string, PhysicalFileInfo> _staticFilesCache = new();
private static bool _firstRequest = true;
private static readonly object LockFirstRequest = new();
private static void Configure(WebApplication app)
{
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
IdentityModelEventSource.ShowPII = true;
}
else
{
IdentityModelEventSource.ShowPII = true;
app.UseExceptionHandler("/Error");
}
app.UseDefaultFiles();
app.UseStaticFiles();
// configuration on first request
app.Use(async (ctx, next) =>
{
var path = ctx.Request.Path;
GlobalSettings? globalSettings = null;
if (_firstRequest)
{
// lock
lock (LockFirstRequest)
{
// recheck
if (_firstRequest)
{
_firstRequest = false;
// set real frontend host
globalSettings = ctx.RequestServices.GetRequiredService<GlobalSettings>();
globalSettings.Host = GetFrontendHost(ctx);
// update identity server
var options = ctx.RequestServices.GetRequiredService<IdentityServerOptions>();
options.IssuerUri = globalSettings.Host;
// update identity server client redirect uri
var clients = ctx.RequestServices.GetRequiredService<IEnumerable<Client>>();
var client = clients.FirstOrDefault()!;
client.RedirectUris = new List<string>
{
$"{globalSettings.Host}/silent-refresh.html"
};
}
}
}
//
if (path.Value.EqualsIgnoreCase("/.well-known/openid-configuration"))
{
globalSettings ??= ctx.RequestServices.GetRequiredService<GlobalSettings>();
ctx.SetIdentityServerOrigin(globalSettings.Host);
}
await next();
});
// send index.html to any routes except coreRoutes
var coreRoutes = new[]
{
// API routes
new PathString("/api"),
// Identity server routes
new PathString("/.well-known"),
new PathString("/connect"),
new PathString("/silent-refresh.html")
};
var webRootPath = app.Configuration.GetValue<string>(WebHostDefaults.ContentRootKey);
var wwwrootPath = Path.Combine(webRootPath, "wwwroot");
app.Use(async (context, next) =>
{
//app.Logger.LogWarning($"?path: {context.Request.Path}");
var path = context.Request.Path;
if (path.Value == null) return;
if (coreRoutes.Any(x => path.StartsWithSegments(x, StringComparison.OrdinalIgnoreCase)))
{
//app.Logger.LogWarning($"*path: {path.Value}");
await next();
}
else
{
if (!_staticFilesCache.TryGetValue(path.Value, out var physicalFileInfo))
{
var segments = path.Value.Split('/', '\\');
var fileInfo = new FileInfo(Path.Combine(wwwrootPath, segments.LastOrDefault()!));
if (!fileInfo.Exists)
{
//app.Logger.LogWarning($"-path: {fileInfo.FullName}");
return;
}
//app.Logger.LogWarning($"+path: {fileInfo.FullName}");
physicalFileInfo = new PhysicalFileInfo(fileInfo);
_staticFilesCache.TryAdd(path.Value, physicalFileInfo);
}
await context.Response.SendFileAsync(physicalFileInfo);
await context.Response.CompleteAsync();
}
});
app.UseCors(builder => builder
.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod());
app.UseIdentityServer();
app.UseAuthorization();
app.MapControllers();
}
private static string GetFrontendHost(HttpContext ctx)
{
// get X-Forwarded-Proto when reversed proxy used
// internet <-> nginx (https) <-> site(http)
var host = $"{ctx.Request.Headers["X-Forwarded-Proto"]}";
host = host.IsMissing()
? ctx.Request.IsHttps ? "https" : "http"
: host!;
host += $"://{ctx.Request.Host.Value}";
return host;
}
private static SigningCredentials CreateSigningCredential()
{
var rsaSecurityKey = new RsaSecurityKey(new RSACryptoServiceProvider(2048));
var credentials = new SigningCredentials(rsaSecurityKey, SecurityAlgorithms.RsaSha256);
return credentials;
}
}
@@ -0,0 +1,42 @@
{
"profiles": {
"MyOffice.Web": {
"commandName": "Project",
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
},
"applicationUrl": "https://localhost:7097;http://localhost:5097",
"dotnetRunMessages": true
},
"MyOffice.SPA": {
"commandName": "Project"
},
"IIS Express": {
"commandName": "IISExpress",
"launchUrl": "swagger",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
},
"IIS": {
"commandName": "IIS",
"environmentVariables": {
"ASPNETCORE_ENVIRONMENT": "Development"
}
}
},
"$schema": "https://json.schemastore.org/launchsettings.json",
"iisSettings": {
"windowsAuthentication": false,
"anonymousAuthentication": true,
"iis": {
"applicationUrl": "http://localhost:9100",
"sslPort": 9101
},
"iisExpress": {
"applicationUrl": "http://localhost:9100",
"sslPort": 9101
}
}
}
+31
View File
@@ -0,0 +1,31 @@
{
"DatabaseProvider": "sqlite",
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft.AspNetCore": "Warning"
}
},
"Kestrel": {
"EndPoints": {
"Http": {
"Url": "http://*:9100"
}
/*,
"Https": {
"Url": "https://*:9101"
}*/
}
},
"AllowedHosts": "*",
"ExternalProviders": {
"Auth0": {
"ClientId": "",
"Domain": "",
"SecretKey": ""
},
"Google": {
"ClientId": ""
}
}
}
+18
View File
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration xmlns:xdt="http://schemas.microsoft.com/XML-Document-Transform">
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="bin\Debug\net6.0\MyOffice.Web.exe" arguments="" stdoutLogEnabled="true"
stdoutLogFile=".\logs\stdout" hostingModel="InProcess">
<environmentVariables xdt:Transform="InsertIfMissing">
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="" xdt:Locator="Match(name)" xdt:Transform="Remove" />
<environmentVariable name="ASPNETCORE_HTTPS_PORT" value="" xdt:Locator="Match(name)" xdt:Transform="Remove" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
+16
View File
@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<location path="." inheritInChildApplications="false">
<system.webServer>
<handlers>
<add name="aspNetCore" path="*" verb="*" modules="AspNetCoreModuleV2" resourceType="Unspecified" />
</handlers>
<aspNetCore processPath="bin\Debug\net6.0\MyOffice.Web.exe" arguments="" stdoutLogEnabled="true" stdoutLogFile=".\logs\stdout" hostingModel="InProcess">
<environmentVariables>
<environmentVariable name="ASPNETCORE_ENVIRONMENT" value="Development" />
<environmentVariable name="ASPNETCORE_HTTPS_PORT" value="9101" />
</environmentVariables>
</aspNetCore>
</system.webServer>
</location>
</configuration>
+23
View File
@@ -0,0 +1,23 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>MyOffice</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
html, body { height: 100% }
body {
margin: 0;
font-family: Roboto, Helvetica Neue, sans-serif
}
</style><link rel="stylesheet" href="styles.08cfaa5c1b59bc73.css" media="print" onload="this.media = 'all'">
<noscript>
<link rel="stylesheet" href="styles.08cfaa5c1b59bc73.css">
</noscript>
</head>
<body>
<h1>DEVELOPER INDEX.HTML</h1>
</body>
</html>