How can I identify a user logged into DiallogFlow via webhook request? - c#

I am using Dialogflow and would like to know if through the questions of a user to a bot it is possible to identify which user is asking this question.
Attached is a section of the code for reading the data already received.
I tried using the google documentation ('' https://developers.google.com/assistant/identity/google-sign-in#java "), but was unsuccessful.
WebhookRequest request;
using (var reader = new StreamReader(Request.Body))
{
request = jsonParser.Parse<WebhookRequest>(reader);
}
var pas = request.QueryResult.Parameters;
var queryText = request.QueryResult.QueryText;
var response = new WebhookResponse();
StringBuilder sb = new StringBuilder();
//interactionDAO.SaveInteration(new Interaction(Guid.NewGuid(), "google", queryText));
var intent = request.QueryResult.Intent.DisplayName;
var listaObjetos = await _service.DetectIntentAsync(new[] { queryText }, intent);
foreach (var item in listaObjetos)
{
var convertItem = JsonConvert.DeserializeObject<Fulfillment>(item.ToString());
if (!String.IsNullOrWhiteSpace(convertItem.FulfillmentText))
{
sb.Append(convertItem.FulfillmentText);
}
if (convertItem.Parameters != null && convertItem.Parameters.ContainsKey("date-time"))
{
sb.Append(convertItem.Parameters["date-time"]);
}
//sb.Append(item);
}
response.FulfillmentText = sb.ToString();
return Json(response);

Look for "session" in the JSON you receive in your webhook from DialogFlow, it is a unique identifier for the conversation.
Usually it has a format like this:
"session": "projects/${PROJECTID}/agent/sessions/${SESSIONID}"
Just extract the SESSIONID from the last part.
You can find more about DialogFlow Webhook JSON format here:
https://developers.google.com/assistant/actions/build/json/dialogflow-webhook-json

DialogFlow generally only identifies the session. Providing data to uniquely identify the user is part of the client and usually included in the payload.
For example, a signed in user from Google Assistant can be extracted like this (requires the System.IdentityModel.Tokens.Jwt package):
WebhookRequest request;
if (!request.OriginalDetectIntentRequest.Payload.Fields.ContainsKey("user"))
{
throw new ArgumentException("Payload does not contain user.");
}
string idToken = request.OriginalDetectIntentRequest.Payload.Fields["user"]
.StructValue.Fields["idToken"].StringValue;
var userInfo = new JwtSecurityTokenHandler().ReadJwtToken(idToken).Payload;
if (!userInfo["iss"].ToString().EndsWith("accounts.google.com")
|| !userInfo["aud"].ToString().Equals("((your_action_id))")
{
throw new SecurityException("Issuer or authentication token do not match expected value.");
}
string accountName = userInfo["email"].ToString();
if (string.IsNullOrEmpty(accountName))
{
throw new ArgumentException("Id token does not contain mail address.");
}
return accountName;
You need to configure the project as detailed in the article you already linked. It is then possible to mark any DialogFlow intent as "Sign-in required" via the Google Assistant integration settings or use the helper intent for optional sign-in (see this question for details on implementing the helper).

Related

is there a way to associate a spotify access token to a ASP.NET identity user?

I'm working on a multilanguage project for accademic purpose. I've written a simple Python Client that make requests to an API server written in ASP.NET. The server retrives spotify info about users. The server interacts with a DB filled by a Golang server that only makes scraping on API's exposed from Spotify. I'm aware that it's a misuse and there are better solutions
Clearly, Golang server, in order to make requests to Spotify API's, needs to know the access token returned from spotify Authorization Code Flow. Overlooking about spotify token expire time, the idea is: after user authentication through Identity module of ASP.NET server (using JWT token), associate the access token obtained calling https://accounts.spotify.com/api/token to user's informations. So, i expose an API in ASP.NET server like this
[AllowAnonymous]
[HttpPost("token")]
public async Task<ContentResult> getTokenAsync(string? code = null)
{
//to retrive information about who is the user that making call -> need later for associate spotifytoken
string accessToken = Request.Headers[HeaderNames.Authorization].ToString().Replace("Bearer ", "");
JwtSecurityTokenHandler t = new JwtSecurityTokenHandler();
var token = t.ReadJwtToken(accessToken);
var user = _userManager.FindByIdAsync(token.Subject).Result;
string s = "https://accounts.spotify.com/api/token";
if (code == null)
{
var qb = new QueryBuilder();
qb.Add("response_type", "code");
qb.Add("client_id", _config["SpotiSetting:clientId"]);
qb.Add("scope", "user-read-private user-read-email user-library-read");
qb.Add("redirect_uri", _config["SpotiSetting:redirectUser"]);
qb.Add("show_dialog", "true");
return new ContentResult
{
ContentType = "text/html",
Content = "https://accounts.spotify.com/authorize/" + qb.ToQueryString().ToString()
//Content = JsonConvert.SerializeObject(user.Result)
};
} else
{
//if i'm here, api is the callback designed for spotify
var qb = new QueryBuilder();
qb.Add("grant_type", "authorization_code");
qb.Add("code", code);
qb.Add("redirect_uri", "https://localhost:44345/spotify/token");
var client = new HttpClient();
var req = new HttpRequestMessage(HttpMethod.Post, s);
req.Content = new FormUrlEncodedContent(qb);
req.Headers.Authorization = new AuthenticationHeaderValue("Basic", "here_my_secret_encoded_CLIENTID:CLIENT_SECRET");
var response = await client.SendAsync(req);
var result = response.Content.ReadAsStringAsync().Result;
AccessToken json = JsonConvert.DeserializeObject<AccessToken>(result);
user.spotifyInformation.authToken = code;
user.spotifyInformation.accessToken = json;
var res = _userManager.UpdateAsync(user);
if (res.IsCompletedSuccessfully)
{
return Content("ok");
}
else
{
Content("Problem");
}
} return Content("");
}
The problem is that the second time that API is invoked, it's spotify that is sending the first authorization token (needed to request access_token), so I lost user information retrived in the first request. Should be better write two distinct API and separate callback from user request?
It's my first question here, so please to have mercy

ASP.Net Core - Turn SAML assertion into ClaimsPrincipal

There's a question about using SAML in ASP.Net Core, but I need additional help.
The only answer there mentions Kentor.AuthServices, but I don't understand how to use it. Everything I find on this or other SAML libraries, the documentation, blog posts, and sample applications are all about contacting some external authentication service and handling login and logout.
But I don't need any of that. The setup I'm working with does that in an edge-facing firewall application, and login/logout requests never reach my application. All I get is a SAML token in a cookie, which I need to validate and turn into a ClaimsPrincipal. I can't (the deployment network setup is insanely paranoid) and don't want to contact any identity provider.
Currently I've written a piece of middleware that takes the cookie, parses it, and parses out the parts I need for the claims principal. But I don't do any validation, either of the XML signature or of the SAML validity (valid time attributes etc). With .Net Core 2.0 Preview 2 I can do the XML signature validation, but I'm still stuck on doing the SAML validation. Is there a library that simply validates SAML constraints and does nothing else (or, at least, where I can ignore everything else)? I believe Kentor or ITfoxtec or elerch's SAML2.Core must contain such functionality, but I can't figure out where it is.
I have done this with SecurityTokenHandlerCollection class in System.IdentityModel.Tokens
I hope this code will help you.
public Saml2SecurityToken DeserializeSAMLResponse(string samlResponse)
{
//Deserializing saml response
Saml2SecurityToken token;
using (var reader = XmlReader.Create(new StringReader(samlResponse)))
{
reader.ReadToFollowing("Assertion", Infrastructure.Enumerations.StringEnum.GetStringValue(SAMLProtocoles.SAML_20_ASSERTION));
// Deserialize the token so that data can be taken from it and plugged into the RSTR
SecurityTokenHandlerCollection tokenHandlerCollection = SecurityTokenHandlerCollection.CreateDefaultSecurityTokenHandlerCollection();
token = (Saml2SecurityToken)tokenHandlerCollection.ReadToken(reader.ReadSubtree());
}
//Deserializing successful
return token;
}
It will internally validate the SAML and parse it in Saml2SecurityToken
After you get the token you can the the Users Credentials like this
public User ReadSamlResponse(string samlResponse, string profileName, bool isSAMLProfile = true)
{
User User = new User();
var DecodedSamlResponse = Convert.FromBase64String(samlResponse);
string ResponseDecoded = coding.UTF8.GetString(DecodedSamlResponse);
Saml2SecurityToken Token = _samlAuthenticationService.DeserializeSAMLResponse(ResponseDecoded);
if ()// apply condition here if you need to validate signature
{
if (!_samlAuthenticationService.ValidateSamlToken(ResponseDecoded, AuthenticationConnector, isSAMLProfile))
throw new Exception("Signature is invalid");
}
User = GetUserFromToken(Token);
return User;
}
And to get User for Security Token you can do this
public User GetUserFromToken(Saml2SecurityToken Token)
{
//Get user information from the token started
User User = new User();
if (Token != null)
{
if (Token.Assertion.Subject.NameId != null && (Token.Assertion.Subject.NameId.Format == null || Token.Assertion.Subject.NameId.Format.OriginalString == "urn:oasis:names:tc:SAML:1.1:nameid-format:emailAddress"))
User.EmailAddress = Token.Assertion.Subject.NameId.Value;
foreach (var Statement in Token.Assertion.Statements)
{
var AttributeStatement = Statement as Saml2AttributeStatement;
var AuthenticationStatement = Statement as Saml2AuthenticationStatement;
if (AttributeStatement != null)
foreach (var Saml2Attribute in AttributeStatement.Attributes)
{
if (Saml2Attribute.Name.Equals("mail") || Saml2Attribute.Name.Equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"))
User.EmailAddress = Saml2Attribute.Values[0];
if (Saml2Attribute.Name.Equals("uid") || Saml2Attribute.Name.Equals("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"))
User.Name = Saml2Attribute.Values[0];
if (Saml2Attribute.Name.Equals("phone"))
User.MobileNumber = Saml2Attribute.Values[0];
if (Saml2Attribute.Name.Equals("title"))
User.JobTitle = Saml2Attribute.Values[0];
if (Saml2Attribute.Name.Equals("company"))
User.CompanyName = Saml2Attribute.Values[0];
}
if (AuthenticationStatement != null)
{
User.SAMLSessionIndex = AuthenticationStatement.SessionIndex;
}
}
}
//Successfully parsed user credentials
return User;
}
http://blog.scottlogic.com/2015/11/19/oauth2-with-saml2.html
This blog of scott has explained it in simple way.

Azure Media Service - generate new AES encryption token for playback

I am working on open source community project Azure Media Services Upload and Play Videos in MVC since 2015. I was not using any delivery encryption earlier, so I started working on AES.
In all the source code/samples by Azure Media Services Team, i noticed test token was being generated just after uploading the content and this works well in my case too. But, how do I generate test token next time onward for playback?
What I understood is that, we need token each time player requests playback. Technically, player creates a request to key service provider and received updated token.
So to get updated token, I tried couple of ways n not able to fix this, i see error "A ContentKey (Id = '...', Type = 'EnvelopeEncryption') which contains the same type already links to this asset".
This looks like a valid error message because key of type EnvelopeEncryption was already added and associated with asset after uploading content, and upon requesting again this pops-up.
The code given below is copied from here.
public ActionResult Index()
{
var model = new List<VideoViewModel>();
var videos = db.Videos.OrderByDescending(o => o.Id).ToList();
foreach (var video in videos)
{
var viewModel = new VideoViewModel();
viewModel.Id = video.Id;
viewModel.EncodedAssetId = video.EncodedAssetId;
viewModel.IsEncrypted = video.IsEncrypted;
viewModel.LocatorUri = video.LocatorUri;
// If encrypted content, then get token to play
if (video.IsEncrypted)
{
IAsset asset = GetAssetById(video.EncodedAssetId);
IContentKey key = CreateEnvelopeTypeContentKey(asset);
viewModel.Token = GenerateToken(key);
}
model.Add(viewModel);
}
return View(model);
}
Above method calls media service key service provider.
How do I fix this?
You can look into AMS explorer sources
when you creating a restriction policy yo are doing something like this:
//Initilizing ContentKeyAuthorizationPolicyRestriction
ContentKeyAuthorizationPolicyRestriction restriction = new ContentKeyAuthorizationPolicyRestriction
{
Name = "Authorization Policy with Token Restriction",
KeyRestrictionType = (int)ContentKeyRestrictionType.TokenRestricted,
Requirements = TokenRestrictionTemplateSerializer.Serialize(restrictionTemplate)};
restrictions.Add(restriction);
//Saving IContentKeyAuthorizationPolicyOption on server so it can be associated with IContentKeyAuthorizationPolicy
IContentKeyAuthorizationPolicyOption policyOption = objCloudMediaContext.ContentKeyAuthorizationPolicyOptions.Create("myDynamicEncryptionPolicy", ContentKeyDeliveryType.BaselineHttp, restrictions, String.Empty);
policy.Options.Add(policyOption);
//Saving Policy
policy.UpdateAsync();
Key field here is irements = TokenRestrictionTemplateSerializer.Serialize(restriction.Requirements)};
You need to fetch corresponding asset restriction you created first place and desirialize TokenRestriction Template back with
TokenRestrictionTemplate tokenTemplate = TokenRestrictionTemplateSerializer.Deserialize(tokenTemplateString);
Based on what type of key and encryption you use
if (tokenTemplate.PrimaryVerificationKey.GetType() == typeof(SymmetricVerificationKey))
{
InMemorySymmetricSecurityKey tokenSigningKey = new InMemorySymmetricSecurityKey((tokenTemplate.PrimaryVerificationKey as SymmetricVerificationKey).KeyValue);
signingcredentials = new SigningCredentials(tokenSigningKey, SecurityAlgorithms.HmacSha256Signature, SecurityAlgorithms.Sha256Digest);
}
else if (tokenTemplate.PrimaryVerificationKey.GetType() == typeof(X509CertTokenVerificationKey))
{
if (signingcredentials == null)
{
X509Certificate2 cert = DynamicEncryption.GetCertificateFromFile(true).Certificate;
if (cert != null) signingcredentials = new X509SigningCredentials(cert);
}
}
JwtSecurityToken token = new JwtSecurityToken(issuer: tokenTemplate.Issuer, audience: tokenTemplate.Audience, notBefore: DateTime.Now.AddMinutes(-5), expires: DateTime.Now.AddMinutes(Properties.Settings.Default.DefaultTokenDuration), signingCredentials: signingcredentials, claims: myclaims);
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
string token = handler.WriteToken(token);

How to get sms records by subaccount

I know about using the class MessageListRequest to ask for a list of SMS messages from the Twilio server. There is no Subaccount filter as a parameter when defining the request. Does anyone know how to get the message list for only those messages associated with a specific subaccount? Thanks.
I have used the subaccount credentials to get the list of messages but the result of the Twilio request is a list object with zero entries . I know there are messages in the sub account because I can see them in the Twilio dashboard for the subaccount. Can you please tell me what I am doing wrong in this code?
var aRequest = new MessageListRequest();
aRequest.From = null;
aRequest.To = null;
aRequest.DateSent = null;
GetSubAccounts();
if (mySubAccountSid != null)
{
TwilioRestClient aTwilio = new TwilioRestClient(mySubAccountSid,
mySubAccountToken);
var aResult = aTwilio.ListMessages(aRequest);
if (aResult != null)
{
foreach (var aMessage in aResult.Messages)
{
mySQLManager.UpdateSMSLogTable(aMessage, myVesselID);
Methods.WriteLog(aMessage.Sid + " " + aMessage.To + " " + aMessage.Body);
}
}
return aList;
}
The simple way, which is likely what you mean, is even if you use
your master credentials for auth, but a subaccount SID in the URL
provided for Message List Resource,
/2010-04-01/Accounts/{AccountSid}/Messages, you get the resources
for that subaccount.
The C# library has a GetAccount method that takes an account
SID and returns the Account object (representing the subaccount) for
which you should then be able to make API calls as normal.
var account = twilio.GetAccount("SUBACCOUNT_SID");
Eventually if you want to track things in a more sophisticated
manner, you may decide to use UsageRecords.
Using UsageRecords combined with Subaccounts will allow you
to build usage reports and set triggers based upon some behavior.
The two links provided above will show you how to work with each in
more detail but an example of grabbing a list of all-time usage for
sms would like this in C#:
// Download the twilio-csharp library from twilio.com/docs/csharp/install
using System;
using Twilio;
class Example
{
static void Main(string[] args)
{
// Find your Account Sid and Auth Token at twilio.com/user/account
string AccountSid = "ACCOUNT_SID";
string AuthToken = "AUTH_TOKEN";
var twilio = new TwilioRestClient(AccountSid, AuthToken);
var records = twilio.ListUsage("sms", null, null, null, null, null);
foreach (var record in records.UsageRecords)
{
Console.WriteLine(record.Count);
}
}
}

401 when attempting to Tweet with Linq to Twitter

So I've looked at all the of the suggestions from the Linq to Twitter documentation regarding 401 statuses with Oauth and I honestly don't know what I'm doing wrong.
var auth = new PinAuthorizer
{
Credentials = new InMemoryCredentials
{
ConsumerKey = ConfigurationManager.AppSettings["twitterConsumerKey"],
ConsumerSecret = ConfigurationManager.AppSettings["twitterConsumerSecret"],
//OAuthToken = ConfigurationManager.AppSettings["twitterOAuthToken"], //don't include this
//AccessToken = ConfigurationManager.AppSettings["twitterAccessToken"] //or this for new users.
},
//
UseCompression = true,
GoToTwitterAuthorization = pageLink => Process.Start(pageLink),
GetPin = () =>
{
Console.WriteLine("/nAfter twitter authorizes your application you will be returned here or something/n");
Console.Write("Enter Pin here:");
return Console.ReadLine();
}
};
auth.Authorize();
using (var twitterCtx = new TwitterContext(auth, "https://api.twitter.com/1/",
"https://search.twitter.com/"))
{
try
{
twitterCtx.Log = Console.Out;
Console.WriteLine("Please provide tweet text");
string tweet = Console.ReadLine();
twitterCtx.UpdateStatus(tweet);
}
catch (Exception e)
{
Console.WriteLine(e.Message);
}
}
I've ran this using the Pin Authentication method as well as the single user method (providing the oauth keys with config file). I'm able to query tweets but I can't update my status or send direct messages (I receive a 403 forbidden when I try to DM). I've provided a callback URL (albeit fake) so I can't think of why this isn't working. Any help would be appreciated.
PS this runs in Main, not sure if that matters
All you need is this overload of the TwitterContext ctor and it will use the proper base URLs:
new TwitterContext(auth)
The example you're using is for v1.0 URLs and LINQ to Twitter is on Twitter API v1.1 now. It will default to the proper base URLs.
If you're querying okay, but getting errors on update and DM, double check to make sure you aren't trying to tweet the same text. That's why I append a DateTime.Now to the end of test tweets - to guarantee uniqueness.

Categories

Resources