Skip to content

Commit 881e8af

Browse files
committed
Version 5.0.0-beta
1 parent ab25709 commit 881e8af

File tree

187 files changed

+10117
-6028
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

187 files changed

+10117
-6028
lines changed

Auth/AuthResponseService.cs

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* ============================================================================
3+
* Mozilla Public License 2.0 (MPL-2.0)
4+
* Autor: FISCAL API S. DE R.L. DE C.V. - https://fiscalapi.com
5+
* ============================================================================
6+
*
7+
* Este código está sujeto a los términos de la Mozilla Public License v2.0.
8+
* Licencia completa: https://mozilla.org/MPL/2.0
9+
*
10+
* AVISO: Este software se proporciona "tal como está" sin garantías de ningún
11+
* tipo. Al usar, modificar o distribuir este código debe mantener esta
12+
* atribución y las referencias al autor.
13+
*
14+
* ============================================================================
15+
*/
16+
17+
using Fiscalapi.Credentials.Core;
18+
using XmlDownloader.Auth.Models;
19+
using XmlDownloader.Auth.Models.Sat;
20+
using XmlDownloader.Common;
21+
using XmlDownloader.Common.Enums;
22+
using XmlDownloader.Common.Http;
23+
24+
namespace XmlDownloader.Auth;
25+
26+
public static class AuthResponseService
27+
{
28+
public static AuthResponse Build(SatResponse satResponse, ICredential credential)
29+
{
30+
/*
31+
*<s:envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/" xmlns:u="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd">
32+
<s:header>
33+
<o:security s:mustunderstand="1" xmlns:o="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
34+
<u:timestamp u:id="_0">
35+
<u:created>2023-07-14T15:42:58.786Z</u:created>
36+
<u:expires>2023-07-14T15:47:58.786Z</u:expires>
37+
</u:timestamp>
38+
</o:security>
39+
</s:header>
40+
<s:body>
41+
<autenticaresponse xmlns="http://DescargaMasivaTerceros.gob.mx">
42+
<autenticaresult>token...</autenticaresult>
43+
</autenticaresponse>
44+
</s:body>
45+
</s:envelope>
46+
47+
<s:envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
48+
<s:body>
49+
<s:fault>
50+
<faultcode xmlns:a="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">a:InvalidSecurity</faultcode>
51+
<faultstring xml:lang="en-US">An error occurred when verifying security for the message.</faultstring>
52+
</s:fault>
53+
</s:body>
54+
</s:envelope>
55+
*/
56+
if (satResponse.IsSuccessStatusCode)
57+
{
58+
var envelope = XmlSerializerService.Deserialize<AuthEnvelope>(satResponse.RawResponse!);
59+
return new AuthResponse
60+
{
61+
Succeeded = true,
62+
SatStatus = SatStatus.RequestSucceeded,
63+
SatStatusCode = SatStatus.RequestSucceeded.ToEnumCode(),
64+
SatMessage = "",
65+
TokenValue = envelope?.Body?.AutenticaResponse?.AutenticaResult,
66+
ValidFrom = envelope?.Header?.Security?.Timestamp?.Created,
67+
ValidTo = envelope?.Header?.Security?.Timestamp?.Expires,
68+
RawRequest = satResponse.RawRequest,
69+
RawResponse = satResponse.RawResponse,
70+
Tin = credential.Certificate.Rfc
71+
};
72+
}
73+
74+
if (satResponse.RawResponse is not null && satResponse.RawResponse.ToLowerInvariant().Contains("fault"))
75+
{
76+
var faultEnvelope = XmlSerializerService.Deserialize<AuthFaultEnvelope>(satResponse.RawResponse);
77+
return new AuthResponse
78+
{
79+
Succeeded = false,
80+
SatStatus = SatStatus.Unknown,
81+
SatStatusCode = faultEnvelope?.Body?.Fault?.FaultCode,
82+
SatMessage = faultEnvelope?.Body?.Fault?.FaultMessage,
83+
RawRequest = satResponse.RawRequest,
84+
RawResponse = satResponse.RawResponse,
85+
};
86+
}
87+
88+
return new AuthResponse
89+
{
90+
Succeeded = false,
91+
SatStatus = SatStatus.Unknown,
92+
SatStatusCode = "UnknownError",
93+
SatMessage = $"StatusCode: {satResponse.HttpStatusCode} Message: {satResponse.ReasonPhrase}",
94+
RawRequest = satResponse.RawRequest,
95+
RawResponse = satResponse.RawResponse,
96+
};
97+
}
98+
}

Auth/AuthService.cs

Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
/*
2+
* ============================================================================
3+
* Mozilla Public License 2.0 (MPL-2.0)
4+
* Autor: FISCAL API S. DE R.L. DE C.V. - https://fiscalapi.com
5+
* ============================================================================
6+
*
7+
* Este código está sujeto a los términos de la Mozilla Public License v2.0.
8+
* Licencia completa: https://mozilla.org/MPL/2.0
9+
*
10+
* AVISO: Este software se proporciona "tal como está" sin garantías de ningún
11+
* tipo. Al usar, modificar o distribuir este código debe mantener esta
12+
* atribución y las referencias al autor.
13+
*
14+
* ============================================================================
15+
*/
16+
17+
using Fiscalapi.Credentials.Common;
18+
using Fiscalapi.Credentials.Core;
19+
using XmlDownloader.Auth.Models;
20+
using XmlDownloader.Common;
21+
using XmlDownloader.Common.Http;
22+
23+
namespace XmlDownloader.Auth;
24+
25+
/// <summary>
26+
/// SAT Authentication Service.
27+
/// </summary>
28+
public class AuthService : SatService, IAuthService
29+
{
30+
/// <summary>
31+
/// Authenticates with SAT using the provided credential and returns the authentication token
32+
/// </summary>
33+
public async Task<AuthResponse> AuthenticateAsync(ICredential credential,
34+
CancellationToken cancellationToken = default)
35+
{
36+
// Generate Sat XML security token ID
37+
var uuid = CreateSecurityToken();
38+
39+
// Create digest and signature using unified template
40+
var digest = CreateDigest(credential);
41+
var signature = CreateSignature(digest, credential, uuid);
42+
43+
// Build SOAP envelope
44+
var authXml = BuildEnvelope(digest, uuid, credential.Certificate.RawDataBytes.ToBase64String(),
45+
signature);
46+
47+
// Send request
48+
var satResponse = await SendRequestAsync(
49+
url: SatUrl.AuthUrl,
50+
action: SatUrl.AuthAction,
51+
payload: authXml,
52+
cancellationToken: cancellationToken);
53+
54+
// Map response
55+
var authResponse = AuthResponseService.Build(satResponse, credential);
56+
57+
return authResponse;
58+
}
59+
60+
/// <summary>
61+
/// Creates the digest for authentication
62+
/// </summary>
63+
private static Digest CreateDigest(ICredential credential)
64+
{
65+
var created = DateTime.UtcNow;
66+
var expires = created.AddSeconds(300);
67+
68+
var timestampXml = XmlTemplates.TimestampTemplate
69+
.Replace(XmlPlaceholders.Created, created.ToSatFormat())
70+
.Replace(XmlPlaceholders.Expires, expires.ToSatFormat());
71+
72+
var base64Hash = credential.CreateHash(timestampXml.Clean());
73+
74+
return new Digest
75+
{
76+
Created = created,
77+
Expires = expires,
78+
Base64Digested = base64Hash
79+
};
80+
}
81+
82+
/// <summary>
83+
/// Creates the complete signature
84+
/// </summary>
85+
private static string CreateSignature(Digest digest, ICredential credential, string uuid)
86+
{
87+
// Create SignedInfo
88+
var signedInfo = XmlTemplates.SignedInfo
89+
.Replace(XmlPlaceholders.Base64Digested, digest.Base64Digested)
90+
.Replace(XmlPlaceholders.ReferenceUri, "#_0"); // Auth siempre usa #_0
91+
92+
// Sign the SignedInfo
93+
var signatureValue = credential.SignData(signedInfo.Clean()).ToBase64String();
94+
95+
// Remove namespace from SignedInfo for embedding
96+
var cleanSignedInfo = signedInfo.Replace(
97+
@"<SignedInfo xmlns=""http://www.w3.org/2000/09/xmldsig#"">",
98+
"<SignedInfo>");
99+
100+
// Create KeyInfo
101+
var keyInfo = XmlTemplates.SecurityTokenReferenceKeyInfo
102+
.Replace(XmlPlaceholders.Uuid, uuid);
103+
104+
// Build complete signature
105+
var signature = XmlTemplates.SignatureTemplate
106+
.Replace(XmlPlaceholders.SignedInfo, cleanSignedInfo)
107+
.Replace(XmlPlaceholders.Base64Signature, signatureValue)
108+
.Replace(XmlPlaceholders.KeyInfo, keyInfo);
109+
110+
return signature;
111+
}
112+
113+
/// <summary>
114+
/// Builds the complete authentication envelope
115+
/// </summary>
116+
private static string BuildEnvelope(Digest digest, string uuid, string certificate, string signature)
117+
{
118+
var binarySecurityToken = XmlTemplates.BinarySecurityToken
119+
.Replace(XmlPlaceholders.Uuid, uuid)
120+
.Replace(XmlPlaceholders.Base64Cer, certificate);
121+
122+
var authEnvelope = XmlTemplates.AuthEnvelope
123+
.Replace(XmlPlaceholders.Created, digest.Created.ToSatFormat())
124+
.Replace(XmlPlaceholders.Expires, digest.Expires.ToSatFormat())
125+
.Replace(XmlPlaceholders.Uuid, uuid)
126+
.Replace(XmlPlaceholders.Base64Cer, certificate)
127+
.Replace(XmlPlaceholders.SignatureData, signature);
128+
129+
return authEnvelope.Clean();
130+
}
131+
132+
/// <summary>
133+
/// Creates a unique XML security token ID
134+
/// </summary>
135+
private static string CreateSecurityToken()
136+
{
137+
var uuid = Guid.NewGuid().ToString("D").ToLowerInvariant();
138+
139+
return $"uuid-{uuid}-4";
140+
}
141+
}

Auth/IAuthService.cs

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
/*
2+
* ============================================================================
3+
* Mozilla Public License 2.0 (MPL-2.0)
4+
* Autor: FISCAL API S. DE R.L. DE C.V. - https://fiscalapi.com
5+
* ============================================================================
6+
*
7+
* Este código está sujeto a los términos de la Mozilla Public License v2.0.
8+
* Licencia completa: https://mozilla.org/MPL/2.0
9+
*
10+
* AVISO: Este software se proporciona "tal como está" sin garantías de ningún
11+
* tipo. Al usar, modificar o distribuir este código debe mantener esta
12+
* atribución y las referencias al autor.
13+
*
14+
* ============================================================================
15+
*/
16+
17+
using Fiscalapi.Credentials.Core;
18+
using XmlDownloader.Auth.Models;
19+
20+
namespace XmlDownloader.Auth;
21+
22+
/// <summary>
23+
/// Interface for the SAT Authentication Service.
24+
/// </summary>
25+
public interface IAuthService
26+
{
27+
/// <summary>
28+
/// Authenticates with SAT using the provided credential and returns the authentication token.
29+
/// </summary>
30+
/// <param name="credential"></param>
31+
/// <param name="cancellationToken">CancellationToken</param>
32+
/// <returns></returns>
33+
Task<AuthResponse> AuthenticateAsync(ICredential credential, CancellationToken cancellationToken = default);
34+
}

Auth/Models/AuthResponse.cs

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
/*
2+
* ============================================================================
3+
* Mozilla Public License 2.0 (MPL-2.0)
4+
* Autor: FISCAL API S. DE R.L. DE C.V. - https://fiscalapi.com
5+
* ============================================================================
6+
*
7+
* Este código está sujeto a los términos de la Mozilla Public License v2.0.
8+
* Licencia completa: https://mozilla.org/MPL/2.0
9+
*
10+
* AVISO: Este software se proporciona "tal como está" sin garantías de ningún
11+
* tipo. Al usar, modificar o distribuir este código debe mantener esta
12+
* atribución y las referencias al autor.
13+
*
14+
* ============================================================================
15+
*/
16+
17+
using XmlDownloader.Common.Http;
18+
19+
namespace XmlDownloader.Auth.Models;
20+
21+
/// <summary>
22+
/// Response from the authentication service
23+
/// </summary>
24+
public class AuthResponse : BaseResponse
25+
{
26+
/// <summary>
27+
/// The JWT token returned in a successful response.
28+
/// </summary>
29+
public string? TokenValue { get; set; }
30+
31+
/// <summary>
32+
/// The timestamp indicating when the token becomes valid.
33+
/// </summary>
34+
public DateTime? ValidFrom { get; set; }
35+
36+
/// <summary>
37+
/// The timestamp indicating when the token expires.
38+
/// </summary>
39+
public DateTime? ValidTo { get; set; }
40+
41+
/// <summary>
42+
/// RFC associated with the Auth response. The tax identification number (TIN)
43+
/// </summary>
44+
public string? Tin { get; set; }
45+
46+
/// <summary>
47+
/// Get token details as a structured object
48+
/// </summary>
49+
public Token? Token
50+
{
51+
get
52+
{
53+
if (!string.IsNullOrWhiteSpace(TokenValue))
54+
{
55+
return new Token
56+
{
57+
Value = TokenValue,
58+
ValidFrom = ValidFrom,
59+
ValidTo = ValidTo,
60+
Tin = Tin
61+
};
62+
}
63+
64+
return null;
65+
}
66+
}
67+
}

Auth/Models/Digest.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/*
2+
* ============================================================================
3+
* Mozilla Public License 2.0 (MPL-2.0)
4+
* Autor: FISCAL API S. DE R.L. DE C.V. - https://fiscalapi.com
5+
* ============================================================================
6+
*
7+
* Este código está sujeto a los términos de la Mozilla Public License v2.0.
8+
* Licencia completa: https://mozilla.org/MPL/2.0
9+
*
10+
* AVISO: Este software se proporciona "tal como está" sin garantías de ningún
11+
* tipo. Al usar, modificar o distribuir este código debe mantener esta
12+
* atribución y las referencias al autor.
13+
*
14+
* ============================================================================
15+
*/
16+
17+
namespace XmlDownloader.Auth.Models;
18+
19+
public class Digest
20+
{
21+
public DateTime Created { get; set; }
22+
public DateTime Expires { get; set; }
23+
public string Base64Digested { get; set; } = string.Empty;
24+
}

0 commit comments

Comments
 (0)