public async Task <IActionResult> Negotiate( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = "messaging/negotiate")] HttpRequest req, IBinder binder) { string userId = req.Headers["X-User-Id"]; if (!Guid.TryParse(userId, out Guid userIdGuid)) { _logger.LogError($"User call to /negotiate did not include a valid GUID in the header. We found '{userId}' in the X-User-Id header."); return(new BadRequestResult()); } // Force override of the SignalR user ID. // Normally this method would be decorated with a [SignalRConnectionInfo] attribute, which would be // where we could set the UserId attribute by using function dot-binding, i.e. UserId = "{headers.x-user-id}". // Unfortunately, the only headers visible to the function seem to be the Azure Auth-based claims headers, // and we're sending up a custom user ID. // So instead, we short-circuit the normal binding process, forcibly create our own binding attribute by hand, // at a point in time where we have access to our user ID, and then tell the binder to use that instead. SignalRConnectionInfoAttribute attribute = new SignalRConnectionInfoAttribute { HubName = Constants.ChatHubName, UserId = userIdGuid.ToIdString(), }; SignalRConnectionInfo info = await binder.BindAsync <SignalRConnectionInfo>(attribute); return(new OkObjectResult(info)); }
public SignalRConnectionInfo GetClientConnectionInfo(SignalRConnectionInfoAttribute attribute) { IEnumerable <Claim> claims1 = attribute.GetClaims(); string hubName = attribute.HubName; IEnumerable <Claim> claims2 = claims1; return(GetClientConnectionInfo(hubName, claims2)); }
public static IEnumerable <Claim> GetClaims(this SignalRConnectionInfoAttribute attribute) { List <Claim> claimList = new List <Claim>(); if (!string.IsNullOrEmpty(attribute.UserId)) { claimList.Add(new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/nameidentifier", attribute.UserId)); } return((IEnumerable <Claim>)claimList); }
public void GetClaims_WithUserId_ReturnsUserIdInClaims() { var attr = new SignalRConnectionInfoAttribute { UserId = "foo" }; var claims = attr.GetClaims(); Assert.Contains(claims, c => c.Type == ClaimTypes.NameIdentifier && c.Value == "foo"); }
public void GetClaims_WithNoUserId_ReturnsEmptyClaims() { var attr = new SignalRConnectionInfoAttribute { UserId = null }; var claims = attr.GetClaims(); Assert.Empty(claims); }
public static async Task <SignalRConnectionInfo> Negotiate( [HttpTrigger(AuthorizationLevel.Anonymous)] HttpRequest req, IBinder binder) { var userId = req.Query["oid"]; if (string.IsNullOrEmpty(userId)) { return(null); } var attribute = new SignalRConnectionInfoAttribute { HubName = "chat", UserId = userId }; return(await binder.BindAsync <SignalRConnectionInfo>(attribute).ConfigureAwait(false)); }
public static async Task <SignalRConnectionInfo> GetSignalRInfo2( [HttpTrigger(AuthorizationLevel.Anonymous, "post", "get")] HttpRequest req, IBinder binder, ILogger log) { string hub = "billtedRoom"; string userId = req.Query["user"]; SignalRConnectionInfoAttribute attribute = new SignalRConnectionInfoAttribute { HubName = hub, UserId = userId }; // This style is an example of imperative attribute binding; the mechanism for declarative binding described below does not work // UserId = "{headers.x-my-custom-header}" https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-concept-serverless-development-config SignalRConnectionInfo connection = await binder.BindAsync <SignalRConnectionInfo>(attribute); return(connection); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post")] HttpRequest req, IBinder binder) // [SignalRConnectionInfo(HubName = Constants.SignalRTasksHubName, ConnectionStringSetting = Constants.SignalRConnectionStringSetting)] SignalRConnectionInfo connectionInfo) { SignalRConnectionInfoAttribute attribute = new SignalRConnectionInfoAttribute { HubName = Constants.SignalRTasksHubName, ConnectionStringSetting = Constants.SignalRConnectionStringSetting, // Use random user id for anonymous usage UserId = $"userid:{Guid.NewGuid().ToString()}" // UserId = "myUserId" }; // This style is an example of imperative attribute binding; the mechanism for declarative binding described below does not work // UserId = "{headers.x-my-custom-header}" https://docs.microsoft.com/en-us/azure/azure-signalr/signalr-concept-serverless-development-config SignalRConnectionInfo connectionInfo = await binder.BindAsync <SignalRConnectionInfo>(attribute); return(new OkObjectResult(connectionInfo)); }
public static async Task <SignalRConnectionInfo> GetSignalRInfo( [HttpTrigger(AuthorizationLevel.Function, "post")] string request, Binder binder, ILogger log) { log.LogInformation($"GetSignalRConnection: Received request: {request}."); // We need to extract the Entity Id from the context. The entity id is going to be used as the SignalR user id associated with the caller's connection. var context = JsonConvert.DeserializeObject <FunctionExecutionContext <object> >(request); log.LogInformation($"Creating PubSub subscription for user {context.CallerEntityProfile.Entity.Id}."); // In Azure Functions, the SignalR connection is normally created through an input binding attribute. Here, because we need to specify the user id dynamically, // we're going to manually create the attribute and then use it to mint connection info. // The connection string to use is pulled from the AzureSignalRConnectionString setting by default. var signalRConnectionAttribute = new SignalRConnectionInfoAttribute() { HubName = "PubSub", UserId = context.CallerEntityProfile.Entity.Id }; return(await binder.BindAsync <SignalRConnectionInfo>(signalRConnectionAttribute)); }