added oauth authentication handle and options

master
Young 6 months ago
parent 3105287588
commit 20b969c30f

@ -0,0 +1,15 @@
using Microsoft.AspNetCore.Authentication.OAuth;
namespace Infrastructure.OAuth
{
public class AuthenticationOAuthOptions : OAuthOptions
{
public virtual string RedirectUri { get; set; }
public virtual string OpenId { get; set; } = "urn:openid";
public virtual string AccessToken { get; set; } = "urn:access_token";
public virtual string Name { get; set; } = "urn:name";
}
}

@ -0,0 +1,34 @@
using Microsoft.AspNetCore.Authentication;
namespace Infrastructure.OAuth.Gitee
{
public static class GiteeAuthenticationExtensions
{
public const string AuthenticationSchemeName = "Gitee";
public static AuthenticationBuilder AddGiteeAuthentication(this AuthenticationBuilder builder)
{
return builder.AddGiteeAuthentication(AuthenticationSchemeName, options => { });
}
public static AuthenticationBuilder AddGiteeAuthentication(this AuthenticationBuilder builder,
Action<GiteeAuthenticationOptions> configureOptions)
{
return builder.AddGiteeAuthentication(AuthenticationSchemeName, configureOptions);
}
public static AuthenticationBuilder AddGiteeAuthentication(this AuthenticationBuilder builder, string scheme,
Action<GiteeAuthenticationOptions> configureOptions)
{
return builder.AddGiteeAuthentication(scheme, AuthenticationSchemeName, configureOptions);
}
public static AuthenticationBuilder AddGiteeAuthentication(this AuthenticationBuilder builder, string scheme,
string displayName,
Action<GiteeAuthenticationOptions> configureOptions)
{
return builder.AddScheme<GiteeAuthenticationOptions, GiteeAuthenticationHandler>(scheme, displayName,
configureOptions);
}
}
}

@ -0,0 +1,48 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
namespace Infrastructure.OAuth.Gitee
{
public class GiteeAuthenticationHandler(
IOptionsMonitor<GiteeAuthenticationOptions> options,
ILoggerFactory logger,
IHttpClientFactory httpClientFactory,
UrlEncoder encoder)
: OAuthenticationHandlerBase<GiteeAuthenticationOptions>(options, logger, httpClientFactory, encoder)
{
protected override string AuthenticationSchemeName { get; set; } =
GiteeAuthenticationExtensions.AuthenticationSchemeName;
protected override async Task<IList<Claim>> GenerateClaimsByCode(string code)
{
var tokenQueryPairs = new List<KeyValuePair<string, string?>>()
{
new("grant_type", "authorization_code"),
new("client_id", Options.ClientId),
new("client_secret", Options.ClientSecret),
new("redirect_uri", Options.RedirectUri),
new("code", code)
};
var giteeToken =
await SeedHttpMessageAsync<GiteeToken>(Options.TokenEndpoint, tokenQueryPairs, HttpMethod.Post);
var userInfoQueryPairs = new List<KeyValuePair<string, string?>>()
{
new("access_token", giteeToken.AccessToken),
};
var userInfo =
await SeedHttpMessageAsync<GiteeUserInfo>(Options.UserInformationEndpoint, userInfoQueryPairs);
var claims = new List<Claim>()
{
new(Options.AvatarUrl, userInfo.AvatarUrl),
new(Options.Url, userInfo.Url),
new(Options.OpenId, userInfo.Id.ToString()),
new(Options.Name, userInfo.Name),
new(Options.AccessToken, giteeToken.AccessToken)
};
return claims;
}
}
}

@ -0,0 +1,32 @@
using System.Security.Claims;
using Microsoft.AspNetCore.Authentication;
namespace Infrastructure.OAuth.Gitee
{
public class GiteeAuthenticationOptions : AuthenticationOAuthOptions
{
public string UserEmailsEndpoint { get; } = "https://gitee.com/api/v5/emails";
public string Url { get; } = "urn:gitee:url";
public string AvatarUrl { get; } = "urn:gitee:avatarUrl";
public GiteeAuthenticationOptions()
{
ClaimsIssuer = GiteeAuthenticationExtensions.AuthenticationSchemeName;
CallbackPath = "/signin-gitee";
AuthorizationEndpoint = "https://gitee.com/oauth/authorize";
TokenEndpoint = "https://gitee.com/oauth/token";
UserInformationEndpoint = "https://gitee.com/api/v5/user";
Scope.Add("user_info");
Scope.Add("emails");
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "id");
ClaimActions.MapJsonKey(ClaimTypes.Actor, "login");
ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
ClaimActions.MapJsonKey(Url, "url");
}
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace Infrastructure.OAuth.Gitee
{
public class GiteeToken
{
[JsonProperty("access_token")]
public string AccessToken { get; set; }
[JsonProperty("token_type")]
public string TokenType { get; set; }
[JsonProperty("expires_in")]
public int ExpiresIn { get; set; }
[JsonProperty("refresh_token")]
public string RefreshToken { get; set; }
[JsonProperty("scope")]
public string Scope { get; set; }
[JsonProperty("created_at")]
public long CreatedAt { get; set; }
}
}

@ -0,0 +1,25 @@
using Newtonsoft.Json;
namespace Infrastructure.OAuth.Gitee
{
public class GiteeUserInfo()
{
[JsonProperty("id")]
public int Id { get; set; }
[JsonProperty("login")]
public string Login { get; set; }
[JsonProperty("name")]
public string Name { get; set; }
[JsonProperty("avatar_url")]
public string AvatarUrl { get; set; }
[JsonProperty("url")]
public string Url { get; set; }
[JsonProperty("email")]
public string Email { get; set; }
}
}

@ -0,0 +1,70 @@
using System.Security.Claims;
using System.Text.Encodings.Web;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.WebUtilities;
using Newtonsoft.Json;
namespace Infrastructure.OAuth;
public abstract class OAuthenticationHandlerBase<TOptions>(
IOptionsMonitor<TOptions> options,
ILoggerFactory logger,
IHttpClientFactory httpClientFactory,
UrlEncoder encoder)
: AuthenticationHandler<TOptions>(options, logger, encoder)
where TOptions : AuthenticationSchemeOptions, new()
{
private readonly HttpClient _httpClient = httpClientFactory.CreateClient();
protected abstract string AuthenticationSchemeName { get; set; }
protected abstract Task<IList<Claim>> GenerateClaimsByCode(string code);
protected override async Task<AuthenticateResult> HandleAuthenticateAsync()
{
if (!Context.Request.Query.ContainsKey("code"))
{
return AuthenticateResult.Fail("query parameter code is missing");
}
var code = Context.Request.Query["code"].ToString();
try
{
var claims = await GenerateClaimsByCode(code);
var claimsIdentity = new ClaimsIdentity(claims.ToArray(), AuthenticationSchemeName);
var ticket = new AuthenticationTicket(new ClaimsPrincipal(claimsIdentity), AuthenticationSchemeName);
return AuthenticateResult.Success(ticket);
}
catch (Exception e)
{
return AuthenticateResult.Fail(e.Message);
}
}
protected async Task<T?> SeedHttpMessageAsync<T>(string url, IEnumerable<KeyValuePair<string, string?>> query,
HttpMethod? httpMethod = null)
{
httpMethod = httpMethod ?? HttpMethod.Get;
var queryUrl = QueryHelpers.AddQueryString(url, query);
var response = default(HttpResponseMessage);
if (httpMethod == HttpMethod.Get)
{
response = await _httpClient.GetAsync(queryUrl);
}
else if (httpMethod == HttpMethod.Post)
{
response = await _httpClient.PostAsync(queryUrl, null);
}
var content = await response.Content.ReadAsStringAsync();
if (!response.IsSuccessStatusCode)
{
throw new HttpRequestException($"授权服务器请求错误,请求地址:{queryUrl},错误信息:{content}");
}
return JsonConvert.DeserializeObject<T>(content);
}
}
Loading…
Cancel
Save