mirror of
https://github.com/bitwarden/server.git
synced 2025-04-06 21:48:12 -05:00
[PM-6303] Add duo state to 2fa (#3806)
* add duo state to 2fa * Id to UserId
This commit is contained in:
parent
744d21ec5e
commit
d99d3b8380
@ -1,7 +1,9 @@
|
|||||||
using Bit.Core.Auth.Models;
|
using Bit.Core.Auth.Models;
|
||||||
|
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
using Bit.Core.Context;
|
using Bit.Core.Context;
|
||||||
using Bit.Core.Entities;
|
using Bit.Core.Entities;
|
||||||
using Bit.Core.Settings;
|
using Bit.Core.Settings;
|
||||||
|
using Bit.Core.Tokens;
|
||||||
using Duo = DuoUniversal;
|
using Duo = DuoUniversal;
|
||||||
|
|
||||||
namespace Bit.Core.Auth.Identity;
|
namespace Bit.Core.Auth.Identity;
|
||||||
@ -22,6 +24,7 @@ public class TemporaryDuoWebV4SDKService : ITemporaryDuoWebV4SDKService
|
|||||||
{
|
{
|
||||||
private readonly ICurrentContext _currentContext;
|
private readonly ICurrentContext _currentContext;
|
||||||
private readonly GlobalSettings _globalSettings;
|
private readonly GlobalSettings _globalSettings;
|
||||||
|
private readonly IDataProtectorTokenFactory<DuoUserStateTokenable> _tokenDataFactory;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Constructor for the DuoUniversalPromptService. Used to supplement v2 implementation of Duo with v4 SDK
|
/// Constructor for the DuoUniversalPromptService. Used to supplement v2 implementation of Duo with v4 SDK
|
||||||
@ -30,10 +33,12 @@ public class TemporaryDuoWebV4SDKService : ITemporaryDuoWebV4SDKService
|
|||||||
/// <param name="globalSettings">used to fetch vault URL for Redirect URL</param>
|
/// <param name="globalSettings">used to fetch vault URL for Redirect URL</param>
|
||||||
public TemporaryDuoWebV4SDKService(
|
public TemporaryDuoWebV4SDKService(
|
||||||
ICurrentContext currentContext,
|
ICurrentContext currentContext,
|
||||||
GlobalSettings globalSettings)
|
GlobalSettings globalSettings,
|
||||||
|
IDataProtectorTokenFactory<DuoUserStateTokenable> tokenDataFactory)
|
||||||
{
|
{
|
||||||
_currentContext = currentContext;
|
_currentContext = currentContext;
|
||||||
_globalSettings = globalSettings;
|
_globalSettings = globalSettings;
|
||||||
|
_tokenDataFactory = tokenDataFactory;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@ -56,7 +61,7 @@ public class TemporaryDuoWebV4SDKService : ITemporaryDuoWebV4SDKService
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var state = Duo.Client.GenerateState(); //? Not sure on this yet. But required for GenerateAuthUrl
|
var state = _tokenDataFactory.Protect(new DuoUserStateTokenable(user));
|
||||||
var authUrl = duoClient.GenerateAuthUri(user.Email, state);
|
var authUrl = duoClient.GenerateAuthUri(user.Email, state);
|
||||||
|
|
||||||
return authUrl;
|
return authUrl;
|
||||||
@ -82,8 +87,20 @@ public class TemporaryDuoWebV4SDKService : ITemporaryDuoWebV4SDKService
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var parts = token.Split("|");
|
||||||
|
var authCode = parts[0];
|
||||||
|
var state = parts[1];
|
||||||
|
|
||||||
|
_tokenDataFactory.TryUnprotect(state, out var tokenable);
|
||||||
|
if (!tokenable.Valid || !tokenable.TokenIsValid(user))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// duoClient compares the email from the received IdToken with user.Email to verify a bad actor hasn't used
|
||||||
|
// their authCode with a victims credentials
|
||||||
|
var res = await duoClient.ExchangeAuthorizationCodeFor2faResult(authCode, user.Email);
|
||||||
// If the result of the exchange doesn't throw an exception and it's not null, then it's valid
|
// If the result of the exchange doesn't throw an exception and it's not null, then it's valid
|
||||||
var res = await duoClient.ExchangeAuthorizationCodeFor2faResult(token, user.Email);
|
|
||||||
return res.AuthResult.Result == "allow";
|
return res.AuthResult.Result == "allow";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -0,0 +1,37 @@
|
|||||||
|
using Bit.Core.Entities;
|
||||||
|
using Bit.Core.Tokens;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace Bit.Core.Auth.Models.Business.Tokenables;
|
||||||
|
|
||||||
|
public class DuoUserStateTokenable : Tokenable
|
||||||
|
{
|
||||||
|
public const string ClearTextPrefix = "BwDuoUserId";
|
||||||
|
public const string DataProtectorPurpose = "DuoUserIdTokenDataProtector";
|
||||||
|
public const string TokenIdentifier = "DuoUserIdToken";
|
||||||
|
public string Identifier { get; set; } = TokenIdentifier;
|
||||||
|
public Guid UserId { get; set; }
|
||||||
|
|
||||||
|
public override bool Valid => Identifier == TokenIdentifier &&
|
||||||
|
UserId != default;
|
||||||
|
|
||||||
|
[JsonConstructor]
|
||||||
|
public DuoUserStateTokenable()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public DuoUserStateTokenable(User user)
|
||||||
|
{
|
||||||
|
UserId = user?.Id ?? default;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool TokenIsValid(User user)
|
||||||
|
{
|
||||||
|
if (UserId == default || user == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return UserId == user.Id;
|
||||||
|
}
|
||||||
|
}
|
@ -197,6 +197,12 @@ public static class ServiceCollectionExtensions
|
|||||||
OrgUserInviteTokenable.DataProtectorPurpose,
|
OrgUserInviteTokenable.DataProtectorPurpose,
|
||||||
serviceProvider.GetDataProtectionProvider(),
|
serviceProvider.GetDataProtectionProvider(),
|
||||||
serviceProvider.GetRequiredService<ILogger<DataProtectorTokenFactory<OrgUserInviteTokenable>>>()));
|
serviceProvider.GetRequiredService<ILogger<DataProtectorTokenFactory<OrgUserInviteTokenable>>>()));
|
||||||
|
services.AddSingleton<IDataProtectorTokenFactory<DuoUserStateTokenable>>(serviceProvider =>
|
||||||
|
new DataProtectorTokenFactory<DuoUserStateTokenable>(
|
||||||
|
DuoUserStateTokenable.ClearTextPrefix,
|
||||||
|
DuoUserStateTokenable.DataProtectorPurpose,
|
||||||
|
serviceProvider.GetDataProtectionProvider(),
|
||||||
|
serviceProvider.GetRequiredService<ILogger<DataProtectorTokenFactory<DuoUserStateTokenable>>>()));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
|
public static void AddDefaultServices(this IServiceCollection services, GlobalSettings globalSettings)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user