Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
143 changes: 143 additions & 0 deletions HeaderTestConsole/Program.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
using System;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using Yoti.Auth;
using Yoti.Auth.DigitalIdentity;
using Yoti.Auth.DigitalIdentity.Policy;

namespace HeaderTestConsole
{
class Program
{
static async Task Main(string[] args)
{
Console.WriteLine("=== Yoti SDK Header Test ===\n");

// Mock HTTP Handler to simulate response
var mockHandler = new MockHttpMessageHandler();
var httpClient = new HttpClient(mockHandler);

// Create a test key pair (you'd normally load this from file)
string testKeyPem = @"-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAx3dJSSlIMNKFHGLdqOqNk6fYNZ3hXxZ8WHPIp1fxqPEr3qKF
+LNLR5vqVFvNkQ8vq7y6uGPq5z3MJN4hXzHBM2nGv6W6ybJLEZZQEqSI4+qLnH5u
+H5qEq4C6v3qKwZJGq9ZXX8pKW0h8ZX6W0PqCMV7Pnz8W6yLbKL5q3hV2bE9v7RG
WPLQqOqEbXqJVbp5V8JqKQ7eXVL5XqEL3qKF+LNLR5vqVFvNkQ8vq7y6uGPq5z3M
JN4hXzHBM2nGv6W6ybJLEZZQEqSI4+qLnH5u+H5qEq4C6v3qKwZJGq9ZXX8pKW0h
8ZX6W0PqCMV7Pnz8W6yLbKL5q3hV2bE9v7RGWPLQ+QIDAQABAoIBAH4+V3qZhM4h
jZNfVL5C9FvL4kC3D7qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5
L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5
L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5
L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5
L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5L9qQNXqGCJqK5rJ5LAECgYEA5qKF+LNL
R5vqVFvNkQ8vq7y6uGPq5z3MJN4hXzHBM2nGv6W6ybJLEZZQEqSI4+qLnH5u+H5q
Eq4C6v3qKwZJGq9ZXX8pKW0h8ZX6W0PqCMV7Pnz8W6yLbKL5q3hV2bE9v7RGWPLQ
qOqEbXqJVbp5V8JqKQ7eXVL5XqEL3qKF+LNLRAECgYEA3qKF+LNLR5vqVFvNkQ8v
q7y6uGPq5z3MJN4hXzHBM2nGv6W6ybJLEZZQEqSI4+qLnH5u+H5qEq4C6v3qKwZJ
Gq9ZXX8pKW0h8ZX6W0PqCMV7Pnz8W6yLbKL5q3hV2bE9v7RGWPLQqOqEbXqJVbp5
V8JqKQ7eXVL5XqEL3qKF+LNLRQkCgYBGq9ZXX8pKW0h8ZX6W0PqCMV7Pnz8W6yLb
KL5q3hV2bE9v7RGWPLQqOqEbXqJVbp5V8JqKQ7eXVL5XqEL3qKF+LNLR5vqVFvN
kQ8vq7y6uGPq5z3MJN4hXzHBM2nGv6W6ybJLEZZQEqSI4+qLnH5u+H5qEq4C6v3q
KwZJGq9ZXX8pKW0h8ZX6W0PqCMV7PnwBAoGBAOqEbXqJVbp5V8JqKQ7eXVL5XqEL
3qKF+LNLR5vqVFvNkQ8vq7y6uGPq5z3MJN4hXzHBM2nGv6W6ybJLEZZQEqSI4+qL
nH5u+H5qEq4C6v3qKwZJGq9ZXX8pKW0h8ZX6W0PqCMV7Pnz8W6yLbKL5q3hV2bE9
v7RGWPLQqOqEbXqJVbp5V8JqKQ7eXVL5XqECgYBKL5q3hV2bE9v7RGWPLQqOqEbX
qJVbp5V8JqKQ7eXVL5XqEL3qKF+LNLR5vqVFvNkQ8vq7y6uGPq5z3MJN4hXzHBM2
nGv6W6ybJLEZZQEqSI4+qLnH5u+H5qEq4C6v3qKwZJGq9ZXX8pKW0h8ZX6W0PqCM
V7Pnz8W6yLbKL5q3hV2bE9v7RGWPLQqOqA==
-----END RSA PRIVATE KEY-----";

try
{
// Create a DigitalIdentityClient
Console.WriteLine("Creating DigitalIdentityClient...");
var stringReader = new StringReader(testKeyPem);
var streamReader = new StreamReader(new MemoryStream(System.Text.Encoding.UTF8.GetBytes(testKeyPem)));

var client = new DigitalIdentityClient(
httpClient,
"test-sdk-id",
streamReader
);

// Create a simple share session request
Console.WriteLine("Creating ShareSessionRequest...");
var policy = new PolicyBuilder()
.WithFullName()
.WithEmail()
.Build();

var sessionRequest = new ShareSessionRequestBuilder()
.WithPolicy(policy)
.WithRedirectUri("https://example.com/callback")
.Build();

// Call CreateShareSession
Console.WriteLine("Calling CreateShareSession...\n");
var result = client.CreateShareSession(sessionRequest);

// Display headers
Console.WriteLine("=== RESPONSE HEADERS ===");
foreach (var header in result.Headers)
{
Console.WriteLine($"{header.Key}: {string.Join(", ", header.Value)}");
}

Console.WriteLine($"\n=== SPECIAL HEADERS ===");
Console.WriteLine($"X-Request-ID (via RequestId property): {result.RequestId}");
Console.WriteLine($"X-Request-ID (via GetHeaderValue): {result.GetHeaderValue("X-Request-ID")}");
Console.WriteLine($"Content-Type: {result.GetHeaderValue("Content-Type")}");

Console.WriteLine($"\n=== RESPONSE DATA ===");
Console.WriteLine($"Session ID: {result.Data.Id}");
Console.WriteLine($"Status: {result.Data.Status}");
}
catch (Exception ex)
{
Console.WriteLine($"\nError: {ex.Message}");
Console.WriteLine($"Stack: {ex.StackTrace}");
}

Console.WriteLine("\n=== Test Complete ===");
}
}

// Mock HTTP handler for testing
public class MockHttpMessageHandler : HttpMessageHandler
{
protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, System.Threading.CancellationToken cancellationToken)
{
Console.WriteLine($"\n--- HTTP Request ---");
Console.WriteLine($"Method: {request.Method}");
Console.WriteLine($"URI: {request.RequestUri}");
Console.WriteLine($"Request Headers:");
foreach (var header in request.Headers)
{
Console.WriteLine($" {header.Key}: {string.Join(", ", header.Value)}");
}
Console.WriteLine($"--- End Request ---\n");

var response = new HttpResponseMessage(HttpStatusCode.OK);

// Add various headers
response.Headers.Add("X-Request-ID", "mock-request-id-12345");
response.Headers.Add("X-Yoti-Request-Trace", "trace-value-abc");
response.Headers.Add("X-Custom-Header", "custom-value");
response.Headers.Add("Date", DateTime.UtcNow.ToString("R"));

// Add content with content headers
var jsonContent = @"{
""id"": ""mock-session-123"",
""status"": ""CREATED"",
""qr_code"": ""https://example.com/qr""
}";

response.Content = new StringContent(jsonContent, System.Text.Encoding.UTF8, "application/json");
response.Content.Headers.Add("X-Content-Custom", "content-header-value");

return Task.FromResult(response);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,18 @@ public IActionResult DigitalIdentity()

var SessionResult = yotiClient.CreateShareSession(sessionReq);

// Log all headers
_logger.LogInformation("=== CreateSession Headers ===");
foreach (var header in SessionResult.Headers)
{
_logger.LogInformation($"Header: {header.Key} = {string.Join(", ", header.Value)}");
}
_logger.LogInformation($"X-Request-ID from helper: {SessionResult.RequestId}");
_logger.LogInformation("=== End Headers ===");

var sharedReceiptResponse = new SharedReceiptResponse();
ViewBag.YotiClientSdkId = _clientSdkId;
ViewBag.sessionID = SessionResult.Id;
ViewBag.sessionID = SessionResult.Data.Id;

return View("AdvancedIdentityShare", sharedReceiptResponse);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,18 @@ public IActionResult DigitalIdentity()

var SessionResult = yotiClient.CreateShareSession(sessionReq);

// Log all headers
_logger.LogInformation("=== CreateSession Headers ===");
foreach (var header in SessionResult.Headers)
{
_logger.LogInformation($"Header: {header.Key} = {string.Join(", ", header.Value)}");
}
_logger.LogInformation($"X-Request-ID from helper: {SessionResult.RequestId}");
_logger.LogInformation("=== End Headers ===");

var sharedReceiptResponse = new SharedReceiptResponse();
ViewBag.YotiClientSdkId = _clientSdkId;
ViewBag.sessionID = SessionResult.Id;
ViewBag.sessionID = SessionResult.Data.Id;

return View("DigitalIdentity", sharedReceiptResponse);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,14 @@ public IActionResult ReceiptInfo(string ReceiptID)

var ReceiptResult = yotiClient.GetShareReceipt(ReceiptID);

DisplayAttributes displayAttributes = CreateDisplayAttributes(ReceiptResult.UserContent.UserProfile.AttributeCollection);
if (ReceiptResult.UserContent.UserProfile.FullName != null)
DisplayAttributes displayAttributes = CreateDisplayAttributes(ReceiptResult.Data.UserContent.UserProfile.AttributeCollection);
if (ReceiptResult.Data.UserContent.UserProfile.FullName != null)
{
displayAttributes.FullName = ReceiptResult.UserContent.UserProfile.FullName.GetValue();
displayAttributes.FullName = ReceiptResult.Data.UserContent.UserProfile.FullName.GetValue();
}

YotiAttribute<Image> selfie = ReceiptResult.UserContent.UserProfile.Selfie;
if (ReceiptResult.UserContent.UserProfile.Selfie != null)
YotiAttribute<Image> selfie = ReceiptResult.Data.UserContent.UserProfile.Selfie;
if (ReceiptResult.Data.UserContent.UserProfile.Selfie != null)
{
displayAttributes.Base64Selfie = selfie.GetValue().GetBase64URI();
}
Expand Down
143 changes: 143 additions & 0 deletions src/Yoti.Auth/DigitalIdentity/DigitalIdentityService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,49 @@ internal static async Task<ShareSessionResult> CreateShareSession(HttpClient htt
}
}

/// <summary>
/// Creates a share session and returns the result with HTTP response headers
/// </summary>
internal static async Task<YotiHttpResponse<ShareSessionResult>> CreateShareSessionWithHeaders(HttpClient httpClient, Uri apiUrl, string sdkId, AsymmetricCipherKeyPair keyPair, ShareSessionRequest shareSessionRequestPayload)
{
Validation.NotNull(httpClient, nameof(httpClient));
Validation.NotNull(apiUrl, nameof(apiUrl));
Validation.NotNull(sdkId, nameof(sdkId));
Validation.NotNull(keyPair, nameof(keyPair));
Validation.NotNull(shareSessionRequestPayload, nameof(shareSessionRequestPayload));

string serializedScenario = JsonConvert.SerializeObject(
shareSessionRequestPayload,
new JsonSerializerSettings
{
NullValueHandling = NullValueHandling.Ignore
});
byte[] body = Encoding.UTF8.GetBytes(serializedScenario);

Request shareSessionRequest = new RequestBuilder()
.WithKeyPair(keyPair)
.WithBaseUri(apiUrl)
.WithHeader(yotiAuthId, sdkId)
.WithEndpoint(sessionCreation)
.WithQueryParam("sdkID", sdkId)
.WithHttpMethod(HttpMethod.Post)
.WithContent(body)
.Build();

return await shareSessionRequest.ExecuteWithHeaders(httpClient, async response =>
{
if (!response.IsSuccessStatusCode)
{
Response.CreateYotiExceptionFromStatusCode<DigitalIdentityException>(response);
}

var responseObject = await response.Content.ReadAsStringAsync();
var deserialized = await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<ShareSessionResult>(responseObject));

return deserialized;
}).ConfigureAwait(false);
}

internal static async Task<GetSessionResult> GetSession(HttpClient httpClient, Uri apiUrl, string sdkId, AsymmetricCipherKeyPair keyPair, string sessionId)
{
Validation.NotNull(httpClient, nameof(httpClient));
Expand Down Expand Up @@ -297,6 +340,106 @@ public static async Task<SharedReceiptResponse> GetShareReceipt(HttpClient httpC
}
}

/// <summary>
/// Gets a share receipt and returns the result with HTTP response headers
/// </summary>
public static async Task<YotiHttpResponse<SharedReceiptResponse>> GetShareReceiptWithHeaders(HttpClient httpClient, string clientSdkId, Uri apiUrl, AsymmetricCipherKeyPair key, string receiptId)
{
Validation.NotNullOrEmpty(receiptId, nameof(receiptId));

string receiptUrl = Base64ToBase64URL(receiptId);
string endpoint = string.Format(receiptRetrieval, receiptUrl);

Request receiptRequest = new RequestBuilder()
.WithKeyPair(key)
.WithBaseUri(apiUrl)
.WithHeader(yotiAuthId, clientSdkId)
.WithEndpoint(endpoint)
.WithQueryParam("sdkID", clientSdkId)
.WithHttpMethod(HttpMethod.Get)
.Build();

// Use ExecuteWithHeaders to capture the response headers
return await receiptRequest.ExecuteWithHeaders(httpClient, async response =>
{
try
{
if (!response.IsSuccessStatusCode)
{
Response.CreateYotiExceptionFromStatusCode<DigitalIdentityException>(response);
}

var responseObject = await response.Content.ReadAsStringAsync();
var receiptResponse = await Task.Factory.StartNew(() => JsonConvert.DeserializeObject<ReceiptResponse>(responseObject));

var itemKeyId = receiptResponse.WrappedItemKeyId;
var encryptedItemKeyResponse = await GetReceiptItemKey(httpClient, itemKeyId, clientSdkId, apiUrl, key);
var receiptContentKey = CryptoEngine.UnwrapReceiptKey(receiptResponse.WrappedKey, encryptedItemKeyResponse.Value, encryptedItemKeyResponse.Iv, key);

var (attrData, aextra, decryptAttrDataError) = DecryptReceiptContent(receiptResponse.Content, receiptContentKey);
if (decryptAttrDataError != null)
{
throw new Exception($"An unexpected error occurred: {decryptAttrDataError.Message}");
}

var parsedAttributesApp = AttributeConverter.ConvertToBaseAttributes(attrData);
var appProfile = new ApplicationProfile(parsedAttributesApp);

var (attrOtherData, aOtherExtra, decryptOtherAttrDataError) = DecryptReceiptContent(receiptResponse.OtherPartyContent, receiptContentKey);
if (decryptOtherAttrDataError != null)
{
throw new Exception($"An unexpected error occurred: {decryptOtherAttrDataError.Message}");
}

var userProfile = new YotiProfile();
if (attrOtherData != null)
{
var parsedAttributesUser = AttributeConverter.ConvertToBaseAttributes(attrOtherData);
userProfile = new YotiProfile(parsedAttributesUser);
}

ExtraData userExtraData = new ExtraData();
if (aOtherExtra != null)
{
userExtraData = ExtraDataConverter.ParseExtraDataProto(aOtherExtra);
}

ExtraData appExtraData = new ExtraData();
if (aextra != null)
{
appExtraData = ExtraDataConverter.ParseExtraDataProto(aextra);
}

var sharedReceiptResponse = new SharedReceiptResponse
{
ID = receiptResponse.ID,
SessionID = receiptResponse.SessionID,
RememberMeID = receiptResponse.RememberMeID,
ParentRememberMeID = receiptResponse.ParentRememberMeID,
Timestamp = receiptResponse.Timestamp,
UserContent = new UserContent
{
UserProfile = userProfile,
ExtraData = userExtraData
},
ApplicationContent = new ApplicationContent
{
ApplicationProfile = appProfile,
ExtraData = appExtraData
},
Error = receiptResponse.Error,
ErrorDetails = receiptResponse.ErrorDetails
};

return sharedReceiptResponse;
}
catch (Exception ex)
{
throw new Exception($"An unexpected error occurred: {ex.Message}");
}
}).ConfigureAwait(false);
}

private static async Task<ReceiptItemKeyResponse> GetReceiptItemKey(HttpClient httpClient, string receiptItemKeyId, string sdkId, Uri apiUrl, AsymmetricCipherKeyPair keyPair)
{
Validation.NotNull(httpClient, nameof(httpClient));
Expand Down
Loading
Loading