1
0
mirror of https://github.com/bitwarden/server.git synced 2025-07-01 08:02:49 -05:00

fixes for configuring u2f device

This commit is contained in:
Kyle Spearrin
2017-06-22 17:03:35 -04:00
parent 3ae96bd510
commit f28ae5ccd9
19 changed files with 89 additions and 33 deletions

View File

@ -9,6 +9,8 @@ using Microsoft.AspNetCore.Identity;
using Bit.Core.Models.Table; using Bit.Core.Models.Table;
using Bit.Core.Enums; using Bit.Core.Enums;
using System.Linq; using System.Linq;
using Bit.Core;
using Newtonsoft.Json;
namespace Bit.Api.Controllers namespace Bit.Api.Controllers
{ {
@ -17,13 +19,16 @@ namespace Bit.Api.Controllers
public class TwoFactorController : Controller public class TwoFactorController : Controller
{ {
private readonly IUserService _userService; private readonly IUserService _userService;
private readonly GlobalSettings _globalSettings;
private readonly UserManager<User> _userManager; private readonly UserManager<User> _userManager;
public TwoFactorController( public TwoFactorController(
IUserService userService, IUserService userService,
GlobalSettings globalSettings,
UserManager<User> userManager) UserManager<User> userManager)
{ {
_userService = userService; _userService = userService;
_globalSettings = globalSettings;
_userManager = userManager; _userManager = userManager;
} }
@ -117,7 +122,7 @@ namespace Bit.Api.Controllers
{ {
var user = await CheckPasswordAsync(model.MasterPasswordHash); var user = await CheckPasswordAsync(model.MasterPasswordHash);
var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f); var provider = user.GetTwoFactorProvider(TwoFactorProviderType.U2f);
if(!provider.Enabled || (provider?.MetaData != null && provider.MetaData.Count > 0)) if(provider == null || !provider.Enabled || (provider.MetaData?.Count ?? 0) > 0)
{ {
var reg = await _userService.StartU2fRegistrationAsync(user); var reg = await _userService.StartU2fRegistrationAsync(user);
var response = new TwoFactorU2fResponseModel(user, provider, reg); var response = new TwoFactorU2fResponseModel(user, provider, reg);
@ -130,6 +135,34 @@ namespace Bit.Api.Controllers
} }
} }
[HttpGet("~/app-id.json")]
//[Produces("application/fido.trusted-apps+json")]
[AllowAnonymous]
public string GetU2fAppId()
{
return JsonConvert.SerializeObject(new
{
trustedFacets = new object[]
{
new
{
version = new
{
major = 1,
minor = 1
},
ids = new string[]
{
_globalSettings.U2f.AppId,
//"ios:bundle-id:com.8bit.bitwarden",
//"android:apk-key-hash:585215fd5153209a7e246f53286035838a0be227",
//"chrome-extension://nngceckbapebfimnlniiiahkandclblb"
}
}
}
});
}
[HttpPut("u2f")] [HttpPut("u2f")]
[HttpPost("u2f")] [HttpPost("u2f")]
public async Task<TwoFactorU2fResponseModel> PutU2f([FromBody]TwoFactorU2fRequestModel model) public async Task<TwoFactorU2fResponseModel> PutU2f([FromBody]TwoFactorU2fRequestModel model)

View File

@ -4,7 +4,7 @@
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:4000", "applicationUrl": "http://localhost:4000",
"sslPort": 0 "sslPort": 44377
} }
}, },
"profiles": { "profiles": {

View File

@ -153,9 +153,9 @@ namespace Bit.Api
// Add IdentityServer to the request pipeline. // Add IdentityServer to the request pipeline.
app.UseIdentityServer(); app.UseIdentityServer();
app.UseIdentityServerAuthentication( app.UseIdentityServerAuthentication(
GetIdentityOptions(env, IdentityServerAuthority(env, "identity", "33656"), "3")); GetIdentityOptions(env, IdentityServerAuthority(env, "identity", "44392"), "3"));
app.UseIdentityServerAuthentication( app.UseIdentityServerAuthentication(
GetIdentityOptions(env, IdentityServerAuthority(env, "api", "4000"), "2")); GetIdentityOptions(env, IdentityServerAuthority(env, "api", "44377"), "2"));
// Add current context // Add current context
app.UseMiddleware<CurrentContextMiddleware>(); app.UseMiddleware<CurrentContextMiddleware>();
@ -195,7 +195,7 @@ namespace Bit.Api
} }
else else
{ {
return $"http://localhost:{port}"; return $"https://localhost:{port}";
//return $"http://192.168.1.6:{port}"; // Desktop external //return $"http://192.168.1.6:{port}"; // Desktop external
} }
} }

View File

@ -1,5 +1,9 @@
{ {
"globalSettings": { "globalSettings": {
"baseVaultUri": "https://preview-vault.bitwarden.com/#" "baseVaultUri": "https://preview-vault.bitwarden.com/#",
"baseApiUri": "https://preview-api.bitwarden.com/",
"u2f": {
"appId": "https://preview-vault.bitwarden.com"
}
} }
} }

View File

@ -1,5 +1,9 @@
{ {
"globalSettings": { "globalSettings": {
"baseVaultUri": "https://vault.bitwarden.com/#" "baseVaultUri": "https://vault.bitwarden.com/#",
"baseApiUri": "https://api.bitwarden.com/",
"u2f": {
"appId": "https://vault.bitwarden.com"
}
} }
} }

View File

@ -1,5 +1,9 @@
{ {
"globalSettings": { "globalSettings": {
"baseVaultUri": "https://vault.bitwarden.com/#" "baseVaultUri": "https://vault.bitwarden.com/#",
"baseApiUri": "https://api.bitwarden.com/",
"u2f": {
"appId": "https://vault.bitwarden.com"
}
} }
} }

View File

@ -2,6 +2,7 @@
"globalSettings": { "globalSettings": {
"siteName": "bitwarden", "siteName": "bitwarden",
"baseVaultUri": "http://localhost:4001/#", "baseVaultUri": "http://localhost:4001/#",
"baseApiUri": "http://localhost:4000/",
"jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)", "jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)",
"stripeApiKey": "SECRET", "stripeApiKey": "SECRET",
"sqlServer": { "sqlServer": {
@ -47,7 +48,7 @@
"aKey": "SECRET" "aKey": "SECRET"
}, },
"u2f": { "u2f": {
"appId": "https://bitwarden.com" "appId": "https://localhost:4001"
} }
}, },
"IpRateLimitOptions": { "IpRateLimitOptions": {

View File

@ -2,6 +2,7 @@
"globalSettings": { "globalSettings": {
"siteName": "bitwarden", "siteName": "bitwarden",
"baseVaultUri": "http://localhost:4001/#", "baseVaultUri": "http://localhost:4001/#",
"baseApiUri": "http://localhost:4000/",
"jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)", "jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)",
"stripeApiKey": "SECRET", "stripeApiKey": "SECRET",
"sqlServer": { "sqlServer": {

View File

@ -49,13 +49,14 @@
<PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.EnvironmentVariables" Version="1.1.2" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" /> <PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="1.1.2" />
<PackageReference Include="Newtonsoft.Json" Version="10.0.2" /> <PackageReference Include="Newtonsoft.Json" Version="10.0.2" />
<PackageReference Include="Portable.BouncyCastle" Version="1.8.1.2" />
<PackageReference Include="RazorLight" Version="1.1.0" /> <PackageReference Include="RazorLight" Version="1.1.0" />
<PackageReference Include="Sendgrid" Version="9.2.0" /> <PackageReference Include="Sendgrid" Version="9.2.0" />
<PackageReference Include="PushSharp" Version="4.0.10" /> <PackageReference Include="PushSharp" Version="4.0.10" />
<PackageReference Include="Serilog.Extensions.Logging" Version="1.4.0" /> <PackageReference Include="Serilog.Extensions.Logging" Version="1.4.0" />
<PackageReference Include="Serilog.Sinks.AzureDocumentDB" Version="3.6.1" /> <PackageReference Include="Serilog.Sinks.AzureDocumentDB" Version="3.6.1" />
<PackageReference Include="Stripe.net" Version="7.8.0" /> <PackageReference Include="Stripe.net" Version="7.8.0" />
<PackageReference Include="U2F.Core" Version="1.0.1" /> <PackageReference Include="u2flib" Version="1.0.5" />
<PackageReference Include="WindowsAzure.Storage" Version="8.1.1" /> <PackageReference Include="WindowsAzure.Storage" Version="8.1.1" />
<PackageReference Include="Otp.NET" Version="1.0.1" /> <PackageReference Include="Otp.NET" Version="1.0.1" />
<PackageReference Include="YubicoDotNetClient" Version="1.0.0" /> <PackageReference Include="YubicoDotNetClient" Version="1.0.0" />

View File

@ -4,6 +4,7 @@
{ {
public virtual string SiteName { get; set; } public virtual string SiteName { get; set; }
public virtual string BaseVaultUri { get; set; } public virtual string BaseVaultUri { get; set; }
public virtual string BaseApiUri { get; set; }
public virtual string JwtSigningKey { get; set; } public virtual string JwtSigningKey { get; set; }
public virtual string StripeApiKey { get; set; } public virtual string StripeApiKey { get; set; }
public virtual SqlServerSettings SqlServer { get; set; } = new SqlServerSettings(); public virtual SqlServerSettings SqlServer { get; set; } = new SqlServerSettings();

View File

@ -5,12 +5,13 @@ using Bit.Core.Enums;
using Bit.Core.Models; using Bit.Core.Models;
using Bit.Core.Services; using Bit.Core.Services;
using Bit.Core.Repositories; using Bit.Core.Repositories;
using U2F.Core.Models;
using U2fLib = U2F.Core.Crypto.U2F;
using Newtonsoft.Json; using Newtonsoft.Json;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using U2F.Core.Exceptions; using u2flib.Data;
using u2flib;
using u2flib.Data.Messages;
using u2flib.Exceptions;
namespace Bit.Core.Identity namespace Bit.Core.Identity
{ {
@ -65,7 +66,7 @@ namespace Bit.Core.Identity
{ {
var registration = new DeviceRegistration(key.KeyHandleBytes, key.PublicKeyBytes, var registration = new DeviceRegistration(key.KeyHandleBytes, key.PublicKeyBytes,
key.CertificateBytes, key.Counter); key.CertificateBytes, key.Counter);
var auth = U2fLib.StartAuthentication(_globalSettings.U2f.AppId, registration); var auth = U2F.StartAuthentication(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings), registration);
// Maybe move this to a bulk create when we support more than 1 key? // Maybe move this to a bulk create when we support more than 1 key?
await _u2fRepository.CreateAsync(new U2f await _u2fRepository.CreateAsync(new U2f
@ -116,7 +117,7 @@ namespace Bit.Core.Identity
return false; return false;
} }
var authenticateResponse = BaseModel.FromJson<AuthenticateResponse>(token); var authenticateResponse = DataObject.FromJson<AuthenticateResponse>(token);
var key = keys.FirstOrDefault(f => f.KeyHandle == authenticateResponse.KeyHandle); var key = keys.FirstOrDefault(f => f.KeyHandle == authenticateResponse.KeyHandle);
if(key == null) if(key == null)
@ -139,7 +140,7 @@ namespace Bit.Core.Identity
try try
{ {
var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle); var auth = new StartedAuthentication(challenge.Challenge, challenge.AppId, challenge.KeyHandle);
U2fLib.FinishAuthentication(auth, authenticateResponse, registration); U2F.FinishAuthentication(auth, authenticateResponse, registration);
} }
catch(U2fException) catch(U2fException)
{ {

View File

@ -19,7 +19,7 @@ namespace Bit.Core.Models.Api
{ {
Challenge = new ChallengeModel(user, registration); Challenge = new ChallengeModel(user, registration);
} }
Enabled = provider.Enabled; Enabled = provider?.Enabled ?? false;
} }
public TwoFactorU2fResponseModel(User user) public TwoFactorU2fResponseModel(User user)

View File

@ -1,6 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using U2F.Core.Utils; using u2flib.Util;
namespace Bit.Core.Models namespace Bit.Core.Models
{ {

View File

@ -10,11 +10,12 @@ using System.Linq;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Bit.Core.Enums; using Bit.Core.Enums;
using System.Security.Claims; using System.Security.Claims;
using U2fLib = U2F.Core.Crypto.U2F;
using U2F.Core.Models;
using Bit.Core.Models; using Bit.Core.Models;
using Bit.Core.Models.Business; using Bit.Core.Models.Business;
using U2F.Core.Utils; using u2flib.Data.Messages;
using u2flib.Util;
using u2flib;
using u2flib.Data;
namespace Bit.Core.Services namespace Bit.Core.Services
{ {
@ -219,7 +220,7 @@ namespace Bit.Core.Services
public async Task<U2fRegistration> StartU2fRegistrationAsync(User user) public async Task<U2fRegistration> StartU2fRegistrationAsync(User user)
{ {
await _u2fRepository.DeleteManyByUserIdAsync(user.Id); await _u2fRepository.DeleteManyByUserIdAsync(user.Id);
var reg = U2fLib.StartRegistration(_globalSettings.U2f.AppId); var reg = U2F.StartRegistration(Utilities.CoreHelpers.U2fAppIdUrl(_globalSettings));
await _u2fRepository.CreateAsync(new U2f await _u2fRepository.CreateAsync(new U2f
{ {
AppId = reg.AppId, AppId = reg.AppId,
@ -249,11 +250,11 @@ namespace Bit.Core.Services
return false; return false;
} }
var registerResponse = BaseModel.FromJson<RegisterResponse>(deviceResponse); var registerResponse = DataObject.FromJson<RegisterResponse>(deviceResponse);
var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null); var challenge = challenges.OrderBy(i => i.Id).Last(i => i.KeyHandle == null);
var statedReg = new StartedRegistration(challenge.Challenge, challenge.AppId); var statedReg = new StartedRegistration(challenge.Challenge, challenge.AppId);
var reg = U2fLib.FinishRegistration(statedReg, registerResponse); var reg = U2F.FinishRegistration(statedReg, registerResponse);
await _u2fRepository.DeleteManyByUserIdAsync(user.Id); await _u2fRepository.DeleteManyByUserIdAsync(user.Id);

View File

@ -119,5 +119,11 @@ namespace Bit.Core.Utilities
{ {
return _epoc.AddMilliseconds(milliseconds); return _epoc.AddMilliseconds(milliseconds);
} }
public static string U2fAppIdUrl(GlobalSettings globalSettings)
{
//return $"{globalSettings.BaseApiUri}app-id.json";
return globalSettings.U2f.AppId;
}
} }
} }

View File

@ -1,10 +1,10 @@
{ {
"iisSettings": { "iisSettings": {
"windowsAuthentication": false, "windowsAuthentication": false,
"anonymousAuthentication": true, "anonymousAuthentication": true,
"iisExpress": { "iisExpress": {
"applicationUrl": "http://localhost:33656/", "applicationUrl": "http://localhost:33656/",
"sslPort": 0 "sslPort": 44392
} }
}, },
"profiles": { "profiles": {

View File

@ -2,6 +2,7 @@
"globalSettings": { "globalSettings": {
"siteName": "bitwarden", "siteName": "bitwarden",
"baseVaultUri": "http://localhost:4001/#", "baseVaultUri": "http://localhost:4001/#",
"baseApiUri": "http://localhost:4000/",
"jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)", "jwtSigningKey": "THIS IS A SECRET. IT KEEPS YOUR TOKEN SAFE. :)",
"stripeApiKey": "SECRET", "stripeApiKey": "SECRET",
"sqlServer": { "sqlServer": {

View File

@ -12,7 +12,6 @@ BEGIN
INSERT INTO [dbo].[U2f] INSERT INTO [dbo].[U2f]
( (
[Id],
[UserId], [UserId],
[KeyHandle], [KeyHandle],
[Challenge], [Challenge],
@ -22,7 +21,6 @@ BEGIN
) )
VALUES VALUES
( (
@Id,
@UserId, @UserId,
@KeyHandle, @KeyHandle,
@Challenge, @Challenge,

View File

@ -1,10 +1,10 @@
CREATE TABLE [dbo].[U2f] ( CREATE TABLE [dbo].[U2f] (
[Id] INT IDENTITY (1, 1) NOT NULL, [Id] INT IDENTITY (1, 1) NOT NULL,
[UserId] UNIQUEIDENTIFIER NOT NULL, [UserId] UNIQUEIDENTIFIER NOT NULL,
[KeyHandle] VARCHAR (50) NOT NULL, [KeyHandle] VARCHAR (MAX) NULL,
[Challenge] VARCHAR (50) NOT NULL, [Challenge] VARCHAR (MAX) NOT NULL,
[AppId] VARCHAR (50) NOT NULL, [AppId] VARCHAR (50) NOT NULL,
[Version] VARCHAR (50) NOT NULL, [Version] VARCHAR (20) NOT NULL,
[CreationDate] DATETIME2 (7) NOT NULL, [CreationDate] DATETIME2 (7) NOT NULL,
CONSTRAINT [PK_U2f] PRIMARY KEY CLUSTERED ([Id] ASC), CONSTRAINT [PK_U2f] PRIMARY KEY CLUSTERED ([Id] ASC),
CONSTRAINT [FK_U2f_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id]) CONSTRAINT [FK_U2f_User] FOREIGN KEY ([UserId]) REFERENCES [dbo].[User] ([Id])