Harden public account registration
Deploy Promiscuity Auth API / deploy (push) Successful in 1m13s
Deploy Promiscuity Character API / deploy (push) Successful in 59s
Deploy Promiscuity Crafting API / deploy (push) Successful in 58s
Deploy Promiscuity Inventory API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 59s
Deploy Promiscuity Mail API / deploy (push) Successful in 59s
Deploy Promiscuity World API / deploy (push) Successful in 59s
k8s smoke test / test (push) Successful in 20s
Deploy Promiscuity Auth API / deploy (push) Successful in 1m13s
Deploy Promiscuity Character API / deploy (push) Successful in 59s
Deploy Promiscuity Crafting API / deploy (push) Successful in 58s
Deploy Promiscuity Inventory API / deploy (push) Successful in 58s
Deploy Promiscuity Locations API / deploy (push) Successful in 59s
Deploy Promiscuity Mail API / deploy (push) Successful in 59s
Deploy Promiscuity World API / deploy (push) Successful in 59s
k8s smoke test / test (push) Successful in 20s
This commit is contained in:
@@ -3,9 +3,12 @@ using AuthApi.Services;
|
||||
using Microsoft.AspNetCore.Authorization;
|
||||
using Microsoft.AspNetCore.Mvc;
|
||||
using Microsoft.IdentityModel.Tokens;
|
||||
using MongoDB.Driver;
|
||||
using System.IdentityModel.Tokens.Jwt;
|
||||
using System.Net.Mail;
|
||||
using System.Security.Claims;
|
||||
using System.Text;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace AuthApi.Controllers;
|
||||
|
||||
@@ -25,15 +28,31 @@ public class AuthController : ControllerBase
|
||||
[HttpPost("register")]
|
||||
public async Task<IActionResult> Register([FromBody] RegisterRequest req)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(req.Username) || string.IsNullOrWhiteSpace(req.Password))
|
||||
return BadRequest("Username and password required");
|
||||
var username = req.Username?.Trim() ?? "";
|
||||
var password = req.Password ?? "";
|
||||
var email = req.Email?.Trim() ?? "";
|
||||
|
||||
if (await _users.GetByUsernameAsync(req.Username) != null)
|
||||
return BadRequest("User already exists");
|
||||
if (!Regex.IsMatch(username, "^[A-Za-z0-9_]{3,24}$"))
|
||||
return BadRequest("Username must be 3-24 characters using only letters, numbers, or underscores");
|
||||
if (password.Length < 8)
|
||||
return BadRequest("Password must be at least 8 characters");
|
||||
var emailDomain = email.Split('@').LastOrDefault() ?? "";
|
||||
if (string.IsNullOrWhiteSpace(email) || !MailAddress.TryCreate(email, out _) || !emailDomain.Contains('.'))
|
||||
return BadRequest("A valid email address is required");
|
||||
|
||||
var hash = BCrypt.Net.BCrypt.HashPassword(req.Password);
|
||||
var user = new User { Username = req.Username, PasswordHash = hash, Role = "USER", Email = req.Email };
|
||||
await _users.CreateAsync(user);
|
||||
if (await _users.GetByUsernameAsync(username) != null)
|
||||
return Conflict("Username already exists");
|
||||
|
||||
var hash = BCrypt.Net.BCrypt.HashPassword(password);
|
||||
var user = new User { Username = username, PasswordHash = hash, Role = "USER", Email = email };
|
||||
try
|
||||
{
|
||||
await _users.CreateAsync(user);
|
||||
}
|
||||
catch (MongoWriteException ex) when (ex.WriteError.Category == ServerErrorCategory.DuplicateKey)
|
||||
{
|
||||
return Conflict("Username already exists");
|
||||
}
|
||||
return Ok("User created");
|
||||
}
|
||||
|
||||
|
||||
@@ -9,9 +9,12 @@ Inbound JSON documents
|
||||
{
|
||||
"username": "string",
|
||||
"password": "string",
|
||||
"email": "string (optional)"
|
||||
"email": "string"
|
||||
}
|
||||
```
|
||||
Usernames must be 3-24 characters containing only letters, numbers, or
|
||||
underscores. Passwords must contain at least 8 characters, and email is
|
||||
required with a valid address format.
|
||||
- LoginRequest (`POST /api/auth/login`)
|
||||
```json
|
||||
{
|
||||
@@ -42,7 +45,7 @@ Stored documents (MongoDB)
|
||||
"username": "string",
|
||||
"passwordHash": "string",
|
||||
"role": "USER | SUPER",
|
||||
"email": "string (optional)",
|
||||
"email": "string",
|
||||
"refreshToken": "string (optional)",
|
||||
"refreshTokenExpiry": "string (optional, ISO-8601 datetime)"
|
||||
}
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
See `DOCUMENTS.md` for request payloads and stored document shapes.
|
||||
|
||||
## Endpoints
|
||||
- `POST /api/auth/register` Register a new user.
|
||||
- `POST /api/auth/register` Register a USER account with a username, email, and password.
|
||||
- `POST /api/auth/login` Issue access and refresh tokens.
|
||||
- `POST /api/auth/refresh` Refresh an access token.
|
||||
- `POST /api/auth/logout` Revoke the current access token.
|
||||
|
||||
@@ -19,8 +19,8 @@ public class UserService
|
||||
_col.Indexes.CreateOne(new CreateIndexModel<User>(keys, new CreateIndexOptions { Unique = true }));
|
||||
}
|
||||
|
||||
public Task<User?> GetByUsernameAsync(string username) =>
|
||||
_col.Find(u => u.Username == username).FirstOrDefaultAsync();
|
||||
public async Task<User?> GetByUsernameAsync(string username) =>
|
||||
await _col.Find(u => u.Username == username).FirstOrDefaultAsync();
|
||||
|
||||
public Task CreateAsync(User user) => _col.InsertOneAsync(user);
|
||||
|
||||
|
||||
Reference in New Issue
Block a user