Why the resume function don't work for context.Forward in bot framework? - botframework

I'm new in Bot framework and I face this issue:
I want to move from dialog to another dialog, the callback function is working for context.Call but not for context.Forward?
I try multiple solutions such as put this.CallbackFunctionName but it dosen't work.
Here is my code for call new dialog:
switch (submitType)
{
case "alarm":
context.Call(new AlarmDialog(), ResumeAfterAlarmDialog);
context.Done(true);
return;
case "game":
await context.Forward(new AlarmDialog(), ResumeAfterAlarmDialog, value, CancellationToken.None);
return;
}
And here the method that I call:
private async Task ResumeAfterAlarmDialog(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync($"You are finish the alarm dialog");
context.Wait(this.MessageReceivedAsync);
}
And this is the error that I have for the context.Forward:
cannot use a method group as an argument to a dynamically dispatched operation.Did you intend to invoke the method?
Here the full implementation of the class:
namespace CardEx.Dialogs
{
[Serializable]
public class RootDialog : IDialog<object>
{
public Task StartAsync(IDialogContext context)
{
context.Wait(MessageReceivedAsync);
return Task.CompletedTask;
}
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<object> result)
{
var activity = await result as Activity;
if (activity.Value != null)
{
// Got an Action Submit
dynamic value = activity.Value;
string submitType = value.Type.ToString();
switch (submitType)
{
case "alarm":
context.Call(new AlarmDialog(), ResumeAfterAlarmDialog);
return;
case "alarm2":
await context.Forward(new AlarmDialog(), ResumeAfterAlarmDialog, value, CancellationToken.None);
return;
}
}
AdaptiveCard aCard = new AdaptiveCard()
{
Body = new List<AdaptiveElement>()
{
new AdaptiveTextBlock()
{
Text = "Welcome!",
Weight = AdaptiveTextWeight.Bolder,
Size = AdaptiveTextSize.Large
},
new AdaptiveTextBlock() { Text = "Please choose one of the following:" },
},
Actions = new List<AdaptiveAction>()
{
new AdaptiveSubmitAction()
{
Title = "Set an alarm",
DataJson = "{ \"Type\": \"alarm\" }"
},
new AdaptiveSubmitAction()
{
Title = "Play a game",
DataJson = "{ \"Type\": \"game\" }"
}
}
};
Attachment attachment = new Attachment()
{
ContentType = AdaptiveCard.ContentType,
Content = aCard
};
var reply = context.MakeMessage();
reply.Attachments.Add(attachment);
await context.PostAsync(reply, CancellationToken.None);
context.Wait(MessageReceivedAsync);
}
private async Task ResumeAfterAlarmDialog(IDialogContext context, IAwaitable<object> result)
{
await context.PostAsync($"You are finish the alarm dialog");
context.Wait(this.MessageReceivedAsync);
}
}
}
Thank you.

cannot use a method group as an argument to a dynamically dispatched operation.Did you intend to invoke the method?
the parameter value used in context.Forward is a dynamic type, which raised this issue.
Please try following code:
await context.Forward(new TestDialog(), ResumeAfterAlarmDialog, (object)value, CancellationToken.None);

Related

Unable to complete MSAL login in Xamarin app

I'm trying to get my feet wet with Xamarin and I'm having trouble adding in my organization's login. The screen shot below is as far as I can get attempting to login. When I click "Continue" the same page just loads again. Not really sure what's going on.
The image is the screen I'm stuck on.
I've added code that represents the app class and the code behind for the XAML page attempting to login, leaving out what I "think" is irrelvant.
Any suggestions?
public partial class App : Application
{
public static string AzureBackendUrl =
DeviceInfo.Platform == DevicePlatform.Android ? "http://10.0.2.2:5000" : "http://localhost:5000";
public static bool UseMockDataStore = true;
public static IPublicClientApplication PCA = null;
public static string ClientID = "CLIENT_ID";
public static string[] Scopes = { "User.Read" };
public static string Username = string.Empty;
public static object ParentWindow { get; set; }
public App()
{
InitializeComponent();
if (UseMockDataStore)
DependencyService.Register<MockDataStore>();
else
DependencyService.Register<AzureDataStore>();
PCA = PublicClientApplicationBuilder.Create(ClientID)
.WithRedirectUri($"msal{App.ClientID}://auth")
//.WithParentActivityOrWindow(() => App.ParentWindow)
.Build();
MainPage = new MSAL_Example();
}
}
public partial class MSAL_Example : ContentPage
{
public static string tenant_name = "MY_TENANT_NAME";
public MSAL_Example()
{
InitializeComponent();
App.ParentWindow = this;
}
public async Task SignOutAsync()
{
IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
try
{
while (accounts.Any())
{
await App.PCA.RemoveAsync(accounts.FirstOrDefault());
accounts = await App.PCA.GetAccountsAsync();
}
slUser.IsVisible = false;
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign in"; });
}
catch (Exception ex)
{
Debug.WriteLine("\tERROR {0}", ex.Message);
}
}
public async Task SignInAsync()
{
AuthenticationResult authResult = null;
IEnumerable<IAccount> accounts = await App.PCA.GetAccountsAsync();
// let's see if we have a user in our belly already
try
{
IAccount firstAccount = accounts.FirstOrDefault();
authResult = await App.PCA.AcquireTokenSilent(App.Scopes, firstAccount)
.ExecuteAsync();
await RefreshUserDataAsync(authResult.AccessToken).ConfigureAwait(false);
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
}
catch (MsalUiRequiredException ex)
{
try
{
authResult = await App.PCA.AcquireTokenInteractive(App.Scopes)
.WithParentActivityOrWindow(App.ParentWindow)
.WithAuthority("https://login.microsoftonline.com/" + tenant_name)
.ExecuteAsync();
await RefreshUserDataAsync(authResult.AccessToken);
Device.BeginInvokeOnMainThread(() => { btnSignInSignOut.Text = "Sign out"; });
}
catch (Exception ex2)
{
Debug.WriteLine("\tERROR {0}", ex2.Message);
}
}
}
public async Task RefreshUserDataAsync(string token)
{
//get data from API
HttpClient client = new HttpClient();
HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, "https://graph.microsoft.com/v1.0/me");
message.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token);
HttpResponseMessage response = await client.SendAsync(message);
string responseString = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
JObject user = JObject.Parse(responseString);
slUser.IsVisible = true;
Device.BeginInvokeOnMainThread(() =>
{
lblDisplayName.Text = user["displayName"].ToString();
lblGivenName.Text = user["givenName"].ToString();
lblId.Text = user["id"].ToString();
lblSurname.Text = user["surname"].ToString();
lblUserPrincipalName.Text = user["userPrincipalName"].ToString();
// just in case
btnSignInSignOut.Text = "Sign out";
});
}
else
{
await DisplayAlert("Something went wrong with the API call", responseString, "Dismiss");
}
}
}

Welcome card doesnot pop up on its own while integrating it with direct line channel

I have created a bot locally using bot framework v4 c#. It has a welcome card that automatically pops up as soon I connect my local url with emulator, but recently I deployed my bot on azure and integrated it using direct line channel in my website. Now whenever I click, it opens the bot but the welcome card does not come on its own ,it appears when I write something from my chatbot. I just want the welcome card to appaer automatically as it appears in the emulator. Guys can you help me out please? Below is the code of direct line which I am integrating in my website.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8" />
<title></title>
<!-- Paste line 7 to 27 after the title tag in _Layout.cshtml -->
<link href="https://cdn.botframework.com/botframework-webchat/latest/botchat.css" rel="stylesheet"
/>
<script src="https://cdn.botframework.com/botframework-webchat/latest/botchat.js"></script>
<style>
#mychat {
margin: 10px;
position: fixed;
bottom: 30px;
left: 10px;
z-index: 1000000;
}
.botIcon {
float: left !important;
border-radius: 50%;
}
.userIcon {
float: right !important;
border-radius: 50%;
}
</style>
</head>
< body>
<!-- Paste line from 31 to 33 before the </body> tag at the end of code -->
<div id="container">
<img id="mychat" src=""/>
</div>
</body>
<!-- Paste line 38 to 88 after the </html> tag -->
<script>
(function () {
var div = document.createElement("div");
var user = {
id: "",
name: ''
};
var bot = {
id: '',
name: 'SaathiBot'
};
const botConnection = new BotChat.DirectLine({
secret: '',
webSocket: false
})
document.getElementsByTagName('body')[0].appendChild(div);
div.outerHTML = "<div id='botDiv' style='width: 400px; height: 0px; margin:10px; position:
fixed; bottom: 0; left:0; z-index: 1000;><div id='botTitleBar' style='height: 40px; width: 400px;
position:fixed; cursor: pointer;'>";
BotChat.App({
botConnection: botConnection,
user: user,
bot: bot
}, document.getElementById("botDiv"));
document.getElementsByClassName("wc-header")[0].setAttribute("id", "chatbotheader");
document.querySelector('body').addEventListener('click', function (e) {
e.target.matches = e.target.matches || e.target.msMatchesSelector;
if (e.target.matches('#chatbotheader')) {
var botDiv = document.querySelector('#botDiv');
botDiv.style.height = "0px";
document.getElementById("mychat").style.display = "block";
};
});
document.getElementById("mychat").addEventListener("click", function (e) {
document.getElementById("botDiv").style.height = '500px';
e.target.style.display = "none";
})
}());
</script>
Also here is my welcome card code in c#
namespace Microsoft.BotBuilderSamples
{
public class WelcomeUser : SaathiDialogBot<MainDialog>
{
protected readonly string[] _cards =
{
Path.Combine(".", "Resources", "WelcomeCard.json"),
};
public WelcomeUser(ConversationState conversationState, UserState userState, MainDialog dialog, ILogger<SaathiDialogBot<MainDialog>> logger)
: base(conversationState, userState, dialog, logger)
{
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
await SendWelcomeMessageAsync(turnContext, cancellationToken);
Random r = new Random();
var cardAttachment = CreateAdaptiveCardAttachment(_cards[r.Next(_cards.Length)]);
await turnContext.SendActivityAsync(MessageFactory.Attachment(cardAttachment), cancellationToken);
}
private static async Task SendWelcomeMessageAsync(ITurnContext turnContext, CancellationToken cancellationToken)
{
foreach (var member in turnContext.Activity.MembersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
if (DateTime.Now.Hour < 12)
{
await turnContext.SendActivityAsync(
$"Hi,Good Morning {member.Name}",
cancellationToken: cancellationToken);
}
else if (DateTime.Now.Hour < 17)
{
await turnContext.SendActivityAsync(
$"Hi,Good Afternoon {member.Name}",
cancellationToken: cancellationToken);
}
else
{
await turnContext.SendActivityAsync(
$"Hi,Good Evening {member.Name}",
cancellationToken: cancellationToken);
}
}
}
}
private static Attachment CreateAdaptiveCardAttachment(string filePath)
{
var adaptiveCardJson = File.ReadAllText(filePath);
var adaptiveCardAttachment = new Attachment()
{
ContentType = "application/vnd.microsoft.card.adaptive",
Content = JsonConvert.DeserializeObject(adaptiveCardJson),
};
return adaptiveCardAttachment;
}
}
}
Here the saathiDialog code which inherited in welcome card. These are the two files inside my bot folder
public class SaathiDialogBot<T> : ActivityHandler where T : Dialog
{
protected readonly BotState ConversationState;
protected readonly Dialog Dialog;
protected readonly ILogger Logger;
protected readonly BotState UserState;
private DialogSet Dialogs { get; set; }
public SaathiDialogBot(ConversationState conversationState, UserState userState, T dialog, ILogger<SaathiDialogBot<T>> logger)
{
ConversationState = conversationState;
UserState = userState;
Dialog = dialog;
Logger = logger;
}
public override async Task OnTurnAsync(ITurnContext turnContext, CancellationToken cancellationToken = default(CancellationToken))
{
var activity = turnContext.Activity;
if (string.IsNullOrWhiteSpace(activity.Text) && activity.Value != null)
{
activity.Text = JsonConvert.SerializeObject(activity.Value);
}
if (turnContext.Activity.Text == "Yes")
{
await turnContext.SendActivityAsync($"Good bye. I will be here if you need me. ", cancellationToken: cancellationToken);
await turnContext.SendActivityAsync($"Say Hi to wake me up.", cancellationToken: cancellationToken);
}
else
{
await base.OnTurnAsync(turnContext, cancellationToken);
}
await ConversationState.SaveChangesAsync(turnContext, false, cancellationToken);
await UserState.SaveChangesAsync(turnContext, false, cancellationToken);
}
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
Logger.LogInformation("Running dialog with Message Activity.");
await Dialog.RunAsync(turnContext, ConversationState.CreateProperty<DialogState>(nameof(DialogState)), cancellationToken);
}
}
}
here is main Dialog code
namespace Microsoft.BotBuilderSamples
{
public class MainDialog : ComponentDialog
{
private const string UserInfo = "value-userInfo";
protected readonly ILogger _logger;
protected readonly string[] _cards =
{
Path.Combine(".", "Resources", "ValidationCard.json"),
};
public MainDialog(ILogger<MainDialog> logger) : base(nameof(MainDialog))
{
_logger = logger;
AddDialog(new ProductIssue($"{nameof(MainDialog)}.fromMain"));
AddDialog(new ProductIssue($"{nameof(Confirm)}.fromConfirm"));
AddDialog(new ProductIssue($"{ nameof(Resolution)}.resolution"));
AddDialog(new Confirm());
AddDialog(new ChoicePrompt($"{nameof(MainDialog)}.issue"));
AddDialog(new TextPrompt($"{nameof(MainDialog)}.callDialog"));
AddDialog(new NumberPrompt<int>($"{nameof(MainDialog)}.num", valinatiotionAsync));
AddDialog(new WaterfallDialog($"{nameof(MainDialog)}.mainFlow", new WaterfallStep[]
{
MoblieNumberAsync,
ChoiceCardStepAsync,
ShowCardStepAsync,
CallingDialogsAsync
}));
AddDialog(new TextPrompt(nameof(TextPrompt)));
InitialDialogId = $"{nameof(MainDialog)}.mainFlow";
}
private async Task<DialogTurnResult> MoblieNumberAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
stepContext.Values[UserInfo] = new UserInput();
var options = new PromptOptions()
{
Prompt = MessageFactory.Text("Kindly enter your 10 digit mobile number without any spaces, dashes and country code. We will be sending an OTP later to this number "),
RetryPrompt = MessageFactory.Text("Incorrect mobile number entered. Please only enter the 10 digits of your mobile without any spaces, dashes and country code.")
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.num", options, cancellationToken);
}
private async Task<DialogTurnResult> ChoiceCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
BotAPIBLL botApiBLL = new BotAPIBLL();
var response = botApiBLL.GetCustomerDetail(stepContext.Context.Activity.Text);
await stepContext.Context.SendActivityAsync(MessageFactory.Text("Fetching your details from our systems. This may take a moment"), cancellationToken);
var options = new PromptOptions()
{
Prompt = MessageFactory.Text("Welcome user, How can we serve you ? "),
RetryPrompt = MessageFactory.Text("That was not a valid choice, please select a option between 1 to 4."),
Choices = GetChoices(),
};
return await stepContext.PromptAsync($"{nameof(MainDialog)}.issue", options, cancellationToken);
}
private async Task<DialogTurnResult> ShowCardStepAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
var attachments = new List<Attachment>();
var reply = MessageFactory.Text("");
var user_choice = ((FoundChoice)stepContext.Result).Value;
switch (user_choice)
{
case "Product issue":
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment3());
break;
case "Register Product":
reply.Attachments.Add(Cards.GetHeroCard1().ToAttachment());
break;
case "Online Purchase":
reply.Attachments.Add(Cards.GetHeroCard2().ToAttachment());
break;
case "Customer Grivance":
reply.Attachments.Add(Cards.GetHeroCard3().ToAttachment());
break;
default:
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.CreateAdaptiveCardAttachment3());
reply.AttachmentLayout = AttachmentLayoutTypes.Carousel;
reply.Attachments.Add(Cards.GetHeroCard1().ToAttachment());
break;
}
if (user_choice == "Register Product" || user_choice == "Online Purchase" || user_choice == "Customer Grivance")
{
await stepContext.Context.SendActivityAsync(reply, cancellationToken);
Random r = new Random();
var validationcard = Cards.CreateAdaptiveCardAttachment2(_cards[r.Next(_cards.Length)]);
await stepContext.Context.SendActivityAsync(MessageFactory.Attachment(validationcard), cancellationToken);
return await stepContext.EndDialogAsync(null, cancellationToken);
}
else
{
var options2 = new PromptOptions() { Prompt = reply, RetryPrompt = MessageFactory.Text("Retry") };
return await stepContext.PromptAsync($"{nameof(MainDialog)}.callDialog", options2, cancellationToken);
}
}
private async Task<DialogTurnResult> CallingDialogsAsync(WaterfallStepContext stepContext, CancellationToken cancellationToken)
{
string choice = stepContext.Result.ToString();
if (choice.ToLower() == "no")
{
return await stepContext.BeginDialogAsync($"{nameof(MainDialog)}.fromMain", null, cancellationToken);
}
else
return await stepContext.BeginDialogAsync(nameof(Confirm), null, cancellationToken);
}
private IList<Choice> GetChoices()
{
var cardOptions = new List<Choice>()
{
new Choice() { Value = "Product issue", Synonyms = new List<string>() { "adaptive" } },
new Choice() { Value = "Register Product", Synonyms = new List<string>() { "hero" } },
new Choice() { Value = "Online Purchase", Synonyms = new List<string>() { "hero" } },
new Choice() { Value = "Customer Grivance", Synonyms = new List<string>() { "hero" } },
};
return cardOptions;
}
private Task<bool> valinatiotionAsync(PromptValidatorContext<int> promptContext, CancellationToken cancellationToken)
{
string value = (string)promptContext.Context.Activity.Text;
if (Regex.IsMatch(value, "^[0-9]{10}$"))
{
return Task.FromResult(true);
}
else
{
return Task.FromResult(false);
}
}
}
}
You have to use the ConversationUpdate event ActivityTypes.ConversationUpdate
This is an example inside of a Switch statement where i check the different ActivityTypes i get, and the conversationUpdate is used to show the welcome message
case ActivityTypes.ConversationUpdate:
{
if (activity.MembersAdded?.Count > 0)
{
await innerDc.BeginDialogAsync(nameof(dialog));
}
break;
}
UPDATE
Look at this working example and explanation on how to send a Welcome Message Event from WebChat.
If you are using WebChat or directline, the bot’s ConversationUpdate is sent when the conversation is created and the user sides’ ConversationUpdate is sent when they first send a message. When ConversationUpdate is initially sent, there isn’t enough information in the message to construct the dialog stack. The reason that this appears to work in the emulator, is that the emulator simulates a sort of pseudo DirectLine, but both conversationUpdates are resolved at the same time in the emulator, and this is not the case for how the actual service performs.
A workaround would be to send a back channel welcome event to the bot when the DirectLine connection is established and send a welcome message from the onEventAsync handler instead of onMembersAdded.
Embedded HTML for Web Chat
<!DOCTYPE html>
<html>
<head>
<script src="https://cdn.botframework.com/botframework-webchat/latest/webchat.js"></script>
<style>
#webchat {
height: 100%;
width: 100%;
}
</style>
</head>
<body>
<div style="display: flex">
<div style="position: relative; height: 500px; width: 500px"><div id="bot" ></div></div>
</div>
<script>
(async function() {
const res = await fetch('/directline/token', { method: 'POST' });
const { token } = await res.json();
var userinfo = {
id: 'user-id',
name: 'user name',
locale: 'es'
};
var botConnection = new window.BotChat.DirectLine({ token });
botConnection.connectionStatus$
.subscribe(connectionStatus => {
switch(connectionStatus) {
case window.BotChat.ConnectionStatus.Online:
botConnection.postActivity({
from: { id: 'myUserId', name: 'myUserName' },
type: 'event',
name: 'webchat/join',
value: { locale: 'en-US' }
}).subscribe(
id => console.log("Posted welcome event, assigned ID ", id),
error => console.log("Error posting activity", error)
);
break;
}
});
BotChat.App({
botConnection: botConnection,
user: userinfo,
bot: { id: 'botid' },
resize: 'detect'
}, document.getElementById("bot"));
})().catch(err => console.log(err));
</script>
</body>
</html>
Bot Code in C#
protected override async Task OnEventActivityAsync(ITurnContext<IEventActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.Name == "webchat/join") {
await turnContext.SendActivityAsync("Welcome Message!");
}
}
protected override async Task OnMembersAddedAsync(IList<ChannelAccount> membersAdded, ITurnContext<IConversationUpdateActivity> turnContext, CancellationToken cancellationToken)
{
if (turnContext.Activity.ChannelId != "webchat" && turnContext.Activity.ChannelId != "directline") {
foreach (var member in membersAdded)
{
if (member.Id != turnContext.Activity.Recipient.Id)
{
await turnContext.SendActivityAsync($"Hi there - {member.Name}. {WelcomeMessage}", cancellationToken: cancellationToken);
await turnContext.SendActivityAsync(InfoMessage, cancellationToken: cancellationToken);
await turnContext.SendActivityAsync(PatternMessage, cancellationToken: cancellationToken);
}
}
}
}
Hope this helps.

ReplyToId' cannot be null

This exception is continuously throwing. This started since i updated botframework to 3.5.3.
Code :
MessagesController :
await Conversation.SendAsync(activity, () => new DefaultDialog());
Than in my DefaultDialog :
private async Task MessageReceivedAsync(IDialogContext context, IAwaitable<IMessageActivity> argument)
{
var msg = await argument;
await Helper.CallMenu(context, msg);
In CallMenu function :
internal static async Task CallMenu(IDialogContext context, IMessageActivity msg)
{
await MenuFirstPart(context, msg);
In MenuFirstPart function :
internal static async Task MenuFirstPart(IDialogContext context, IMessageActivity msg)
{
await context.PostAsync("I can assist you with : ");
msg.AttachmentLayout = AttachmentLayoutTypes.Carousel;
msg.Attachments = new List<Attachment>();
ThumbnailCard thumbnailCard = new ThumbnailCard()
{
Buttons = new List<CardAction>
{
new CardAction ()
{
Value = "Method_News",
Type = "postBack",
Title = "News"
},
new CardAction()
{
Value = "Method_About",
Type = "postBack",
Title = "About"
},
new CardAction ()
{
Value = "Method_Help",
Type = "postBack",
Title = "Help"
},
},
};
msg.Attachments.Add(thumbnailCard.ToAttachment());
await context.PostAsync(msg);
}
Exception is thrown when context is trying to post msg.
Any help ? Or how to downgrade my bot to 3.5.0. This worked fine there ...
You are using the incoming message (msg) and sending that back.
You need to create a new message instead. Use the following:
var reply = context.MakeMessage();

web api - asp.net identity token expires even for the subsequent request

I am using asp.net identity for the token based authentication in web api.
For refresh token, I've implemented based on the following link
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/
I've added the following two classes and mentioned in the start up configuration.
From the ui I've called with username and password alone through the api
http://domain/token
When I call the above api, the request directly goes to the method ValidateClientAuthentication.
But in this method the logic is, we need to send the client id and client secret.
How do we know these two before the user login for the specific user?
I thought the work flow should be like, we need to check username and password against database and should generate the access token and refresh token.
But here where do i do this logic.
What is the work flow of this system mentioned in the sample?
Before this system, I'll call the Common/login api in my application, and after successful verification,
I'll call the code to make the user as logged in
var userIdentity=await user.GenerateUserIdentityAsync(UserManager);
AuthenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, userIdentity);
After the above code, I'll generate the access token from the user identity.
I've tried many times with the following implementation and fed up with the flow.
Help me regarding the logic and the flow mentioned here.
SimpleAuthorizationServerProvider
namespace AngularJSAuthentication.API.Providers
{
public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId = string.Empty;
string clientSecret = string.Empty;
Client client = null;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
if (context.ClientId == null)
{
//Remove the comments from the below line context.SetError, and invalidate context
//if you want to force sending clientId/secrects once obtain access tokens.
context.Validated();
//context.SetError("invalid_clientId", "ClientId should be sent.");
return Task.FromResult<object>(null);
}
using (AuthRepository _repo = new AuthRepository())
{
client = _repo.FindClient(context.ClientId);
}
if (client == null)
{
context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
return Task.FromResult<object>(null);
}
if (client.ApplicationType == Models.ApplicationTypes.NativeConfidential)
{
if (string.IsNullOrWhiteSpace(clientSecret))
{
context.SetError("invalid_clientId", "Client secret should be sent.");
return Task.FromResult<object>(null);
}
else
{
if (client.Secret != Helper.GetHash(clientSecret))
{
context.SetError("invalid_clientId", "Client secret is invalid.");
return Task.FromResult<object>(null);
}
}
}
if (!client.Active)
{
context.SetError("invalid_clientId", "Client is inactive.");
return Task.FromResult<object>(null);
}
context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());
context.Validated();
return Task.FromResult<object>(null);
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
if (allowedOrigin == null) allowedOrigin = "*";
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
using (AuthRepository _repo = new AuthRepository())
{
IdentityUser user = await _repo.FindUser(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
}
var identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "user"));
identity.AddClaim(new Claim("sub", context.UserName));
var props = new AuthenticationProperties(new Dictionary<string, string>
{
{
"as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
},
{
"userName", context.UserName
}
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
public override Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
var currentClient = context.ClientId;
if (originalClient != currentClient)
{
context.SetError("invalid_clientId", "Refresh token is issued to a different clientId.");
return Task.FromResult<object>(null);
}
// Change auth ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
var newClaim = newIdentity.Claims.Where(c => c.Type == "newClaim").FirstOrDefault();
if (newClaim != null)
{
newIdentity.RemoveClaim(newClaim);
}
newIdentity.AddClaim(new Claim("newClaim", "newValue"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
return Task.FromResult<object>(null);
}
public override Task TokenEndpoint(OAuthTokenEndpointContext context)
{
foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
{
context.AdditionalResponseParameters.Add(property.Key, property.Value);
}
return Task.FromResult<object>(null);
}
}
}
SimpleRefreshTokenProvider
namespace AngularJSAuthentication.API.Providers
{
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
var refreshTokenId = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
{
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });
string hashedTokenId = Helper.GetHash(context.Token);
using (AuthRepository _repo = new AuthRepository())
{
var refreshToken = await _repo.FindRefreshToken(hashedTokenId);
if (refreshToken != null )
{
//Get protectedTicket from refreshToken class
context.DeserializeTicket(refreshToken.ProtectedTicket);
var result = await _repo.RemoveRefreshToken(hashedTokenId);
}
}
}
public void Create(AuthenticationTokenCreateContext context)
{
throw new NotImplementedException();
}
public void Receive(AuthenticationTokenReceiveContext context)
{
throw new NotImplementedException();
}
}
}
How about using refresh tokens and storing them in a database, like in these two examples:
http://bitoftech.net/2014/07/16/enable-oauth-refresh-tokens-angularjs-app-using-asp-net-web-api-2-owin/
http://leastprivilege.com/2013/11/15/adding-refresh-tokens-to-a-web-api-v2-authorization-server/
As broadly described in the first link, you can create your own token provider implementation to handle token refresh:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var clientid = context.Ticket.Properties.Dictionary["as:client_id"];
if (string.IsNullOrEmpty(clientid))
{
return;
}
var refreshTokenId = Guid.NewGuid().ToString("n");
using (AuthRepository _repo = new AuthRepository())
{
var refreshTokenLifeTime = context.OwinContext.Get<string>("as:clientRefreshTokenLifeTime");
var token = new RefreshToken()
{
Id = Helper.GetHash(refreshTokenId),
ClientId = clientid,
Subject = context.Ticket.Identity.Name,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddMinutes(Convert.ToDouble(refreshTokenLifeTime))
};
context.Ticket.Properties.IssuedUtc = token.IssuedUtc;
context.Ticket.Properties.ExpiresUtc = token.ExpiresUtc;
token.ProtectedTicket = context.SerializeTicket();
var result = await _repo.AddRefreshToken(token);
if (result)
{
context.SetToken(refreshTokenId);
}
}
}
}

Confirmation Dialog on back button press event Xamarin.Forms

I am stuck at one point in Xamarin.Forms application
On Back button press simply I want to ask user to confirm whether he really wants to exit or not,
"OnBackButtonPressed" I want to show Alert dialog and proceed as user response.
But "OnBackButtonPressed" is not Async I cannot write await DisplayAlert...
Please direct me how should i implement this feature?
I am using ContentPage as my root Page of NavigationPage
public class frmHome : ContentPage
Here is the code :
protected override bool OnBackButtonPressed()
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
//user wants to exit
//Terminate application
}
else
{
//Dont do anything
}
}
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(async() => {
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result) await this.Navigation.PopAsync(); // or anything else
});
return true;
}
Here's the code that worked for me
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(async () =>
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
System.Diagnostics.Process.GetCurrentProcess().CloseMainWindow(); // Or anything else
}
});
return true;
}
Easy way to override hardware back button and show a confirmation dialog box to user
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(async () =>
{
if (await DisplayAlert("Alert", "Are you sure you want to go back ?", "Yes", "No"))
{
base.OnBackButtonPressed();
await Navigation.PopAsync();
}
});
return true;
}
If you are on Mainpage and want to exit your app then this code will help you.
In your UI (PCL)
protected override bool OnBackButtonPressed()
{
Device.BeginInvokeOnMainThread(new Action(async () => {
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
if (Device.RuntimePlatform == Device.Android)
DependencyService.Get<IAndroidMethods>().CloseApp();
}
}));
return true;
}
Also create an Interface (in your UI PCL):
public interface IAndroidMethods
{
void CloseApp();
}
Now implement the Android-specific logic in your Android project:
[assembly: Xamarin.Forms.Dependency(typeof(AndroidMethods))]
namespace Your.Namespace
{
public class AndroidMethods : IAndroidMethods
{
public void CloseApp()
{
var activity = (Activity)Forms.Context;
activity.FinishAffinity();
}
}
}
public override void OnBackPressed()
{
RunOnUiThread(
async () =>
{
var isCloseApp = await AlertAsync(this, "NameOfApp", "Do you want to close this app?", "Yes", "No");
if (isCloseApp)
{
var activity = (Activity)Forms.Context;
activity.FinishAffinity();
}
});
}
public Task<bool> AlertAsync(Context context, string title, string message, string positiveButton, string negativeButton)
{
var tcs = new TaskCompletionSource<bool>();
using (var db = new AlertDialog.Builder(context))
{
db.SetTitle(title);
db.SetMessage(message);
db.SetPositiveButton(positiveButton, (sender, args) => { tcs.TrySetResult(true); });
db.SetNegativeButton(negativeButton, (sender, args) => { tcs.TrySetResult(false); });
db.Show();
}
return tcs.Task;
}
Xamarin.Android await AlertDialog.Builder
I am done like this
protected override bool OnBackButtonPressed()
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
Process.GetCurrentProcess().CloseMainWindow();
Process.GetCurrentProcess().Close();
}
else
{
//Dont do anything
}
}
private async void OnDelete(object sender, EventArgs e)
{
var result = await this.DisplayAlert("Alert!", "Do you really want to exit?", "Yes", "No");
if (result)
{
var menuitem = sender as MenuItem;
string name = menuitem.BindingContext as string;
lielements.Remove(name);
}
else
{
}
}

Resources