This repository was archived by the owner on Jun 28, 2026. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 3
Enable registration api #3
Closed
Rinary1
wants to merge
6
commits into
Space-Wizards-Federation:master
from
Rinary1:enable-registration
Closed
Changes from all commits
Commits
Show all changes
6 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,19 +1,20 @@ | ||
| using System; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Text.Json.Serialization; | ||
| using System.Threading.Tasks; | ||
| using Microsoft.AspNetCore.Authorization; | ||
| using Microsoft.AspNetCore.Identity; | ||
| using Microsoft.AspNetCore.Mvc; | ||
| using Microsoft.AspNetCore.RateLimiting; | ||
| using Microsoft.AspNetCore.WebUtilities; | ||
| using Microsoft.Extensions.Configuration; | ||
| using Microsoft.Extensions.Internal; | ||
| using SS14.Auth.Shared; | ||
| using SS14.Auth.Shared.Data; | ||
| using SS14.Auth.Shared.Emails; | ||
| using SS14.Auth.Shared.Sessions; | ||
| using System; | ||
| using System.ComponentModel.DataAnnotations; | ||
| using System.Diagnostics; | ||
| using System.Linq; | ||
| using System.Text; | ||
| using System.Text.Json.Serialization; | ||
| using System.Threading.Tasks; | ||
|
|
||
| namespace SS14.Auth.Controllers; | ||
|
|
||
|
|
@@ -26,16 +27,25 @@ public class AuthApiController : ControllerBase | |
| { | ||
| private readonly SessionManager _sessionManager; | ||
| private readonly IEmailSender _emailSender; | ||
| private readonly ISystemClock _systemClock; | ||
| private readonly TimeProvider _systemClock; | ||
| private readonly IConfiguration _cfg; | ||
|
|
||
| private readonly SpaceUserManager _userManager; | ||
| private readonly SignInManager<SpaceUser> _signInManager; | ||
|
|
||
| private readonly Lazy<SpaceUser> _dummyUser = new(() => | ||
| { | ||
| var u = new SpaceUser(); | ||
| u.PasswordHash = new PasswordHasher<SpaceUser>().HashPassword(u, "timing-equalizer"); | ||
| return u; | ||
| }); | ||
|
|
||
| private string WebBaseUrl => _cfg.GetValue<string>("WebBaseUrl") ?? ""; | ||
|
|
||
| private const string DuplicateEmailCode = "DuplicateEmail"; | ||
|
|
||
| public AuthApiController(SpaceUserManager userManager, SignInManager<SpaceUser> signInManager, | ||
| SessionManager sessionManager, IEmailSender emailSender, ISystemClock systemClock, IConfiguration cfg) | ||
| SessionManager sessionManager, IEmailSender emailSender, TimeProvider systemClock, IConfiguration cfg) | ||
| { | ||
| _userManager = userManager; | ||
| _signInManager = signInManager; | ||
|
|
@@ -45,17 +55,17 @@ public AuthApiController(SpaceUserManager userManager, SignInManager<SpaceUser> | |
| _cfg = cfg; | ||
| } | ||
|
|
||
| [EnableRateLimiting("authenticate")] | ||
| [HttpPost("authenticate")] | ||
| public async Task<IActionResult> Authenticate(AuthenticateRequest request) | ||
| { | ||
| // Password may never be null, and only either username OR userID can be used for login, not both. | ||
| if (!(request.Username == null ^ request.UserId == null)) | ||
| { | ||
| return BadRequest(); | ||
| } | ||
|
|
||
| // Console.WriteLine(Request.Headers["SS14-Launcher-Fingerprint"]); | ||
| // Console.WriteLine(Request.Headers["User-Agent"]); | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why was this removed?
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I'm not sure if this is that important, for me it looks like dead code. If anyone needs to debug it, they can just add it locally, right? |
||
| return BadRequest(new | ||
| { | ||
| Error = "invalid_request", | ||
| Message = "Exactly one of Username or UserId must be provided." | ||
| }); | ||
|
|
||
| SpaceUser? user; | ||
| if (request.Username != null) | ||
|
|
@@ -65,12 +75,13 @@ public async Task<IActionResult> Authenticate(AuthenticateRequest request) | |
| else | ||
| { | ||
| Debug.Assert(request.UserId != null); | ||
|
|
||
| user = await _userManager.FindByIdAsync(request.UserId!.Value.ToString()); | ||
| } | ||
|
|
||
| if (user == null) | ||
| { | ||
| await _signInManager.CheckPasswordSignInAsync(_dummyUser.Value, request.Password, false); | ||
|
|
||
| return Unauthorized(new AuthenticateDenyResponse( | ||
| new[] { "Invalid login credentials." }, | ||
| AuthenticateDenyResponseCode.InvalidCredentials)); | ||
|
|
@@ -113,57 +124,68 @@ public async Task<IActionResult> Authenticate(AuthenticateRequest request) | |
| new[] { "" }, | ||
| AuthenticateDenyResponseCode.TfaRequired)); | ||
| } | ||
|
|
||
| var verify = await _userManager.VerifyTwoFactorTokenAsync( | ||
| user, | ||
| _userManager.Options.Tokens.AuthenticatorTokenProvider, | ||
| request.TfaCode); | ||
|
|
||
| if (!verify) | ||
| { | ||
| return Unauthorized(new AuthenticateDenyResponse( | ||
| new[] { "" }, | ||
| AuthenticateDenyResponseCode.TfaInvalid)); | ||
| } | ||
|
|
||
| // 2FA passed, we're good. | ||
| } | ||
|
|
||
| var (token, expireTime) = | ||
| await _sessionManager.RegisterNewSession(user, SessionManager.DefaultExpireTime); | ||
|
|
||
| return Ok(new AuthenticateResponse(token.AsBase64, user.UserName!, user.Id, expireTime)); | ||
| } | ||
|
|
||
| // Launcher registration disabled due to spam risk. | ||
| /* | ||
| [EnableRateLimiting("registration")] | ||
| [HttpPost("register")] | ||
| public async Task<IActionResult> Register(RegisterRequest request) | ||
| { | ||
| var userName = request.Username.Trim(); | ||
| var email = request.Email.Trim(); | ||
|
|
||
| var user = ModelShared.CreateNewUser(userName, email, _systemClock); | ||
| var user = ModelShared.CreateNewUser(userName, email, _systemClock.GetUtcNow()); | ||
| var result = await _userManager.CreateAsync(user, request.Password); | ||
|
|
||
| var successStatus = _userManager.Options.SignIn.RequireConfirmedEmail | ||
| ? RegisterResponseStatus.RegisteredNeedConfirmation | ||
| : RegisterResponseStatus.Registered; | ||
|
|
||
| if (!result.Succeeded) | ||
| { | ||
| var errors = result.Errors.Select(p => p.Description).ToArray(); | ||
| var errors = result.Errors | ||
| .Where(e => e.Code != DuplicateEmailCode) | ||
| .Select(e => e.Description) | ||
| .ToArray(); | ||
|
|
||
| if (errors.Length == 0) | ||
| { | ||
| var loginUrl = $"{WebBaseUrl}Identity/Account/Login"; | ||
| await ModelShared.SendAccountExistsEmail(_emailSender, email, loginUrl); | ||
|
|
||
| return Ok(new RegisterResponse(successStatus)); | ||
| } | ||
|
|
||
| return UnprocessableEntity(new RegisterResponseError(errors)); | ||
| } | ||
|
|
||
| var confirmLink = await GenerateEmailConfirmLink(user); | ||
|
|
||
| await ModelShared.SendConfirmEmail(_emailSender, email, confirmLink); | ||
|
|
||
| var status = _userManager.Options.SignIn.RequireConfirmedAccount | ||
| ? RegisterResponseStatus.RegisteredNeedConfirmation | ||
| : RegisterResponseStatus.Registered; | ||
|
|
||
| return Ok(new RegisterResponse(status)); | ||
| return Ok(new RegisterResponse(successStatus)); | ||
| } | ||
| */ | ||
|
|
||
| [EnableRateLimiting("reset-password")] | ||
| [HttpPost("resetPassword")] | ||
| public async Task<IActionResult> ResetPassword(ResetPasswordRequest request) | ||
| { | ||
|
|
@@ -185,27 +207,22 @@ public async Task<IActionResult> ResetPassword(ResetPasswordRequest request) | |
| return Ok(); | ||
| } | ||
|
|
||
| // Launcher resend confirmation disabled due to spam risk. | ||
| /* | ||
| [EnableRateLimiting("resend-confirmation")] | ||
| [HttpPost("resendConfirmation")] | ||
| public async Task<IActionResult> ResendConfirmation(ResendConfirmationRequest request) | ||
| { | ||
| var email = request.Email.Trim(); | ||
|
|
||
| var user = await _userManager.FindByEmailAsync(email); | ||
|
|
||
| if (user == null) | ||
| if (user != null && !await _userManager.IsEmailConfirmedAsync(user)) | ||
| { | ||
| return Ok(); | ||
| var confirmLink = await GenerateEmailConfirmLink(user); | ||
| await ModelShared.SendConfirmEmail(_emailSender, email, confirmLink); | ||
| } | ||
|
|
||
| var confirmLink = await GenerateEmailConfirmLink(user); | ||
|
|
||
| await ModelShared.SendConfirmEmail(_emailSender, email, confirmLink); | ||
|
|
||
| return Ok(); | ||
| } | ||
| */ | ||
|
|
||
| [Authorize(AuthenticationSchemes = "SS14Auth")] | ||
| [HttpGet("ping")] | ||
|
|
@@ -281,40 +298,27 @@ public enum AuthenticateDenyResponseCode | |
| // @formatter:on | ||
| } | ||
|
|
||
| public sealed record RegisterRequest(string Username, string Email, string Password) | ||
| { | ||
| } | ||
| public sealed record RegisterRequest( | ||
| [Required, StringLength(32, MinimumLength = 3)] string Username, | ||
| [Required, EmailAddress] string Email, | ||
| [Required, StringLength(100, MinimumLength = 6)] string Password); | ||
|
|
||
| public sealed record ResetPasswordRequest(string Email) | ||
| { | ||
| } | ||
| public sealed record ResetPasswordRequest([Required, EmailAddress] string Email); | ||
|
|
||
| public sealed record ResendConfirmationRequest(string Email) | ||
| { | ||
| } | ||
| public sealed record ResendConfirmationRequest([Required, EmailAddress] string Email); | ||
|
|
||
| public sealed record RegisterResponse(RegisterResponseStatus Status) | ||
| { | ||
| } | ||
| public sealed record RegisterResponse(RegisterResponseStatus Status); | ||
|
|
||
| public sealed record RegisterResponseError(string[] Errors) | ||
| { | ||
| } | ||
| public sealed record RegisterResponseError(string[] Errors); | ||
|
|
||
| public sealed record LogoutRequest(string Token) | ||
| { | ||
| } | ||
| public sealed record LogoutRequest(string Token); | ||
|
|
||
| public sealed record RefreshRequest(string Token) | ||
| { | ||
| } | ||
| public sealed record RefreshRequest(string Token); | ||
|
|
||
| public sealed record RefreshResponse(DateTimeOffset ExpireTime, string NewToken) | ||
| { | ||
| } | ||
| public sealed record RefreshResponse(DateTimeOffset ExpireTime, string NewToken); | ||
|
|
||
| public enum RegisterResponseStatus | ||
| { | ||
| Registered, | ||
| RegisteredNeedConfirmation | ||
| } | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.