mirror of
https://github.com/bitwarden/server.git
synced 2025-07-11 21:03:47 -05:00
Implement User-based API Keys (#981)
* added column ApiKey to dbo.User * added dbo.User.ApiKey to User_Update * added dbo.User.ApiKey to User_Create * wrote migration script for implementing dbo.User.ApiKey * Added ApiKey prop to the User table model * Created AccountsController method for getting a user's API Key * Created AccountsController method for rotating a user API key * Added support to ApiClient for passed-through ClientSecrets when the request comes from the cli * Added a new conditional to ClientStore to account for user API keys * Wrote unit tests for new user API Key methods * Added a refresh of dbo.UserView to new migration script for ApiKey * Let client_credentials grants into the custom token logic * Cleanup for ApiKey auth in the CLI feature * Created user API key on registration * Removed uneeded code for user API keys * Changed a .Contains() to a .StartsWith() in ClientStore * Changed index that an array is searched on * Added more claims to the user apikey clients * Moved some claim finding logic to a helper method
This commit is contained in:
@ -1,4 +1,5 @@
|
||||
using IdentityServer4.Stores;
|
||||
using System.Linq;
|
||||
using IdentityServer4.Stores;
|
||||
using System.Threading.Tasks;
|
||||
using IdentityServer4.Models;
|
||||
using System.Collections.Generic;
|
||||
@ -6,6 +7,9 @@ using Bit.Core.Repositories;
|
||||
using System;
|
||||
using IdentityModel;
|
||||
using Bit.Core.Utilities;
|
||||
using System.Security.Claims;
|
||||
using Bit.Core.Services;
|
||||
using System.Collections.ObjectModel;
|
||||
|
||||
namespace Bit.Core.IdentityServer
|
||||
{
|
||||
@ -13,19 +17,31 @@ namespace Bit.Core.IdentityServer
|
||||
{
|
||||
private readonly IInstallationRepository _installationRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository;
|
||||
private readonly IUserRepository _userRepository;
|
||||
private readonly GlobalSettings _globalSettings;
|
||||
private readonly StaticClientStore _staticClientStore;
|
||||
private readonly ILicensingService _licensingService;
|
||||
private readonly CurrentContext _currentContext;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository;
|
||||
|
||||
public ClientStore(
|
||||
IInstallationRepository installationRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IUserRepository userRepository,
|
||||
GlobalSettings globalSettings,
|
||||
StaticClientStore staticClientStore)
|
||||
StaticClientStore staticClientStore,
|
||||
ILicensingService licensingService,
|
||||
CurrentContext currentContext,
|
||||
IOrganizationUserRepository organizationUserRepository)
|
||||
{
|
||||
_installationRepository = installationRepository;
|
||||
_organizationRepository = organizationRepository;
|
||||
_userRepository = userRepository;
|
||||
_globalSettings = globalSettings;
|
||||
_staticClientStore = staticClientStore;
|
||||
_licensingService = licensingService;
|
||||
_currentContext = currentContext;
|
||||
_organizationUserRepository = organizationUserRepository;
|
||||
}
|
||||
|
||||
public async Task<Client> FindClientByIdAsync(string clientId)
|
||||
@ -106,6 +122,45 @@ namespace Bit.Core.IdentityServer
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (clientId.StartsWith("user."))
|
||||
{
|
||||
var idParts = clientId.Split('.');
|
||||
if (idParts.Length > 1 && Guid.TryParse(idParts[1], out var id))
|
||||
{
|
||||
var user = await _userRepository.GetByIdAsync(id);
|
||||
if (user != null)
|
||||
{
|
||||
var claims = new Collection<ClientClaim>()
|
||||
{
|
||||
new ClientClaim(JwtClaimTypes.Subject, user.Id.ToString()),
|
||||
new ClientClaim(JwtClaimTypes.AuthenticationMethod, "Application", "external")
|
||||
};
|
||||
var orgs = await _currentContext.OrganizationMembershipAsync(_organizationUserRepository, user.Id);
|
||||
var isPremium = await _licensingService.ValidateUserPremiumAsync(user);
|
||||
foreach (var claim in CoreHelpers.BuildIdentityClaims(user, orgs, isPremium))
|
||||
{
|
||||
var upperValue = claim.Value.ToUpperInvariant();
|
||||
var isBool = upperValue == "TRUE" || upperValue == "FALSE";
|
||||
claims.Add(isBool ?
|
||||
new ClientClaim(claim.Key, claim.Value, ClaimValueTypes.Boolean) :
|
||||
new ClientClaim(claim.Key, claim.Value)
|
||||
);
|
||||
}
|
||||
|
||||
return new Client
|
||||
{
|
||||
ClientId = clientId,
|
||||
RequireClientSecret = true,
|
||||
ClientSecrets = { new Secret(user.ApiKey.Sha256()) },
|
||||
AllowedScopes = new string[] { "api" },
|
||||
AllowedGrantTypes = GrantTypes.ClientCredentials,
|
||||
AccessTokenLifetime = 3600 * 1,
|
||||
ClientClaimsPrefix = null,
|
||||
Claims = claims
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return _staticClientStore.ApiClients.ContainsKey(clientId) ?
|
||||
_staticClientStore.ApiClients[clientId] : null;
|
||||
|
Reference in New Issue
Block a user