194 lines
7.4 KiB
C#
194 lines
7.4 KiB
C#
using System.Text.Json;
|
|
using Ory.Kratos.Client.Api;
|
|
using Ory.Kratos.Client.Model;
|
|
using Ory.Kratos.Client.Client;
|
|
|
|
namespace khmer_eid_backend.Integrations.Ory;
|
|
|
|
public class KratosIntegration
|
|
{
|
|
private readonly FrontendApi _frontendApi;
|
|
private readonly IdentityApi _identityApi;
|
|
private readonly ILogger<KratosIntegration> _logger;
|
|
|
|
public KratosIntegration(IConfiguration config, ILogger<KratosIntegration> logger)
|
|
{
|
|
var publicCfg = new Configuration { BasePath = config["Ory:Kratos:PublicUrl"]! };
|
|
var adminCfg = new Configuration { BasePath = config["Ory:Kratos:AdminUrl"]! };
|
|
|
|
_frontendApi = new FrontendApi(publicCfg);
|
|
_identityApi = new IdentityApi(adminCfg);
|
|
_logger = logger;
|
|
}
|
|
|
|
public async Task<dynamic> CreateOtpRegistrationFlowAsync(string phone)
|
|
{
|
|
var flow = await _frontendApi.CreateNativeRegistrationFlowAsync(returnSessionTokenExchangeCode: true);
|
|
_logger.LogInformation("Started registration flow: {FlowId}", flow.Id);
|
|
|
|
phone = phone.Trim();
|
|
var method = new KratosUpdateRegistrationFlowWithCodeMethod(
|
|
traits: new { phone = phone },
|
|
method: "code"
|
|
);
|
|
|
|
try
|
|
{
|
|
var body = new KratosUpdateRegistrationFlowBody(method);
|
|
_logger.LogInformation("Updating OTP registration flow for {Phone}, flow {FlowId}", phone, flow.Id);
|
|
var updatedFlow = _frontendApi.UpdateRegistrationFlow(flow.Id, body);
|
|
|
|
return flow;
|
|
}
|
|
catch (ApiException ex)
|
|
{
|
|
var res = JsonSerializer.Deserialize<JsonElement>(ex.ErrorContent.ToString()!);
|
|
return ex.ErrorCode switch
|
|
{
|
|
400 => res.GetProperty("state").ToString() == "sent_email" ?
|
|
new
|
|
{
|
|
FlowId = res.GetProperty("id").GetString(),
|
|
State = res.GetProperty("state").GetString(),
|
|
ExpiredAt = res.GetProperty("expires_at").GetDateTime()
|
|
} : throw new Exception("Unhandled Kratos API exception: " + ex.Message),
|
|
_ => throw new Exception("Unhandled Kratos API exception: " + ex.Message),
|
|
};
|
|
}
|
|
}
|
|
|
|
public async Task<dynamic> CompleteOtpRegistrationFlowAsync(string flowId, string phone,string otp)
|
|
{
|
|
phone = phone.Trim();
|
|
var method = new KratosUpdateRegistrationFlowWithCodeMethod(
|
|
code: otp,
|
|
traits: new { phone = phone},
|
|
method: "code"
|
|
);
|
|
var body = new KratosUpdateRegistrationFlowBody(method);
|
|
|
|
try
|
|
{
|
|
var result = await _frontendApi.UpdateRegistrationFlowAsync(flowId, body);
|
|
|
|
_logger.LogInformation("Completed OTP registration flow for flow {FlowId}", flowId);
|
|
return new
|
|
{
|
|
accessToken = result.SessionToken,
|
|
expiredAt = result.Session?.ExpiresAt
|
|
};
|
|
}
|
|
catch (ApiException ex)
|
|
{
|
|
var res = JsonSerializer.Deserialize<JsonElement>(ex.ErrorContent.ToString()!);
|
|
return ex.ErrorCode switch
|
|
{
|
|
400 => res.GetProperty("ui").GetProperty("messages").EnumerateArray().First().GetProperty("text").GetString()!,
|
|
404 => throw new Exception("Registration flow not found."),
|
|
_ => throw new Exception("Unhandled Kratos API exception: " + ex.Message),
|
|
};
|
|
}
|
|
}
|
|
|
|
public async Task<dynamic> CreateOtpLoginFlowAsync(string phone)
|
|
{
|
|
var flow = await _frontendApi.CreateNativeLoginFlowAsync(returnSessionTokenExchangeCode: true);
|
|
_logger.LogInformation("Started login flow: {FlowId}", flow.Id);
|
|
|
|
phone = phone.Trim();
|
|
var method = new KratosUpdateLoginFlowWithCodeMethod(
|
|
method: "code",
|
|
csrfToken: "dfdfdfde",
|
|
identifier: phone
|
|
);
|
|
|
|
try
|
|
{
|
|
var body = new KratosUpdateLoginFlowBody(method);
|
|
_logger.LogInformation("Updating OTP registration flow for {Phone}, flow {FlowId}", phone, flow.Id);
|
|
var updatedFlow = await _frontendApi.UpdateLoginFlowAsync(flow.Id, body);
|
|
|
|
return flow;
|
|
}
|
|
catch (ApiException ex)
|
|
{
|
|
var res = JsonSerializer.Deserialize<JsonElement>(ex.ErrorContent.ToString()!);
|
|
return ex.ErrorCode switch
|
|
{
|
|
400 => res.GetProperty("state").ToString() == "sent_email" ?
|
|
new
|
|
{
|
|
FlowId = res.GetProperty("id").GetString(),
|
|
State = res.GetProperty("state").GetString(),
|
|
ExpiredAt = res.GetProperty("expires_at").GetDateTime()
|
|
} : throw new Exception("Unhandled Kratos API exception: " + ex.Message),
|
|
_ => throw new Exception("Unhandled Kratos API exception: " + ex.Message),
|
|
};
|
|
}
|
|
}
|
|
|
|
public async Task<dynamic> CompleteOtpLoginFlowAsync(string flowId, string phone, string otp)
|
|
{
|
|
phone = "+" + phone.Trim();
|
|
var method = new KratosUpdateLoginFlowWithCodeMethod(
|
|
code: otp,
|
|
method: "code",
|
|
csrfToken: "dfdfdfde",
|
|
identifier: phone
|
|
);
|
|
var body = new KratosUpdateLoginFlowBody(method);
|
|
|
|
try
|
|
{
|
|
var result = await _frontendApi.UpdateLoginFlowAsync(flowId, body);
|
|
|
|
_logger.LogInformation("Completed OTP login flow for flow {FlowId}", flowId);
|
|
return new
|
|
{
|
|
accessToken = result.SessionToken,
|
|
expiredAt = result.Session?.ExpiresAt
|
|
};
|
|
}
|
|
catch (ApiException ex)
|
|
{
|
|
var res = JsonSerializer.Deserialize<JsonElement>(ex.ErrorContent.ToString()!);
|
|
return ex.ErrorCode switch
|
|
{
|
|
400 => res,
|
|
404 => throw new Exception("Login flow not found."),
|
|
_ => throw new Exception("Unhandled Kratos API exception: " + ex.Message),
|
|
};
|
|
}
|
|
}
|
|
|
|
public async Task<dynamic> Logout(string sessionToken)
|
|
{
|
|
var body = new KratosPerformNativeLogoutBody(sessionToken: sessionToken);
|
|
await _frontendApi.PerformNativeLogoutAsync(body);
|
|
_logger.LogInformation("Logged out session with token {SessionToken}", sessionToken);
|
|
return new { Message = "Logged out successfully." };
|
|
}
|
|
|
|
public async Task<dynamic> GetMe(string sessionToken)
|
|
{
|
|
var session = await _frontendApi.ToSessionAsync(xSessionToken: sessionToken);
|
|
_logger.LogInformation("Fetched session for identity {IdentityId}", session.Identity.Id);
|
|
return session;
|
|
}
|
|
|
|
public async Task<KratosSuccessfulNativeRegistration> PasswordRegistrationFlowAsync(string flowId, string phone)
|
|
{
|
|
var method = new KratosUpdateRegistrationFlowWithPasswordMethod(
|
|
password: "add3ae4d8ae8",
|
|
traits: new { phone = phone, identifier = phone },
|
|
method: "password"
|
|
);
|
|
var body = new KratosUpdateRegistrationFlowBody(method);
|
|
var result = await _frontendApi.UpdateRegistrationFlowAsync(flowId, body);
|
|
|
|
Console.WriteLine(JsonSerializer.Serialize(result, new JsonSerializerOptions { WriteIndented = true }));
|
|
_logger.LogInformation("Completed registration flow for {Phone}", phone);
|
|
return result;
|
|
}
|
|
}
|