using Dalion.HttpMessageSigning.Signing; | |
using Dalion.HttpMessageSigning; | |
using Microsoft.IdentityModel.Tokens; | |
using Newtonsoft.Json; | |
using System; | |
using System.Net.Http.Headers; | |
using System.Net.Http; | |
using System.Security.Cryptography.X509Certificates; | |
using System.Security.Cryptography; | |
using System.Threading.Tasks; | |
using System.Collections.Generic; | |
using System.Net; | |
using System.IdentityModel.Tokens.Jwt; | |
using System.Security.Claims; | |
namespace Services | |
{ | |
internal class MagdaAccessToken | |
{ | |
internal MagdaAccessToken() | |
{ | |
this.TokenDate = DateTime.Now; | |
} | |
[JsonProperty("access_token")] | |
public string AccessToken { get; set; } | |
public DateTime ExpireDate | |
{ | |
get | |
{ | |
return this.TokenDate.AddSeconds(this.ExpiresIn); | |
} | |
} | |
public DateTime TokenDate { get; private set; } | |
[JsonProperty("expires_in")] | |
public int ExpiresIn { get; set; } | |
[JsonProperty("token_type")] | |
public string TokenType { get; set; } | |
[JsonProperty("scope")] | |
public string Scope { get; set; } | |
} | |
public class MagdaTokenBasedServicesExample | |
{ | |
private readonly string _keyId = "rsa-key-1"; | |
private readonly string _clientId = "1234"; //TODO: Provide your client id | |
private readonly string _authUrl = "https://beta.oauth.vlaanderen.be/authorization/ws/oauth/v2/token"; | |
private readonly string _messagesEndpointUrl = "https://iv.api.tni-vlaanderen.be/api/v1/messages/messages"; | |
private MagdaAccessToken _accessToken; | |
private X509Certificate2 _clientCertificate; | |
private IRequestSignerFactory _requestSignerFactory; | |
private HttpClient _httpClient; | |
private JwtSecurityTokenHandler _tokenHandler; | |
public MagdaTokenBasedServicesExample(IRequestSignerFactory requestSignerFactory) | |
{ | |
this._requestSignerFactory = requestSignerFactory; | |
this._clientCertificate = null; // TODO: Load your VO-DCB client certificate (pfx) | |
var httpClientHandler = new HttpClientHandler(); | |
httpClientHandler.ClientCertificates.Add(this._clientCertificate); | |
this._httpClient = new HttpClient(httpClientHandler); | |
this._tokenHandler = new JwtSecurityTokenHandler(); | |
} | |
public async Task<HttpResponseMessage> SendMessage() | |
{ | |
if (this._accessToken == null || (this._accessToken != null && this._accessToken.ExpireDate <= DateTime.Now.AddMinutes(1))) | |
this._accessToken = await this.GetAccessToken(); | |
var request = new HttpRequestMessage | |
{ | |
Method = HttpMethod.Post, | |
RequestUri = new Uri(this._messagesEndpointUrl) | |
}; | |
await SignHttpRequestMessage(request); | |
// Add additional payload properties here | |
// Like MessageToSend | |
return await this._httpClient.SendAsync(request); | |
} | |
private async Task SignHttpRequestMessage(HttpRequestMessage request) | |
{ | |
request.Headers.Add("signature-public-key", WrapCertificateInJWK(this._clientCertificate, _keyId)); | |
request.Headers.Date = DateTimeOffset.UtcNow; | |
SigningSettings settings = new SigningSettings | |
{ | |
SignatureAlgorithm = SignatureAlgorithm.CreateForSigning(this._clientCertificate, HashAlgorithmName.SHA512), | |
DigestHashAlgorithm = HashAlgorithmName.SHA512, | |
EnableNonce = false, | |
Headers = new[] | |
{ | |
new HeaderName("signature-public-key") | |
}, | |
UseDeprecatedAlgorithmParameter = true | |
}; | |
var requestSigner = this._requestSignerFactory.Create(_keyId, settings); | |
await requestSigner.Sign(request); | |
request.Headers.Add("Signature", request.Headers.Authorization.Parameter); | |
request.Headers.Add("x-correlation-id", Guid.NewGuid().ToString()); | |
request.Headers.Add("Accept", "application/json"); | |
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", this._accessToken.AccessToken); | |
} | |
private string WrapCertificateInJWK(X509Certificate2 certificate, string keyName) | |
{ | |
var x509SigningKey = new X509SigningCredentials(certificate); | |
var x509Key = new X509SecurityKey(certificate); | |
var cert64 = Convert.ToBase64String(x509Key.Certificate.RawData); | |
var pubKey = x509Key.PublicKey as RSA; | |
var parameters = pubKey.ExportParameters(false); | |
var exponent = Convert.ToBase64String(parameters.Exponent); | |
var modulus = Convert.ToBase64String(parameters.Modulus); | |
var webKey = new JsonWebKey | |
{ | |
Kty = "RSA", | |
Use = "sig", | |
Kid = keyName, | |
X5t = x509Key.Certificate.Thumbprint, | |
E = exponent, | |
N = modulus, | |
Alg = x509SigningKey.Algorithm | |
}; | |
webKey.X5c.Add(cert64); | |
var simplifiedWebKey = new { kty = webKey.Kty, kid = webKey.Kid, n = webKey.N, e = webKey.E, x5c = webKey.X5c }; | |
return JsonConvert.SerializeObject(simplifiedWebKey); | |
} | |
private async Task<MagdaAccessToken> GetAccessToken() | |
{ | |
var request = new HttpRequestMessage | |
{ | |
Method = HttpMethod.Post, | |
RequestUri = new Uri(this._authUrl) | |
}; | |
request.Headers.Add("Accept", "application/json"); | |
request.Headers.Add("Accept-Encoding", "gzip, deflate, br"); | |
List<string> scopes = new List<string>() { | |
"msg_mailbox_v1_P", | |
"msg_msg_v1_P", | |
"msg_statuses_v1_P" | |
}; | |
Dictionary<string, string> postParams = new Dictionary<string, string>(); | |
postParams.Add("grant_type", "client_credentials"); | |
postParams.Add("scope", string.Join(" ", scopes)); | |
postParams.Add("client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); | |
postParams.Add("client_assertion", this.GetAuthJwtToken(string.Join(" ", scopes))); | |
request.Content = new FormUrlEncodedContent(postParams); | |
var response = await this._httpClient.SendAsync(request); | |
var responseContent = await response.Content.ReadAsStringAsync(); | |
switch (response.StatusCode) | |
{ | |
case HttpStatusCode.OK: | |
return JsonConvert.DeserializeObject<MagdaAccessToken>(responseContent); | |
default: | |
throw new Exception($"HTTP call GetToken failed with status code {response.StatusCode} and message {responseContent}"); | |
} | |
} | |
private string GetAuthJwtToken(string scopes) | |
{ | |
X509SecurityKey privateKey = new X509SecurityKey(this._clientCertificate); | |
ClaimsIdentity claimsIdentity = new ClaimsIdentity(); | |
claimsIdentity.AddClaim(new Claim("sub", this._clientId)); | |
claimsIdentity.AddClaim(new Claim("scope", scopes)); | |
claimsIdentity.AddClaim(new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString())); | |
return this._tokenHandler.CreateEncodedJwt(this._clientId, this._authUrl, claimsIdentity, null, DateTime.UtcNow.AddMinutes(10), DateTime.UtcNow, new SigningCredentials(privateKey, SecurityAlgorithms.RsaSha256Signature)); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment