Completed first working version of Volo.Abp.Http.Client.IdentityModel

pull/714/head
Halil ibrahim Kalkan 7 years ago
parent 33ad12f89d
commit b153028743

@ -1,12 +0,0 @@
using Volo.Abp.Modularity;
namespace Volo.Abp.Http.Client.IdentityModel
{
[DependsOn(
typeof(AbpHttpClientModule)
)]
public class AbpHttpClientIdentityModelModule : AbpModule
{
}
}

@ -1,44 +0,0 @@
using System.Net.Http.Headers;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.Authentication;
namespace NewFolder.Abp.Http.Client.IdentityModel
{
[Dependency(ReplaceServices = true)]
public class IdentityModelHttpClientAuthenticator : IHttpClientAuthenticator, ITransientDependency
{
public IHttpContextAccessor HttpContextAccessor { get; set; }
public async Task Authenticate(HttpClientAuthenticateContext context)
{
var accessToken = await GetAccessTokenFromHttpContextOrNullAsync() ??
await GetAccessTokenFromServerOrNullAsync(context);
if (accessToken != null)
{
//TODO: "Bearer" should be configurable
context.Client.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", accessToken);
}
}
protected virtual Task<string> GetAccessTokenFromServerOrNullAsync(HttpClientAuthenticateContext context)
{
return Task.FromResult((string) null);
}
protected virtual async Task<string> GetAccessTokenFromHttpContextOrNullAsync()
{
var httpContext = HttpContextAccessor?.HttpContext;
if (httpContext == null)
{
return null;
}
return await httpContext.GetTokenAsync("access_token");
}
}
}

@ -14,6 +14,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="IdentityModel" Version="3.10.4" />
<ProjectReference Include="..\Volo.Abp.Http.Client\Volo.Abp.Http.Client.csproj" />
</ItemGroup>

@ -0,0 +1,18 @@
using Microsoft.Extensions.DependencyInjection;
using Volo.Abp.Modularity;
namespace Volo.Abp.Http.Client.IdentityModel
{
[DependsOn(
typeof(AbpHttpClientModule)
)]
public class AbpHttpClientIdentityModelModule : AbpModule
{
public override void ConfigureServices(ServiceConfigurationContext context)
{
var configuration = context.Services.GetConfiguration();
Configure<IdentityClientOptions>(configuration);
}
}
}

@ -0,0 +1,93 @@
using System.Collections.Generic;
using IdentityModel;
namespace Volo.Abp.Http.Client.IdentityModel
{
public class IdentityClientConfiguration : Dictionary<string, string>
{
/// <summary>
/// Possible values: "client_credentials" or "password".
/// Default value: "client_credentials".
/// </summary>
public string GrantType
{
get => this.GetOrDefault(nameof(GrantType));
set => this[GrantType] = value;
}
/// <summary>
/// Client Id.
/// </summary>
public string ClientId
{
get => this.GetOrDefault(nameof(ClientId));
set => this[ClientId] = value;
}
/// <summary>
/// Client secret (as plain text - without hashed).
/// </summary>
public string ClientSecret
{
get => this.GetOrDefault(nameof(ClientSecret));
set => this[ClientSecret] = value;
}
/// <summary>
/// User name.
/// Valid only if <see cref="GrantType"/> is "password".
/// </summary>
public string UserName
{
get => this.GetOrDefault(nameof(UserName));
set => this[UserName] = value;
}
/// <summary>
/// Password of the <see cref="UserName"/>.
/// Valid only if <see cref="GrantType"/> is "password".
/// </summary>
public string UserPassword
{
get => this.GetOrDefault(nameof(UserPassword));
set => this[UserPassword] = value;
}
/// <summary>
/// Authority.
/// </summary>
public string Authority
{
get => this.GetOrDefault(nameof(Authority));
set => this[Authority] = value;
}
/// <summary>
/// Scope.
/// </summary>
public string Scope
{
get => this.GetOrDefault(nameof(Scope));
set => this[Scope] = value;
}
public IdentityClientConfiguration()
{
}
public IdentityClientConfiguration(
string clientId,
string clientSecret,
string grantType = OidcConstants.GrantTypes.ClientCredentials,
string userName = null,
string userPassword = null)
{
this[nameof(ClientId)] = clientId;
this[nameof(ClientSecret)] = clientSecret;
this[nameof(GrantType)] = grantType;
this[nameof(UserName)] = userName;
this[nameof(UserPassword)] = userPassword;
}
}
}

@ -0,0 +1,15 @@
using System.Collections.Generic;
namespace Volo.Abp.Http.Client.IdentityModel
{
public class IdentityClientConfigurationDictionary : Dictionary<string, IdentityClientConfiguration>
{
public const string DefaultName = "Default";
public IdentityClientConfiguration Default
{
get => this.GetOrDefault(DefaultName);
set => this[DefaultName] = value;
}
}
}

@ -0,0 +1,12 @@
namespace Volo.Abp.Http.Client.IdentityModel
{
public class IdentityClientOptions
{
public IdentityClientConfigurationDictionary IdentityClients { get; set; }
public IdentityClientOptions()
{
IdentityClients = new IdentityClientConfigurationDictionary();
}
}
}

@ -0,0 +1,116 @@
using System;
using System.Collections.Generic;
using System.Net.Http.Headers;
using System.Threading.Tasks;
using IdentityModel;
using IdentityModel.Client;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Options;
using Volo.Abp.DependencyInjection;
using Volo.Abp.Http.Client.Authentication;
namespace Volo.Abp.Http.Client.IdentityModel
{
//TODO: This class should be optimized and improved:
[Dependency(ReplaceServices = true)]
public class IdentityModelHttpClientAuthenticator : IHttpClientAuthenticator, ITransientDependency
{
public IHttpContextAccessor HttpContextAccessor { get; set; }
protected IdentityClientOptions ClientOptions { get; }
public IdentityModelHttpClientAuthenticator(
IOptions<IdentityClientOptions> options)
{
ClientOptions = options.Value;
}
public async Task Authenticate(HttpClientAuthenticateContext context)
{
var accessToken = await GetAccessTokenFromHttpContextOrNullAsync() ??
await GetAccessTokenFromServerOrNullAsync(context);
if (accessToken != null)
{
//TODO: "Bearer" should be configurable
context.Client.DefaultRequestHeaders.Authorization
= new AuthenticationHeaderValue("Bearer", accessToken);
}
}
protected virtual async Task<string> GetAccessTokenFromHttpContextOrNullAsync()
{
var httpContext = HttpContextAccessor?.HttpContext;
if (httpContext == null)
{
return null;
}
return await httpContext.GetTokenAsync("access_token");
}
protected virtual async Task<string> GetAccessTokenFromServerOrNullAsync(HttpClientAuthenticateContext context)
{
var configuration = GetClientConfiguration(context);
if (configuration == null)
{
return null;
}
var discoveryResponse = await GetDiscoveryResponse(configuration);
if (discoveryResponse.IsError)
{
return null;
}
var tokenResponse = await GetTokenResponse(discoveryResponse, configuration);
if (tokenResponse.IsError)
{
return null;
}
return tokenResponse.AccessToken;
}
private IdentityClientConfiguration GetClientConfiguration(HttpClientAuthenticateContext context)
{
var identityClientName = context.RemoteService.GetIdentityClient();
if (identityClientName.IsNullOrEmpty())
{
return ClientOptions.IdentityClients.Default;
}
return ClientOptions.IdentityClients.GetOrDefault(identityClientName) ??
ClientOptions.IdentityClients.Default;
}
protected virtual async Task<DiscoveryResponse> GetDiscoveryResponse(IdentityClientConfiguration configuration)
{
return await DiscoveryClient.GetAsync(configuration.Authority);
}
protected virtual async Task<TokenResponse> GetTokenResponse(DiscoveryResponse discoveryResponse, IdentityClientConfiguration configuration)
{
var tokenClient = new TokenClient(discoveryResponse.TokenEndpoint, configuration.ClientId, configuration.ClientSecret);
switch (configuration.GrantType)
{
case OidcConstants.GrantTypes.ClientCredentials:
return await tokenClient.RequestClientCredentialsAsync(
configuration.Scope
);
case OidcConstants.GrantTypes.Password:
return await tokenClient.RequestResourceOwnerPasswordAsync(
configuration.UserName,
configuration.UserPassword,
configuration.Scope
);
default:
throw new AbpException("Grant type was not implemented: " + configuration.GrantType);
}
}
}
}

@ -0,0 +1,22 @@
using System.Collections.Generic;
using JetBrains.Annotations;
namespace Volo.Abp.Http.Client
{
public static class RemoteServiceConfigurationExtensions
{
[CanBeNull]
public static string GetIdentityClient([NotNull] this RemoteServiceConfiguration configuration)
{
Check.NotNullOrEmpty(configuration, nameof(configuration));
return configuration.GetOrDefault("IdentityClient");
}
public static RemoteServiceConfiguration SetIdentityClient([NotNull] this RemoteServiceConfiguration configuration, [CanBeNull] string value)
{
configuration["IdentityClient"] = value;
return configuration;
}
}
}

@ -8,16 +8,16 @@ namespace Volo.Abp.Http.Client.Authentication
public HttpRequestMessage Request { get; }
public string RemoteServiceName { get; }
public RemoteServiceConfiguration RemoteService { get; }
public HttpClientAuthenticateContext(
HttpClient client,
HttpRequestMessage request,
string remoteServiceName)
HttpRequestMessage request,
RemoteServiceConfiguration remoteService)
{
Client = client;
Request = request;
RemoteServiceName = remoteServiceName;
RemoteService = remoteService;
}
}
}

@ -109,11 +109,11 @@ namespace Volo.Abp.Http.Client.DynamicProxying
using (var client = _httpClientFactory.Create())
{
var clientConfig = _clientOptions.HttpClientProxies.GetOrDefault(typeof(TService)) ?? throw new AbpException($"Could not get DynamicHttpClientProxyConfig for {typeof(TService).FullName}.");
var remoteServiceConfig = _remoteServiceOptions.RemoteServices.GetConfigurationOrDefault(clientConfig.RemoteServiceName);
var baseUrl = GetBaseUrl(clientConfig);
var action = await _apiDescriptionFinder.FindActionAsync(baseUrl, typeof(TService), invocation.Method);
var action = await _apiDescriptionFinder.FindActionAsync(remoteServiceConfig.BaseUrl, typeof(TService), invocation.Method);
var apiVersion = GetApiVersionInfo(action);
var url = baseUrl + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary, apiVersion);
var url = remoteServiceConfig.BaseUrl + UrlBuilder.GenerateUrlWithParameters(action, invocation.ArgumentsDictionary, apiVersion);
var requestMessage = new HttpRequestMessage(action.GetHttpMethod(), url)
{
@ -126,7 +126,7 @@ namespace Volo.Abp.Http.Client.DynamicProxying
new HttpClientAuthenticateContext(
client,
requestMessage,
clientConfig.RemoteServiceName
remoteServiceConfig
)
);
@ -191,13 +191,6 @@ namespace Volo.Abp.Http.Client.DynamicProxying
}
}
private string GetBaseUrl(DynamicHttpClientProxyConfig config)
{
return _remoteServiceOptions.RemoteServices.GetOrDefault(config.RemoteServiceName)?.BaseUrl
?? _remoteServiceOptions.RemoteServices.Default?.BaseUrl
?? throw new AbpException($"Could not find Base URL for {typeof(TService).FullName}.");
}
private string GetConfiguredApiVersion()
{
var clientConfig = _clientOptions.HttpClientProxies.GetOrDefault(typeof(TService))

@ -1,10 +1,26 @@
namespace Volo.Abp.Http.Client
using System.Collections.Generic;
namespace Volo.Abp.Http.Client
{
public class RemoteServiceConfiguration
public class RemoteServiceConfiguration : Dictionary<string, string>
{
public string BaseUrl { get; set; }
/// <summary>
/// Base Url.
/// </summary>
public string BaseUrl
{
get => this.GetOrDefault(nameof(BaseUrl));
set => this[BaseUrl] = value;
}
public string Version { get; set; }
/// <summary>
/// Version.
/// </summary>
public string Version
{
get => this.GetOrDefault(nameof(Version));
set => this[Version] = value;
}
public RemoteServiceConfiguration()
{
@ -13,8 +29,8 @@
public RemoteServiceConfiguration(string baseUrl, string version = null)
{
BaseUrl = baseUrl;
Version = version;
this[nameof(BaseUrl)] = baseUrl;
this[nameof(Version)] = version;
}
}
}

@ -8,8 +8,15 @@ namespace Volo.Abp.Http.Client
public RemoteServiceConfiguration Default
{
get { return this.GetOrDefault(DefaultName); }
set { this[DefaultName] = value; }
get => this.GetOrDefault(DefaultName);
set => this[DefaultName] = value;
}
public RemoteServiceConfiguration GetConfigurationOrDefault(string name)
{
return this.GetOrDefault(name)
?? Default
?? throw new AbpException($"Remote service '{name}' was not found and there is no default configuration.");
}
}
}
Loading…
Cancel
Save