- Added async GetAuthorizationUrlAsync() methods to all OIDC providers - Updated base OidcService with abstract async contract and backward-compatible sync wrapper - Modified OidcController to use async authorization URL generation - Removed sync blocks using .GetAwaiter().GetResult() in Google provider - Maintained backward compatibility with existing sync method calls - Eliminated thread blocking and improved async flow throughout auth pipeline - Enhanced scalability by allowing non-blocking async authorization URL generation
		
			
				
	
	
		
			99 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			99 lines
		
	
	
		
			3.8 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System.Text.Json;
 | 
						|
using DysonNetwork.Shared.Cache;
 | 
						|
 | 
						|
namespace DysonNetwork.Pass.Auth.OpenId;
 | 
						|
 | 
						|
public class AfdianOidcService(
 | 
						|
    IConfiguration configuration,
 | 
						|
    IHttpClientFactory httpClientFactory,
 | 
						|
    AppDatabase db,
 | 
						|
    AuthService auth,
 | 
						|
    ICacheService cache,
 | 
						|
    ILogger<AfdianOidcService> logger
 | 
						|
)
 | 
						|
    : OidcService(configuration, httpClientFactory, db, auth, cache)
 | 
						|
{
 | 
						|
    public override string ProviderName => "Afdian";
 | 
						|
    protected override string DiscoveryEndpoint => ""; // Afdian doesn't have a standard OIDC discovery endpoint
 | 
						|
    protected override string ConfigSectionName => "Afdian";
 | 
						|
 | 
						|
    public override Task<string> GetAuthorizationUrlAsync(string state, string nonce)
 | 
						|
    {
 | 
						|
        return Task.FromResult(GetAuthorizationUrl(state, nonce));
 | 
						|
    }
 | 
						|
 | 
						|
    public override string GetAuthorizationUrl(string state, string nonce)
 | 
						|
    {
 | 
						|
        var config = GetProviderConfig();
 | 
						|
        var queryParams = new Dictionary<string, string>
 | 
						|
        {
 | 
						|
            { "client_id", config.ClientId },
 | 
						|
            { "redirect_uri", config.RedirectUri },
 | 
						|
            { "response_type", "code" },
 | 
						|
            { "scope", "basic" },
 | 
						|
            { "state", state },
 | 
						|
        };
 | 
						|
 | 
						|
        var queryString = string.Join("&", queryParams.Select(p => $"{p.Key}={Uri.EscapeDataString(p.Value)}"));
 | 
						|
        return $"https://afdian.com/oauth2/authorize?{queryString}";
 | 
						|
    }
 | 
						|
 | 
						|
    protected override Task<OidcDiscoveryDocument?> GetDiscoveryDocumentAsync()
 | 
						|
    {
 | 
						|
        return Task.FromResult(new OidcDiscoveryDocument
 | 
						|
        {
 | 
						|
            AuthorizationEndpoint = "https://afdian.com/oauth2/authorize",
 | 
						|
            TokenEndpoint = "https://afdian.com/api/oauth2/access_token",
 | 
						|
            UserinfoEndpoint = null,
 | 
						|
            JwksUri = null
 | 
						|
        })!;
 | 
						|
    }
 | 
						|
 | 
						|
    public override async Task<OidcUserInfo> ProcessCallbackAsync(OidcCallbackData callbackData)
 | 
						|
    {
 | 
						|
        try
 | 
						|
        {
 | 
						|
            var config = GetProviderConfig();
 | 
						|
            var content = new FormUrlEncodedContent(new Dictionary<string, string>
 | 
						|
            {
 | 
						|
                { "client_id", config.ClientId },
 | 
						|
                { "client_secret", config.ClientSecret },
 | 
						|
                { "grant_type", "authorization_code" },
 | 
						|
                { "code", callbackData.Code },
 | 
						|
                { "redirect_uri", config.RedirectUri },
 | 
						|
            });
 | 
						|
 | 
						|
            var client = HttpClientFactory.CreateClient();
 | 
						|
            var request = new HttpRequestMessage(HttpMethod.Post, "https://afdian.com/api/oauth2/access_token");
 | 
						|
            request.Content = content;
 | 
						|
            
 | 
						|
            var response = await client.SendAsync(request);
 | 
						|
            response.EnsureSuccessStatusCode();
 | 
						|
            
 | 
						|
            var json = await response.Content.ReadAsStringAsync();
 | 
						|
            logger.LogInformation("Trying get userinfo from afdian, response: {Response}", json);
 | 
						|
            var afdianResponse = JsonDocument.Parse(json).RootElement;
 | 
						|
 | 
						|
            var user = afdianResponse.TryGetProperty("data", out var dataElement) ? dataElement : default;
 | 
						|
            var userId = user.TryGetProperty("user_id", out var userIdElement) ? userIdElement.GetString() ?? "" : "";
 | 
						|
            var avatar = user.TryGetProperty("avatar", out var avatarElement) ? avatarElement.GetString() : null;
 | 
						|
 | 
						|
            return new OidcUserInfo
 | 
						|
            {
 | 
						|
                UserId = userId,
 | 
						|
                DisplayName = (user.TryGetProperty("name", out var nameElement)
 | 
						|
                    ? nameElement.GetString()
 | 
						|
                    : null) ?? "",
 | 
						|
                ProfilePictureUrl = avatar,
 | 
						|
                Provider = ProviderName
 | 
						|
            };
 | 
						|
        }
 | 
						|
        catch (Exception ex)
 | 
						|
        {
 | 
						|
            // Due to afidan's API isn't compliant with OAuth2, we want more logs from it to investigate.
 | 
						|
            logger.LogError(ex, "Failed to get user info from Afdian");
 | 
						|
            throw;
 | 
						|
        }
 | 
						|
    }
 | 
						|
}
 |