This is the data that is being send by alexa to my skills backend. In the backend code I would like to test if the session is new or not and based on this information I would like to produce a different output. I try to access the session.new, but I don't know how and I could now find anything about it online so far.
const { attributesManager } = handlerInput;
const requestAttributes = attributesManager.getRequestAttributes();
requestAttributes.session.new
//this leads to the error "cannot read property new of undefined"
const { attributesManager } = handlerInput;
const requestAttributes = attributesManager.getSessionAttributes();
sessionAttributes.session.new
//this leads to the same error
I finally found out that this is not possible using the AttributesManager because this only allows access to request, session and permanent attributes. But session.new is not part of those. If you want to check if the Alexa session is new you have to use the so called RequestEnvelopeUtils.
Using those you can access if the session is new or not by using the following statement.
(Alexa.isNewSession(handlerInput.requestEnvelope))
Related
I'm implementing an auth system with django and react. The two app run respectively on port 8000, 3000. I have implemented the authentication system using the Djoser package. This package uses some dependencies social_core and social_django. Everything seems to be configured ok. I click on login google button...I'm redirected to the google login page and then back to my front-end react app at port 3000 with the state and code parameters on the url.
At this point I'm posting those parameters to the backend. The backend trying to validate the state checking if the state key is present in the session storage using the code below from (social_core/backends/oauth.py)
def validate_state(self):
"""Validate state value. Raises exception on error, returns state
value if valid."""
if not self.STATE_PARAMETER and not self.REDIRECT_STATE:
return None
state = self.get_session_state()
request_state = self.get_request_state()
if not request_state:
raise AuthMissingParameter(self, 'state')
elif not state:
raise AuthStateMissing(self, 'state')
elif not constant_time_compare(request_state, state):
raise AuthStateForbidden(self)
else:
return state
At this point for some reasons the state session key is not there..and I receive an error saying that state cannot be found in session data ( error below )
{"error":["State could not be found in server-side session data."],"status_code":400}
I recap all the action I do:
Front-end request to backend to generate given the provider google-oauth2 a redirect url. With this action the url is generated also the state key is stored on session with a specific value ( google-oauth2_state ).
Front-end receive the url and redirect to google auth page.
Authentication with google and redirection back to the front-end with a state and code parameters on the url.
Front-end get the data form url and post data to back-end to verify that the state received is equal to the generated on the point (1).
For some reasons the state code is not persisted... Any ideas and help will be really appreciated.
Thanks to all.
ok so this is a common problem while you are working with social auth. I had the same problem for so many times.
The flow:
make a request to http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:3000/ (example)
you will get a authorization_url. if you notice in this authorization_url there is a state presented . this is the 'state of server side'.
now you need to click the authorization_url link.Then you will get the google auth page.After that you will be redirect to your redirect url with a state and a code. Remember this state should be the same state as the server side state .(2)
make post req to http://127.0.0.1:8000/auth/o/google-oauth2/?state=''&code=''.
if your states are not the same then you will get some issue.
everytime you wanna login , you need to make a request to http://127.0.0.1:8000/auth/o/google-oauth2/?redirect_uri=http://localhost:3000/
and then to http://127.0.0.1:8000/auth/o/google-oauth2/?state=''&code='' thus you will get the same state.
Without necessary detailed information, I can only tell 2 possible reasons:
You overrode backend with improper session operations(or the user was logged out before auth was finished).
Front-end used incorrect state parameter
You could test social login without front-end, let's say if you're trying to sign in with Google:
Enter the social login URL in browser, like domain.com:8000/login/google-oauth2/
Authorize
See if the page redirected to your default login page correctly
If yes, then probably you need to check your front-end code, and if no, then check your backend code.
At the end, if you're not so sensitive to the potential risk, you could also override GoogleOAuth2 class as following to disable state check:
from social_core.backends import google
class GoogleOAuth2(google.GoogleOAuth2):
STATE_PARAMETER = False
I think you may need some changes in you authorizing flow in step NO.3 and 4.
3.Authentication with google and redirection back to the front-end with a state and code parameters on the url.
4.Front-end get the data form url and post data to back-end to verify that the state received is equal to the generated on the point (1).
maybe you should redirect back to server side after google's authorization.
then at the server side, do the check! validate the state and code (maybe do more things).
then let server redirect to the front-end site you wanted to before.
for some reason, redirect to front-end directly will miss the param.. :-)
Finally, I reach a point where everything is working 200 percent fine, on local as well as production.
The issue was totally related to the cookies and sessions:
So rite answer typo is
make it look to your backend server as if the request is coming from localhost:8000, not localhost:3000,
means the backend domain should be the same always.
For making it possible you have two ways:
1: server should serve the build of the frontend then your frontend will always be on the same domain as the backend.
2: make a simple view in django and attach an empty template to it with only a script tag including logic to handle google auth. always when you click on signing with google move back you you're that view and handle the process and at the end when you get back your access token pass it to the frontend through params.
I used 2nd approach as this was appropriate for me.
what you need to do is just make a simple View and attach a template to it so on clicking on signIN with google that view get hit. and other process will be handled by the view and on your given URL access token will be moved.
View Code:
class GoogleCodeVerificationView(TemplateView):
permission_classes = []
template_name = 'social/google.html'
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
context["redirect_uri"] = "{}://{}".format(
settings.SOCIAL_AUTH_PROTOCOL, settings.SOCIAL_AUTH_DOMAIN)
context['success_redirect_uri'] = "{}://{}".format(
settings.PASSWORD_RESET_PROTOCOL, settings.PASSWORD_RESET_DOMAIN)
return context
backend script code:
<body>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/axios/0.21.1/axios.min.js"></script>
<script>
function redirectToClientSide(success_redirect_uri) {
window.location.replace(`${success_redirect_uri}/signin/`);
}
function getFormBoday(details) {
return Object.keys(details)
.map(
(key) =>
encodeURIComponent(key) + "=" + encodeURIComponent(details[key])
)
.join("&");
}
try {
const urlSearchParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlSearchParams.entries());
const redirect_uri = "{{redirect_uri|safe}}";
const success_redirect_uri = "{{success_redirect_uri|safe}}";
if (params.flag === "google") {
axios
.get(
`/api/accounts/auth/o/google-oauth2/?redirect_uri=${redirect_uri}/api/accounts/google`
)
.then((res) => {
window.location.replace(res.data.authorization_url);
})
.catch((errors) => {
redirectToClientSide(success_redirect_uri);
});
} else if (params.state && params.code && !params.flag) {
const details = {
state: params.state,
code: params.code,
};
const formBody = getFormBoday(details);
// axios.defaults.withCredentials = true;
axios
.post(`/api/accounts/auth/o/google-oauth2/?${formBody}`)
.then((res) => {
const formBody = getFormBoday(res.data);
window.location.replace(
`${success_redirect_uri}/google/?${formBody}`
);
})
.catch((errors) => {
redirectToClientSide(success_redirect_uri);
});
} else {
redirectToClientSide(success_redirect_uri);
}
} catch {
redirectToClientSide(success_redirect_uri);
}
</script>
</body>
In my Alexa Skill i set dynamic entities in 'addRequestInterceptors' as the entities must be available as soon as possible, the issue is that if the user says 'Alexa, open SKILLNAME' the entities are initialized correctly, but if the user instead says 'Alexa, ask SKILLNAME where is DYNAMICENTITY' at this point the skill fails as the DYNAMICENTITY is not yet set.
So my question is, how can i set my dynamic entities as soon as possible like when the skill is created? I've tryed the LaunchIntent but as in the case above it's avoided if the user make the question inside the invocation..
So LaunchIntent and addRequestInterceptors aren't good to do it..
Here is my code which i should run as soon as possible:
const LoadListaPuntiVendita = {
async process(handlerInput) {
const {attributesManager, requestEnvelope} = handlerInput;
const sessionAttributes = attributesManager.getSessionAttributes();
const attributes = await attributesManager.getPersistentAttributes() || {};
const piva = attributes.hasOwnProperty('piva') ? attributes.piva : null;
sessionAttributes["piva"] = piva;
if (!piva || piva === null) {
return;
}
let negozi = sessionAttributes['negozi']
if (!negozi) {
const response = await logic.fetchNegozi(piva);
sessionAttributes['negozi'] = response;
addDynamicEntities(handlerInput.responseBuilder, 'PuntoVenditaType', response);
}
}
};
Here's one possible bug on first glance. This will only run as expected after you've had at least one exchange in which "piva" had been saved and set as a persistent value for the customer.
If you're running this the moment the skill is initialized, it still must be run after the persistence adapter has been initialized and retrieved the value of "piva" from your store AND there must be have been a value stored for "piva" or the code returns early without setting the entity.
My Goal
My goal is to figure out why Collection#find returns undefined when I try to find a user in my server, but they're offline and have no roles.
Expectation
Usually the console logs an array of all the properties of the user
Actual Result
The console logs Collection#find, as undefined if the user in my server is offline and has no roles
What I've Tried
I've tried using Collection#get, but it turns out that it returns the same response. I've tried searching this up on Google, but no one has asked this question before.
Reproduction Steps
const Discord = require('discord.js');
const client = new Discord.Client();
const {prefix, token} = require('./config.json');
client.once('ready', () => {
console.log('Client is online');
const user = client.users.cache.find(user => user.tag === 'Someone#1234');
console.log(user);
};
client.login(token);
Make sure that whoever is helping you, whether it's an alt account or your friend, that they have no roles, and they're completely offline in your server
Output:
Client is online undefined
I had the same problem. I don't know why this happens, but I used this line to fix that:
((await m.guild.members.fetch()).filter(mb => mb.id === "The ID")).first()`
Basically, this collect all the members, then filters them with the property you want, and the first() at the end is to make it a single object instead of a collection.
Instead of the user.tag property, try using the user.username and the user.discriminator properties. This worked for me
const user = client.users.cache.find(user => user.username === 'Someone' && user.discriminator === '1234');
Also check spelling, capitalization, ect.
A simple solution to my problem is just create an async function and use await guild.members.fetch() to cache the users and then use Collection#find to get the user.
When using extensions in the Graph API:
graphUser = graphClient.Users.Request().AddAsync(graphUser).Result;
OpenTypeExtension newExtension = new OpenTypeExtension()
{
ExtensionName = "CustomName",
AdditionalData = new Dictionary<string, object>
{ { "CustomID", user.CustomID }
}
};
graphClient.Users[graphUser.UserPrincipalName]
.Extensions
.Request()
.AddAsync(newExtension)
.Wait();
I randomly get these errors:
Code: AccessDenied
Message: Access Denied
Sometimes it works, sometimes it doesn't. I can't seem to find a correlation.
When I step trough the code in the debugger it works more often then if I run it without interruption. But if I add a sleep between the lines (to account for processing delay), it doesn't fix the issue.
The application has all the required rights to access the API.
The issue isn't solely in the POST, but also on the GET as illustrated in the code sample below which results in the same error.
User user = graphClient.Users[userName]
.Request()
.GetAsync()
.Result;
user.Extensions = graphClient.Users[userName]
.Extensions
.Request()
.GetAsync()
.Result;
Does anyone have experience with this issue? Thanks in advance!
EDIT:
I figured out that once the errors start showing, the user needs to be deleted. Errors on one user don't necessarily mean errors on another user.
EDIT 2:
I also posted this as an issue on GitHub. Here you can find more information about this problem. It's now labeled as a bug.
It turns out that the User Principal Name is a cached reference.
Since I was running tests, meaning recreating the same test user a lot, the reference UPN was pointing to the old user object resulting in the Access Denied errors.
The issue can be avoided by using the Id of the object, like this:
graphClient.Users[graphUser.Id]
.Extensions
.Request()
.AddAsync(newExtension)
.Wait();
I believe the team is going to fix the reference bug, but I can't speak for them of course. In either case I would recommend using the Id attribute to be sure.
Based on the test, when we create a new user in Azure Active Directory, it seems that there is some delay we can operate for that user. Even the user is returned successfully when I using the Graph to filter the user, it still may failed when I add the extension to that user.
For this issue, I added a line of addition code to make the current thread sleep and then it works.
var userPrincipalName = "userPrincipalName8#adfei.onmicrosoft.com";
var graphUser = new User() { AccountEnabled = true, MailNickname = "MailNickname", UserPrincipalName = userPrincipalName, DisplayName = "userPrincipalName", PasswordProfile = new PasswordProfile() { Password = "islkdifde123!", ForceChangePasswordNextSignIn = false } };
graphUser = graphClient.Users.Request().AddAsync(graphUser).Result;
OpenTypeExtension newExtension = new OpenTypeExtension()
{
ExtensionName = "CustomName",
AdditionalData = new Dictionary<string, object>
{
{ "CustomID", "abc" }
}
};
Thread.Sleep(4000);
graphClient.Users[graphUser.UserPrincipalName]
.Extensions
.Request()
.AddAsync(newExtension)
.Wait();
However the detailed error message for this issue should be code=ResourceNotFound,message=User not found. Please check whether the error is same and this workaround is helpful.
Following is a code to get google contacts.
It was working fine but since few days I m getting exception of "Authentication request returned unexpected result: 404".
using Google.GData.Client;
using Google.Contacts;
using Google.GData.Extensions;
private void FetchContactList()
{
List<string> lstContacts = new List<string>();
RequestSettings rsLoginInfo = new RequestSettings("my application", "abc#gmail.com", "XXXXXX");
rsLoginInfo.AutoPaging = true;
ContactsRequest cRequest = new ContactsRequest(rsLoginInfo);
Feed<contact> feedContacts = cRequest.GetContacts();
foreach (Contact gmailAddresses in feedContacts.Entries)
{
// Looping to read email addresses
foreach (EMail emailId in gmailAddresses.Emails)
{
lstContacts.Add(emailId.Address);
}
}
GridView1.DataSource = lstContacts;
GridView1.DataBind();
}
Is google change something from their side?
Please suggest me way to solve the problem.
Update your current api with google api to version 3 and then make changes to the code according. Probably this may be the reason for the error.
https://developers.google.com/analytics/devguides/reporting/core/v3/gdataLibraries
i further suggest you use oauth2.0 for authentication as per the current requirements of your application and if you are using the older version of the api then you must use oauth2.0
Here is the link for oauth2.0 support: https://developers.google.com/google-apps/spreadsheets/authorize
though this is basically for spreadsheets but you can see how it is and working and change it to the requirements of your gmail api