mirror of
https://github.com/CannedNet/CannedNet.git
synced 2026-10-25 15:09:23 -07:00
139 lines
5.1 KiB
C#
139 lines
5.1 KiB
C#
using System.Security.Cryptography;
|
|
using System.Security.Claims;
|
|
using Microsoft.IdentityModel.Tokens;
|
|
using System.IdentityModel.Tokens.Jwt;
|
|
using CannedNet.Services;
|
|
|
|
namespace CannedNet;
|
|
|
|
public class JwtTokenService
|
|
{
|
|
private static readonly RSA _rsa = RSA.Create(2048);
|
|
private readonly RsaSecurityKey _securityKey;
|
|
private readonly SigningCredentials _signingCredentials;
|
|
private const string KeyId = "7C2F041398671515B0862CB23FAF95B03";
|
|
|
|
public JwtTokenService()
|
|
{
|
|
var rsa = Signatures.GetRsaInstance()
|
|
?? throw new InvalidOperationException("jwt - sigs service was not found");
|
|
|
|
_securityKey = new RsaSecurityKey(rsa) { KeyId = KeyId };
|
|
|
|
_signingCredentials = new SigningCredentials(_securityKey, SecurityAlgorithms.RsaSha256);
|
|
}
|
|
|
|
public string GenerateToken(string accountId, string platformId, string platform = "Steam")
|
|
{
|
|
var now = DateTime.UtcNow;
|
|
var exp = now.AddHours(1);
|
|
|
|
// theres gotta be some other better way to do this, but im fucking stupid so :shrug:
|
|
// if it works it works ig idk, someone can clean this up later or i will later
|
|
|
|
var claims = new List<Claim>
|
|
{
|
|
new Claim("iss", "https://api.lapis.codes"),
|
|
new Claim("nbf", ((DateTimeOffset)now).ToUnixTimeSeconds().ToString()),
|
|
new Claim("iat", ((DateTimeOffset)now).ToUnixTimeSeconds().ToString()),
|
|
new Claim("exp", ((DateTimeOffset)exp).ToUnixTimeSeconds().ToString()),
|
|
new Claim("aud", "https://api.lapis.codes/resources"),
|
|
new Claim("amr", "cached_login"),
|
|
new Claim("client_id", "recroom"),
|
|
new Claim("sub", accountId),
|
|
new Claim("auth_time", ((DateTimeOffset)now).ToUnixTimeSeconds().ToString()),
|
|
new Claim("idp", "local"),
|
|
new Claim("platform", platform),
|
|
new Claim("platform_id", platformId),
|
|
new Claim("rn.ver", "20210129"),
|
|
new Claim("rn.plat", "0"),
|
|
new Claim("role", "gameClient"),
|
|
new Claim("role", "developer"),
|
|
new Claim("role", "moderator"),
|
|
new Claim("jti", Guid.NewGuid().ToString("N").Substring(0, 32).ToUpper())
|
|
};
|
|
|
|
claims.Add(new Claim("scope", "profile"));
|
|
claims.Add(new Claim("scope", "rn"));
|
|
claims.Add(new Claim("scope", "rn.accounts"));
|
|
claims.Add(new Claim("scope", "rn.accounts.gc"));
|
|
claims.Add(new Claim("scope", "rn.api"));
|
|
claims.Add(new Claim("scope", "rn.chat"));
|
|
claims.Add(new Claim("scope", "rn.clubs"));
|
|
claims.Add(new Claim("scope", "rn.commerce"));
|
|
claims.Add(new Claim("scope", "rn.match.read"));
|
|
claims.Add(new Claim("scope", "rn.match.write"));
|
|
claims.Add(new Claim("scope", "rn.notify"));
|
|
claims.Add(new Claim("scope", "rn.rooms"));
|
|
claims.Add(new Claim("scope", "rn.storage"));
|
|
claims.Add(new Claim("scope", "offline_access"));
|
|
|
|
var identity = new ClaimsIdentity(claims, "jwt");
|
|
|
|
var handler = new JwtSecurityTokenHandler();
|
|
var token = handler.CreateJwtSecurityToken(
|
|
issuer: "https://auth.lapis.codes",
|
|
audience: "https://auth.lapis.codes/resources",
|
|
subject: identity,
|
|
notBefore: now,
|
|
issuedAt: now,
|
|
expires: exp,
|
|
signingCredentials: _signingCredentials);
|
|
|
|
return handler.WriteToken(token);
|
|
}
|
|
|
|
public string? ValidateAndGetAccountId(string token)
|
|
{
|
|
try
|
|
{
|
|
var handler = new JwtSecurityTokenHandler();
|
|
handler.InboundClaimTypeMap.Clear();
|
|
|
|
var parameters = new TokenValidationParameters
|
|
{
|
|
ValidateIssuerSigningKey = true,
|
|
IssuerSigningKey = _securityKey,
|
|
ValidateIssuer = true,
|
|
ValidIssuer = "https://auth.lapis.codes",
|
|
ValidateAudience = true,
|
|
ValidAudience = "https://auth.lapis.codes/resources",
|
|
ValidateLifetime = true,
|
|
ClockSkew = TimeSpan.Zero
|
|
};
|
|
|
|
var principal = handler.ValidateToken(token, parameters, out SecurityToken validatedToken);
|
|
|
|
var accountIdClaim = principal.Claims.FirstOrDefault(c => c.Type == "sub");
|
|
|
|
return accountIdClaim?.Value;
|
|
}
|
|
catch (Exception ex)
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
|
|
public int? GetAccountIdFromContext(HttpContext context)
|
|
{
|
|
try
|
|
{
|
|
var authHeader = context.Request.Headers.Authorization.ToString();
|
|
if (string.IsNullOrEmpty(authHeader) || !authHeader.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase))
|
|
return null;
|
|
|
|
var token = authHeader.Substring("Bearer ".Length);
|
|
var accountId = ValidateAndGetAccountId(token);
|
|
|
|
if (string.IsNullOrEmpty(accountId) || !int.TryParse(accountId, out var id))
|
|
return null;
|
|
|
|
return id;
|
|
}
|
|
catch
|
|
{
|
|
return null;
|
|
}
|
|
}
|
|
}
|