internal static string GenerateSignalNowKey(string userName, string companyOrTenant, string groupOrTeam, string signalNowKey) { string nameHash1 = SignalRGroupUtils.GetNameHash( SignalRGroupUtils.GetUserGroupId(_hashSecuritySeed, companyOrTenant, groupOrTeam, userName), signalNowKey); string nameHash2 = SignalRGroupUtils.GetNameHash(nameHash1, signalNowKey); return(nameHash1 + nameHash2); }
public string GenerateAccessToken(string audience, string userId, TimeSpan?lifetime = null) { IEnumerable <Claim> claims = null; if (userId != null) { claims = new[] { new Claim(ClaimTypes.GivenName, userId), new Claim(ClaimTypes.NameIdentifier, SignalRGroupUtils.GetNameHash(userId, AccessKey)) }; } return(GenerateAccessTokenInternal(audience, claims, lifetime ?? TimeSpan.FromHours(1))); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, Route = null)] HttpRequest req, ILogger log) { string errorMessage = string.Empty; var deviceId = req.Headers["deviceid"].ToString().ToLowerInvariant(); // Device id, either a MAC address or a random string per session var userName = req.Headers["username"].ToString().ToLowerInvariant(); // Username as registered in choosen authernication service (e.g. AAD userPrincipalName, GitHub username) var team = req.Headers["teamname"].ToString().ToLowerInvariant(); // Teamname - id or name of the target group or team. Must be group id for AAD, team id for GitHub var company = req.Headers["companyname"].ToString().ToLowerInvariant(); // Company name or tenant id. Must be tenant id for AAD, company id for GitHub var authServiceToken = req.Headers["authservicetoken"].ToString(); // Authentication token received from authentication service. Bearer token for AAD or GitHub. var authService = req.Headers["authservicename"].ToString().ToLowerInvariant(); // Authentication sercvice name. "graph.microsoft.com" for AAD, "github.com" for GitHub var provider = GraphServiceFactory.GetGraphService(authService); GraphAuthStatus authStatus = GraphAuthStatus.Unauthorized; if (provider == null) { errorMessage = $"{authService} is not a valid graph service name."; } else { authStatus = await provider.IsGroupMember(userName, company, team, authServiceToken, log); } ObjectResult funcResult = null; string userId = string.Empty; if (authStatus == GraphAuthStatus.OK) { userId = SignalRGroupUtils.GetFullUserId(authService, company, team, userName, deviceId); userId = userId.ToLowerInvariant(); ServiceUtils utils = new ServiceUtils(ConfigUtils.GetSignalRConnection()); var hubName = ConfigUtils.GetHubName(); var clientUrl = $"{utils.Endpoint}/client/?hub={hubName}"; var clientToken = utils.GenerateAccessToken(clientUrl, userId, TimeSpan.FromMinutes(UInt64.Parse(ConfigUtils.GetAuthTokenLifetimeMinutes()))); string serverTime = ((long)(DateTime.UtcNow - DateTime.UnixEpoch).TotalSeconds).ToString(); string turnServersAuthorization = ConfigUtils.MakeTURNAuthToken(company); funcResult = new OkObjectResult(new string[] { userId, clientToken, clientUrl, serverTime, turnServersAuthorization }); } else { if (string.IsNullOrEmpty(errorMessage)) { errorMessage = $"Graph verification failed. Reason: {authStatus}"; } } if (!string.IsNullOrEmpty(errorMessage)) { log.LogError(errorMessage); } else { log.LogInformation($"Successfully negotiated for {userName} as {userId}"); } return(string.IsNullOrEmpty(errorMessage) ? (ActionResult)funcResult : new BadRequestObjectResult(new { message = errorMessage })); }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req, ILogger log) { var authToken = req.Headers["authtoken"].ToString(); // SignalNow authentication token received from GetMessageToken var sendTo = req.Headers["sendto"].ToString().ToLowerInvariant(); // SignalNow username or group name var userOrGroup = req.Headers["userorgroup"].ToString(); // Message's recipient - "user" or "group" var messageType = req.Headers["messagetype"].ToString(); // Message type (any string, defined by the signaling protocol) string payload = string.Empty; // Message payload may be passed in "payload" header or, if there is no such header, via request body if (req.Headers.ContainsKey("payload")) { payload = req.Headers["payload"].ToString(); } else { // If message pack was used to create playload, there should be "withmessagepack" header. // If its value is "lz", then payload is compressed with MessagePack.LZ4MessagePackSerializer. // We don't use req.ContentType because of ambiguity between application/x-msgpack and application/msgpack // https://github.com/msgpack/msgpack/issues/194 (plus we also need to differentiate between uncompressed and compressed) if (req.Headers.ContainsKey("withmessagepack")) { var mpValue = req.Headers["withmessagepack"].ToString(); bool useLZ = string.Equals("lz", mpValue, StringComparison.InvariantCultureIgnoreCase); int capacity = 0; if (req.Body.Length >= 0 && req.Body.Length <= int.MaxValue) { capacity = (int)req.Body.Length; } using (var ms = new MemoryStream(capacity)) { await req.Body.CopyToAsync(ms); byte[] messageData = ms.ToArray(); payload = useLZ ? MessagePack.LZ4MessagePackSerializer.ToJson(messageData) : MessagePack.MessagePackSerializer.ToJson(messageData); } } else { using (var sr = new StreamReader(req.Body)) { payload = await sr.ReadToEndAsync(); } } } string userIdHash, userId; ServiceUtils utils = new ServiceUtils(ConfigUtils.GetSignalRConnection()); if (!utils.ParseAndValidateToken(authToken, out userIdHash, out userId, null)) { log.LogError($"Invalid authentication token ({authToken})"); return(new UnauthorizedResult()); } if (!SignalRGroupUtils.CanSendMessage(userId, sendTo)) { log.LogError($"{userId} is not allowed to send messages to {sendTo}"); return(new BadRequestObjectResult(new { message = $"{userId} is not allowed to send messages to {sendTo}" })); } var hubName = ConfigUtils.GetHubName(); var sendToHash = SignalRGroupUtils.GetNameHash(sendTo, utils.AccessKey); string url = string.Equals(userOrGroup, "group", StringComparison.InvariantCultureIgnoreCase) ? $"{utils.Endpoint}/api/v1/hubs/{hubName}/groups/{sendToHash}" : $"{utils.Endpoint}/api/v1/hubs/{hubName}/users/{sendToHash}"; string sendToken = utils.GenerateAccessToken(url, userId, TimeSpan.FromMinutes(1)); PayloadMessage message = new PayloadMessage() { Target = "SIGNAL", Arguments = new[] { userId, messageType, payload } }; bool msgSent = false; string errMessage = string.Empty; HttpStatusCode httpStatus = HttpStatusCode.OK; string errPhrase = string.Empty; string errContent = string.Empty; for (int i = 0; i < maxTries; i++) { try { using (var request = new HttpRequestMessage(HttpMethod.Post, url)) { request.Version = System.Net.HttpVersion.Version20; request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", sendToken); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); request.Content = new StringContent(JsonConvert.SerializeObject(message, Formatting.None), Encoding.UTF8, "application/json"); using (var requestResult = await SignalRHttpClient.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) { if (requestResult.IsSuccessStatusCode) { msgSent = true; log.LogInformation($"{userId} sent a message to {sendTo}"); } else { errPhrase = requestResult.ReasonPhrase; if (requestResult.Content != null) { errContent = await requestResult.Content.ReadAsStringAsync(); } log.LogError($"Cannot send signal. Error {requestResult.StatusCode}: {requestResult.ReasonPhrase}"); } } } } catch (Exception ex) { errMessage = ex.Message; errPhrase = ex.GetType().Name; errContent = ex.Message; log.LogError($"Exception when sending a SignalR request: {errMessage}"); } if (msgSent) { break; } } if (msgSent) { return((ActionResult) new OkResult()); } else { return(new BadRequestObjectResult( new { message = $"Cannot send signal. Error {httpStatus}: {errPhrase} ({errContent})" } )); } }
public static async Task <IActionResult> Run( [HttpTrigger(AuthorizationLevel.Anonymous, "post", Route = null)] HttpRequest req, ILogger log) { var authToken = req.Headers["authtoken"].ToString(); ServiceUtils utils = new ServiceUtils(ConfigUtils.GetSignalRConnection()); string userIdHash = string.Empty; string userId = string.Empty; if (!utils.ParseAndValidateToken(authToken, out userIdHash, out userId, null)) { log.LogError($"GetMessageToken: Invalid authentication token ({authToken})"); return(new BadRequestObjectResult("Invalid authentication token")); } TimeSpan messageTokenLifetime = TimeSpan.FromMinutes(UInt64.Parse(ConfigUtils.GetAuthTokenLifetimeMinutes())); var messageToken = utils.GenerateAccessToken(utils.Endpoint, userId, messageTokenLifetime); string errorMessage = string.Empty; string userName; string company; string team; string authService; string deviceId; SignalRGroupUtils.ParseUserId(userId, out userName, out deviceId, out company, out team, out authService); var hubName = ConfigUtils.GetHubName(); var groups = SignalRGroupUtils.GetUserGroups(authService, company, team, userName, deviceId); foreach (var group in groups) { var groupHash = SignalRGroupUtils.GetNameHash(group, utils.AccessKey); var url = $"{utils.Endpoint}/api/v1/hubs/{hubName}/groups/{groupHash}/users/{userIdHash}"; var requestToken = utils.GenerateAccessToken(url, userId, TimeSpan.FromMinutes(1)); using (var request = new HttpRequestMessage(HttpMethod.Put, url)) { request.Version = System.Net.HttpVersion.Version20; request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", requestToken); request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); using (var httpResult = await SignalRHttpClient.HttpClient.SendAsync(request, HttpCompletionOption.ResponseHeadersRead)) { if (!httpResult.IsSuccessStatusCode) { var body = await httpResult.Content.ReadAsStringAsync(); errorMessage = $"Cannot add user {userId} ({userIdHash}) to group {group} ({groupHash}). Code: {httpResult.StatusCode}, Message: {httpResult.ReasonPhrase}, Body: {body}"; log.LogError(errorMessage); break; } } } } return(string.IsNullOrEmpty(errorMessage) ? (ActionResult) new OkObjectResult(messageToken) : new BadRequestObjectResult(new { message = errorMessage })); }