refactor(backend): use explicit types instead of var by default

This commit is contained in:
sam 2024-12-08 15:07:25 +01:00
parent bc7fd6d804
commit 649988db25
Signed by: sam
GPG key ID: B4EF20DDE721CAA1
52 changed files with 506 additions and 420 deletions

View file

@ -123,22 +123,27 @@ public class AuthService(
CancellationToken ct = default
)
{
var user = await db.Users.FirstOrDefaultAsync(
User? user = await db.Users.FirstOrDefaultAsync(
u => u.AuthMethods.Any(a => a.AuthType == AuthType.Email && a.RemoteId == email),
ct
);
if (user == null)
{
throw new ApiError.NotFound(
"No user with that email address found, or password is incorrect",
ErrorCode.UserNotFound
);
}
var pwResult = await VerifyHashedPasswordAsync(user, password, ct);
PasswordVerificationResult pwResult = await VerifyHashedPasswordAsync(user, password, ct);
if (pwResult == PasswordVerificationResult.Failed) // TODO: this seems to fail on some valid passwords?
{
throw new ApiError.NotFound(
"No user with that email address found, or password is incorrect",
ErrorCode.UserNotFound
);
}
if (pwResult == PasswordVerificationResult.SuccessRehashNeeded)
{
user.Password = await HashPasswordAsync(user, password, ct);
@ -169,7 +174,7 @@ public class AuthService(
throw new FoxnounsError("Password for user supplied to ValidatePasswordAsync was null");
}
var pwResult = await VerifyHashedPasswordAsync(user, password, ct);
PasswordVerificationResult pwResult = await VerifyHashedPasswordAsync(user, password, ct);
return pwResult
is PasswordVerificationResult.SuccessRehashNeeded
or PasswordVerificationResult.Success;
@ -231,13 +236,15 @@ public class AuthService(
AssertValidAuthType(authType, app);
// This is already checked when
var currentCount = await db
int currentCount = await db
.AuthMethods.Where(m => m.UserId == userId && m.AuthType == authType)
.CountAsync(ct);
if (currentCount >= AuthUtils.MaxAuthMethodsPerType)
{
throw new ApiError.BadRequest(
"Too many linked accounts of this type, maximum of 3 per account."
);
}
var authMethod = new AuthMethod
{
@ -262,13 +269,15 @@ public class AuthService(
)
{
if (!AuthUtils.ValidateScopes(application, scopes))
{
throw new ApiError.BadRequest(
"Invalid scopes requested for this token",
"scopes",
scopes
);
}
var (token, hash) = GenerateToken();
(string? token, byte[]? hash) = GenerateToken();
return (
token,
new Token
@ -293,9 +302,9 @@ public class AuthService(
CancellationToken ct = default
)
{
var frontendApp = await db.GetFrontendApplicationAsync(ct);
Application frontendApp = await db.GetFrontendApplicationAsync(ct);
var (tokenStr, token) = GenerateToken(
(string? tokenStr, Token? token) = GenerateToken(
user,
frontendApp,
["*"],
@ -308,17 +317,12 @@ public class AuthService(
await db.SaveChangesAsync(ct);
return new CallbackResponse(
HasAccount: true,
Ticket: null,
RemoteUsername: null,
User: await userRenderer.RenderUserAsync(
user,
selfUser: user,
renderMembers: false,
ct: ct
),
Token: tokenStr,
ExpiresAt: token.ExpiresAt
true,
null,
null,
await userRenderer.RenderUserAsync(user, user, renderMembers: false, ct: ct),
tokenStr,
token.ExpiresAt
);
}
@ -340,8 +344,8 @@ public class AuthService(
private static (string, byte[]) GenerateToken()
{
var token = AuthUtils.RandomToken();
var hash = SHA512.HashData(Convert.FromBase64String(token));
string token = AuthUtils.RandomToken();
byte[] hash = SHA512.HashData(Convert.FromBase64String(token));
return (token, hash);
}

View file

@ -18,22 +18,25 @@ public partial class FediverseAuthService
Snowflake? existingAppId = null
)
{
var resp = await _client.PostAsJsonAsync(
HttpResponseMessage resp = await _client.PostAsJsonAsync(
$"https://{instance}/api/v1/apps",
new CreateMastodonApplicationRequest(
ClientName: $"pronouns.cc (+{_config.BaseUrl})",
RedirectUris: MastodonRedirectUri(instance),
Scopes: "read read:accounts",
Website: _config.BaseUrl
$"pronouns.cc (+{_config.BaseUrl})",
MastodonRedirectUri(instance),
"read read:accounts",
_config.BaseUrl
)
);
resp.EnsureSuccessStatusCode();
var mastodonApp = await resp.Content.ReadFromJsonAsync<PartialMastodonApplication>();
PartialMastodonApplication? mastodonApp =
await resp.Content.ReadFromJsonAsync<PartialMastodonApplication>();
if (mastodonApp == null)
{
throw new FoxnounsError(
$"Application created on Mastodon-compatible instance {instance} was null"
);
}
FediverseApplication app;
@ -75,7 +78,7 @@ public partial class FediverseAuthService
if (state != null)
await _keyCacheService.ValidateAuthStateAsync(state);
var tokenResp = await _client.PostAsync(
HttpResponseMessage tokenResp = await _client.PostAsync(
MastodonTokenUri(app.Domain),
new FormUrlEncodedContent(
new Dictionary<string, string>
@ -95,7 +98,7 @@ public partial class FediverseAuthService
}
tokenResp.EnsureSuccessStatusCode();
var token = (
string? token = (
await tokenResp.Content.ReadFromJsonAsync<MastodonTokenResponse>()
)?.AccessToken;
if (token == null)
@ -106,9 +109,9 @@ public partial class FediverseAuthService
var req = new HttpRequestMessage(HttpMethod.Get, MastodonCurrentUserUri(app.Domain));
req.Headers.Add("Authorization", $"Bearer {token}");
var currentUserResp = await _client.SendAsync(req);
HttpResponseMessage currentUserResp = await _client.SendAsync(req);
currentUserResp.EnsureSuccessStatusCode();
var user = await currentUserResp.Content.ReadFromJsonAsync<FediverseUser>();
FediverseUser? user = await currentUserResp.Content.ReadFromJsonAsync<FediverseUser>();
if (user == null)
{
throw new FoxnounsError($"User response from instance {app.Domain} was invalid");
@ -131,7 +134,7 @@ public partial class FediverseAuthService
"An app credentials refresh was requested for {ApplicationId}, creating a new application",
app.Id
);
app = await CreateMastodonApplicationAsync(app.Domain, existingAppId: app.Id);
app = await CreateMastodonApplicationAsync(app.Domain, app.Id);
}
state ??= HttpUtility.UrlEncode(await _keyCacheService.GenerateAuthStateAsync());

View file

@ -43,7 +43,7 @@ public partial class FediverseAuthService
string? state = null
)
{
var app = await GetApplicationAsync(instance);
FediverseApplication app = await GetApplicationAsync(instance);
return await GenerateAuthUrlAsync(app, forceRefresh, state);
}
@ -56,13 +56,15 @@ public partial class FediverseAuthService
public async Task<FediverseApplication> GetApplicationAsync(string instance)
{
var app = await _db.FediverseApplications.FirstOrDefaultAsync(a => a.Domain == instance);
FediverseApplication? app = await _db.FediverseApplications.FirstOrDefaultAsync(a =>
a.Domain == instance
);
if (app != null)
return app;
_logger.Debug("No application for fediverse instance {Instance}, creating it", instance);
var softwareName = await GetSoftwareNameAsync(instance);
string softwareName = await GetSoftwareNameAsync(instance);
if (IsMastodonCompatible(softwareName))
{
@ -76,13 +78,14 @@ public partial class FediverseAuthService
{
_logger.Debug("Requesting software name for fediverse instance {Instance}", instance);
var wellKnownResp = await _client.GetAsync(
HttpResponseMessage wellKnownResp = await _client.GetAsync(
new Uri($"https://{instance}/.well-known/nodeinfo")
);
wellKnownResp.EnsureSuccessStatusCode();
var wellKnown = await wellKnownResp.Content.ReadFromJsonAsync<WellKnownResponse>();
var nodeInfoUrl = wellKnown?.Links.FirstOrDefault(l => l.Rel == NodeInfoRel)?.Href;
WellKnownResponse? wellKnown =
await wellKnownResp.Content.ReadFromJsonAsync<WellKnownResponse>();
string? nodeInfoUrl = wellKnown?.Links.FirstOrDefault(l => l.Rel == NodeInfoRel)?.Href;
if (nodeInfoUrl == null)
{
throw new FoxnounsError(
@ -90,10 +93,10 @@ public partial class FediverseAuthService
);
}
var nodeInfoResp = await _client.GetAsync(nodeInfoUrl);
HttpResponseMessage nodeInfoResp = await _client.GetAsync(nodeInfoUrl);
nodeInfoResp.EnsureSuccessStatusCode();
var nodeInfo = await nodeInfoResp.Content.ReadFromJsonAsync<PartialNodeInfo>();
PartialNodeInfo? nodeInfo = await nodeInfoResp.Content.ReadFromJsonAsync<PartialNodeInfo>();
return nodeInfo?.Software.Name
?? throw new FoxnounsError(
$"Nodeinfo response for instance {instance} was invalid, no software name"

View file

@ -29,7 +29,7 @@ public class RemoteAuthService(
)
{
var redirectUri = $"{config.BaseUrl}/auth/callback/discord";
var resp = await _httpClient.PostAsync(
HttpResponseMessage resp = await _httpClient.PostAsync(
_discordTokenUri,
new FormUrlEncodedContent(
new Dictionary<string, string>
@ -45,7 +45,7 @@ public class RemoteAuthService(
);
if (!resp.IsSuccessStatusCode)
{
var respBody = await resp.Content.ReadAsStringAsync(ct);
string respBody = await resp.Content.ReadAsStringAsync(ct);
_logger.Error(
"Received error status {StatusCode} when exchanging OAuth token: {ErrorBody}",
(int)resp.StatusCode,
@ -55,16 +55,18 @@ public class RemoteAuthService(
}
resp.EnsureSuccessStatusCode();
var token = await resp.Content.ReadFromJsonAsync<DiscordTokenResponse>(ct);
DiscordTokenResponse? token = await resp.Content.ReadFromJsonAsync<DiscordTokenResponse>(
ct
);
if (token == null)
throw new FoxnounsError("Discord token response was null");
var req = new HttpRequestMessage(HttpMethod.Get, _discordUserUri);
req.Headers.Add("Authorization", $"{token.token_type} {token.access_token}");
var resp2 = await _httpClient.SendAsync(req, ct);
HttpResponseMessage resp2 = await _httpClient.SendAsync(req, ct);
resp2.EnsureSuccessStatusCode();
var user = await resp2.Content.ReadFromJsonAsync<DiscordUserResponse>(ct);
DiscordUserResponse? user = await resp2.Content.ReadFromJsonAsync<DiscordUserResponse>(ct);
if (user == null)
throw new FoxnounsError("Discord user response was null");
@ -104,7 +106,7 @@ public class RemoteAuthService(
string? instance = null
)
{
var existingAccounts = await db
int existingAccounts = await db
.AuthMethods.Where(m => m.UserId == userId && m.AuthType == authType)
.CountAsync();
if (existingAccounts > AuthUtils.MaxAuthMethodsPerType)
@ -131,7 +133,9 @@ public class RemoteAuthService(
string? instance = null
)
{
var accountState = await keyCacheService.GetAddExtraAccountStateAsync(state);
AddExtraAccountState? accountState = await keyCacheService.GetAddExtraAccountStateAsync(
state
);
if (
accountState == null
|| accountState.AuthType != authType