Neuron®
The Neuron® is the basis for the creation of open and secure federated networks for smart societies.
Loading...
Searching...
No Matches
RemoteLogin.cs
1using Paiwise;
2using System;
4using System.Threading.Tasks;
5using Waher.Content;
6using Waher.Events;
12using Waher.Security;
15
17{
22 {
27 DelayedResponse,
28
32 Poll,
33
37 Callback,
38
42 WebSocketEvent
43 }
44
50 {
51 private readonly Dictionary<string, bool> pendingPetitions = new Dictionary<string, bool>();
52
56 public RemoteLogin()
57 : base("/RemoteLogin")
58 {
59 }
60
64 public override bool HandlesSubPaths => false;
65
69 public override bool UserSessions => false;
70
74 public bool AllowsGET => true;
75
79 public bool AllowsPOST => true;
80
89 {
91 }
92
99 public override Task OPTIONS(HttpRequest Request, HttpResponse Response)
100 {
102 return base.OPTIONS(Request, Response);
103 }
104
111 public async Task GET(HttpRequest Request, HttpResponse Response)
112 {
113 string Location = "/Documentation/Neuron/RemoteLogin.md";
114
116 {
117 string Domain = await LegalComponent.GetOnboardingNeuronDomainName();
118 Location = "https://" + Domain + Location;
119 }
120
121 await Response.SendResponse(new FoundException(Location));
122 }
123
130 public async Task POST(HttpRequest Request, HttpResponse Response)
131 {
132 string Key = null;
133 bool PreviousFailed = false;
134
135 try
136 {
137 IUser User = Request.User;
138 if (User is null)
139 {
140 await Response.SendResponse(new ForbiddenException(Request, "Unauthorized access prohibited: No user authenticated"));
141 return;
142 }
143
144 if (!Request.HasData)
145 {
146 await Response.SendResponse(new BadRequestException("No data provided in post."));
147 return;
148 }
149
150 ContentResponse Content = await Request.DecodeDataAsync();
151
152 if (Content.HasError)
153 {
154 await Response.SendResponse(Content.Error);
155 return;
156 }
157
158 if (!(Content.Decoded is Dictionary<string, object> Json))
159 {
160 await Response.SendResponse(new BadRequestException("Expected JSON object."));
161 return;
162 }
163
164 #region Polling status of Petition
165
166 Uri ChallengeUri;
167
168 if (Json.TryGetValue("PetitionId", out object Obj))
169 {
170 if (!(Obj is string PetitionId2))
171 {
172 await Response.SendResponse(new BadRequestException("Invalid PetitionId."));
173 return;
174 }
175
176 if (!XmppServerModule.Legal.TryGetItemFromCache(PetitionId2 + "|petition", out Obj) ||
177 !(Obj is PetitionInfo PetitionInfo))
178 {
179 await Response.SendResponse(new NotFoundException("Petition not found, or rejected."));
180 return;
181 }
182
183 if (User.UserName != PetitionInfo.User.UserName)
184 {
185 await Response.SendResponse(new ForbiddenException(Request, "Access to petition denied."));
186 return;
187 }
188
189 bool Pending = string.IsNullOrEmpty(PetitionInfo.Token);
190
191 Dictionary<string, object> ResponseObject = new Dictionary<string, object>
192 {
193 { "Pending", Pending },
194 { "Token", PetitionInfo.Token },
195 };
196
197 if (Pending)
198 {
199 ChallengeUri = await PetitionInfo.RemoteAuthentication.GetChallengeUri(
200 PetitionInfo.Purpose, PetitionInfo.RemoteEndPoint);
201
202 if (!(ChallengeUri is null))
203 ResponseObject["ChallengeUri"] = ChallengeUri.ToString();
204 }
205
206 await Response.Return(ResponseObject);
207
208 return;
209 }
210
211 #endregion
212
213 #region LoginAuditor Integration
214
215 if (!Json.TryGetValue("RemoteEndPoint", out Obj) ||
216 !(Obj is string RemoteEndPoint) ||
217 string.IsNullOrEmpty(RemoteEndPoint) ||
218 RemoteEndPoint.Length > 128)
219 {
220 RemoteEndPoint = Request.RemoteEndPoint;
221 }
222
223 DateTime? Next = await Gateway.LoginAuditor.GetEarliestLoginOpportunity(RemoteEndPoint, "RemoteLogin");
224 if (Next.HasValue)
225 {
226 if (Next.Value == DateTime.MaxValue)
227 {
228 await Response.SendResponse(new ForbiddenException(
229 "Remote Endpoint (" + RemoteEndPoint + ") permanently blocked."));
230
231 return;
232 }
233 else
234 {
235 await Response.SendResponse(new TooManyRequestsException(
236 new Dictionary<string, object>()
237 {
238 { "RemoteEndPoint", RemoteEndPoint },
239 { "Next", Next.Value }
240 }));
241
242 return;
243 }
244 }
245
246 #endregion
247
248 #region Validating or Refreshing Token
249
250 if (Json.TryGetValue("Token", out Obj))
251 {
252 if (!(Obj is string Token))
253 {
254 await Response.SendResponse(new BadRequestException("Invalid Token."));
255 return;
256 }
257
258 bool IsValid = XmppServerModule.Instance.ValidateJwtToken(Token, true,
259 out JwtToken ParsedToken);
260
261 if (!Json.TryGetValue("Seconds", out Obj) || !IsValid)
262 {
263 await Response.Return(new Dictionary<string, object>
264 {
265 { "Valid", IsValid }
266 });
267 return;
268 }
269
270 if (!(Obj is int Seconds2) ||
271 Seconds2 < 1 || Seconds2 > 3600)
272 {
273 await Response.SendResponse(new BadRequestException("Invalid number of seconds. Permitted range: 1-3600"));
274 return;
275 }
276
278 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(JSON.UnixEpoch).TotalSeconds);
279 int Expires = IssuedAt + Seconds2;
280
281 foreach (KeyValuePair<string, object> Claim in ParsedToken.Claims)
282 {
283 switch (Claim.Key)
284 {
285 case JwtClaims.JwtId:
286 Claims.Add(new KeyValuePair<string, object>(JwtClaims.JwtId,
287 Convert.ToBase64String(Gateway.NextBytes(32))));
288 break;
289
290 case JwtClaims.IssueTime:
291 Claims.Add(new KeyValuePair<string, object>(JwtClaims.IssueTime, IssuedAt));
292 break;
293
295 Claims.Add(new KeyValuePair<string, object>(JwtClaims.ExpirationTime, Expires));
296 break;
297
298 case JwtClaims.Audience:
299 if (User.UserName != Claim.Value?.ToString())
300 {
301 await Response.SendResponse(new ForbiddenException(Request, "Not authorized to refresh token."));
302 return;
303 }
304
305 Claims.Add(Claim);
306 break;
307
308 case JwtClaims.Actor:
309 Claims.Add(new KeyValuePair<string, object>(JwtClaims.Actor, RemoteEndPoint));
310 break;
311
312 default:
313 Claims.Add(Claim);
314 break;
315 }
316 }
317
318 Token = XmppServerModule.Instance.CreateJwtToken(Claims);
319
320 await Response.Return(new Dictionary<string, object>
321 {
322 { "Valid", IsValid },
323 { "Token", Token }
324 });
325 return;
326 }
327
328 #endregion
329
330 #region Initiating new Petition
331
332 if (!Json.TryGetValue("Seconds", out Obj))
333 {
334 await Response.SendResponse(new BadRequestException("Seconds missing."));
335 return;
336 }
337
338 if (!(Obj is int Seconds) ||
339 Seconds < 1 || Seconds > 3600)
340 {
341 await Response.SendResponse(new BadRequestException("Invalid number of seconds. Permitted range: 1-3600"));
342 return;
343 }
344
345 if (!Json.TryGetValue("ResponseMethod", out Obj))
346 {
347 await Response.SendResponse(new BadRequestException("ResponseMethod missing."));
348 return;
349 }
350
351 if (!(Obj is string ResponseMethodStr) ||
352 !Enum.TryParse(ResponseMethodStr, true, out RemoteLoginResponseMethod ResponseMethod))
353 {
354 await Response.SendResponse(new BadRequestException("Invalid ResponseMethod."));
355 return;
356 }
357
358 Uri CallbackUrl = null;
359 string TabID = null;
360 string Function = null;
361
362 switch (ResponseMethod)
363 {
364 case RemoteLoginResponseMethod.Callback:
365 if (!Json.TryGetValue("CallbackURL", out Obj))
366 {
367 await Response.SendResponse(new BadRequestException("CallbackURL missing."));
368 return;
369 }
370
371 if (!(Obj is string CallbackUrlStr) ||
372 string.IsNullOrEmpty(CallbackUrlStr) ||
373 !Uri.TryCreate(CallbackUrlStr, UriKind.Absolute, out CallbackUrl) ||
374 (string.Compare(CallbackUrl.Scheme, "https", true) != 0 &&
375 string.Compare(CallbackUrl.Scheme, "httpx", true) != 0))
376 {
377 await Response.SendResponse(new BadRequestException("Invalid CallbackURL."));
378 return;
379 }
380 break;
381
382 case RemoteLoginResponseMethod.WebSocketEvent:
383 if (!Json.TryGetValue("TabID", out Obj))
384 {
385 await Response.SendResponse(new BadRequestException("TabID missing."));
386 return;
387 }
388
389 if (!(Obj is string TabIDStr) ||
390 string.IsNullOrEmpty(TabIDStr))
391 {
392 await Response.SendResponse(new BadRequestException("Invalid Tab ID."));
393 return;
394 }
395
396 if (!Json.TryGetValue("Function", out Obj))
397 {
398 await Response.SendResponse(new BadRequestException("Function missing."));
399 return;
400 }
401
402 if (!(Obj is string FunctionStr) ||
403 string.IsNullOrEmpty(FunctionStr))
404 {
405 await Response.SendResponse(new BadRequestException("Invalid Function name."));
406 return;
407 }
408
409 TabID = TabIDStr;
410 Function = FunctionStr;
411 break;
412 }
413
414 string Privilege = nameof(RemoteLogin) + ".Method." + ResponseMethodStr;
415 if (!User.HasPrivilege(Privilege))
416 {
417 await Response.SendResponse(ForbiddenException.AccessDenied(Request, this.ResourceName, User.UserName, Privilege));
418 return;
419 }
420
421 if (!Json.TryGetValue("AddressType", out Obj))
422 {
423 await Response.SendResponse(new BadRequestException("AddressType missing."));
424 return;
425 }
426
427 if (!(Obj is string AddressTypeStr))
428 {
429 await Response.SendResponse(new BadRequestException("Invalid AddressType."));
430 return;
431 }
432
433 Privilege = nameof(RemoteLogin) + ".Type." + AddressTypeStr;
434 if (!User.HasPrivilege(Privilege))
435 {
436 await Response.SendResponse(ForbiddenException.AccessDenied(Request, this.ResourceName, User.UserName, Privilege));
437 return;
438 }
439
440 if (!Json.TryGetValue("Address", out Obj))
441 {
442 await Response.SendResponse(new BadRequestException("Address missing."));
443 return;
444 }
445
446 if (!(Obj is string AddressStr))
447 {
448 await Response.SendResponse(new BadRequestException("Invalid Address."));
449 return;
450 }
451
452 RemoteIdentifier RemoteIdentifier = new RemoteIdentifier(AddressTypeStr, AddressStr);
454
455 if (Authenticator is null)
456 {
457 await Response.SendResponse(new BadRequestException("Unrecognized AddressType."));
458 return;
459 }
460
461 IRemoteAuthentication RemoteAuthentication = await Authenticator.CreateAuthentication(RemoteIdentifier);
462
463 if (!await RemoteAuthentication.IsValidAddress())
464 {
465 await Response.SendResponse(new BadRequestException("Address does not conform to address type."));
466 return;
467 }
468
469 if (!await RemoteAuthentication.IsLegalIdAvailable())
470 {
471 await Response.SendResponse(new UnavailableForLegalReasonsException("Remote Address does not have a remote Legal ID associated with it."));
472 return;
473 }
474
475 if (!await RemoteAuthentication.IsPermitted(User))
476 {
477 await Response.SendResponse(ForbiddenException.AccessDenied(Request, this.ResourceName, User.UserName, Privilege));
478 return;
479 }
480
481 if (!Json.TryGetValue("Purpose", out Obj))
482 {
483 await Response.SendResponse(new BadRequestException("Purpose missing."));
484 return;
485 }
486
487 if (!(Obj is string Purpose) || string.IsNullOrEmpty(Purpose))
488 {
489 await Response.SendResponse(new BadRequestException("Invalid Purpose."));
490 return;
491 }
492
493 if (!Purpose.Contains(User.UserName))
494 Purpose = User.UserName + ": " + Purpose;
495
496 ChallengeUri = await RemoteAuthentication.GetChallengeUri(Purpose, RemoteEndPoint);
497
498 if (!(ChallengeUri is null) && ResponseMethod == RemoteLoginResponseMethod.DelayedResponse)
499 {
500 await Response.SendResponse(new BadRequestException("Method uses a Challenge URI, which is incompatible with the delayed response method."));
501 return;
502 }
503
504 // TODO: Properties & Attachments
505
506 Key = RemoteEndPoint + " " + RemoteAuthentication.Key;
507
508 lock (this.pendingPetitions)
509 {
510 if (this.pendingPetitions.ContainsKey(Key))
511 PreviousFailed = true;
512 else
513 this.pendingPetitions[Key] = true;
514 }
515
516 if (PreviousFailed)
517 {
518 LoginAuditor.Fail("Remote API repetitive Login attempt.",
519 User.UserName, RemoteEndPoint, "RemoteLogin",
520 new KeyValuePair<string, object>("Remote Identifier", RemoteAuthentication.Key));
521 }
522
523 await RuntimeCounters.IncrementCounter("RemoteLogin." + RemoteAuthentication.GetType().FullName);
524
525 string PetitionId = string.Empty;
526
527 PetitionId = await RemoteAuthentication.Start(Purpose, RemoteEndPoint,
528 async (_, e) =>
529 {
530 lock (this.pendingPetitions)
531 {
532 this.pendingPetitions.Remove(Key);
533 }
534
535 if (e.Ok)
536 {
537 int IssuedAt = (int)Math.Round(DateTime.UtcNow.Subtract(JSON.UnixEpoch).TotalSeconds);
538 int Expires = IssuedAt + Seconds;
539
540 string Token = XmppServerModule.Instance.CreateJwtToken(
541 new KeyValuePair<string, object>[]
542 {
543 new KeyValuePair<string, object>(JwtClaims.JwtId, Convert.ToBase64String(Gateway.NextBytes(32))),
544 new KeyValuePair<string, object>(JwtClaims.Issuer, Gateway.Domain.Value),
545 new KeyValuePair<string, object>(JwtClaims.Subject, User.UserName),
546 new KeyValuePair<string, object>(JwtClaims.Actor, RemoteEndPoint),
547 new KeyValuePair<string, object>(JwtClaims.IssueTime, IssuedAt),
548 new KeyValuePair<string, object>(JwtClaims.ExpirationTime, Expires)
549 }.Join(e.JwtClaims));
550
551 XmppServerModule.Legal.AddItemToCache(PetitionId + "|petition", new PetitionInfo()
552 {
553 PetitionId = PetitionId,
554 User = User,
555 Token = Token,
556 Purpose = Purpose,
557 RemoteEndPoint = RemoteEndPoint,
558 RemoteAuthentication = RemoteAuthentication
559 });
560
561 await this.ReturnResponse(ResponseMethod, Response, CallbackUrl,
562 TabID, Function, PetitionId, Token);
563
564 LoginAuditor.Success("Remote API Login successful.",
565 User.UserName, RemoteEndPoint, "RemoteLogin",
566 new KeyValuePair<string, object>("Remote Identifier", RemoteAuthentication.Key));
567 }
568 else
569 {
570 XmppServerModule.Legal.RemoveItemFromCache(PetitionId + "|petition");
571
572 await this.ReturnResponse(ResponseMethod, Response, CallbackUrl,
573 TabID, Function, PetitionId, null);
574
575 if (e.LogAuditFailure)
576 {
577 LoginAuditor.Fail(e.ErrorMessage,
578 User.UserName, RemoteEndPoint, "RemoteLogin",
579 new KeyValuePair<string, object>("Remote Identifier", RemoteAuthentication.Key));
580 }
581 }
582 },
583 null);
584
585 if (ResponseMethod != RemoteLoginResponseMethod.DelayedResponse)
586 {
587 XmppServerModule.Legal.AddItemToCache(PetitionId + "|petition", new PetitionInfo()
588 {
589 PetitionId = PetitionId,
590 User = User,
591 Token = null,
592 Purpose = Purpose,
593 RemoteEndPoint = RemoteEndPoint,
594 RemoteAuthentication = RemoteAuthentication
595 });
596
597 Dictionary<string, object> ResponseObject = new Dictionary<string, object>
598 {
599 { "PetitionId", PetitionId }
600 };
601
602 if (!(ChallengeUri is null))
603 ResponseObject["ChallengeUri"] = ChallengeUri.ToString();
604
605 await Response.Return(ResponseObject);
606 return;
607 }
608
609 #endregion
610 }
611 catch (Exception ex)
612 {
613 if (!string.IsNullOrEmpty(Key))
614 {
615 lock (this.pendingPetitions)
616 {
617 this.pendingPetitions.Remove(Key);
618 }
619 }
620
621 Log.Exception(ex);
622
623 await Response.SendResponse(new ServiceUnavailableException(
624 "Authentication service unavailable."));
625 }
626 }
627
628 private async Task ReturnResponse(RemoteLoginResponseMethod Method,
629 HttpResponse Response, Uri CallbackUrl, string TabID, string Function,
630 string PetitionId, string TokenResult)
631 {
632 switch (Method)
633 {
634 case RemoteLoginResponseMethod.DelayedResponse:
635 if (string.IsNullOrEmpty(TokenResult))
636 {
637 await Response.SendResponse(new NotFoundException("Petition rejected."));
638 return;
639 }
640 else
641 {
642 await Response.Return(new Dictionary<string, object>
643 {
644 { "Pending", false },
645 { "Token", TokenResult }
646 });
647 }
648 break;
649
650 case RemoteLoginResponseMethod.Callback:
651 await InternetContent.PostAsync(CallbackUrl,
652 new Dictionary<string, object>
653 {
654 { "PetitionId", PetitionId },
655 { "Rejected", string.IsNullOrEmpty(TokenResult) },
656 { "Token", TokenResult }
658 break;
659
660 case RemoteLoginResponseMethod.WebSocketEvent:
661 await ClientEvents.PushEvent(new string[] { TabID }, Function,
662 JSON.Encode(new Dictionary<string, object>
663 {
664 { "PetitionId", PetitionId },
665 { "Rejected", string.IsNullOrEmpty(TokenResult) },
666 { "Token", TokenResult }
667 }, false), true);
668 break;
669 }
670 }
671
672 private class PetitionInfo
673 {
674 public string PetitionId;
675 public IUser User;
676 public string Token;
677 public string Purpose;
678 public string RemoteEndPoint;
679 public IRemoteAuthentication RemoteAuthentication;
680 }
681 }
682}
Contains information about a remote identifier.
Contains information about a response to a content request.
bool HasError
If an error occurred.
object Decoded
Decoded object.
Exception Error
Error response.
Static class managing encoding and decoding of internet content.
static Task< ContentResponse > PostAsync(Uri Uri, object Data, params KeyValuePair< string, string >[] Headers)
Posts to a resource, using a Uniform Resource Identifier (or Locator).
Helps with common JSON-related tasks.
Definition: JSON.cs:16
static readonly DateTime UnixEpoch
Unix Date and Time epoch, starting at 1970-01-01T00:00:00Z
Definition: JSON.cs:20
static string Encode(string s)
Encodes a string for inclusion in JSON.
Definition: JSON.cs:537
Static class managing the application event log. Applications and services log events on this static ...
Definition: Log.cs:14
static void Exception(Exception Exception, string Object, string Actor, string EventId, EventLevel Level, string Facility, string Module, params KeyValuePair< string, object >[] Tags)
Logs an exception. Event type will be determined by the severity of the exception.
Definition: Log.cs:1657
The ClientEvents class allows applications to push information asynchronously to web clients connecte...
Definition: ClientEvents.cs:51
static Task< int > PushEvent(string[] TabIDs, string Type, object Data)
Puses an event to a set of Tabs, given their Tab IDs.
Static class managing the runtime environment of the IoT Gateway.
Definition: Gateway.cs:147
static CaseInsensitiveString Domain
Domain name.
Definition: Gateway.cs:3087
static X509Certificate2 Certificate
Domain certificate.
Definition: Gateway.cs:3082
static LoginAuditor LoginAuditor
Current Login Auditor. Should be used by modules accepting user logins, to protect the system from un...
Definition: Gateway.cs:3860
static byte[] NextBytes(int NrBytes)
Generates an array of random bytes.
Definition: Gateway.cs:4335
static bool TryGetLocalResourceFileName(string Resource, string Host, out string FileName)
Tries to get a file name for a resource, if local.
Definition: Gateway.cs:6305
static HttpFolderResource Root
Root folder resource.
Definition: Gateway.cs:3152
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...
static ForbiddenException AccessDenied(string ObjectId, string ActorId)
Returns a ForbiddenException object, and logs a entry in the event log about the event.
The requested resource resides temporarily under a different URI. Since the redirection might be alte...
Base class for all asynchronous HTTP resources. An asynchronous resource responds outside of the meth...
Base class for all HTTP authentication schemes, as defined in RFC-7235: https://datatracker....
void SetDefaultResponseHeaders(HttpResponse Response)
Sets any default response headers registered on the file folder object, to a HTTP Response object.
Represents an HTTP request.
Definition: HttpRequest.cs:22
string RemoteEndPoint
Remote end-point.
Definition: HttpRequest.cs:243
bool HasData
If the request has data.
Definition: HttpRequest.cs:113
IUser User
Authenticated user, if available, or null if not available.
Definition: HttpRequest.cs:203
async Task< ContentResponse > DecodeDataAsync()
Decodes data sent in request.
Definition: HttpRequest.cs:139
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...
Task Return(Exception ex)
Returns an error to the client.
The server has not found anything matching the Request-URI. No indication is given of whether the con...
The server is currently unable to handle the request due to a temporary overloading or maintenance of...
The user has sent too many requests in a given amount of time. Intended for use with rate limiting sc...
A chunked list is a linked list of chunks of objects of type T .
Definition: ChunkedList.cs:54
Static class managing persistent counters.
static Task< long > IncrementCounter(CaseInsensitiveString Key)
Increments a counter.
Static class that dynamically manages types and interfaces available in the runtime environment.
Definition: Types.cs:15
Static class containing predefined JWT claim names.
Definition: JwtClaims.cs:10
const string Issuer
Issuer of the JWT
Definition: JwtClaims.cs:14
const string Audience
Recipient for which the JWT is intended
Definition: JwtClaims.cs:24
const string IssueTime
Time at which the JWT was issued; can be used to determine age of the JWT
Definition: JwtClaims.cs:39
const string JwtId
Unique identifier; can be used to prevent the JWT from being replayed (allows a token to be used only...
Definition: JwtClaims.cs:44
const string Subject
Subject of the JWT (the user)
Definition: JwtClaims.cs:19
const string ExpirationTime
Time after which the JWT expires
Definition: JwtClaims.cs:29
const string Actor
Actor
Definition: JwtClaims.cs:144
Contains information about a Java Web Token (JWT). JWT is defined in RFC 7519: https://tools....
Definition: JwtToken.cs:22
Class that monitors login events, and help applications determine malicious intent....
Definition: LoginAuditor.cs:26
async Task< DateTime?> GetEarliestLoginOpportunity(string RemoteEndPoint, string Protocol)
Checks when a remote endpoint can login.
static async void Success(string Message, string UserName, string RemoteEndPoint, string Protocol, params KeyValuePair< string, object >[] Tags)
Handles a successful login attempt.
static void Fail(string Message, string UserName, string RemoteEndPoint, string Protocol)
Handles a failed login attempt.
Service Module hosting the XMPP broker and its components.
HttpAuthenticationScheme[] DefaultAuthenticationSchemesAdmin
Default HTTP Authentication schemes for administrative resources.
Interface for remote authentications.
Task< Uri > GetChallengeUri(string Purpose, string RemoteEndPoint)
Gets a Challenge URI to send to the requestor of the remote authentication.
Task< string > Start(string Purpose, string RemoteEndPoint, EventHandlerAsync< RemoteAuthenticationEventArgs > Callback, object State)
Starts the remote authentication process.
string Key
Key identifying the remote authentication.
Task< bool > IsLegalIdAvailable()
Checks if a Legal Id is available for the remote ID.
Task< bool > IsValidAddress()
Checks if the remote address is valid.
Task< bool > IsPermitted(IUser User)
Checks is the remote address is permitted to perform the requested operation.
Interface for remote authenticators.
Task< IRemoteAuthentication > CreateAuthentication(IRemoteIdentifier RemoteIdentifier)
Creates a remote authentication object instance for a remote identifier.
Interface for remote identifiers.
GET Interface for HTTP resources.
POST Interface for HTTP resources.
bool HasPrivilege(string Privilege)
If the object has a given privilege.
Basic interface for a user.
Definition: IUser.cs:7
string UserName
User Name.
Definition: IUser.cs:12
Definition: ImplTypes.g.cs:58