Added all necessary api endpoints for OpenID

This commit is contained in:
2024-12-22 10:55:24 +01:00
parent ba7584c771
commit 9b38a10797
15 changed files with 233 additions and 17 deletions

View File

@@ -7,8 +7,8 @@ using Microsoft.AspNetCore.Mvc;
namespace HopFrame.Api.Controller;
[ApiController]
[Route("api/v1/authentication")]
public class SecurityController(IAuthLogic auth) : ControllerBase {
[Route("api/v1/auth")]
public class AuthController(IAuthLogic auth) : ControllerBase {
[HttpPut("login")]
public async Task<ActionResult<SingleValueResult<string>>> Login([FromBody] UserLogin login) {

View File

@@ -0,0 +1,16 @@
using System.Reflection;
using Microsoft.AspNetCore.Mvc.Controllers;
namespace HopFrame.Api.Controller;
public class HopFrameFeatureProvider(params Type[] controllerTypes) : ControllerFeatureProvider {
protected override bool IsController(TypeInfo typeInfo) {
if (typeInfo.Namespace != typeof(HopFrameFeatureProvider).Namespace)
return base.IsController(typeInfo);
if (controllerTypes.All(c => c.Name != typeInfo.Name))
return false;
return base.IsController(typeInfo);
}
}

View File

@@ -0,0 +1,79 @@
using HopFrame.Api.Models;
using HopFrame.Security.Authentication.OpenID;
using HopFrame.Security.Authentication.OpenID.Options;
using HopFrame.Security.Claims;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Options;
namespace HopFrame.Api.Controller;
[ApiController, Route("api/v1/openid")]
public class OpenIdController(IOpenIdAccessor accessor, IOptions<OpenIdOptions> options) : ControllerBase {
[HttpGet("redirect")]
public async Task<IActionResult> RedirectToProvider([FromQuery] string redirectAfter, [FromQuery] int performRedirect = 1) {
var uri = await accessor.ConstructAuthUri(redirectAfter);
if (performRedirect == 1) {
return Redirect(uri);
}
return Ok(new SingleValueResult<string>(uri));
}
[HttpGet("callback")]
public async Task<IActionResult> Callback([FromQuery] string code, [FromQuery] string state) {
if (string.IsNullOrEmpty(code)) {
return BadRequest("Authorization code is missing");
}
var token = await accessor.RequestToken(code);
Response.Cookies.Append(ITokenContext.AccessTokenType, token.AccessToken, new CookieOptions {
MaxAge = TimeSpan.FromSeconds(token.ExpiresIn),
HttpOnly = false,
Secure = true
});
Response.Cookies.Append(ITokenContext.RefreshTokenType, token.RefreshToken, new CookieOptions {
MaxAge = options.Value.RefreshToken.ConstructTimeSpan,
HttpOnly = false,
Secure = true
});
if (string.IsNullOrEmpty(state)) {
return Ok(new SingleValueResult<string>(token.AccessToken));
}
return Redirect(state.Replace("{token}", token.AccessToken));
}
[HttpGet("refresh")]
public async Task<IActionResult> Refresh() {
var refreshToken = Request.Cookies[ITokenContext.RefreshTokenType];
if (string.IsNullOrEmpty(refreshToken))
return BadRequest("Refresh token not provided");
var token = await accessor.RefreshAccessToken(refreshToken);
if (token is null)
return NotFound("Refresh token not valid");
Response.Cookies.Append(ITokenContext.AccessTokenType, token.AccessToken, new CookieOptions {
MaxAge = TimeSpan.FromSeconds(token.ExpiresIn),
HttpOnly = false,
Secure = true
});
return Ok(new SingleValueResult<string>(token.AccessToken));
}
[HttpDelete("logout")]
public IActionResult Logout() {
Response.Cookies.Delete(ITokenContext.RefreshTokenType);
Response.Cookies.Delete(ITokenContext.AccessTokenType);
return Ok();
}
}

View File

@@ -83,4 +83,4 @@ public static class MvcExtensions {
return true;
}
}
}
}

View File

@@ -19,8 +19,13 @@ public static class ServiceCollectionExtensions {
/// <param name="configuration">The configuration used to configure HopFrame authentication</param>
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
public static void AddHopFrame<TDbContext>(this IServiceCollection services, ConfigurationManager configuration) where TDbContext : HopDbContextBase {
services.AddMvcCore().UseSpecificControllers(typeof(SecurityController));
var controllers = new List<Type> { typeof(AuthController) };
if (configuration.GetValue<bool>("HopFrame:Authentication:OpenID:Enabled"))
controllers.Add(typeof(OpenIdController));
AddHopFrameNoEndpoints<TDbContext>(services, configuration);
services.AddMvcCore().UseSpecificControllers(controllers.ToArray());
}
/// <summary>
@@ -30,6 +35,11 @@ public static class ServiceCollectionExtensions {
/// <param name="configuration">The configuration used to configure HopFrame authentication</param>
/// <typeparam name="TDbContext">The data source for all HopFrame entities</typeparam>
public static void AddHopFrameNoEndpoints<TDbContext>(this IServiceCollection services, ConfigurationManager configuration) where TDbContext : HopDbContextBase {
services.AddMvcCore().ConfigureApplicationPartManager(manager => {
var endpoints = manager.ApplicationParts.SingleOrDefault(p => p.Name == typeof(ServiceCollectionExtensions).Namespace!.Replace(".Extensions", ""));
manager.ApplicationParts.Remove(endpoints);
});
services.AddHopFrameRepositories<TDbContext>();
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IAuthLogic, AuthLogic>();

View File

@@ -101,9 +101,7 @@ internal class AuthLogic(IUserRepository users, ITokenRepository tokens, ITokenC
var refreshToken = accessor.HttpContext?.Request.Cookies[ITokenContext.RefreshTokenType];
if (string.IsNullOrEmpty(accessToken) || string.IsNullOrEmpty(refreshToken))
return LogicResult.Conflict("access or refresh token not provided");
await tokens.DeleteUserTokens(tokenContext.User);
await tokens.DeleteUserTokens(tokenContext.User);
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.RefreshTokenType);
accessor.HttpContext?.Response.Cookies.Delete(ITokenContext.AccessTokenType);