I am getting an issue when creating subscription.
My steps are:
Register app at https://apps.dev.microsoft.com
Update permissions
Read Mail and User's info
Then update code, do same steps at https://learn.microsoft.com/en-us/outlook/rest/dotnet-tutorial
After login, I can get access token
Screen after login
Then I try to create a subscription for Inbox
var newSub = new Subscription
{
Resource = "me/mailFolders{'Inbox'}/messages",
ChangeType = "created,updated",
NotificationUrl = notificationUrl,
ClientState = clientState,
ExpirationDateTime = DateTime.Now.AddMinutes(15)
};
var result = await graphClient.Subscriptions.Request().AddAsync(newSub);
Implement for notification in notification URL - I can get validation token and return in plain text.
public async Task<ActionResult> Listen()
{
if (Request.QueryString["validationToken"] != null)
{
var token = Request.QueryString["validationToken"];
return Content(token, "plain/text");
}
}
But I always get this error.
Is there anyone know problem?
You must expose a public HTTPS endpoint to create a subscription and receive notifications from Microsoft Graph.
Related
I am currently working out the Microsoft Graph tutorial with C# .Net Core, and in the process I came across the following C#-method for Subscription:
[HttpGet]
public async Task<ActionResult<string>> Get()
{
var graphServiceClient = GetGraphClient();
var sub = new Microsoft.Graph.Subscription();
sub.ChangeType = "updated";
sub.NotificationUrl = config.Ngrok + "/api/notifications";
sub.Resource = "/users";
sub.ExpirationDateTime = DateTime.UtcNow.AddMinutes(15);
sub.ClientState = "SecretClientState";
var newSubscription = await graphServiceClient
.Subscriptions
.Request()
.AddAsync(sub);
Subscriptions[newSubscription.Id] = newSubscription;
if (subscriptionTimer == null)
{
subscriptionTimer = new Timer(CheckSubscriptions, null, 5000, 15000);
}
return $"Subscribed. Id: {newSubscription.Id}, Expiration: {newSubscription.ExpirationDateTime}";
}
and wanted to know how I can change it for sharepoint lists instead of users.
If I change it to /sites/{site-id} or similar it does not work. (see sub.Resource)
Github-Link: MS Repo
Microsoft Graph API uses a webhook mechanism to deliver change notifications to clients. Using the Microsoft Graph API, an app can subscribe to changes for list under a SharePoint site.
Resource Path - Changes to content within the list:
/sites/{id}/lists/{id}
For details round how to subscribe to and handle incoming notifications, see Set up notifications for changes in user data
Also make sure you check necessary permissions needed here.
I found the solution myself with the sub.Resource: /sites/{site-id}/lists/{list-id}
I have a bot written with the help of bot framework v4. The bot is integrated with Microsoft Teams. I want to send a welcome message to the user when the user installed the bot and joins the 1:1 conversation. In Teams the conversationUpdate is fired exactly once (this is when the suer joins the 1:1 conversation) and then never again for that user. My idea was to write a function that is triggered by a chat message to send the updateConversation activity manually to debug the welcome message.
I failed so far and got a
BadArgument: Unknown activity type exception.
I have tried using the Microsoft.Bot.Builder.Teams nuget using the ConnectorClient to send the conversationUpdate activity to the conversation.
Also I set up a console application and tried using the v3/directline/conversations/{conversationId}/activities and got a Forbidden error.
private async Task SendConversationUpdateToTeamsAsync(ITurnContext turnContext, CancellationToken cToken = default)
{
var connectorClient = turnContext.TurnState.Get<IConnectorClient>();
var conversationUpdateMessage = new Activity
{
Type = ActivityTypes.ConversationUpdate,
Id = turnContext.Activity.Id,
ServiceUrl = turnContext.Activity.ServiceUrl,
From = turnContext.Activity.From,
Recipient = turnContext.Activity.Recipient,
Conversation = turnContext.Activity.Conversation,
ChannelData = turnContext.Activity.ChannelData,
ChannelId = turnContext.Activity.ChannelId,
Timestamp = turnContext.Activity.Timestamp,
MembersAdded = new List<ChannelAccount>
{
turnContext.Activity.From,
turnContext.Activity.Recipient
},
};
var result = await connectorClient.Conversations.SendToConversationAsync(conversationUpdateMessage, cToken);
}
I expect that sending a conversationUpdate manually to debug the behavior in Teams works. Creating new users in the office portal and installing the bot for them to debug the conversationUpdate behavior is no option for me, because it is to time consuming. If there is another workaround to trigger the conversationUpdate in Teams please let me know.
I'm not sure of a way to force a ConversationUpdate to be sent in the way you're attempting to. Instead, I'd just throw something like this in OnMessageAsync():
if (turnContext.Activity.Text == "fakeConversationUpdate")
{
var fakeTurnContext = new TurnContext(turnContext.Adapter, MessageFactory.Text(string.Empty));
fakeTurnContext.Activity.AsConversationUpdateActivity();
fakeTurnContext.Activity.Type = ActivityTypes.ConversationUpdate;
fakeTurnContext.Activity.MembersAdded = new List<ChannelAccount>()
{
new ChannelAccount()
{
Id = "fakeUserId",
Name = "fakeUserName"
}
};
await OnConversationUpdateActivityAsync(new DelegatingTurnContext<IConversationUpdateActivity>(fakeTurnContext), cancellationToken);
}
Then to debug, you just write "fakeConversationUpdate" (which you can change/customize) to the bot in chat and it will send your fakeTurnContext (which you can change/customize) through OnConversationUpdateActivityAsync()
I recently implemented custom authentication with Azure Mobile App - All the server side works fine and also my web application which is using that mobile app service is working fine. I tested the server-side in details with POSTMAN and with different scenarios, everything works fine until I try to LoginAsync on Xamarin.
When I pass email and password in POSTMAN, I get the following response as a clear indication that it is working
but when I send a request from my app using LoginAsync I get the following error.
Cannot access child value on Newtonsoft.Json.Linq.JValue
My code to send request is fairly simple as following
public async Task<bool> Authenticate()
{
string username = "todo#gmail.com";
string password = "todo";
string message = string.Empty;
var success = false;
var credentials = new JObject
{
["email"] = username,
["password"] = password
};
try
{
MobileServiceUser user = await client.LoginAsync("CustomAuth", credentials);
if (user != null)
{
success = true;
CreateAndShowDialog("OK", "Auth");
}
}
catch (Exception ex)
{
CreateAndShowDialog(ex, "Auth Error");
}
return success;
}
where I am calling it as follows
private MobileServiceClient client;
client = new MobileServiceClient(applicationURL);
await Authenticate();
Any idea why I am getting Cannot access child value on Newtonsoft.Json.Linq.JValue error?
Cheers
EDIT POST
As a workaround, I am temporarily using InvokeApiAsync with JObject.FromObject instead of LoginAsync
await client.InvokeApiAsync("/.auth/login/CustomAuth", JObject.FromObject(credentials), HttpMethod.Post, null);
I am still not sure why LoginAsync does not work - Until I find a solution I will keep using InvokdeApiAsync as a workaround
AFAIK, your initialization for credentials is correct. For the below error:
Cannot access child value on Newtonsoft.Json.Linq.JValue
I checked your testing result via POSTMAN and found that you did not return userId to your client. The essential properties returned to your client would look like as follows:
{
"authenticationToken":"***",
"user":{
"userId":"***"
}
}
When using MobileServiceClient.LoginAsync, the client SDK would internally invoke LoginAsync() method under MobileServiceAuthentication.cs as follows:
JToken authToken = JToken.Parse(response);
// Get the Mobile Services auth token and user data
this.Client.CurrentUser = new MobileServiceUser((string)authToken["user"]["userId"]);
this.Client.CurrentUser.MobileServiceAuthenticationToken = (string)authToken[LoginAsyncAuthenticationTokenKey];
You would find that it would try to extract the userId property under user to construct the MobileServiceUser instance and assign to MobileServiceClient.CurrentUser.
Using the backend of my app, I am attempting to capture information from Microsoft Graph for a user that has been authenticated and then add that user to a database. The authentication appears to be working correctly, but the user is never added to the database. I am really stuck on this. I've studied the online documentation extensively, but have been unable to find a solution. If I could just tell if the user properties were getting populated, I could figure out what's going on, but I've been unable to do that since the code runs on the server. (I've attempted to remote debug, but have been unable to successfully set a breakpoint.) Can anyone tell me what I'm doing wrong in the code below?
class MicrosoftAccountInfo
{
public string id { get; set; }
public string displayName { get; set; }
public string mail { get; set; }
}
[MobileAppController]
public class MicrosoftAccountController : ApiController
{
MicrosoftAccountCredentials credentials;
string msRequestUrl;
MyAppContext context;
EntityDomainManager<User> domainManager;
// GET api/<controller>
public async Task<User> Get()
{
if (credentials == null)
{
credentials = await this.User.GetAppServiceIdentityAsync<MicrosoftAccountCredentials>(this.Request);
}
msRequestUrl = "https://graph.microsoft.com/v1.0/me/?$select=id,displayName,mail";
var client = new System.Net.Http.HttpClient();
var headerValue = "Bearer" + credentials.AccessToken;
client.DefaultRequestHeaders.Add("Authorization", headerValue);
var resp = await client.GetAsync(msRequestUrl);
resp.EnsureSuccessStatusCode();
var msInfo = await resp.Content.ReadAsStringAsync();
MicrosoftAccountInfo info = JsonConvert.DeserializeObject<MicrosoftAccountInfo>(msInfo);
context = new MyAppContext();
domainManager = new EntityDomainManager<User>(context, Request);
var user = context.Users.FirstOrDefault(u => u.Email == info.mail);
if (user == null)
{
user = new DataObjects.User { Email = info.mail, UserName = info.displayName, ProviderId = info.id };
await domainManager.InsertAsync(user);
}
else if (string.IsNullOrEmpty(user.ProviderId))
{
user.UserName = info.displayName;
user.ProviderId = info.id;
await context.SaveChangesAsync();
}
return user;
}
}
As to why this is failing, it is difficult to determine without an actual error message. There are simply to many variables/potential failure points involved to say for sure.
That said, you can reduce the number of potential failure points by using the Microsoft Graph .NET Client Library. There is also a NuGet package available: Install-Package Microsoft.Graph.
This library will handle composing the Microsoft Graph and deserializing the response into an object. Along with removing a risk factor, it will greatly simplify your code:
Microsoft.Graph.GraphServiceClient graphClient =
new Microsoft.Graph.GraphServiceClient(new DelegateAuthenticationProvider((requestMessage) =>
{
requestMessage.Headers.Authorization =
new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", "{your-access-token}");
return Task.FromResult(0);
}));
Microsoft.Graph.User user = await graphClient.Me.Request().GetAsync();
I would also suggest implementing a monitoring solution that can trap exceptions on the server. This will help with debugging. If you're running on Azure, I strongly recommend using Application Insights. Aside from being free to get started, it is effectively a "click once, get monitoring" solution. It will handle wiring up the server and provide reporting for any exceptions it runs into.
Note that you can also use App Insights with your own servers or apps hosted on other services (i.e. AWS, RackSpace), there may however be some manual configuration required.
I have set up a webhook in my Braintree account https://example.com/Webhook/Accept
I have the site setup to create a Customer & Subscription via a purchase link.
When I make a purchase via the link these are both created ok in the Braintree vault, the webhook fires and I receive 2 notifications.
This all seems great, all is working, except the for 'Check URL' in the Braintree Control panel Webhook section, where im getting a HTTP error 500 internal server error.
I think its a bt_challenge issue on the GET but having tried a few variations I simply cant get it working.
Here is my controller:
public class WebhookController : Controller
{
public IBraintreeConfiguration config = new BraintreeConfiguration();
public ActionResult Accept()
{
CrmDB db = new CrmDB();
Log log;
var gateway = config.GetGateway();
if (Request.HttpMethod == "POST")
{
WebhookNotification webhookNotification = gateway.WebhookNotification.Parse(
Request.Params["bt_signature"],
Request.Params["bt_payload"]
);
string message = string.Format(
"[Webhook Received {0}] | Kind: {1} | Subscription: {2}",
webhookNotification.Timestamp.Value,
webhookNotification.Kind,
webhookNotification.Subscription.Id
);
System.Console.WriteLine(message);
// Save to Db
log = new Log
{
Stamp = DateTime.Now,
App = "api/Webhook/Accept",
IsInsight = true,
Insight = message
};
// Db
db.Log.Add(log);
db.SaveChanges();
return new HttpStatusCodeResult(200);
}
else
{
string msg = gateway.WebhookNotification.Verify(Request.QueryString["bt_challenge"]);
// Save to Db
log = new Log
{
Stamp = DateTime.Now,
App = "Webhook - bt_challenge",
IsInsight = true,
Insight = msg
};
// Db
db.Log.Add(log);
db.SaveChanges();
return Content(msg);
}
}
}
I believe the issue is in the else section (ie HTTP GET) but I cant work out what it is.
Why am I getting the 500 internal error from Webhook control Panel?