Skip to content

Commit

Permalink
Allow tokens to delete themselves (#165)
Browse files Browse the repository at this point in the history
  • Loading branch information
LucHeart authored Mar 9, 2025
1 parent 486f061 commit e083e12
Show file tree
Hide file tree
Showing 4 changed files with 83 additions and 30 deletions.
30 changes: 0 additions & 30 deletions API/Controller/Tokens/TokenController.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,8 @@
using System.ComponentModel.DataAnnotations;
using System.Net;
using System.Net.Mime;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OpenShock.API.Models.Response;
using OpenShock.API.Utils;
using OpenShock.Common;
using OpenShock.Common.Authentication;
using OpenShock.Common.Authentication.Attributes;
using OpenShock.Common.Constants;
using OpenShock.Common.Errors;
using OpenShock.Common.Models;
Expand Down Expand Up @@ -72,30 +66,6 @@ public async Task<IActionResult> GetTokenById([FromRoute] Guid tokenId)
return Ok(apiToken);
}

/// <summary>
/// Revoke a token from the current user
/// </summary>
/// <param name="tokenId"></param>
/// <response code="200">Successfully deleted token</response>
/// <response code="404">The token does not exist or you do not have access to it.</response>
[HttpDelete("{tokenId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType<OpenShockProblem>(StatusCodes.Status404NotFound, MediaTypeNames.Application.ProblemJson)] // ApiTokenNotFound
public async Task<IActionResult> DeleteToken([FromRoute] Guid tokenId)
{
var apiToken = await _db.ApiTokens
.Where(x => x.Id == tokenId)
.WhereIsUserOrPrivileged(x => x.User, CurrentUser)
.ExecuteDeleteAsync();

if (apiToken <= 0)
{
return Problem(ApiTokenError.ApiTokenNotFound);
}

return Ok();
}

/// <summary>
/// Create a new token
/// </summary>
Expand Down
70 changes: 70 additions & 0 deletions API/Controller/Tokens/TokenDeleteController.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
using System.Net.Mime;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using OpenShock.Common;
using OpenShock.Common.Authentication;
using OpenShock.Common.Authentication.ControllerBase;
using OpenShock.Common.Errors;
using OpenShock.Common.OpenShockDb;
using OpenShock.Common.Problems;
using OpenShock.Common.Utils;

namespace OpenShock.API.Controller.Tokens;

[ApiController]
[Route("/{version:apiVersion}/tokens")]
[Authorize(AuthenticationSchemes = OpenShockAuthSchemas.UserSessionApiTokenCombo)]
public sealed class TokenDeleteController : AuthenticatedSessionControllerBase
{
private readonly OpenShockContext _db;
private readonly ILogger<TokensController> _logger;

public TokenDeleteController(OpenShockContext db, ILogger<TokensController> logger)
{
_db = db;
_logger = logger;
}

/// <summary>
/// Revoke a token
/// </summary>
/// <param name="tokenId"></param>
/// <response code="200">Successfully deleted token</response>
/// <response code="404">The token does not exist or you do not have access to it.</response>
[HttpDelete("{tokenId}")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesResponseType<OpenShockProblem>(StatusCodes.Status404NotFound,
MediaTypeNames.Application.ProblemJson)] // ApiTokenNotFound
public async Task<IActionResult> DeleteToken([FromRoute] Guid tokenId)
{
var auth = HttpContext.GetAuthenticationMethod();

var query = _db.ApiTokens.Where(x => x.Id == tokenId);


switch (auth)
{
case OpenShockAuthSchemas.UserSessionCookie:
query = query.WhereIsUserOrPrivileged(x => x.User, CurrentUser);
break;
case OpenShockAuthSchemas.ApiToken:
{
var requestTokenId = Guid.Parse(HttpContext.User.Claims.First(x => x.Type == OpenShockAuthClaims.ApiTokenId).Value);
if (requestTokenId != tokenId) return Problem(ApiTokenError.ApiTokenCanOnlyDelete);
break;
}
default:
throw new Exception("Unknown auth method");
}

var apiToken = await query.ExecuteDeleteAsync();

if (apiToken <= 0)
{
return Problem(ApiTokenError.ApiTokenNotFound);
}

return Ok();
}
}
1 change: 1 addition & 0 deletions Common/Errors/ApiTokenError.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ namespace OpenShock.Common.Errors;
public static class ApiTokenError
{
public static OpenShockProblem ApiTokenNotFound => new("ApiToken.NotFound", "Api token not found", HttpStatusCode.NotFound);
public static OpenShockProblem ApiTokenCanOnlyDelete => new("ApiToken.CanOnlyDelete own", "You can only delete your own api token in token authentication scope", HttpStatusCode.Forbidden);
}
12 changes: 12 additions & 0 deletions Common/Utils/AuthUtils.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using OpenShock.Common.Constants;
using System.Diagnostics.CodeAnalysis;
using System.Security.Claims;

namespace OpenShock.Common.Utils;

Expand Down Expand Up @@ -89,4 +90,15 @@ public static bool TryGetDeviceTokenFromHeader(this HttpContext context, [NotNul
return false;
}

public static string GetAuthenticationMethod(this HttpContext context)
{
var authMethodClaim = context.User.Claims.FirstOrDefault(x => x.Type == ClaimTypes.AuthenticationMethod);
if (authMethodClaim == null)
{
throw new Exception("No authentication method claim found, this should not happen and is a bug!");
}

return authMethodClaim.Value;
}

}

0 comments on commit e083e12

Please sign in to comment.