refactor(backend): use explicit types instead of var by default
This commit is contained in:
parent
bc7fd6d804
commit
649988db25
52 changed files with 506 additions and 420 deletions
|
@ -33,14 +33,16 @@ public class AuthController(
|
|||
config.GoogleAuth.Enabled,
|
||||
config.TumblrAuth.Enabled
|
||||
);
|
||||
var state = HttpUtility.UrlEncode(await keyCacheService.GenerateAuthStateAsync(ct));
|
||||
string state = HttpUtility.UrlEncode(await keyCacheService.GenerateAuthStateAsync(ct));
|
||||
string? discord = null;
|
||||
if (config.DiscordAuth is { ClientId: not null, ClientSecret: not null })
|
||||
{
|
||||
discord =
|
||||
$"https://discord.com/oauth2/authorize?response_type=code"
|
||||
+ $"&client_id={config.DiscordAuth.ClientId}&scope=identify"
|
||||
+ $"&prompt=none&state={state}"
|
||||
+ $"&redirect_uri={HttpUtility.UrlEncode($"{config.BaseUrl}/auth/callback/discord")}";
|
||||
}
|
||||
|
||||
return Ok(new UrlsResponse(config.EmailAuth.Enabled, discord, null, null));
|
||||
}
|
||||
|
@ -86,7 +88,7 @@ public class AuthController(
|
|||
)]
|
||||
public async Task<IActionResult> GetAuthMethodAsync(Snowflake id)
|
||||
{
|
||||
var authMethod = await db
|
||||
AuthMethod? authMethod = await db
|
||||
.AuthMethods.Include(a => a.FediverseApplication)
|
||||
.FirstOrDefaultAsync(a => a.UserId == CurrentUser!.Id && a.Id == id);
|
||||
if (authMethod == null)
|
||||
|
@ -99,17 +101,19 @@ public class AuthController(
|
|||
[Authorize("*")]
|
||||
public async Task<IActionResult> DeleteAuthMethodAsync(Snowflake id)
|
||||
{
|
||||
var authMethods = await db
|
||||
List<AuthMethod> authMethods = await db
|
||||
.AuthMethods.Where(a => a.UserId == CurrentUser!.Id)
|
||||
.ToListAsync();
|
||||
if (authMethods.Count < 2)
|
||||
{
|
||||
throw new ApiError(
|
||||
"You cannot remove your last authentication method.",
|
||||
HttpStatusCode.BadRequest,
|
||||
ErrorCode.LastAuthMethod
|
||||
);
|
||||
}
|
||||
|
||||
var authMethod = authMethods.FirstOrDefault(a => a.Id == id);
|
||||
AuthMethod? authMethod = authMethods.FirstOrDefault(a => a.Id == id);
|
||||
if (authMethod == null)
|
||||
throw new ApiError.NotFound("No authentication method with that ID found.");
|
||||
|
||||
|
|
|
@ -34,8 +34,10 @@ public class DiscordAuthController(
|
|||
CheckRequirements();
|
||||
await keyCacheService.ValidateAuthStateAsync(req.State);
|
||||
|
||||
var remoteUser = await remoteAuthService.RequestDiscordTokenAsync(req.Code);
|
||||
var user = await authService.AuthenticateUserAsync(AuthType.Discord, remoteUser.Id);
|
||||
RemoteAuthService.RemoteUser remoteUser = await remoteAuthService.RequestDiscordTokenAsync(
|
||||
req.Code
|
||||
);
|
||||
User? user = await authService.AuthenticateUserAsync(AuthType.Discord, remoteUser.Id);
|
||||
if (user != null)
|
||||
return Ok(await authService.GenerateUserTokenAsync(user));
|
||||
|
||||
|
@ -45,23 +47,14 @@ public class DiscordAuthController(
|
|||
remoteUser.Id
|
||||
);
|
||||
|
||||
var ticket = AuthUtils.RandomToken();
|
||||
string ticket = AuthUtils.RandomToken();
|
||||
await keyCacheService.SetKeyAsync(
|
||||
$"discord:{ticket}",
|
||||
remoteUser,
|
||||
Duration.FromMinutes(20)
|
||||
);
|
||||
|
||||
return Ok(
|
||||
new CallbackResponse(
|
||||
HasAccount: false,
|
||||
Ticket: ticket,
|
||||
RemoteUsername: remoteUser.Username,
|
||||
User: null,
|
||||
Token: null,
|
||||
ExpiresAt: null
|
||||
)
|
||||
);
|
||||
return Ok(new CallbackResponse(false, ticket, remoteUser.Username, null, null, null));
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
|
@ -70,9 +63,10 @@ public class DiscordAuthController(
|
|||
[FromBody] AuthController.OauthRegisterRequest req
|
||||
)
|
||||
{
|
||||
var remoteUser = await keyCacheService.GetKeyAsync<RemoteAuthService.RemoteUser>(
|
||||
$"discord:{req.Ticket}"
|
||||
);
|
||||
RemoteAuthService.RemoteUser? remoteUser =
|
||||
await keyCacheService.GetKeyAsync<RemoteAuthService.RemoteUser>(
|
||||
$"discord:{req.Ticket}"
|
||||
);
|
||||
if (remoteUser == null)
|
||||
throw new ApiError.BadRequest("Invalid ticket", "ticket", req.Ticket);
|
||||
if (
|
||||
|
@ -88,7 +82,7 @@ public class DiscordAuthController(
|
|||
throw new ApiError.BadRequest("Invalid ticket", "ticket", req.Ticket);
|
||||
}
|
||||
|
||||
var user = await authService.CreateUserWithRemoteAuthAsync(
|
||||
User user = await authService.CreateUserWithRemoteAuthAsync(
|
||||
req.Username,
|
||||
AuthType.Discord,
|
||||
remoteUser.Id,
|
||||
|
@ -104,12 +98,12 @@ public class DiscordAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var state = await remoteAuthService.ValidateAddAccountRequestAsync(
|
||||
string state = await remoteAuthService.ValidateAddAccountRequestAsync(
|
||||
CurrentUser!.Id,
|
||||
AuthType.Discord
|
||||
);
|
||||
|
||||
var url =
|
||||
string url =
|
||||
$"https://discord.com/oauth2/authorize?response_type=code"
|
||||
+ $"&client_id={config.DiscordAuth.ClientId}&scope=identify"
|
||||
+ $"&prompt=none&state={state}"
|
||||
|
@ -132,10 +126,12 @@ public class DiscordAuthController(
|
|||
AuthType.Discord
|
||||
);
|
||||
|
||||
var remoteUser = await remoteAuthService.RequestDiscordTokenAsync(req.Code);
|
||||
RemoteAuthService.RemoteUser remoteUser = await remoteAuthService.RequestDiscordTokenAsync(
|
||||
req.Code
|
||||
);
|
||||
try
|
||||
{
|
||||
var authMethod = await authService.AddAuthMethodAsync(
|
||||
AuthMethod authMethod = await authService.AddAuthMethodAsync(
|
||||
CurrentUser.Id,
|
||||
AuthType.Discord,
|
||||
remoteUser.Id,
|
||||
|
@ -169,8 +165,10 @@ public class DiscordAuthController(
|
|||
private void CheckRequirements()
|
||||
{
|
||||
if (!config.DiscordAuth.Enabled)
|
||||
{
|
||||
throw new ApiError.BadRequest(
|
||||
"Discord authentication is not enabled on this instance."
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -39,11 +39,7 @@ public class EmailAuthController(
|
|||
if (!req.Email.Contains('@'))
|
||||
throw new ApiError.BadRequest("Email is invalid", "email", req.Email);
|
||||
|
||||
var state = await keyCacheService.GenerateRegisterEmailStateAsync(
|
||||
req.Email,
|
||||
userId: null,
|
||||
ct
|
||||
);
|
||||
string state = await keyCacheService.GenerateRegisterEmailStateAsync(req.Email, null, ct);
|
||||
|
||||
// If there's already a user with that email address, pretend we sent an email but actually ignore it
|
||||
if (
|
||||
|
@ -63,23 +59,14 @@ public class EmailAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var state = await keyCacheService.GetRegisterEmailStateAsync(req.State);
|
||||
RegisterEmailState? state = await keyCacheService.GetRegisterEmailStateAsync(req.State);
|
||||
if (state is not { ExistingUserId: null })
|
||||
throw new ApiError.BadRequest("Invalid state", "state", req.State);
|
||||
|
||||
var ticket = AuthUtils.RandomToken();
|
||||
string ticket = AuthUtils.RandomToken();
|
||||
await keyCacheService.SetKeyAsync($"email:{ticket}", state.Email, Duration.FromMinutes(20));
|
||||
|
||||
return Ok(
|
||||
new CallbackResponse(
|
||||
HasAccount: false,
|
||||
Ticket: ticket,
|
||||
RemoteUsername: state.Email,
|
||||
User: null,
|
||||
Token: null,
|
||||
ExpiresAt: null
|
||||
)
|
||||
);
|
||||
return Ok(new CallbackResponse(false, ticket, state.Email, null, null, null));
|
||||
}
|
||||
|
||||
[HttpPost("register")]
|
||||
|
@ -89,14 +76,18 @@ public class EmailAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var email = await keyCacheService.GetKeyAsync($"email:{req.Ticket}");
|
||||
string? email = await keyCacheService.GetKeyAsync($"email:{req.Ticket}");
|
||||
if (email == null)
|
||||
throw new ApiError.BadRequest("Unknown ticket", "ticket", req.Ticket);
|
||||
|
||||
var user = await authService.CreateUserWithPasswordAsync(req.Username, email, req.Password);
|
||||
var frontendApp = await db.GetFrontendApplicationAsync();
|
||||
User user = await authService.CreateUserWithPasswordAsync(
|
||||
req.Username,
|
||||
email,
|
||||
req.Password
|
||||
);
|
||||
Application frontendApp = await db.GetFrontendApplicationAsync();
|
||||
|
||||
var (tokenStr, token) = authService.GenerateToken(
|
||||
(string? tokenStr, Token? token) = authService.GenerateToken(
|
||||
user,
|
||||
frontendApp,
|
||||
["*"],
|
||||
|
@ -110,7 +101,7 @@ public class EmailAuthController(
|
|||
|
||||
return Ok(
|
||||
new AuthController.AuthResponse(
|
||||
await userRenderer.RenderUserAsync(user, selfUser: user, renderMembers: false),
|
||||
await userRenderer.RenderUserAsync(user, user, renderMembers: false),
|
||||
tokenStr,
|
||||
token.ExpiresAt
|
||||
)
|
||||
|
@ -126,19 +117,16 @@ public class EmailAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var (user, authenticationResult) = await authService.AuthenticateUserAsync(
|
||||
req.Email,
|
||||
req.Password,
|
||||
ct
|
||||
);
|
||||
(User? user, AuthService.EmailAuthenticationResult authenticationResult) =
|
||||
await authService.AuthenticateUserAsync(req.Email, req.Password, ct);
|
||||
if (authenticationResult == AuthService.EmailAuthenticationResult.MfaRequired)
|
||||
throw new NotImplementedException("MFA is not implemented yet");
|
||||
|
||||
var frontendApp = await db.GetFrontendApplicationAsync(ct);
|
||||
Application frontendApp = await db.GetFrontendApplicationAsync(ct);
|
||||
|
||||
_logger.Debug("Logging user {Id} in with email and password", user.Id);
|
||||
|
||||
var (tokenStr, token) = authService.GenerateToken(
|
||||
(string? tokenStr, Token? token) = authService.GenerateToken(
|
||||
user,
|
||||
frontendApp,
|
||||
["*"],
|
||||
|
@ -152,12 +140,7 @@ public class EmailAuthController(
|
|||
|
||||
return Ok(
|
||||
new AuthController.AuthResponse(
|
||||
await userRenderer.RenderUserAsync(
|
||||
user,
|
||||
selfUser: user,
|
||||
renderMembers: false,
|
||||
ct: ct
|
||||
),
|
||||
await userRenderer.RenderUserAsync(user, user, renderMembers: false, ct: ct),
|
||||
tokenStr,
|
||||
token.ExpiresAt
|
||||
)
|
||||
|
@ -184,7 +167,7 @@ public class EmailAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var emails = await db
|
||||
List<AuthMethod> emails = await db
|
||||
.AuthMethods.Where(m => m.UserId == CurrentUser!.Id && m.AuthType == AuthType.Email)
|
||||
.ToListAsync();
|
||||
if (emails.Count > AuthUtils.MaxAuthMethodsPerType)
|
||||
|
@ -207,12 +190,12 @@ public class EmailAuthController(
|
|||
await db.SaveChangesAsync();
|
||||
}
|
||||
|
||||
var state = await keyCacheService.GenerateRegisterEmailStateAsync(
|
||||
string state = await keyCacheService.GenerateRegisterEmailStateAsync(
|
||||
req.Email,
|
||||
userId: CurrentUser!.Id
|
||||
CurrentUser!.Id
|
||||
);
|
||||
|
||||
var emailExists = await db
|
||||
bool emailExists = await db
|
||||
.AuthMethods.Where(m => m.AuthType == AuthType.Email && m.RemoteId == req.Email)
|
||||
.AnyAsync();
|
||||
if (emailExists)
|
||||
|
@ -230,13 +213,13 @@ public class EmailAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var state = await keyCacheService.GetRegisterEmailStateAsync(req.State);
|
||||
RegisterEmailState? state = await keyCacheService.GetRegisterEmailStateAsync(req.State);
|
||||
if (state?.ExistingUserId != CurrentUser!.Id)
|
||||
throw new ApiError.BadRequest("Invalid state", "state", req.State);
|
||||
|
||||
try
|
||||
{
|
||||
var authMethod = await authService.AddAuthMethodAsync(
|
||||
AuthMethod authMethod = await authService.AddAuthMethodAsync(
|
||||
CurrentUser.Id,
|
||||
AuthType.Email,
|
||||
state.Email
|
||||
|
@ -252,7 +235,7 @@ public class EmailAuthController(
|
|||
authMethod.Id,
|
||||
AuthType.Email,
|
||||
authMethod.RemoteId,
|
||||
RemoteUsername: null
|
||||
null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ public class FediverseAuthController(
|
|||
if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.'))
|
||||
throw new ApiError.BadRequest("Not a valid domain.", "instance", instance);
|
||||
|
||||
var url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh);
|
||||
string url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh);
|
||||
return Ok(new AuthController.SingleUrlResponse(url));
|
||||
}
|
||||
|
||||
|
@ -42,22 +42,19 @@ public class FediverseAuthController(
|
|||
[ProducesResponseType<CallbackResponse>(statusCode: StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> FediverseCallbackAsync([FromBody] CallbackRequest req)
|
||||
{
|
||||
var app = await fediverseAuthService.GetApplicationAsync(req.Instance);
|
||||
var remoteUser = await fediverseAuthService.GetRemoteFediverseUserAsync(
|
||||
app,
|
||||
req.Code,
|
||||
req.State
|
||||
);
|
||||
FediverseApplication app = await fediverseAuthService.GetApplicationAsync(req.Instance);
|
||||
FediverseAuthService.FediverseUser remoteUser =
|
||||
await fediverseAuthService.GetRemoteFediverseUserAsync(app, req.Code, req.State);
|
||||
|
||||
var user = await authService.AuthenticateUserAsync(
|
||||
User? user = await authService.AuthenticateUserAsync(
|
||||
AuthType.Fediverse,
|
||||
remoteUser.Id,
|
||||
instance: app
|
||||
app
|
||||
);
|
||||
if (user != null)
|
||||
return Ok(await authService.GenerateUserTokenAsync(user));
|
||||
|
||||
var ticket = AuthUtils.RandomToken();
|
||||
string ticket = AuthUtils.RandomToken();
|
||||
await keyCacheService.SetKeyAsync(
|
||||
$"fediverse:{ticket}",
|
||||
new FediverseTicketData(app.Id, remoteUser),
|
||||
|
@ -66,12 +63,12 @@ public class FediverseAuthController(
|
|||
|
||||
return Ok(
|
||||
new CallbackResponse(
|
||||
HasAccount: false,
|
||||
Ticket: ticket,
|
||||
RemoteUsername: $"@{remoteUser.Username}@{app.Domain}",
|
||||
User: null,
|
||||
Token: null,
|
||||
ExpiresAt: null
|
||||
false,
|
||||
ticket,
|
||||
$"@{remoteUser.Username}@{app.Domain}",
|
||||
null,
|
||||
null,
|
||||
null
|
||||
)
|
||||
);
|
||||
}
|
||||
|
@ -82,14 +79,16 @@ public class FediverseAuthController(
|
|||
[FromBody] AuthController.OauthRegisterRequest req
|
||||
)
|
||||
{
|
||||
var ticketData = await keyCacheService.GetKeyAsync<FediverseTicketData>(
|
||||
FediverseTicketData? ticketData = await keyCacheService.GetKeyAsync<FediverseTicketData>(
|
||||
$"fediverse:{req.Ticket}",
|
||||
delete: true
|
||||
true
|
||||
);
|
||||
if (ticketData == null)
|
||||
throw new ApiError.BadRequest("Invalid ticket", "ticket", req.Ticket);
|
||||
|
||||
var app = await db.FediverseApplications.FindAsync(ticketData.ApplicationId);
|
||||
FediverseApplication? app = await db.FediverseApplications.FindAsync(
|
||||
ticketData.ApplicationId
|
||||
);
|
||||
if (app == null)
|
||||
throw new FoxnounsError("Null application found for ticket");
|
||||
|
||||
|
@ -111,12 +110,12 @@ public class FediverseAuthController(
|
|||
throw new ApiError.BadRequest("Invalid ticket", "ticket", req.Ticket);
|
||||
}
|
||||
|
||||
var user = await authService.CreateUserWithRemoteAuthAsync(
|
||||
User user = await authService.CreateUserWithRemoteAuthAsync(
|
||||
req.Username,
|
||||
AuthType.Fediverse,
|
||||
ticketData.User.Id,
|
||||
ticketData.User.Username,
|
||||
instance: app
|
||||
app
|
||||
);
|
||||
|
||||
return Ok(await authService.GenerateUserTokenAsync(user));
|
||||
|
@ -132,13 +131,13 @@ public class FediverseAuthController(
|
|||
if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.'))
|
||||
throw new ApiError.BadRequest("Not a valid domain.", "instance", instance);
|
||||
|
||||
var state = await remoteAuthService.ValidateAddAccountRequestAsync(
|
||||
string state = await remoteAuthService.ValidateAddAccountRequestAsync(
|
||||
CurrentUser!.Id,
|
||||
AuthType.Fediverse,
|
||||
instance
|
||||
);
|
||||
|
||||
var url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh, state);
|
||||
string url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh, state);
|
||||
return Ok(new AuthController.SingleUrlResponse(url));
|
||||
}
|
||||
|
||||
|
@ -153,11 +152,12 @@ public class FediverseAuthController(
|
|||
req.Instance
|
||||
);
|
||||
|
||||
var app = await fediverseAuthService.GetApplicationAsync(req.Instance);
|
||||
var remoteUser = await fediverseAuthService.GetRemoteFediverseUserAsync(app, req.Code);
|
||||
FediverseApplication app = await fediverseAuthService.GetApplicationAsync(req.Instance);
|
||||
FediverseAuthService.FediverseUser remoteUser =
|
||||
await fediverseAuthService.GetRemoteFediverseUserAsync(app, req.Code);
|
||||
try
|
||||
{
|
||||
var authMethod = await authService.AddAuthMethodAsync(
|
||||
AuthMethod authMethod = await authService.AddAuthMethodAsync(
|
||||
CurrentUser.Id,
|
||||
AuthType.Fediverse,
|
||||
remoteUser.Id,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue