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);
 | |
|     }
 | |
| } |