Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
OAuthResource.cs
1using System;
3using System.Diagnostics.CodeAnalysis;
4using System.Text;
5using System.Threading.Tasks;
10
12{
16 public abstract class OAuthResource : HttpSynchronousResource
17 {
21 public const string OAuthScopePrivilegePrefix = "OAUTH.Scope.";
22
23 private readonly OAuth2Environment environment;
24 private HttpAuthenticationScheme[]? authenticationSchemes = null;
25
32 : base(ResourceName)
33 {
34 this.environment = Environment;
35 }
36
40 public override bool UserSessions => false;
41
45 public override bool HandlesSubPaths => false;
46
50 public OAuth2Environment Environment => this.environment;
51
55 public IUserSource? Users => this.environment.UserSource;
56
60 public string? Realm => this.environment.Realm;
61
65 public int MinStrength => this.environment.MinStrength;
66
70 public bool Encrypted => this.environment.Encrypted;
71
75 public HttpAuthenticationScheme[]? AuthenticationSchemes => this.authenticationSchemes;
76
80 protected JwtFactory JwtFactory => this.environment.JwtFactory;
81
90 {
91 string s;
92
93 if (Request.Header.Authorization is null)
94 return null;
95
96 // If empty client_id is sent in a Basic Authorization header, do not consider
97 // it an authentication attempt.
98 if (!(Request.Header.Authorization is null) &&
99 (s = Request.Header.Authorization.Value).StartsWith("Basic "))
100 {
101 s = Encoding.UTF8.GetString(Convert.FromBase64String(s[6..]));
102 int i = s.IndexOf(':');
103 if (i == s.Length - 1)
104 return null;
105 }
106
107 this.InitAuthentication();
108
109 return this.authenticationSchemes;
110 }
111
116 protected bool InitAuthentication()
117 {
118 if (!(this.Users is null))
119 {
120 this.authenticationSchemes ??= this.CreateAuthenticationSchemes(
121 this.JwtFactory, this.Users);
122 }
123
124 return !(this.authenticationSchemes is null);
125 }
126
135 {
136 // Note: Restricted set of authentication schemes, as compared to
137 // HttpModule.GetAuthenticationSchemes().
138
139 List<HttpAuthenticationScheme> Schemes = new List<HttpAuthenticationScheme>();
140
141 if (!(JwtFactory is null))
142 {
143 Schemes.Add(new JwtAuthentication(this.Encrypted, this.MinStrength,
144 this.Realm, Users, JwtFactory));
145 }
146
148
149 if (!(Server is null) && Server.ClientCertificates != ClientCertificates.NotUsed)
150 Schemes.Add(new MutualTlsAuthentication(Users));
151
152 Schemes.Add(new BasicAuthentication(this.Encrypted, this.MinStrength,
153 this.Realm, Users));
154
155 Schemes.Add(new DigestAuthentication(this.Encrypted, this.MinStrength,
156 DigestAlgorithm.MD5, this.Realm, Users));
157
158 Schemes.Add(new DigestAuthentication(this.Encrypted, this.MinStrength,
159 DigestAlgorithm.SHA256, this.Realm, Users));
160
161 Schemes.Add(new DigestAuthentication(this.Encrypted, this.MinStrength,
162 DigestAlgorithm.SHA3_256, this.Realm, Users));
163
164 if (!(Server is null))
165 Schemes.Add(new SessionAuthentication(Server));
166
167 Schemes.Add(new OAuthClientCredentialsAuthentication(this.Encrypted,
168 this.MinStrength, Users));
169
170 return Schemes.ToArray();
171 }
172
178 public override Task<object> DefaultErrorContent(int StatusCode)
179 {
180 return Task.FromResult<object>(new Dictionary<string, object>()
181 {
182 { "error", "invalid_client" },
183 { "error_description", "Unauthorized access prohibited." }
184 });
185 }
186
195 protected static Task ReturnError(HttpResponse Response, string ErrorCode,
196 string ErrorDescription, int StatusCode, string StatusMessage)
197 {
198 Response.StatusCode = StatusCode;
199 Response.StatusMessage = StatusMessage;
200 Response.SetHeader("Cache-Control", "max-age=0, no-cache, no-store");
201 Response.SetHeader("Pragma", "no-cache");
202
203 return Response.Return(ErrorResponse(ErrorCode, ErrorDescription));
204 }
205
206 private static Dictionary<string, object> ErrorResponse(string ErrorCode,
207 string ErrorDescription)
208 {
209 Dictionary<string, object> Result = new Dictionary<string, object>()
210 {
211 { "error", ErrorCode },
212 { "error_description", ErrorDescription }
213 };
214
215 return Result;
216 }
217
224 protected static Task BadRequest(HttpResponse Response, string ErrorCode,
225 string ErrorDescription)
226 {
227 return ReturnError(Response, ErrorCode, ErrorDescription,
229 }
230
237 protected static Task Forbidden(HttpResponse Response, string ErrorCode,
238 string ErrorDescription)
239 {
240 return ReturnError(Response, ErrorCode, ErrorDescription,
242 }
243
251 protected static Task Unauthorized(HttpResponse Response, string ErrorCode,
252 string ErrorDescription, string[] Challenges)
253 {
254 Response.SetHeader("Cache-Control", "max-age=0, no-cache, no-store");
255 Response.SetHeader("Pragma", "no-cache");
256
257 return Response.SendResponse(new UnauthorizedException(
258 ErrorResponse(ErrorCode, ErrorDescription), Challenges));
259 }
260
267 protected static Task NotFound(HttpResponse Response, string ErrorCode,
268 string ErrorDescription)
269 {
270 return ReturnError(Response, ErrorCode, ErrorDescription,
272 }
273
280 protected static Task ServiceUnavailable(HttpResponse Response, string ErrorCode,
281 string ErrorDescription)
282 {
283 return ReturnError(Response, ErrorCode, ErrorDescription,
285 }
286
292 protected static bool IsValidScope(string Scope)
293 {
294 if (string.IsNullOrEmpty(Scope))
295 return false; // For an individual supplied value. Omitted scope is different.
296
297 string[] Tokens = Scope.Split(' ');
298
299 foreach (string Token in Tokens)
300 {
301 if (Token.Length == 0)
302 return false;
303
304 foreach (char ch in Token)
305 {
306 if (ch == 0x21)
307 continue;
308
309 if (ch >= 0x23 && ch <= 0x5B)
310 continue;
311
312 if (ch >= 0x5D && ch <= 0x7E)
313 continue;
314
315 return false;
316 }
317 }
318
319 return true;
320 }
321
330 public static bool HasScopePrivileges(string Scopes, IUser User,
331 [NotNullWhen(false)] out string? MissingPrivilege)
332 {
333 return HasScopePrivileges(
334 Scopes.Split(' ', StringSplitOptions.RemoveEmptyEntries),
335 User, out MissingPrivilege);
336 }
337
346 public static bool HasScopePrivileges(string[] Scopes, IUser User,
347 [NotNullWhen(false)] out string? MissingPrivilege)
348 {
349 foreach (string Scope in Scopes)
350 {
351 string Privilege = OAuthScopePrivilegePrefix + Scope.Replace(':', '.');
352 if (!User.HasPrivilege(Privilege))
353 {
354 MissingPrivilege = Privilege;
355 return false;
356 }
357 }
358
359 MissingPrivilege = null;
360 return true;
361 }
362 }
363}
Basic authentication mechanism, as defined in RFC 2617: https://tools.ietf.org/html/rfc2617
Digest authentication mechanism, as defined in RFC 2617: https://tools.ietf.org/html/rfc2617
mTLS authentication mechanism, where identity is taken from a valid client certificate.
Authentication mechanism that makes sure the user has an established session with the web server.
The request could not be understood by the server due to malformed syntax. The client SHOULD NOT repe...
The server understood the request, but is refusing to fulfill it. Authorization will not help and the...
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
HttpFieldAuthorization Authorization
Authorization HTTP Field header. (RFC 2616, §14.8)
Represents an HTTP request.
Definition: HttpRequest.cs:22
HttpRequestHeader Header
Request header.
Definition: HttpRequest.cs:182
string ResourceName
Name of resource.
Represets a response of an HTTP client request.
Definition: HttpResponse.cs:23
async Task SendResponse()
Sends the response back to the client. If the resource is synchronous, there's no need to call this m...
void SetHeader(string FieldName, string Value)
Sets a custom header field value.
Task Return(Exception ex)
Returns an error to the client.
Implements an HTTP server.
Definition: HttpServer.cs:41
override void Add(ISniffer Sniffer)
ICommunicationLayer.Add
Definition: HttpServer.cs:1460
Base class for all synchronous HTTP resources. A synchronous resource responds within the method hand...
The server has not found anything matching the Request-URI. No indication is given of whether the con...
OAUTH Client Credentials authentication scheme, as defined in RFCs 6749.
Abstract base class for OAUTH resources.
const string OAuthScopePrivilegePrefix
Privilege prefix for OAUTH scopes.
HttpAuthenticationScheme[] CreateAuthenticationSchemes(JwtFactory JwtFactory, IUserSource Users)
Creates a set of authentication scheme object reference for the resource.
override Task< object > DefaultErrorContent(int StatusCode)
Returns default content for an error, for the resource. If returning null, server will choose default...
override? HttpAuthenticationScheme[] GetAuthenticationSchemes(HttpRequest Request)
Any authentication schemes used to authenticate users before access is granted to the corresponding r...
IUserSource? Users
Data source for users, used to authenticate clients.
OAuth2Environment Environment
OAUTH2 environment, used to access clients, tokens, and other resources.
string? Realm
Realm name, if any, used for authentication. Null if no realm is defined.
override bool HandlesSubPaths
If the resource handles sub-paths.
static Task ServiceUnavailable(HttpResponse Response, string ErrorCode, string ErrorDescription)
Returns a Service Unavailable error back to the client.
static bool IsValidScope(string Scope)
Checks if a scope value is valid, according to the OAUTH2 specification.
static Task ReturnError(HttpResponse Response, string ErrorCode, string ErrorDescription, int StatusCode, string StatusMessage)
Returns an error back to the client.
static bool HasScopePrivileges(string[] Scopes, IUser User, [NotNullWhen(false)] out string? MissingPrivilege)
Checks if a user has the privileges associated with a set of scopes.
bool InitAuthentication()
Initializes authentication schemes, if not already initialized.
int MinStrength
Minimum strength of ciphers used in encryption, if any. 0 if no encryption is used.
bool Encrypted
If TLS-encryption is enabled.
static Task BadRequest(HttpResponse Response, string ErrorCode, string ErrorDescription)
Returns a Bad Request error back to the client.
override bool UserSessions
If the resource uses user sessions.
static Task NotFound(HttpResponse Response, string ErrorCode, string ErrorDescription)
Returns a Not Found error back to the client.
static Task Unauthorized(HttpResponse Response, string ErrorCode, string ErrorDescription, string[] Challenges)
Returns an Unauthorized error back to the client.
OAuthResource(OAuth2Environment Environment, string ResourceName)
OAUTH authorize resource.
static Task Forbidden(HttpResponse Response, string ErrorCode, string ErrorDescription)
Returns a Forbidden error back to the client.
static bool HasScopePrivileges(string Scopes, IUser User, [NotNullWhen(false)] out string? MissingPrivilege)
Checks if a user has the privileges associated with a set of scopes.
HttpAuthenticationScheme?[] AuthenticationSchemes
Available authentication schemes, if initialized.
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
Similar to 403 Forbidden, but specifically for use when authentication is required and has failed or ...
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
static bool TryGetModuleParameter(string Name, out object Value)
Tries to get a module parameter value.
Definition: Types.cs:607
Use JWT tokens for authentication. The Bearer scheme defined in RFC 6750 is used: https://tools....
A factory that can create and validate JWT tokens.
Definition: JwtFactory.cs:66
JwtFactory(IJwsAlgorithm Algorithm)
A factory that can create and validate JWT tokens.
Definition: JwtFactory.cs:83
bool HasPrivilege(string Privilege)
If the object has a given privilege.
Basic interface for a user.
Definition: IUser.cs:7
Interface for data sources containing users.
Definition: IUserSource.cs:9
ClientCertificates
Client Certificate Options