1 using Microsoft.AspNetCore.Http.Headers;
2 using Microsoft.Extensions.Primitives;
3 using Microsoft.Net.Http.Headers;
5 using System.Collections.Generic;
8 using System.Net.Http.Headers;
22 public const string ApplicationJson =
"application/json";
27 public const string ApiVersionHeader =
"api";
32 public const string InstanceIdHeader =
"instance";
37 public const string JwtAuthenticationScheme =
"bearer";
42 public const string BasicAuthenticationScheme =
"basic";
47 const string UsernameHeader =
"username";
52 const string PasswordAuthenticationScheme =
"password";
57 static readonly AssemblyName AssemblyName = Assembly.GetExecutingAssembly().GetName();
62 public static readonly Version Version = AssemblyName.
Version.Semver();
67 public long? InstanceId {
get;
set; }
72 public ProductHeaderValue? UserAgent => ProductInfoHeaderValue.TryParse(RawUserAgent, out var userAgent) ? userAgent.Product : null;
77 public string? RawUserAgent {
get; }
82 public Version ApiVersion {
get; }
87 public string? Token {
get; }
92 public string? Username {
get; }
97 public string? Password {
get; }
102 public bool IsTokenAuthentication => Token != null;
109 public static bool CheckCompatibility(Version otherVersion) => Version.Major == (otherVersion?.Major ??
throw new ArgumentNullException(nameof(otherVersion)));
116 public ApiHeaders(ProductHeaderValue userAgent,
string token) : this(userAgent, token, null, null)
118 if (userAgent == null)
119 throw new ArgumentNullException(nameof(userAgent));
121 throw new ArgumentNullException(nameof(token));
130 public ApiHeaders(ProductHeaderValue userAgent,
string username,
string password) : this(userAgent, null, username, password)
132 if (userAgent == null)
133 throw new ArgumentNullException(nameof(userAgent));
134 if (username == null)
135 throw new ArgumentNullException(nameof(username));
136 if (password == null)
137 throw new ArgumentNullException(nameof(password));
146 if (requestHeaders == null)
147 throw new ArgumentNullException(nameof(requestHeaders));
149 var jsonAccept =
new Microsoft.Net.Http.Headers.MediaTypeHeaderValue(ApplicationJson);
150 if (!requestHeaders.Accept.Any(x => x.MediaType == jsonAccept.MediaType))
151 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
"Client does not accept {0}!", ApplicationJson));
153 if (!requestHeaders.Headers.TryGetValue(HeaderNames.UserAgent, out var userAgentValues) || userAgentValues.Count == 0)
154 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
"Missing {0} headers!", HeaderNames.UserAgent));
156 RawUserAgent = userAgentValues.First();
157 if (String.IsNullOrWhiteSpace(RawUserAgent))
158 throw new InvalidOperationException(
"Malformed client User-Agent!");
161 if (!requestHeaders.Headers.TryGetValue(ApiVersionHeader, out var apiUserAgentHeaderValues) || !ProductInfoHeaderValue.TryParse(apiUserAgentHeaderValues.FirstOrDefault(), out var apiUserAgent) || apiUserAgent.Product.Name != AssemblyName.Name)
162 throw new InvalidOperationException(
"Missing API version!");
164 if (!Version.TryParse(apiUserAgent.Product.Version, out var apiVersion))
165 throw new InvalidOperationException(
"Malformed API version!");
167 ApiVersion = apiVersion.Semver();
169 if (!requestHeaders.Headers.TryGetValue(HeaderNames.Authorization, out StringValues authorization))
170 throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture,
"Missing {0} header!", HeaderNames.Authorization));
171 var auth = authorization.First();
172 var splits =
new List<string>(auth.Split(
' '));
173 var scheme = splits.First();
174 if (String.IsNullOrWhiteSpace(scheme))
175 throw new InvalidOperationException(
"Missing authentication scheme!");
178 var parameter = String.Concat(splits);
179 if (String.IsNullOrEmpty(parameter))
180 throw new InvalidOperationException(
"Missing authentication parameter!");
182 if (requestHeaders.Headers.TryGetValue(InstanceIdHeader, out var instanceIdValues))
184 var instanceIdString = instanceIdValues.FirstOrDefault();
185 if (instanceIdString !=
default && Int64.TryParse(instanceIdString, out var instanceId))
186 InstanceId = instanceId;
189 #pragma warning disable CA1308 // Normalize strings to uppercase 190 switch (scheme.ToLowerInvariant())
191 #pragma warning restore CA1308
193 case JwtAuthenticationScheme:
196 case PasswordAuthenticationScheme:
197 Password = parameter;
198 var fail = !requestHeaders.Headers.TryGetValue(UsernameHeader, out var values);
201 Username = values.FirstOrDefault();
202 fail = String.IsNullOrWhiteSpace(Username);
206 throw new InvalidOperationException(
"Missing Username header!");
208 case BasicAuthenticationScheme:
212 var base64Bytes = Convert.FromBase64String(parameter);
213 joinedString = Encoding.UTF8.GetString(base64Bytes);
217 throw new InvalidOperationException(
"Invalid basic Authorization header!");
220 var basicAuthSplits = joinedString.Split(
new char[] {
':' }, StringSplitOptions.RemoveEmptyEntries);
221 if (basicAuthSplits.Length < 2)
222 throw new InvalidOperationException(
"Invalid basic Authorization header!");
224 Username = basicAuthSplits.First();
225 Password = String.Concat(basicAuthSplits.Skip(1));
228 throw new InvalidOperationException(
"Invalid authentication scheme!");
239 ApiHeaders(ProductHeaderValue userAgent,
string? token,
string? username,
string? password)
241 RawUserAgent = userAgent?.ToString();
245 ApiVersion = Version;
252 public bool Compatible() => CheckCompatibility(ApiVersion);
262 throw new ArgumentNullException(nameof(headers));
263 if (instanceId.HasValue && InstanceId.HasValue && instanceId != InstanceId)
264 throw new InvalidOperationException(
"Specified instance ID in constructor and SetRequestHeaders!");
267 headers.Accept.Add(
new MediaTypeWithQualityHeaderValue(ApplicationJson));
268 if (IsTokenAuthentication)
269 headers.Authorization =
new AuthenticationHeaderValue(JwtAuthenticationScheme, Token);
271 headers.Authorization =
new AuthenticationHeaderValue(
272 BasicAuthenticationScheme,
273 Convert.ToBase64String(Encoding.UTF8.GetBytes($
"{Username}:{Password}")));
275 headers.UserAgent.Add(
new ProductInfoHeaderValue(UserAgent));
276 headers.Add(ApiVersionHeader,
new ProductHeaderValue(AssemblyName.Name, ApiVersion.ToString()).ToString());
277 instanceId ??= InstanceId;
278 if (instanceId.HasValue)
279 headers.Add(InstanceIdHeader, instanceId.ToString());