feat: link fediverse account to existing user
This commit is contained in:
parent
03209e4028
commit
57e1ec09c0
17 changed files with 335 additions and 95 deletions
|
@ -104,21 +104,9 @@ public class DiscordAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var existingAccounts = await db
|
||||
.AuthMethods.Where(m => m.UserId == CurrentUser!.Id && m.AuthType == AuthType.Discord)
|
||||
.CountAsync();
|
||||
if (existingAccounts > AuthUtils.MaxAuthMethodsPerType)
|
||||
{
|
||||
throw new ApiError.BadRequest(
|
||||
"Too many linked Discord accounts, maximum of 3 per account."
|
||||
);
|
||||
}
|
||||
|
||||
var state = HttpUtility.UrlEncode(
|
||||
await keyCacheService.GenerateAddExtraAccountStateAsync(
|
||||
AuthType.Discord,
|
||||
CurrentUser!.Id
|
||||
)
|
||||
var state = await remoteAuthService.ValidateAddAccountRequestAsync(
|
||||
CurrentUser!.Id,
|
||||
AuthType.Discord
|
||||
);
|
||||
|
||||
var url =
|
||||
|
@ -138,12 +126,11 @@ public class DiscordAuthController(
|
|||
{
|
||||
CheckRequirements();
|
||||
|
||||
var accountState = await keyCacheService.GetAddExtraAccountStateAsync(req.State);
|
||||
if (
|
||||
accountState is not { AuthType: AuthType.Discord }
|
||||
|| accountState.UserId != CurrentUser!.Id
|
||||
)
|
||||
throw new ApiError.BadRequest("Invalid state", "state", req.State);
|
||||
await remoteAuthService.ValidateAddAccountStateAsync(
|
||||
req.State,
|
||||
CurrentUser!.Id,
|
||||
AuthType.Discord
|
||||
);
|
||||
|
||||
var remoteUser = await remoteAuthService.RequestDiscordTokenAsync(req.Code);
|
||||
try
|
||||
|
|
|
@ -1,5 +1,8 @@
|
|||
using System.Net;
|
||||
using EntityFramework.Exceptions.Common;
|
||||
using Foxnouns.Backend.Database;
|
||||
using Foxnouns.Backend.Database.Models;
|
||||
using Foxnouns.Backend.Middleware;
|
||||
using Foxnouns.Backend.Services;
|
||||
using Foxnouns.Backend.Services.Auth;
|
||||
using Foxnouns.Backend.Utils;
|
||||
|
@ -15,13 +18,14 @@ public class FediverseAuthController(
|
|||
DatabaseContext db,
|
||||
FediverseAuthService fediverseAuthService,
|
||||
AuthService authService,
|
||||
RemoteAuthService remoteAuthService,
|
||||
KeyCacheService keyCacheService
|
||||
) : ApiControllerBase
|
||||
{
|
||||
private readonly ILogger _logger = logger.ForContext<FediverseAuthController>();
|
||||
|
||||
[HttpGet]
|
||||
[ProducesResponseType<FediverseUrlResponse>(statusCode: StatusCodes.Status200OK)]
|
||||
[ProducesResponseType<AuthController.SingleUrlResponse>(statusCode: StatusCodes.Status200OK)]
|
||||
public async Task<IActionResult> GetFediverseUrlAsync(
|
||||
[FromQuery] string instance,
|
||||
[FromQuery] bool forceRefresh = false
|
||||
|
@ -31,7 +35,7 @@ public class FediverseAuthController(
|
|||
throw new ApiError.BadRequest("Not a valid domain.", "instance", instance);
|
||||
|
||||
var url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh);
|
||||
return Ok(new FediverseUrlResponse(url));
|
||||
return Ok(new AuthController.SingleUrlResponse(url));
|
||||
}
|
||||
|
||||
[HttpPost("callback")]
|
||||
|
@ -118,9 +122,74 @@ public class FediverseAuthController(
|
|||
return Ok(await authService.GenerateUserTokenAsync(user));
|
||||
}
|
||||
|
||||
public record CallbackRequest(string Instance, string Code, string State);
|
||||
[HttpGet("add-account")]
|
||||
[Authorize("*")]
|
||||
public async Task<IActionResult> AddFediverseAccountAsync(
|
||||
[FromQuery] string instance,
|
||||
[FromQuery] bool forceRefresh = false
|
||||
)
|
||||
{
|
||||
if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.'))
|
||||
throw new ApiError.BadRequest("Not a valid domain.", "instance", instance);
|
||||
|
||||
private record FediverseUrlResponse(string Url);
|
||||
var state = await remoteAuthService.ValidateAddAccountRequestAsync(
|
||||
CurrentUser!.Id,
|
||||
AuthType.Fediverse,
|
||||
instance
|
||||
);
|
||||
|
||||
var url = await fediverseAuthService.GenerateAuthUrlAsync(instance, forceRefresh, state);
|
||||
return Ok(new AuthController.SingleUrlResponse(url));
|
||||
}
|
||||
|
||||
[HttpPost("add-account/callback")]
|
||||
[Authorize("*")]
|
||||
public async Task<IActionResult> AddAccountCallbackAsync([FromBody] CallbackRequest req)
|
||||
{
|
||||
await remoteAuthService.ValidateAddAccountStateAsync(
|
||||
req.State,
|
||||
CurrentUser!.Id,
|
||||
AuthType.Fediverse,
|
||||
req.Instance
|
||||
);
|
||||
|
||||
var app = await fediverseAuthService.GetApplicationAsync(req.Instance);
|
||||
var remoteUser = await fediverseAuthService.GetRemoteFediverseUserAsync(app, req.Code);
|
||||
try
|
||||
{
|
||||
var authMethod = await authService.AddAuthMethodAsync(
|
||||
CurrentUser.Id,
|
||||
AuthType.Fediverse,
|
||||
remoteUser.Id,
|
||||
remoteUser.Username,
|
||||
app
|
||||
);
|
||||
_logger.Debug(
|
||||
"Added new Fediverse auth method {AuthMethodId} to user {UserId}",
|
||||
authMethod.Id,
|
||||
CurrentUser.Id
|
||||
);
|
||||
|
||||
return Ok(
|
||||
new AuthController.AddOauthAccountResponse(
|
||||
authMethod.Id,
|
||||
AuthType.Fediverse,
|
||||
authMethod.RemoteId,
|
||||
$"{authMethod.RemoteUsername}@{app.Domain}"
|
||||
)
|
||||
);
|
||||
}
|
||||
catch (UniqueConstraintException)
|
||||
{
|
||||
throw new ApiError(
|
||||
"That account is already linked.",
|
||||
HttpStatusCode.BadRequest,
|
||||
ErrorCode.AccountAlreadyLinked
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
public record CallbackRequest(string Instance, string Code, string State);
|
||||
|
||||
private record FediverseTicketData(
|
||||
Snowflake ApplicationId,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue