96 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
			
		
		
	
	
			96 lines
		
	
	
		
			3.0 KiB
		
	
	
	
		
			C#
		
	
	
	
	
	
using System.Security.Cryptography;
 | 
						|
using DysonNetwork.Shared.Models;
 | 
						|
using Microsoft.Extensions.Configuration;
 | 
						|
 | 
						|
namespace DysonNetwork.Pass.Auth;
 | 
						|
 | 
						|
public class CompactTokenService(IConfiguration config)
 | 
						|
{
 | 
						|
    private readonly string _privateKeyPath = config["AuthToken:PrivateKeyPath"] 
 | 
						|
        ?? throw new InvalidOperationException("AuthToken:PrivateKeyPath configuration is missing");
 | 
						|
    
 | 
						|
    public string CreateToken(Session session)
 | 
						|
    {
 | 
						|
        // Load the private key for signing
 | 
						|
        var privateKeyPem = File.ReadAllText(_privateKeyPath);
 | 
						|
        using var rsa = RSA.Create();
 | 
						|
        rsa.ImportFromPem(privateKeyPem);
 | 
						|
        
 | 
						|
        // Create and return a single token
 | 
						|
        return CreateCompactToken(session.Id, rsa);
 | 
						|
    }
 | 
						|
 | 
						|
    private string CreateCompactToken(Guid sessionId, RSA rsa)
 | 
						|
    {
 | 
						|
        // Create the payload: just the session ID
 | 
						|
        var payloadBytes = sessionId.ToByteArray();
 | 
						|
        
 | 
						|
        // Base64Url encode the payload
 | 
						|
        var payloadBase64 = Base64UrlEncode(payloadBytes);
 | 
						|
        
 | 
						|
        // Sign the payload with RSA-SHA256
 | 
						|
        var signature = rsa.SignData(payloadBytes, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
 | 
						|
        
 | 
						|
        // Base64Url encode the signature
 | 
						|
        var signatureBase64 = Base64UrlEncode(signature);
 | 
						|
        
 | 
						|
        // Combine payload and signature with a dot
 | 
						|
        return $"{payloadBase64}.{signatureBase64}";
 | 
						|
    }
 | 
						|
    
 | 
						|
    public bool ValidateToken(string token, out Guid sessionId)
 | 
						|
    {
 | 
						|
        sessionId = Guid.Empty;
 | 
						|
        
 | 
						|
        try
 | 
						|
        {
 | 
						|
            // Split the token
 | 
						|
            var parts = token.Split('.');
 | 
						|
            if (parts.Length != 2)
 | 
						|
                return false;
 | 
						|
            
 | 
						|
            // Decode the payload
 | 
						|
            var payloadBytes = Base64UrlDecode(parts[0]);
 | 
						|
            
 | 
						|
            // Extract session ID
 | 
						|
            sessionId = new Guid(payloadBytes);
 | 
						|
            
 | 
						|
            // Load public key for verification
 | 
						|
            var publicKeyPem = File.ReadAllText(config["AuthToken:PublicKeyPath"]!);
 | 
						|
            using var rsa = RSA.Create();
 | 
						|
            rsa.ImportFromPem(publicKeyPem);
 | 
						|
            
 | 
						|
            // Verify signature
 | 
						|
            var signature = Base64UrlDecode(parts[1]);
 | 
						|
            return rsa.VerifyData(payloadBytes, signature, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1);
 | 
						|
        }
 | 
						|
        catch
 | 
						|
        {
 | 
						|
            return false;
 | 
						|
        }
 | 
						|
    }
 | 
						|
    
 | 
						|
    // Helper methods for Base64Url encoding/decoding
 | 
						|
    private static string Base64UrlEncode(byte[] data)
 | 
						|
    {
 | 
						|
        return Convert.ToBase64String(data)
 | 
						|
            .TrimEnd('=')
 | 
						|
            .Replace('+', '-')
 | 
						|
            .Replace('/', '_');
 | 
						|
    }
 | 
						|
    
 | 
						|
    private static byte[] Base64UrlDecode(string base64Url)
 | 
						|
    {
 | 
						|
        string padded = base64Url
 | 
						|
            .Replace('-', '+')
 | 
						|
            .Replace('_', '/');
 | 
						|
            
 | 
						|
        switch (padded.Length % 4)
 | 
						|
        {
 | 
						|
            case 2: padded += "=="; break;
 | 
						|
            case 3: padded += "="; break;
 | 
						|
        }
 | 
						|
        
 | 
						|
        return Convert.FromBase64String(padded);
 | 
						|
    }
 | 
						|
} |