WRONG APPROACH
I have a problem, I can log in through my program, however I am always sent to another website. (deviceauth)
Every time I start the program, I am asked to enter a code on this website. I want to be logged in directly without having to go through the previous step.
Is there a way to log in directly with the AccessToken? If yes, how?
My Goal is to log in once and everytime I start the program, I should be directly logged in and be able to look at my Mails.
My Code:
using Microsoft.Graph;
Console.WriteLine(".NET Graph Tutorial\n");
//var settings = Settings.LoadSettings("appsettings.json");
var settings = Settings.LoadSettings("appsettingsSLSTracer.json");
// Initialize Graph
InitializeGraph(settings);
// Greet the user by name
await GreetUserAsync();
int choice = -1;
while (choice != 0)
{
Console.WriteLine("Please choose one of the following options:");
Console.WriteLine("0. Exit");
Console.WriteLine("1. Display access token");
Console.WriteLine("2. List my inbox");
Console.WriteLine("3. Send mail");
Console.WriteLine("4. Make a Graph call");
try
{
choice = int.Parse(Console.ReadLine() ?? string.Empty);
}
catch (System.FormatException)
{
// Set to invalid value
choice = -1;
}
switch (choice)
{
case 0:
// Exit the program
Console.WriteLine("Goodbye...");
break;
case 1:
// Display access token
await DisplayAccessTokenAsync();
break;
case 2:
// List emails from user's inbox
await ListInboxAsync();
break;
case 3:
// Send an email message
await SendMailAsync();
break;
case 4:
// Run any Graph code
await MakeGraphCallAsync();
break;
default:
Console.WriteLine("Invalid choice! Please try again.");
break;
}
}
// </ProgramSnippet>
// <InitializeGraphSnippet>
void InitializeGraph(Settings settings)
{
//if (System.IO.File.Exists(#"C:\windows\temp\msaccesstokenSLSTracer.txt"))
//{
//_userClient = System.IO.File.ReadAllText("C:\\temp\\msaccesstoken.txt");
//return;
//}
//else
{
GraphHelper.InitializeGraphForUserAuth(settings,
(info, cancel) =>
{
// Display the device code message to
// the user. This tells them
// where to go to sign in and provides the
// code to use.
Console.WriteLine(info.Message);
return Task.FromResult(0);
});
}
}
// </InitializeGraphSnippet>
// <GreetUserSnippet>
async Task GreetUserAsync()
{
try
{
var user = await GraphHelper.GetUserAsync();
Console.WriteLine($"Hello, {user?.DisplayName}!");
// For Work/school accounts, email is in Mail property
// Personal accounts, email is in UserPrincipalName
Console.WriteLine($"Email: {user?.Mail ?? user?.UserPrincipalName ?? ""}");
}
catch (Exception ex)
{
Console.WriteLine($"Error getting user: {ex.Message}");
}
}
// </GreetUserSnippet>
// <DisplayAccessTokenSnippet>
async Task DisplayAccessTokenAsync()
{
try
{
var userToken = await GraphHelper.GetUserTokenAsync();
Console.WriteLine($"User token: {userToken}");
}
catch (Exception ex)
{
Console.WriteLine($"Error getting user access token: {ex.Message}");
}
}
// </DisplayAccessTokenSnippet>
// <ListInboxSnippet>
async Task ListInboxAsync()
{
try
{
var messagePage = await GraphHelper.GetInboxAsync();
// Output each message's details
foreach (var message in messagePage.CurrentPage)
{
Console.WriteLine($"Message: {message.Subject ?? "NO SUBJECT"}");
Console.WriteLine($" From: {message.From?.EmailAddress?.Name}");
Console.WriteLine($" Status: {(message.IsRead!.Value ? "Read" : "Unread")}");
//Console.WriteLine($" BodyPreview: {message.Body}");
Console.WriteLine($" Attachments: {message.Attachments?.Count ?? 0}");
Console.WriteLine($" Received: {message.ReceivedDateTime?.ToLocalTime().ToString()}\n");
}
// If NextPageRequest is not null, there are more messages
// available on the server
// Access the next page like:
// messagePage.NextPageRequest.GetAsync();
var moreAvailable = messagePage.NextPageRequest != null;
Console.WriteLine($"\nMore messages available? {moreAvailable}");
}
catch (Exception ex)
{
Console.WriteLine($"Error getting user's inbox: {ex.Message}");
}
}
// </ListInboxSnippet>
// <SendMailSnippet>
async Task SendMailAsync()
{
try
{
// Send mail to the signed-in user
// Get the user for their email address
var user = await GraphHelper.GetUserAsync();
var userEmail = user?.Mail ?? user?.UserPrincipalName;
if (string.IsNullOrEmpty(userEmail))
{
Console.WriteLine("Couldn't get your email address, canceling...");
return;
}
await GraphHelper.SendMailAsync("Test des Programmes",
"Es hat geklappt", "Testobjekt1919#outlook.com"); //userEmail
Console.WriteLine("Mail sent.");
}
catch (Exception ex)
{
Console.WriteLine($"Error sending mail: {ex.Message}");
}
}
// </SendMailSnippet>
// <MakeGraphCallSnippet>
async Task MakeGraphCallAsync()
{
await GraphHelper.MakeGraphCallAsync();
}
// </MakeGraphCallSnippet>
Other Code:
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT license.
using Azure.Core;
using Azure.Identity;
using Microsoft.Graph;
class GraphHelper
{
#region User-auth
// <UserAuthConfigSnippet>
// Settings object
private static Settings? _settings;
// User auth token credential
private static DeviceCodeCredential? _deviceCodeCredential;
// Client configured with user authentication
private static GraphServiceClient? _userClient;
public static void InitializeGraphForUserAuth(Settings settings,
Func<DeviceCodeInfo, CancellationToken, Task> deviceCodePrompt)
{
_settings = settings;
_deviceCodeCredential = new DeviceCodeCredential(deviceCodePrompt,
settings.AuthTenant, settings.ClientId);
_userClient = new GraphServiceClient(_deviceCodeCredential, settings.GraphUserScopes);
/// Todo Idee, hier prüfen ob wir nicht den Token mitsenden können
//if (System.IO.File.Exists(#"C:\windows\temp\msaccesstokenSLSTracer.txt"))
//{ }
//_userClient.Token = System.IO.File.ReadAllText("C:\\temp\\msaccesstoken.txt");
}
// </UserAuthConfigSnippet>
// <GetUserTokenSnippet>
public static async Task<string> GetUserTokenAsync()
{
// Ensure credential isn't null
_ = _deviceCodeCredential ??
throw new System.NullReferenceException("Graph has not been initialized for user auth");
// Ensure scopes isn't null
_ = _settings?.GraphUserScopes ?? throw new System.ArgumentNullException("Argument 'scopes' cannot be null");
// Request token with given scopes
var context = new TokenRequestContext(_settings.GraphUserScopes);
var response = await _deviceCodeCredential.GetTokenAsync(context);
System.IO.File.WriteAllText( #"C:\windows\temp\msaccesstokenSLSTracer.txt", response.Token);
return response.Token;
}
// </GetUserTokenSnippet>
// <GetUserSnippet>
public static Task<User> GetUserAsync()
{
// Ensure client isn't null
_ = _userClient ??
throw new System.NullReferenceException("Graph has not been initialized for user auth");
return _userClient.Me
.Request()
.Select(u => new
{
// Only request specific properties
u.DisplayName,
u.Mail,
u.UserPrincipalName
})
.GetAsync();
}
// </GetUserSnippet>
// <GetInboxSnippet>
public static Task<IMailFolderMessagesCollectionPage> GetInboxAsync()
{
// Ensure client isn't null
_ = _userClient ??
throw new System.NullReferenceException("Graph has not been initialized for user auth");
return _userClient.Me
// Only messages from Inbox folder
.MailFolders["Inbox"]
.Messages
.Request()
.Select(m => new
{
// Only request specific properties
m.From,
m.IsRead,
m.ReceivedDateTime,
m.Subject,
m.Attachments
//m.BodyPreview,
//m.Body
})
// Get at most 25 results
.Top(15)
// Sort by received time, newest at the top
.OrderBy("ReceivedDateTime DESC")
.GetAsync();
}
// </GetInboxSnippet>
// <SendMailSnippet>
public static async Task SendMailAsync(string subject, string body, string recipient)
{
// Ensure client isn't null
_ = _userClient ??
throw new System.NullReferenceException("Graph has not been initialized for user auth");
// Create a new message
var message = new Message
{
Subject = subject,
Body = new ItemBody
{
Content = body,
ContentType = BodyType.Text
},
ToRecipients = new Recipient[]
{
new Recipient
{
EmailAddress = new EmailAddress
{
Address = recipient
}
}
}
};
// Send the message
await _userClient.Me
.SendMail(message)
.Request()
.PostAsync();
}
// </SendMailSnippet>
#endregion
#pragma warning disable CS1998
// <MakeGraphCallSnippet>
// This function serves as a playground for testing Graph snippets
// or other code
public async static Task MakeGraphCallAsync()
{
// INSERT YOUR CODE HERE
// Note: if using _appClient, be sure to call EnsureGraphForAppOnlyAuth
// before using it.
// EnsureGraphForAppOnlyAuth();
}
// </MakeGraphCallSnippet>
}
At the moment your code is using the device code oAuth flow https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-device-code so you will always be redirected/prompt that way. For unattended apps you generally want to use the client credentials flow https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-client-creds-grant-flow and application permissions.
If you still want to use delegate access (eg user creds) then you can look at using a persistent cache https://pnp.github.io/pnpcore/demos/Demo.PersistentTokenCache/README.html in that case you should be using the authorization code flow https://learn.microsoft.com/en-us/azure/active-directory/develop/v2-oauth2-auth-code-flow eg https://learn.microsoft.com/en-us/azure/active-directory/develop/scenario-desktop-acquire-token-interactive?tabs=dotnet a very simplified implementation would look like
GraphServiceClient graphServiceClient = new GraphServiceClient(new DelegateAuthenticationProvider(async (requestMessage) =>
{
var authResult = publicClientApplication.AcquireTokenInteractive(new[] { "https://graph.microsoft.com/Mail.Read" })
.ExecuteAsync().GetAwaiter().GetResult();
requestMessage.Headers.Authorization =
new AuthenticationHeaderValue("Bearer", authResult.AccessToken);
}), httpProvider
);
but you need a more complex implementation to use caching
Related
I am looking for some feedback or guidance. I have built a grpc server and client in dotnet core 3.1. Only this client will be talking to the server, but still need to protect the server endpoints. I am trying to implement authentication security on it and the jwt bearer token seems like a good path. I am trying to follow the example here(which came from this MS Doc) which seems like a pretty simple implementation, however seems insecure for production use since anyone can hit the generate token endpoint. I figured it could modify and implement a shared secret that is passed on the get token request. Does this seem like a good approach or other thoughts?
Besides this part, I have a question about the client side code that I don't quite understand. At the start of the client, it establishes a grpc channel by calling the CreateAuthenticatedChannel() which appears to only add the bearer token if the _token is set. You don't get the token until the client starts up and hit number three, but I don't see where the channel or client is rebuilt to include the metadata token value. The sample appears to work to but don't understand it.
class Program
{
// The port number(5001) must match the port of the gRPC server.
private const string Address = "localhost:5001";
private static string _token;
static async Task Main(string[] args)
{
var channel = CreateAuthenticatedChannel($"https://{Address}");
var client = new Ticketer.TicketerClient(channel);
Console.WriteLine("gRPC Ticketer");
Console.WriteLine();
Console.WriteLine("Press a key:");
Console.WriteLine("1: Get available tickets");
Console.WriteLine("2: Purchase ticket");
Console.WriteLine("3: Authenticate");
Console.WriteLine("4: Exit");
Console.WriteLine();
var exiting = false;
while (!exiting)
{
var consoleKeyInfo = Console.ReadKey(intercept: true);
switch (consoleKeyInfo.KeyChar)
{
case '1':
await GetAvailableTickets(client);
break;
case '2':
await PurchaseTicket(client);
break;
case '3':
_token = await Authenticate();
break;
case '4':
exiting = true;
break;
}
}
Console.WriteLine("Exiting");
}
private static GrpcChannel CreateAuthenticatedChannel(string address)
{
var credentials = CallCredentials.FromInterceptor((context, metadata) =>
{
if (!string.IsNullOrEmpty(_token))
{
metadata.Add("Authorization", $"Bearer {_token}");
}
return Task.CompletedTask;
});
// SslCredentials is used here because this channel is using TLS.
// Channels that aren't using TLS should use ChannelCredentials.Insecure instead.
var channel = GrpcChannel.ForAddress(address, new GrpcChannelOptions
{
Credentials = ChannelCredentials.Create(new SslCredentials(), credentials)
});
return channel;
}
private static async Task<string> Authenticate()
{
Console.WriteLine($"Authenticating as {Environment.UserName}...");
var httpClient = new HttpClient();
var request = new HttpRequestMessage
{
RequestUri = new Uri($"https://{Address}/generateJwtToken?name={HttpUtility.UrlEncode(Environment.UserName)}"),
Method = HttpMethod.Get,
Version = new Version(2, 0)
};
var tokenResponse = await httpClient.SendAsync(request);
tokenResponse.EnsureSuccessStatusCode();
var token = await tokenResponse.Content.ReadAsStringAsync();
Console.WriteLine(token);
Console.WriteLine("Successfully authenticated.");
return token;
}
private static async Task PurchaseTicket(Ticketer.TicketerClient client)
{
Console.WriteLine("Purchasing ticket...");
try
{
var response = await client.BuyTicketsAsync(new BuyTicketsRequest { Count = 1 });
if (response.Success)
{
Console.WriteLine("Purchase successful.");
}
else
{
Console.WriteLine("Purchase failed. No tickets available.");
}
}
catch (Exception ex)
{
Console.WriteLine("Error purchasing ticket." + Environment.NewLine + ex.ToString());
}
}
private static async Task GetAvailableTickets(Ticketer.TicketerClient client)
{
Console.WriteLine("Getting available ticket count...");
var response = await client.GetAvailableTicketsAsync(new Empty());
Console.WriteLine("Available ticket count: " + response.Count);
}
}
}
Code:
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
//trust Webchat &SMS channel
MicrosoftAppCredentials.TrustServiceUrl(#"https://webchat.botframework.com", DateTime.MaxValue);
MicrosoftAppCredentials.TrustServiceUrl(#"https://sms.botframework.com", DateTime.MaxValue);
Trace.TraceInformation($"Incoming Activity is {activity.ToJson()}");
if (activity.Type == ActivityTypes.Message)
{
if (!string.IsNullOrEmpty(activity.Text))
{
//detect language of input text
var userLanguage = TranslationHandler.DetectLanguage(activity);
//save user's LanguageCode to Azure Table Storage
var message = activity as IMessageActivity;
try
{
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, message))
{
var botDataStore = scope.Resolve<IBotDataStore<BotData>>();
var key = new AddressKey()
{
BotId = message.Recipient.Id,
ChannelId = message.ChannelId,
UserId = message.From.Id,
ConversationId = message.Conversation.Id,
ServiceUrl = message.ServiceUrl
};
var userData = await botDataStore.LoadAsync(key, BotStoreType.BotUserData, CancellationToken.None);
var storedLanguageCode = userData.GetProperty<string>(StringConstants.UserLanguageKey);
//update user's language in Azure Table Storage
if (storedLanguageCode != userLanguage)
{
userData.SetProperty(StringConstants.UserLanguageKey, userLanguage);
await botDataStore.SaveAsync(key, BotStoreType.BotUserData, userData, CancellationToken.None);
await botDataStore.FlushAsync(key, CancellationToken.None);
}
}
}
catch (Exception ex)
{
throw ex;
}
//translate activity.Text to English before sending to LUIS for intent
activity.Text = TranslationHandler.TranslateTextToDefaultLanguage(activity, userLanguage);
//await Conversation.SendAsync(activity, () => new Dialogs.RootDialog());
await Conversation.SendAsync(activity, MakeRoot);
}
else {
await Conversation.SendAsync(activity, MakeRoot);
}
}
else
{
HandleSystemMessageAsync(activity);
}
var response = Request.CreateResponse(HttpStatusCode.OK);
return response;
}
internal static IDialog<object> MakeRoot()
{
try
{
return Chain.From(() => new RootDialog());
}
catch (Exception ex)
{
throw ex;
}
}
I am implementing Microsoft language translator in Bot framework and also implementing the Language understanding Service(Luis). In my code, once a user sends in a message to the bot, the language translator first detect the user language before sending it to LUIS (my LUIS application is in English) in the root dialog, then the bot translate the response back to the user language using the state data.
Everything was working fine until i made use of the formflow, some input text by the bot user in the form flow are being translated to some other languages if they are not English words. Please how do i correct this without removing the Language Translation API?.
note:
There is a section in my RootDailog that calls the formflow. Is there anyway i can stop the language translator API from translation the form input?
You can inspect the dialog stack and not translate if the last dialog in the stack is the FormFlow dialog. Something like:
bool translate = true;
using (var scope = DialogModule.BeginLifetimeScope(Conversation.Container, activity))
{
var botData = scope.Resolve<IBotData>();
await botData.LoadAsync(default(CancellationToken));
var stack = scope.Resolve<IDialogTask>();
if (stack.Frames != null && stack.Frames.Count > 0)
{
var lastFrame = stack.Frames[stack.Frames.Count - 1];
var frameValue = lastFrame.Target.GetType().GetFields()[0].GetValue(lastFrame.Target);
if (frameValue.GetType() == typeof(FormDialog<SandwichOrder>))
{
//in the FormFlow dialog ... do NOT translate
translate = false;
}
}
}
if (translate)
{
//translate activity.Text to English before sending to LUIS for intent
activity.Text = TranslationHandler.TranslateTextToDefaultLanguage(activity, userLanguage);
}
await Conversation.SendAsync(activity, MakeRoot);
I use RestSharp to pass data between the clien-side (Xamarin android app) and my server.
When there is an error (usually because the server is down) the method that execute the request throw an exception.
I want the exception to go back all the way to the method who called it, so I can throw an error to the user.
For example, I want to login, but lets say the server is down.
A - The method that execute the request
public Task<T> ExecuteAsync<T>(RestRequest request) where T : new()
{
var client = new RestClient
{
BaseUrl = new Uri(BaseUrl),
Authenticator = new HttpBasicAuthenticator(_accountName, _password)
};
var taskCompletionSource = new TaskCompletionSource<T>();
client.ExecuteAsync<T>(request, restResponse =>
{
if (restResponse.ErrorException != null)
{
throw (new Exception("Server returned an error"));
}
taskCompletionSource.SetResult(restResponse.Data);
});
return taskCompletionSource.Task;
}
B - Method that uses method A to execute a request
public static async Task<LoginObject> Login(string accessNumber, string password, string token)
{
var request = new RestRequest
{
Method = Method.POST,
Resource = "Login"
};
request.AddJsonBody(
new
{
accessNumber = accessNumber,
password = password,
token = token
});
var isDone = await Api.ExecuteAsync<LoginObject>(request);
return isDone;
}
C - The method where I want to handle the exception
public async Task Login(string PhoneNumber, string Password)
{
try
{
LoginObject login = await LoginServices.Login(PhoneNumber, Password, Token);
if (login.IsOk)
{
// Move to next activity
}
else
{
Toast.MakeText(this, "Login Error", ToastLength.Short).Show();
}
}
catch (Exception ex) // Here I want to throw the server error
{
Toast.MakeText(this, "Server Error", ToastLength.Short).Show();
return null;
}
}
Now when I run the code, the error is being thrown in A, and the app crash,
I want it to go from A to B and from B to C, and then I'll show an error to the user.
Edit: I tried to put a try/catch block but it still throws the exception in A.
Change method A to have async in the signature and then change your last line to return await taskCompletionSource.Task;
In your A-method, please use taskCompletionSource.SetException like this:
if (restResponse.ErrorException != null)
{
//throw new Exception("Server returned an error");
taskCompletionSource.SetException(new Exception("Server returned an error"));
}
else
{
taskCompletionSource.SetResult(restResponse.Data);
}
In your B-method replace this line:
var isDone = await Api.ExecuteAsync<LoginObject>(request);
with this to re-throw the exception to you C-method:
LoginObject isDone=null;
try
{
isDone = await Api.ExecuteAsync<LoginObject>(request);
}
catch (Exception e)
{
throw e;
}
This article is talking about TaskCompletionSource
My bot keeps failing on the following line of code with the error in Skype, WebChat, and FB chat. I've tried entering the MS App ID and password via the app settings and the web.config. This works fine in the emulator without any errors. When I ran remote debugging I found that this fails on Webchat, Skype, and FB messenger at this line of code:
await connector.Conversations.ReplyToActivityAsync(reply1);
My bot is integrated with SmartThings so the smart accessories are turning on and off as expected but the response back that should be returned in the chat seems to be failing. I've tried creating a new bot service app but it fails as well.
Update including all code:
using System;
using System.Net;
using System.Net.Http;
using System.Threading.Tasks;
using System.Web.Http;
using Microsoft.Bot.Connector;
using System.Net.Http.Headers;
using System.Threading;
using Microsoft.IdentityModel.Protocols;
using System.Configuration;
namespace FirstBotApp
{
[BotAuthentication]
public class Switches
{
public string name { get; set; }
public string value { get; set; }
}
public class MessagesController : ApiController
{
static HttpClient client = new HttpClient();
static HttpResponseMessage responsemain = null;
static int j = 0;
static void ShowSwitches(Switches[] switches)
{
for (int i = switches.Length - 1; i >= 0; i--)
{
Console.WriteLine($"Name: {switches[i].name}\tPrice: {switches[i].value}\t");
}
return;
}
static async Task<Switches[]> GetSwitchesAsync(string path)
{
Switches[] switches = null;
responsemain = await client.GetAsync(path);
//Console.WriteLine(client.GetAsync(path));
//Console.WriteLine(response);
if (responsemain.IsSuccessStatusCode)
{
//Console.WriteLine($"Successful response: {response.StatusCode}");
var response1 = await client.GetStringAsync("");
Console.WriteLine($"{response1}");
switches = await responsemain.Content.ReadAsAsync<Switches[]>();
//JObject j = JObject.Parse(response1);
}
else Console.WriteLine($"Error in response: {responsemain.StatusCode}");
return switches;
}
static async Task<Switches> UpdateSwitchesAsync(Switches switches)
{
Console.WriteLine("Turning off the light");
HttpResponseMessage response = await client.PutAsJsonAsync($"switches/off?room=LivingRoom", switches);
response.EnsureSuccessStatusCode();
Console.WriteLine($"The response from the server was: {response.StatusCode}");
// Deserialize the updated switches from the response body.
switches = await response.Content.ReadAsAsync<Switches>();
Thread.Sleep(10000);
Console.WriteLine($"Turning on the light");
response = await client.PutAsJsonAsync($"switches/on?room=LivingRoom", switches);
Console.WriteLine($"The response from the server was: {response.StatusCode}");
Thread.Sleep(10000);
return switches;
}
static async Task<HttpStatusCode> DeleteSwitchesAsync(string id)
{
HttpResponseMessage response = await client.DeleteAsync($"api/switchess/{id}");
return response.StatusCode;
}
/// <summary>
/// POST: api/Messages
/// Receive a message from a user and reply to it
/// </summary>
public async Task<HttpResponseMessage> Post([FromBody]Activity activity)
{
Switches[] switches = null;
ConnectorClient connector = new ConnectorClient(new Uri(activity.ServiceUrl));
//string appdId = AppSettings.Settings["MicrosoftAppId"];
//string appPassword = ConfigurationManager.AppSettings["MicrosoftAppPassword"];
if (j == 0)
{
//client.Timeout = TimeSpan.FromSeconds(4);
//Declaration of client and Switches variable
string accessToken = "xxxxx";
client.BaseAddress = new Uri("xxxxx");
client.DefaultRequestHeaders.Accept.Clear();
client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
client.DefaultRequestHeaders.Add("Authorization", "Bearer " + accessToken);
j++;
//client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue ("Authorization", "Bearer " + accessToken);
}
if (activity.Type == ActivityTypes.Message)
{
//, appdId, appPassword);
//Switches switches = new Switches;
Activity reply1;// = activity.CreateReply($"breakpoint1");
//await connector.Conversations.ReplyToActivityAsync(reply1);
switch (activity.Text.ToLower())
{
case"turn on livingroom":
try
{
Console.WriteLine("Turning on the light");
responsemain = await client.PutAsJsonAsync($"switches/on?room=LivingRoom", switches);
responsemain.EnsureSuccessStatusCode();
//Console.WriteLine($"The response from the server was: {responsemain.StatusCode}");
// Deserialize the updated product from the response body.
switches = await responsemain.Content.ReadAsAsync<Switches[]>();
reply1 = activity.CreateReply($"Successfully turned on LivingRoom Light");
await connector.Conversations.ReplyToActivityAsync(reply1);
}
catch
{
reply1 = activity.CreateReply($"Error");
await connector.Conversations.ReplyToActivityAsync(reply1);
}
break;
case "turn off livingroom":
try
{
Console.WriteLine("Turning off the light");
responsemain = await client.PutAsJsonAsync($"switches/off?room=LivingRoom", switches);
responsemain.EnsureSuccessStatusCode();
Console.WriteLine($"The response from the server was: {responsemain.StatusCode}");
// Deserialize the updated product from the response body.
switches = await responsemain.Content.ReadAsAsync<Switches[]>();
reply1 = activity.CreateReply($"Successfully turned off LivingRoom Light");
await connector.Conversations.ReplyToActivityAsync(reply1);
}
catch
{
reply1 = activity.CreateReply($"Error");
await connector.Conversations.ReplyToActivityAsync(reply1);
}
break;
default: //"What lights are on?":
try
{
switches = await GetSwitchesAsync("");
//Console.WriteLine($"About to show the product");
ShowSwitches(switches);
//await connector.Conversations.ReplyToActivityAsync(switches[].ToString);
for (int i = switches.Length - 1; i >= 0; i--)
{
reply1 = activity.CreateReply($"Room: ");//{switches[i].name}\tStatus: {switches[i].value}\t");
responsemain.EnsureSuccessStatusCode();
await connector.Conversations.ReplyToActivityAsync(reply1);
}
break;
}
catch
{
}
break;
}
// calculate something for us to return
//int length = (activity.Text ?? string.Empty).Length;
// return our reply to the user
//Activity reply = activity.CreateReply($"You sent {activity.Text} which was {length} characters");
//await connector.Conversations.ReplyToActivityAsync(reply);
}
else
{
HandleSystemMessage(activity);
}
// var response = Request.CreateResponse(HttpStatusCode.OK);
return responsemain;
}
static Activity HandleSystemMessage(Activity message)
{
if (message.Type == ActivityTypes.DeleteUserData)
{
// Implement user deletion here
// If we handle user deletion, return a real message
}
else if (message.Type == ActivityTypes.ConversationUpdate)
{
// Handle conversation state changes, like members being added and removed
// Use Activity.MembersAdded and Activity.MembersRemoved and Activity.Action for info
// Not available in all channels
}
else if (message.Type == ActivityTypes.ContactRelationUpdate)
{
// Handle add/remove from contact lists
// Activity.From + Activity.Action represent what happened
}
else if (message.Type == ActivityTypes.Typing)
{
// Handle knowing tha the user is typing
}
else if (message.Type == ActivityTypes.Ping)
{
}
return null;
}``
//client.dispose();
}
}
I found the error after some additional troubleshooting with remote debugging. Based on #JimLewallen's response I looked closer at the "Credentials" portion of the connector object. The OAuthScope for version 3.0 of the Botframework (connector-> Credentials->OAuthScope in the locals of the remote debugger) was pointing to api.botframework.com/.default when it should have been pointing at "graph.microsoft.com/.default". When I created a new bot service application in Visual Studios using the bot template the OAuthScope showed the correct endpoint.
Incorrect Value:
OAuthScope "api.botframework.com/.default"
Correct Value: Image from the Remote Debugger showing the correct OAuthScope
I configure the server as following on startup.cs
GlobalHost.HubPipeline.RequireAuthentication();
// Make long polling connections wait a maximum of 110 seconds for a
// response. When that time expires, trigger a timeout command and
// make the client reconnect.
GlobalHost.Configuration.ConnectionTimeout = TimeSpan.FromSeconds(40);
// Wait a maximum of 30 seconds after a transport connection is lost
// before raising the Disconnected event to terminate the SignalR connection.
GlobalHost.Configuration.DisconnectTimeout = TimeSpan.FromSeconds(30);
// For transports other than long polling, send a keepalive packet every
// 10 seconds.
// This value must be no more than 1/3 of the DisconnectTimeout value.
GlobalHost.Configuration.KeepAlive = TimeSpan.FromSeconds(10);
GlobalHost.HubPipeline.AddModule(new SOHubPipelineModule());
var hubConfiguration = new HubConfiguration { EnableDetailedErrors = true };
var heartBeat = GlobalHost.DependencyResolver.Resolve<ITransportHeartbeat>();
var monitor = new PresenceMonitor(heartBeat);
monitor.StartMonitoring();
app.MapSignalR(hubConfiguration);
where PresenceMonitor is the class responsible of check unlive data . as I keep them in database using the following code
public class PresenceMonitor
{
private readonly ITransportHeartbeat _heartbeat;
private Timer _timer;
// How often we plan to check if the connections in our store are valid
private readonly TimeSpan _presenceCheckInterval = TimeSpan.FromSeconds(40);
// How many periods need pass without an update to consider a connection invalid
private const int periodsBeforeConsideringZombie = 1;
// The number of seconds that have to pass to consider a connection invalid.
private readonly int _zombieThreshold;
public PresenceMonitor(ITransportHeartbeat heartbeat)
{
_heartbeat = heartbeat;
_zombieThreshold = (int)_presenceCheckInterval.TotalSeconds * periodsBeforeConsideringZombie;
}
public async void StartMonitoring()
{
if (_timer == null)
{
_timer = new Timer(_ =>
{
try
{
Check();
}
catch (Exception ex)
{
// Don't throw on background threads, it'll kill the entire process
Trace.TraceError(ex.Message);
}
},
null,
TimeSpan.Zero,
_presenceCheckInterval);
}
}
private async void Check()
{
// Get all connections on this node and update the activity
foreach (var trackedConnection in _heartbeat.GetConnections())
{
if (!trackedConnection.IsAlive)
{
await trackedConnection.Disconnect();
continue;
}
var log = AppLogFactory.Create<WebApiApplication>();
log.Info($"{trackedConnection.ConnectionId} still live ");
var connection = await (new Hubsrepository()).FindAsync(c => c.ConnectionId == trackedConnection.ConnectionId);
// Update the client's last activity
if (connection != null)
{
connection.LastActivity = DateTimeOffset.UtcNow;
await (new Hubsrepository()).UpdateAsync(connection, connection.Id).ConfigureAwait(false);
}
}
// Now check all db connections to see if there's any zombies
// Remove all connections that haven't been updated based on our threshold
var hubRepository = new Hubsrepository();
var zombies =await hubRepository.FindAllAsync(c =>
SqlFunctions.DateDiff("ss", c.LastActivity, DateTimeOffset.UtcNow) >= _zombieThreshold);
// We're doing ToList() since there's no MARS support on azure
foreach (var connection in zombies.ToList())
{
await hubRepository.DeleteAsync(connection);
}
}
}
and my hub connect disconnect , reconnect looks like
public override async Task OnConnected()
{
var log = AppLogFactory.Create<WebApiApplication>();
if (Context.QueryString["transport"] == "webSockets")
{
log.Info($"Connection is Socket");
}
if (Context.Headers.Any(kv => kv.Key == "CMSId"))
{
// Check For security
var hederchecker = CryptLib.Decrypt(Context.Headers["CMSId"]);
if (string.IsNullOrEmpty(hederchecker))
{
log.Info($"CMSId cannot be decrypted {Context.Headers["CMSId"]}");
return;
}
log.Info($" {hederchecker} CMSId online at {DateTime.UtcNow} ");
var user = await (new UserRepository()).FindAsync(u => u.CMSUserId == hederchecker);
if (user != null)
await (new Hubsrepository()).AddAsync(new HubConnection()
{
UserId = user.Id,
ConnectionId = Context.ConnectionId,
UserAgent = Context.Request.Headers["User-Agent"],
LastActivity = DateTimeOffset.UtcNow
}).ConfigureAwait(false);
//_connections.Add(hederchecker, Context.ConnectionId);
}
return;
}
public override async Task OnDisconnected(bool stopCalled)
{
try
{
//if (!stopCalled)
{
var hubRepo = (new Hubsrepository());
var connection = await hubRepo.FindAsync(c => c.ConnectionId == Context.ConnectionId);
if (connection != null)
{
var user = await (new UserRepository()).FindAsync(u => u.Id == connection.UserId);
await hubRepo.DeleteAsync(connection);
if (user != null)
{
//var log = AppLogFactory.Create<WebApiApplication>();
//log.Info($"CMSId cannot be decrypted {cmsId}");
using (UserStatusRepository repo = new UserStatusRepository())
{
//TODO :: To be changed immediatley in next release , Date of change 22/02/2017
var result = await (new CallLogRepository()).CallEvent(user.CMSUserId);
if (result.IsSuccess)
{
var log = AppLogFactory.Create<WebApiApplication>();
var isStudent = await repo.CheckIfStudent(user.CMSUserId);
log.Info($" {user.CMSUserId} CMSId Disconnected here Before Set offline at {DateTime.UtcNow} ");
var output = await repo.OfflineUser(user.CMSUserId);
log.Info($" {user.CMSUserId} CMSId Disconnected here after Set offline at {DateTime.UtcNow} ");
if (output)
{
log.Info($" {user.CMSUserId} CMSId Disconnected at {DateTime.UtcNow} ");
Clients.All.UserStatusChanged(user.CMSUserId, false, isStudent);
}
}
}
}
}
}
}
catch (Exception e)
{
var log = AppLogFactory.Create<WebApiApplication>();
log.Error($"CMSId cannot Faild to be offline {Context.ConnectionId} with error {e.Message}{Environment.NewLine}{e.StackTrace}");
}
}
public override async Task OnReconnected()
{
string name = Context.User.Identity.Name;
var log = AppLogFactory.Create<WebApiApplication>();
log.Info($" {name} CMSId Reconnected at {DateTime.UtcNow} ");
var connection = await (new Hubsrepository()).FindAsync(c => c.ConnectionId == Context.ConnectionId);
if (connection == null)
{
var user = await (new UserRepository()).FindAsync(u => u.CMSUserId == name);
if (user != null)
await (new Hubsrepository()).AddAsync(new HubConnection()
{
UserId = user.Id,
ConnectionId = Context.ConnectionId,
UserAgent = Context.Request.Headers["User-Agent"],
LastActivity = DateTimeOffset.UtcNow
}).ConfigureAwait(false);
}
else
{
connection.LastActivity = DateTimeOffset.UtcNow;
await (new Hubsrepository()).UpdateAsync(connection, connection.Id).ConfigureAwait(false);
}
}
all test cases passes well except when internet cut on client side the connection keep live for more than 10 minutes, is this related to authentication , or any configuration wrong at my side any help am really don't know what's wrong . client use websocket transport