diff --git a/src/Api/Auth/Controllers/AccountsController.cs b/src/Api/Auth/Controllers/AccountsController.cs
index 9f90d0c4b3..59f9516974 100644
--- a/src/Api/Auth/Controllers/AccountsController.cs
+++ b/src/Api/Auth/Controllers/AccountsController.cs
@@ -209,11 +209,12 @@ public class AccountsController : Controller
throw new UnauthorizedAccessException();
}
- // TODO: should this be feature flagged
Guid? opaqueSessionId = null;
- if (model.OpaqueSessionId != null)
- {
- opaqueSessionId = Guid.Parse(model.OpaqueSessionId);
+ if(_featureService.IsEnabled(FeatureFlagKeys.OpaqueKeyExchange)){
+ if (model.OpaqueSessionId != null)
+ {
+ opaqueSessionId = Guid.Parse(model.OpaqueSessionId);
+ }
}
var result = await _userService.ChangePasswordAsync(user, model.MasterPasswordHash,
diff --git a/src/Core/Auth/Services/IOpaqueKeyExchangeService.cs b/src/Core/Auth/Services/IOpaqueKeyExchangeService.cs
index b6652847bb..b6e692a917 100644
--- a/src/Core/Auth/Services/IOpaqueKeyExchangeService.cs
+++ b/src/Core/Auth/Services/IOpaqueKeyExchangeService.cs
@@ -28,7 +28,7 @@ public interface IOpaqueKeyExchangeService
/// void
public Task FinishRegistration(Guid sessionId, byte[] registrationUpload, User user, RotateableOpaqueKeyset keyset);
///
- /// Returns server crypto material for the client to consume and reply with a login request to the identity/token endpoint.
+ /// Returns server crypto material for the client to consume and reply to the finish-login endpoint for authentication.
/// To protect against account enumeration we will always return a deterministic response based on the user's email.
///
/// client crypto material
diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs
index 9b32eef1c3..9c8a110be1 100644
--- a/src/Identity/Controllers/AccountsController.cs
+++ b/src/Identity/Controllers/AccountsController.cs
@@ -1,7 +1,6 @@
using System.Diagnostics;
using System.Text;
using System.Text.Json;
-using Bit.Api.Auth.Models.Response.Opaque;
using Bit.Core;
using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Models.Api.Request.Accounts;
@@ -291,15 +290,6 @@ public class AccountsController : Controller
};
}
- // TODO: (1) This should be on own controller (2) reconcile this w/ start login on existing controller
- [HttpPost("opaque-ke/start-login")]
- [RequireFeature(FeatureFlagKeys.OpaqueKeyExchange)]
- public async Task GetOpaqueKeyExchangeStartLoginMaterial([FromBody] OpaqueLoginStartRequest request)
- {
- var result = await _opaqueKeyExchangeService.StartLogin(Convert.FromBase64String(request.CredentialRequest), request.Email);
- return new OpaqueLoginStartResponse(result.Item1, Convert.ToBase64String(result.Item2));
- }
-
private UserKdfInformation GetDefaultKdf(string email)
{
if (_defaultKdfHmacKey == null)
diff --git a/src/Api/Auth/Controllers/OpaqueKeyExchangeController.cs b/src/Identity/Controllers/OpaqueKeyExchangeController.cs
similarity index 67%
rename from src/Api/Auth/Controllers/OpaqueKeyExchangeController.cs
rename to src/Identity/Controllers/OpaqueKeyExchangeController.cs
index 66eb9b17cc..90a005d754 100644
--- a/src/Api/Auth/Controllers/OpaqueKeyExchangeController.cs
+++ b/src/Identity/Controllers/OpaqueKeyExchangeController.cs
@@ -1,3 +1,5 @@
+using Bit.Api.Auth.Models.Request.Opaque;
+using Bit.Api.Auth.Models.Response.Opaque;
using Bit.Core;
using Bit.Core.Auth.Models.Api.Request.Opaque;
using Bit.Core.Auth.Models.Api.Response.Opaque;
@@ -7,11 +9,10 @@ using Bit.Core.Utilities;
using Microsoft.AspNetCore.Authorization;
using Microsoft.AspNetCore.Mvc;
-namespace Bit.Api.Auth.Controllers;
+namespace Bit.Identity.Controllers;
-// TODO: move to identity
[RequireFeature(FeatureFlagKeys.OpaqueKeyExchange)]
-[Route("opaque")]
+[Route("opaque-ke")]
[Authorize("Application")]
public class OpaqueKeyExchangeController(
IOpaqueKeyExchangeService opaqueKeyExchangeService,
@@ -37,6 +38,8 @@ public class OpaqueKeyExchangeController(
{
var user = await _userService.GetUserByPrincipalAsync(User)
?? throw new UnauthorizedAccessException();
+ // todo check response
+
await _opaqueKeyExchangeService.FinishRegistration(
request.SessionId, Convert.FromBase64String(request.RegistrationUpload), user, request.KeySet);
}
@@ -46,6 +49,24 @@ public class OpaqueKeyExchangeController(
{
var user = await _userService.GetUserByPrincipalAsync(User)
?? throw new UnauthorizedAccessException();
+ // todo check response
await _opaqueKeyExchangeService.WriteCacheCredentialToDatabase(request.SessionId, user);
}
+
+ [AllowAnonymous]
+ [HttpPost("start-login")]
+ public async Task StartOpaqueLoginAsync([FromBody] OpaqueLoginStartRequest request)
+ {
+ var result = await _opaqueKeyExchangeService.StartLogin(Convert.FromBase64String(request.CredentialRequest), request.Email);
+ return new OpaqueLoginStartResponse(result.Item1, Convert.ToBase64String(result.Item2));
+ }
+
+ [AllowAnonymous]
+ [HttpPost("finish-login")]
+ public async Task FinishLoginAsync([FromBody] OpaqueLoginFinishRequest request)
+ {
+ var result = await _opaqueKeyExchangeService.FinishLogin(
+ request.SessionId, Convert.FromBase64String(request.CredentialFinalization));
+ return result;
+ }
}