using System.Text.Json.Nodes;
using w4c_workflows.Models.Credentials;
namespace w4c_workflows.Services.Credentials;
/// Tokens pulled out of an OAuth-style token response.
public sealed record TokenSet(
string AccessToken,
string TokenType,
int? ExpiresInSeconds,
string? RefreshToken,
string? Scope);
///
/// Reads a token response using a type's extraction strategy. This is the
/// counterpart to : injection places a token on a
/// request, extraction finds it in a token endpoint's response (dot-paths such as
/// authed_user.access_token). The OAuth refresh flow that consumes this is
/// a later increment; the strategy is modelled and tested now.
///
public static class CredentialExtractor
{
public static TokenSet? Extract(JsonObject tokenResponse, CredentialExtraction extraction)
{
var accessToken = ReadPath(tokenResponse, extraction.AccessTokenField);
if (string.IsNullOrEmpty(accessToken))
return null;
var expiresIn = int.TryParse(ReadPath(tokenResponse, extraction.ExpiresInField), out var seconds)
? seconds
: (int?)null;
return new TokenSet(
accessToken,
ReadPath(tokenResponse, extraction.TokenTypeField) ?? extraction.DefaultTokenType,
expiresIn,
ReadPath(tokenResponse, extraction.RefreshTokenField),
ReadPath(tokenResponse, extraction.ScopeField));
}
private static string? ReadPath(JsonObject root, string? path)
{
if (string.IsNullOrWhiteSpace(path))
return null;
JsonNode? current = root;
foreach (var segment in path.Split('.', StringSplitOptions.RemoveEmptyEntries))
{
if (current is not JsonObject obj || !obj.TryGetPropertyValue(segment, out current))
return null;
}
return current is JsonValue value && value.TryGetValue(out var text)
? text
: current?.ToJsonString();
}
}