mirror of
https://github.com/byo-software/steam-openid-connect-provider.git
synced 2025-01-09 18:06:22 +00:00
Refactor code
This commit is contained in:
parent
0e74cd4321
commit
ce7dfb3a9e
9 changed files with 173 additions and 124 deletions
|
@ -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)
|
||||||
{
|
{
|
|
@ -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>
|
11
src/Profile/Models/GetPlayerSummariesResponse.cs
Normal file
11
src/Profile/Models/GetPlayerSummariesResponse.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
58
src/Profile/Models/Player.cs
Normal file
58
src/Profile/Models/Player.cs
Normal 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; }
|
||||||
|
}
|
||||||
|
}
|
10
src/Profile/Models/SteamResponse.cs
Normal file
10
src/Profile/Models/SteamResponse.cs
Normal file
|
@ -0,0 +1,10 @@
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace SteamOpenIdConnectProvider.Profile.Models
|
||||||
|
{
|
||||||
|
public sealed class SteamResponse<T>
|
||||||
|
{
|
||||||
|
[JsonProperty("response")]
|
||||||
|
public T Response { get; set; }
|
||||||
|
}
|
||||||
|
}
|
80
src/Profile/SteamProfileService.cs
Normal file
80
src/Profile/SteamProfileService.cs
Normal 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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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 =>
|
||||||
|
|
Loading…
Reference in a new issue