342 lines
11 KiB
C#
342 lines
11 KiB
C#
using System;
|
|
using System.Net.Http;
|
|
using System.Threading.Tasks;
|
|
using IdentityModel.Client;
|
|
using Microsoft.Extensions.Logging;
|
|
using xCommons.Extensions;
|
|
using xExceptions.Constants;
|
|
using xExceptions.Models;
|
|
using xIdentityModels.Extensions;
|
|
using xIdentityModels.Models;
|
|
using xIds.Extensions;
|
|
using static IdentityModel.OidcConstants;
|
|
|
|
namespace xIds.Providers
|
|
{
|
|
public partial class XIdentityManager
|
|
{
|
|
//
|
|
#region Identity Actions ...
|
|
/// <summary>
|
|
/// Request for Discovery Document
|
|
/// </summary>
|
|
/// <returns>an instance of <see>DiscoveryDocumentResponse</see></returns>
|
|
public async Task<DiscoveryDocumentResponse> RequestDiscoveryDocument()
|
|
{
|
|
//
|
|
var httpClient = GetHttpClient();
|
|
var result = httpClient
|
|
.GetDiscoveryDocumentAsync(IdentityResourceConfiguration.Authority)
|
|
.ContinueWith(docTask =>
|
|
{
|
|
//
|
|
httpClient.Dispose();
|
|
return docTask.Result;
|
|
});
|
|
|
|
//
|
|
return await result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Request AccessToken for Specific XApiScope
|
|
/// </summary>
|
|
/// <param name="scope">a member of <see>XApiScope</see></param>
|
|
/// <returns>an instance of <see>TokenResponse</see></returns>
|
|
public async Task<IdentityModel.Client.TokenResponse> RequestScopeAccessToken(
|
|
string scope
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
if (scope.IsNullOrEmpty())
|
|
{
|
|
XException.InvalidArgs.Throw();
|
|
}
|
|
|
|
//
|
|
// Retrieve Disco Doc ...
|
|
var discoDoc = await RequestDiscoveryDocument();
|
|
if (discoDoc.IsError)
|
|
{
|
|
XException.ActionFailed.Throw();
|
|
}
|
|
|
|
//
|
|
var request = new ClientCredentialsTokenRequest
|
|
{
|
|
Address = discoDoc.TokenEndpoint,
|
|
GrantType = GrantTypes.ClientCredentials,
|
|
ClientId = IdentityResourceConfiguration.ClientId,
|
|
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
|
Scope = scope
|
|
};
|
|
|
|
//
|
|
var httpClient = GetHttpClient();
|
|
var result = await httpClient
|
|
.RequestClientCredentialsTokenAsync(request)
|
|
.ContinueWith(response =>
|
|
{
|
|
//
|
|
httpClient.Dispose();
|
|
return response.Result;
|
|
});
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Authenticate a User
|
|
/// </summary>
|
|
/// <param name="model">an instance of <see>XLoginRequest</see> class which represent Authentication requirements</param>
|
|
/// <returns>an instance of <see>TokenResponse</see></returns>
|
|
public async Task<IdentityModel.Client.TokenResponse> Authenticate(
|
|
XLoginRequest model
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider
|
|
.GroupValidationBuilder()
|
|
.AddNotNull(model)
|
|
.AddNotEmpty(
|
|
model.UserSelectBy,
|
|
model.Password
|
|
)
|
|
.ValidateGroup();
|
|
|
|
//
|
|
// Retrieve Disco Doc ...
|
|
var discoDoc = await RequestDiscoveryDocument();
|
|
if (discoDoc.IsError)
|
|
{
|
|
XException.ActionFailed.Throw();
|
|
}
|
|
|
|
//
|
|
// Create Request ...
|
|
var request = new PasswordTokenRequest
|
|
{
|
|
//
|
|
Address = discoDoc.TokenEndpoint,
|
|
|
|
//
|
|
GrantType = GrantTypes.ClientCredentials,
|
|
ClientId = IdentityResourceConfiguration.ClientId,
|
|
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
|
|
|
//
|
|
UserName = model.UserSelectBy,
|
|
Password = model.Password,
|
|
|
|
//
|
|
// Pass Device to Request ...
|
|
Parameters = { { "force", false.ToJSON () }
|
|
}
|
|
};
|
|
|
|
//
|
|
var httpClient = GetHttpClient();
|
|
var result = await httpClient
|
|
.RequestPasswordTokenAsync(request)
|
|
.ContinueWith(response =>
|
|
{
|
|
//
|
|
httpClient.Dispose();
|
|
return response.Result;
|
|
});
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Do Login based on XLoginRequest
|
|
/// </summary>
|
|
/// <param name="model">an instance of <see>XLoginRequest</see> class which represent Authentication requirements</param>
|
|
/// <returns>an instance of <see>XLoginResponse</see></returns>
|
|
public async Task<XLoginResponse> Login(
|
|
XLoginRequest model
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider
|
|
.GroupValidationBuilder()
|
|
.AddNotNull(model, model.Device)
|
|
.AddNotEmpty(
|
|
model.UserSelectBy,
|
|
model.Password
|
|
)
|
|
.ValidateGroup();
|
|
|
|
//
|
|
// Get Token Response ...
|
|
//
|
|
// Retrieve Disco Doc ...
|
|
var discoDoc = await RequestDiscoveryDocument();
|
|
Logger.LogInformation($"discoDoc: {discoDoc.ToJSON()}");
|
|
|
|
if (discoDoc.IsError)
|
|
{
|
|
XException.ActionFailed.Throw();
|
|
}
|
|
|
|
//
|
|
// Create Request ...
|
|
var request = new PasswordTokenRequest
|
|
{
|
|
//
|
|
Address = discoDoc.TokenEndpoint,
|
|
|
|
//
|
|
GrantType = GrantTypes.ClientCredentials,
|
|
ClientId = IdentityResourceConfiguration.ClientId,
|
|
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
|
|
|
//
|
|
UserName = model.UserSelectBy,
|
|
Password = model.Password,
|
|
|
|
//
|
|
// Pass Device to Request ...
|
|
Parameters = { { "force", false.ToJSON () },
|
|
{ "device", model.Device.ToJSON () },
|
|
{ "language", model.Language }
|
|
}
|
|
};
|
|
|
|
//
|
|
var httpClient = GetHttpClient();
|
|
var authResponse = await httpClient
|
|
.RequestPasswordTokenAsync(request)
|
|
.ContinueWith(response =>
|
|
{
|
|
//
|
|
httpClient.Dispose();
|
|
return response.Result;
|
|
});
|
|
|
|
//
|
|
// Check If Response Has Error ...
|
|
Logger.LogInformation($"AuthResponse: {authResponse.ToJSON()}");
|
|
|
|
//
|
|
if (authResponse.IsError)
|
|
{
|
|
throw authResponse.GetException();
|
|
}
|
|
|
|
//
|
|
// Create XLoginResponse Model ...
|
|
var result = authResponse.CreateXLoginResponse();
|
|
|
|
//
|
|
// Get ans Set User Profile ...
|
|
result.Profile = await GetUserProfileAsync(model.UserSelectBy, model.UserSelectBy);
|
|
|
|
//
|
|
return result;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Refresh Tokens
|
|
/// </summary>
|
|
/// <param name="model">Authentication Tokens, instance of <see>XTokenResponse</see></param>
|
|
/// <returns>an instance of <see>XTokenResponse</see></returns>
|
|
public async Task<XTokenResponse> RefreshTokens(
|
|
XTokenResponse model
|
|
)
|
|
{
|
|
//
|
|
// Validate Args ...
|
|
ValidationProvider
|
|
.GroupValidationBuilder()
|
|
.AddNotNull(model)
|
|
.AddNotEmpty(
|
|
model.AccessToken,
|
|
model.RefreshToken
|
|
)
|
|
.ValidateGroup();
|
|
|
|
//
|
|
var authResponse = await RequestDiscoveryDocument()
|
|
.ContinueWith((discoTask) =>
|
|
{
|
|
//
|
|
var discoDoc = discoTask.Result;
|
|
if (discoDoc.IsError)
|
|
{
|
|
XException.ActionFailed.Throw();
|
|
}
|
|
|
|
//
|
|
using (var httpClient = GetHttpClient())
|
|
{
|
|
return httpClient.RequestRefreshTokenAsync(new RefreshTokenRequest
|
|
{
|
|
//
|
|
Address = discoDoc.TokenEndpoint,
|
|
GrantType = GrantTypes.RefreshToken,
|
|
ClientId = IdentityResourceConfiguration.ClientId,
|
|
ClientSecret = IdentityResourceConfiguration.ClientSecret,
|
|
RefreshToken = model.RefreshToken
|
|
}).Result;
|
|
}
|
|
});
|
|
|
|
//
|
|
// Check If Response Has Error ...
|
|
if (authResponse.IsError)
|
|
{
|
|
//
|
|
XError error = authResponse.ErrorDescription.FromJSON<XError>();
|
|
throw error.ToException();
|
|
}
|
|
|
|
//
|
|
// Create XLoginResponse Model ...
|
|
var result = authResponse.CreateXTokenResponse();
|
|
|
|
//
|
|
return result;
|
|
}
|
|
#endregion
|
|
|
|
//
|
|
#region Requirements ...
|
|
/// <summary>
|
|
/// Get an Instance of Http Client
|
|
/// </summary>
|
|
/// <returns>an instance of <see>HttpClient</see></returns>
|
|
public HttpClient GetHttpClient()
|
|
{
|
|
//
|
|
HttpClient httpClient = null;
|
|
httpClient = new HttpClient();
|
|
var httpClientHandler = new HttpClientHandler()
|
|
{
|
|
ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) =>
|
|
{
|
|
//
|
|
Logger.LogInformation(
|
|
$"SSL Handler: {Environment.NewLine} sender: {sender}, {Environment.NewLine} cert: {cert}, {Environment.NewLine} chain: {chain}, {Environment.NewLine} sslPolicyErrors: {sslPolicyErrors}"
|
|
);
|
|
|
|
//
|
|
return true;
|
|
},
|
|
ClientCertificateOptions = ClientCertificateOption.Manual,
|
|
};
|
|
|
|
//
|
|
httpClient = new HttpClient(httpClientHandler);
|
|
|
|
//
|
|
return httpClient;
|
|
}
|
|
#endregion
|
|
}
|
|
} |