Refactor code

This commit is contained in:
Trojaner 2020-10-08 06:45:54 +03:00
parent 0e74cd4321
commit ce7dfb3a9e
9 changed files with 173 additions and 124 deletions

View file

@ -6,7 +6,7 @@ using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging; using Microsoft.Extensions.Logging;
namespace SteamOpenIdConnectProvider namespace SteamOpenIdConnectProvider.Controllers
{ {
[AllowAnonymous] [AllowAnonymous]
[Route("[action]")] [Route("[action]")]
@ -28,20 +28,20 @@ namespace SteamOpenIdConnectProvider
[HttpGet] [HttpGet]
public async Task<IActionResult> ExternalLogin(string returnUrl = null) public Task<IActionResult> ExternalLogin(string returnUrl = null)
{ {
string provider = "Steam"; const string provider = "Steam";
// Request a redirect to the external login provider.
var redirectUrl = Url.Action("ExternalLoginCallback", new { returnUrl }); var redirectUrl = Url.Action("ExternalLoginCallback", new { returnUrl });
var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl); var properties = _signInManager.ConfigureExternalAuthenticationProperties(provider, redirectUrl);
return new ChallengeResult(provider, properties); return Task.FromResult<IActionResult>(new ChallengeResult(provider, properties));
} }
[HttpGet] [HttpGet]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null) public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{ {
returnUrl = returnUrl ?? Url.Content("~/"); returnUrl ??= Url.Content("~/");
if (remoteError != null) if (remoteError != null)
{ {
throw new Exception($"Error from external provider: {remoteError}"); throw new Exception($"Error from external provider: {remoteError}");
@ -53,7 +53,6 @@ namespace SteamOpenIdConnectProvider
throw new Exception($"Error loading external login information."); throw new Exception($"Error loading external login information.");
} }
// Sign in the user with this external login provider if the user already has a login.
var externalLoginResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true); var externalLoginResult = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false, bypassTwoFactor: true);
if (externalLoginResult.Succeeded) if (externalLoginResult.Succeeded)
{ {
@ -61,13 +60,13 @@ namespace SteamOpenIdConnectProvider
return LocalRedirect(returnUrl); return LocalRedirect(returnUrl);
} }
var userName = info.Principal.FindFirstValue(ClaimTypes.Name); var userName = info.Principal.FindFirstValue(ClaimTypes.Name);
var userId = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier); var userId = info.Principal.FindFirstValue(ClaimTypes.NameIdentifier);
var user = new IdentityUser { UserName = userName, Id = userId }; var user = new IdentityUser { UserName = userName, Id = userId };
_userManager.UserValidators.Clear(); _userManager.UserValidators.Clear();
var result = await _userManager.CreateAsync(user); var result = await _userManager.CreateAsync(user);
if (result.Succeeded) if (result.Succeeded)
{ {

View file

@ -2,7 +2,7 @@
using Microsoft.AspNetCore.Identity.EntityFrameworkCore; using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore;
namespace SteamOpenIdConnectProvider namespace SteamOpenIdConnectProvider.Database
{ {
// This is completely in-memory, we do not need a persistent store. // This is completely in-memory, we do not need a persistent store.
public class AppInMemoryDbContext : IdentityDbContext<IdentityUser> public class AppInMemoryDbContext : IdentityDbContext<IdentityUser>

View file

@ -0,0 +1,11 @@
using System.Collections.Generic;
using Newtonsoft.Json;
namespace SteamOpenIdConnectProvider.Profile.Models
{
public sealed class GetPlayerSummariesResponse
{
[JsonProperty("players")]
public ICollection<Player> Players { get; set; }
}
}

View file

@ -0,0 +1,58 @@
using Newtonsoft.Json;
namespace SteamOpenIdConnectProvider.Profile.Models
{
public sealed class Player
{
[JsonProperty("steamid")]
public ulong SteamId { get; set; }
[JsonProperty("communityvisibilitystate")]
public int CommunityVisibilityState { get; set; }
[JsonProperty("profilestate")]
public int ProfileState { get; set; }
[JsonProperty("personaname")]
public string PersonaName { get; set; }
[JsonProperty("commentpermission")]
public int CommentPermission { get; set; }
[JsonProperty("profileurl")]
public string ProfileUrl { get; set; }
[JsonProperty("avatar")]
public string Avatar { get; set; }
[JsonProperty("avatarmedium")]
public string AvatarMedium { get; set; }
[JsonProperty("avatarfull")]
public string AvatarFull { get; set; }
[JsonProperty("avatarhash")]
public string AvatarHash { get; set; }
[JsonProperty("lastlogoff")]
public int LastLogoff { get; set; }
[JsonProperty("personastate")]
public int PersonaState { get; set; }
[JsonProperty("realname")]
public string RealName { get; set; }
[JsonProperty("primaryclanid")]
public ulong PrimaryClanId { get; set; }
[JsonProperty("timecreated")]
public int TimeCreated { get; set; }
[JsonProperty("personastateflags")]
public int PersonaStateFlags { get; set; }
[JsonProperty("loccountrycode")]
public string LocCountryCode { get; set; }
}
}

View file

@ -0,0 +1,10 @@
using Newtonsoft.Json;
namespace SteamOpenIdConnectProvider.Profile.Models
{
public sealed class SteamResponse<T>
{
[JsonProperty("response")]
public T Response { get; set; }
}
}

View file

@ -0,0 +1,80 @@
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Security.Claims;
using System.Threading.Tasks;
using IdentityServer4.Extensions;
using IdentityServer4.Models;
using IdentityServer4.Services;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
using Newtonsoft.Json;
using SteamOpenIdConnectProvider.Profile.Models;
namespace SteamOpenIdConnectProvider.Profile
{
public class SteamProfileService : IProfileService
{
private readonly HttpClient _httpClient;
private readonly IConfiguration _configuration;
private readonly IUserClaimsPrincipalFactory<IdentityUser> _claimsFactory;
private readonly UserManager<IdentityUser> _userManager;
private async Task<GetPlayerSummariesResponse> GetPlayerSummariesAsync(IEnumerable<string> steamIds)
{
const string baseurl = "https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002";
var applicationKey = _configuration["Authentication:Steam:ApplicationKey"];
var url = $"{baseurl}/?key={applicationKey}&steamids={string.Join(',', steamIds)}";
var res = await _httpClient.GetStringAsync(url);
var response = JsonConvert.DeserializeObject<SteamResponse<GetPlayerSummariesResponse>>(res);
return response.Response;
}
public SteamProfileService(
UserManager<IdentityUser> userManager,
IUserClaimsPrincipalFactory<IdentityUser> claimsFactory,
IConfiguration configuration,
HttpClient httpClient)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
_configuration = configuration;
_httpClient = httpClient;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
const string steamUrl = "https://steamcommunity.com/openid/id/";
var steamId = sub.Substring(steamUrl.Length);
var userSummary = await GetPlayerSummariesAsync(new[] { steamId });
var player = userSummary.Players.FirstOrDefault();
if (player != null)
{
claims.Add(new Claim("picture", player.AvatarFull));
claims.Add(new Claim("nickname", player.PersonaName));
claims.Add(new Claim("given_name", player.RealName));
claims.Add(new Claim("website", player.ProfileUrl));
}
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
}

View file

@ -1,109 +0,0 @@
using IdentityServer4.Models;
using IdentityServer4.Extensions;
using IdentityServer4.Services;
using System;
using System.Linq;
using System.Security.Claims;
using System.Threading.Tasks;
using System.Collections.Generic;
using System.Net.Http;
using Newtonsoft.Json;
using Microsoft.AspNetCore.Identity;
using Microsoft.Extensions.Configuration;
namespace IdentityServer.Services
{
public class ProfileService : IProfileService
{
private IConfiguration _configuration;
private readonly IUserClaimsPrincipalFactory<IdentityUser> _claimsFactory;
private readonly UserManager<IdentityUser> _userManager;
private async Task<IPlayerSummary> FetchUserSummary(string steamid64)
{
using (var httpClient = new HttpClient())
{
var baseurl = "https://api.steampowered.com/ISteamUser/GetPlayerSummaries/v0002";
var applicationKey = _configuration["Authentication:Steam:ApplicationKey"];
var url = $"{baseurl}/?key={applicationKey}&steamids={steamid64}";
var res = await httpClient.GetStringAsync(url);
var json = JsonConvert.DeserializeObject<IPlayerSummary>(res);
return json;
}
}
public class Player {
public string steamid { get; set; }
public int communityvisibilitystate { get; set; }
public int profilestate { get; set; }
public string personaname { get; set; }
public int commentpermission { get; set; }
public string profileurl { get; set; }
public string avatar { get; set; }
public string avatarmedium { get; set; }
public string avatarfull { get; set; }
public string avatarhash { get; set; }
public int lastlogoff { get; set; }
public int personastate { get; set; }
public string realname { get; set; }
public string primaryclanid { get; set; }
public int timecreated { get; set; }
public int personastateflags { get; set; }
public string loccountrycode { get; set; }
}
public class Response {
public List<Player> players { get; set; }
}
public class IPlayerSummary {
public Response response { get; set; }
}
public ProfileService(
UserManager<IdentityUser> userManager,
IUserClaimsPrincipalFactory<IdentityUser> claimsFactory,
IConfiguration configuration
)
{
_userManager = userManager;
_claimsFactory = claimsFactory;
_configuration = configuration;
}
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
var principal = await _claimsFactory.CreateAsync(user);
var claims = principal.Claims.ToList();
claims = claims.Where(claim => context.RequestedClaimTypes.Contains(claim.Type)).ToList();
var steamurl = "https://steamcommunity.com/openid/id/";
var steamid64 = sub.Substring(steamurl.Length);
var userSummary = await FetchUserSummary(steamid64);
var player = userSummary.response.players[0];
if (player != null)
{
claims.Add(new Claim("picture", player.avatarfull));
claims.Add(new Claim("nickname", player.personaname));
claims.Add(new Claim("given_name", player.realname));
claims.Add(new Claim("website", player.profileurl));
}
context.IssuedClaims = claims;
}
public async Task IsActiveAsync(IsActiveContext context)
{
var sub = context.Subject.GetSubjectId();
var user = await _userManager.FindByIdAsync(sub);
context.IsActive = user != null;
}
}
}

View file

@ -19,7 +19,6 @@ namespace SteamOpenIdConnectProvider
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Literate)
.CreateLogger(); .CreateLogger();
CreateWebHostBuilder(args).Build().Run(); CreateWebHostBuilder(args).Build().Run();
} }

View file

@ -1,4 +1,5 @@
using System; using System;
using System.Net.Http;
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Http;
@ -9,7 +10,8 @@ using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Hosting; using Microsoft.Extensions.Hosting;
using IdentityServer4.Services; using IdentityServer4.Services;
using IdentityServer.Services; using SteamOpenIdConnectProvider.Database;
using SteamOpenIdConnectProvider.Profile;
namespace SteamOpenIdConnectProvider namespace SteamOpenIdConnectProvider
{ {
@ -29,8 +31,7 @@ namespace SteamOpenIdConnectProvider
.AddNewtonsoftJson() .AddNewtonsoftJson()
.SetCompatibilityVersion(CompatibilityVersion.Version_3_0); .SetCompatibilityVersion(CompatibilityVersion.Version_3_0);
services.AddSingleton<IConfiguration>(Configuration); services.AddSingleton(Configuration);
services.AddDbContext<AppInMemoryDbContext>(options => services.AddDbContext<AppInMemoryDbContext>(options =>
options.UseInMemoryDatabase("default")); options.UseInMemoryDatabase("default"));
@ -56,7 +57,7 @@ namespace SteamOpenIdConnectProvider
.AddDeveloperSigningCredential(true) .AddDeveloperSigningCredential(true)
.AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources()); .AddInMemoryIdentityResources(IdentityServerConfig.GetIdentityResources());
services.AddScoped<IProfileService, ProfileService>(); services.AddHttpClient<IProfileService, SteamProfileService>();
services.AddAuthentication() services.AddAuthentication()
.AddCookie(options => .AddCookie(options =>