// Create a subscription. public async Task <IActionResult> Create() { string userId = User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier")?.Value; string tenantId = User.FindFirst("http://schemas.microsoft.com/identity/claims/tenantid")?.Value; string clientState = Guid.NewGuid().ToString(); Subscription newSubscription = new Subscription(); try { // Initialize the GraphServiceClient. // This sample passes in the tenant ID to use as a cache key. GraphServiceClient graphClient = sdkHelper.GetAuthenticatedClient(tenantId); // Create a subscription. // The `Resource` property targets the `users/{user-id}` or `users/{user-principal-name}` path (not `me`) when using application permissions. // The NotificationUrl requires the `https` protocol and supports custom query parameters. newSubscription = await graphClient.Subscriptions.Request().AddAsync(new Subscription { Resource = $"users/{ userId }/mailFolders('Inbox')/messages", ChangeType = "created", NotificationUrl = appSettings.NotificationUrl, ClientState = clientState, //ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 4230, 0) // current maximum lifespan for messages ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 15, 0) // shorter duration useful for testing }); // Verify client state, then store the subscription ID and client state to validate incoming notifications. if (newSubscription.ClientState == clientState) { // This sample temporarily stores the subscription data, but production apps will likely use some method of persistent storage. // This sample stores the client state to validate the subscription, the tenant ID to reuse tokens, and the user ID to filter // messages to display by user. subscriptionStore.SaveSubscriptionInfo(newSubscription.Id, newSubscription.ClientState, userId, tenantId); } else { ViewBag.Message = "Warning! Mismatched client state."; } } catch (Exception e) { // If a tenant admin hasn't granted consent, this operation returns an Unauthorized error. // This sample caches the initial unauthorized token, so you'll need to start a new browser session. ViewBag.Message = BuildErrorMessage(e); return(View("Error")); } return(View("Subscription", newSubscription)); }
// Get information about the changed messages and send to browser via SignalR. // A production application would typically queue a background job for reliability. private async Task GetChangedMessagesAsync(IEnumerable <Notification> notifications) { List <Message> messages = new List <Message>(); foreach (var notification in notifications) { if (notification.ResourceData.ODataType != "#Microsoft.Graph.Message") { continue; } // Get the stored user object ID. var subscriptionParams = (Tuple <string, string>)_memoryCache.Get("subscriptionId_" + notification.SubscriptionId); string userObjectId = subscriptionParams.Item2; // Initialize the GraphServiceClient, using the user ID associated with the subscription. GraphServiceClient graphClient = _sdkHelper.GetAuthenticatedClient(userObjectId); MessageRequest request = new MessageRequest(graphClient.BaseUrl + "/" + notification.Resource, graphClient, null); try { messages.Add(await request.GetAsync()); } catch (Exception) { continue; } } if (messages.Count > 0) { //NotificationService notificationService = new NotificationService(); //notificationService.SendNotificationToClient(messages); } }
// Create a subscription. // https://graph.microsoft.io/en-us/docs/api-reference/v1.0/resources/webhooks public async Task <IActionResult> Create() { // Initialize the GraphServiceClient. string userObjectID = (User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier"))?.Value; GraphServiceClient graphClient = sdkHelper.GetAuthenticatedClient(userObjectID); Subscription newSubscription = new Subscription(); string state = Guid.NewGuid().ToString(); try { // Send the `POST /subscriptions` request. newSubscription = await graphClient.Subscriptions.Request().AddAsync(new Subscription { Resource = "me/mailFolders('Inbox')/messages", ChangeType = "created", NotificationUrl = "https://6b6b3cf8.ngrok.io/notification/listen", ClientState = state, ExpirationDateTime = DateTime.UtcNow + new TimeSpan(0, 0, 4230, 0) }); // Verify client state. if (newSubscription.ClientState != state) { ViewBag.Message = "Warning: Mismatched client state."; } // Store the subscription ID to correlate a notification to the corresponding subscription. else { // This sample temporarily stores the current subscription ID, client state, and user object ID. // The NotificationController, which is not authenticated, uses this info to validate the subscription and get an access token keyed from the subscription ID. // Production apps will typically use some method of persistent storage. memoryCache.Set("subscriptionId_" + newSubscription.Id, Tuple.Create(newSubscription.ClientState, HttpContext.User.FindFirst("http://schemas.microsoft.com/identity/claims/objectidentifier").Value), new MemoryCacheEntryOptions().SetAbsoluteExpiration(TimeSpan.FromHours(24))); } } catch (Exception e) { ViewBag.Message = BuildErrorMessage(e); return(View("Error", e)); } return(View("Subscription", newSubscription)); }
// Get information about the changed messages and send to browser via SignalR. // A production application would typically queue a background job for reliability. private async Task GetChangedMessagesAsync(IEnumerable <Notification> notifications) { List <MessageViewModel> messages = new List <MessageViewModel>(); foreach (var notification in notifications) { if (notification.ResourceData.ODataType != "#Microsoft.Graph.Message") { continue; } SubscriptionStore subscription = subscriptionStore.GetSubscriptionInfo(notification.SubscriptionId); // Initialize the GraphServiceClient. This sample uses the tenant ID the cache key. GraphServiceClient graphClient = sdkHelper.GetAuthenticatedClient(subscription.TenantId); MessageRequest request = new MessageRequest(graphClient.BaseUrl + "/" + notification.Resource, graphClient, null); try { messages.Add(new MessageViewModel(await request.GetAsync(), subscription.UserId)); } catch (ServiceException se) { string errorMessage = se.Error.Message; string requestId = se.Error.InnerError.AdditionalData["request-id"].ToString(); string requestDate = se.Error.InnerError.AdditionalData["date"].ToString(); logger.LogError($"RetrievingMessages: { errorMessage } Request ID: { requestId } Date: { requestDate }"); } } if (messages.Count > 0) { NotificationService notificationService = new NotificationService(); // Clients use the subscribedUserId to filter for messages that belong to the current user. notificationService.SendNotificationToClient(connectionManager, messages); } }