SMAPI Alexa Read Scope in Swift - swift

I'm trying to get read & write permissions, so users can see their alexa skill from an ios app.
I successfully managed to let the user sign in with their amazon account (via the LWA iOS SDK). When I add the profile scope to the authentication process I get this error when trying to make a get request to the skill endpoint:
"User has not consented to this operation."
So I added the alexa::ask scope, so I get the right permissions:
let scopeData_alexa: [AnyHashable:Any] = ["productID" : AmazonManager.shared.productId, "productInstanceAttributes": [ "deviceSerialNumber": uniqueDeviceSerialNumber]]
let readPermissions = AMZNScopeFactory.scope(withName: "alexa::ask:skills:readwrite", data: scopeData_alexa)
When I try to authenticate the user I get this response:
Error Domain=AMZNLWAErrorDomain Code=2 "(null)" UserInfo={AMZNLWAErrorNonLocalizedDescription=The+scope+data+list+you+provided+is+invalid+for+your+request}
As said above, the normal login works fine. The scope Data is also working, when I ask the user for Alexa Voice Services.
What to I have to change? Any solutions?

It was super easy to fix.
just remove the data parameter
let readPermissions = AMZNScopeFactory.scope(withName: "alexa::ask:skills:readwrite")
Then it should work fine!

Related

Problem requesting device address for Alexa Skill

So, I am trying to get the address for Alexa units that access my skill per https://developer.amazon.com/en-US/docs/alexa/custom-skills/device-address-api.html#request-message-example-1.
I tried this, but I am getting back a 403 (forbidden). Any idea what I am doing wrong?
device = handler_input.request_envelope.context.system.device
api_access = handler_input.request_envelope.context.system.api_access_token
data = {"Host":"api.amazonalexa.com", "Accept":"application/json", "Authorization":"Bearer " + api_access}
alexa_response = requests.get(f'https://api.amazonalexa.com/v1/devices/{device}/settings/address', data)
print(alexa_response)
From the documentation that you linked:
To test the case where the customer has not provided permissions to your skill, ensure that the address permissions for your skill in the Alexa companion app are not enabled. When you open the skill ("Alexa, open skill_name"), that will cause a LaunchRequest to be sent. This request will contain the deviceId value and the apiAccessToken value, but the apiAccessToken will not specify the correct permissions. Passing this token to the Device Address API will return a 403 Forbidden response code.
I'm assuming this skill is in the development stage. You can check the permissions for dev stage skills enabled on your account from here: https://alexa.amazon.com/spa/index.html#skills/your-skills/?ref-suffix=ysa_gw
Sorry. Thanks for the response, but I failed to send a permission card. Once I sent that and had location enabled on my device, everything worked as it should have. Oops.

Actions on Google implicit account linking works in simulator/browser, but not on device (via Google Home app)

I've implemented the implicit flow for Actions on Google account linking, and am using Dialogflow (previously API.AI) to define intents.
The full flow works in the device simulator (from AOG). The first intent gets a "It looks like your account isn't linked yet..." response, and the debug pane includes a URL to initiate linking:
https://assistant.google.com/services/auth/handoffs/auth/start?account_name=[account]#gmail.com&provider=[project_id]_dev&scopes=email&return_url=https://www.google.com/
If I follow this URI in a cache-less window:
I'm redirected to my app's authentication page
I choose to sign in with my Google account (same as [account] above)
I'm redirected to google.com with a success message in the URI bar
The simulator now accepts actions via my app and responds correctly
However, if I follow the same flow using a physical Google Home & the gH app for Android.
Device tells me account not yet linked
Open Google home and follow 'Link to [my app]' link
Browser opens to authentication page
Sign in as user
Redirected to a white page with a single link "Return to app", which has an href: about:invalid#zClosurez
Linking was unsuccessful, so additional attempts to run intents on the Google Home get the same "Account not yet linked" response.
I've inspected the intermediate access_token and state variables at length, and they all match and look to be correctly formatted:
Authentication URL (app sign in page): https://flowdash.co/auth/google?response_type=token&client_id=[client_id]&redirect_uri=https://oauth-redirect.googleusercontent.com/r/[project_id]&scope=email&state=[state]
After authenticating, redirected to (this is the white screen with 'return to app' broken link): https://oauth-redirect.googleusercontent.com/r/genzai-app#access_token=[token]&token_type=bearer&state=[state]
So, it seems there's something non-parallel about the way the simulator and physical devices work in terms of implicit flow account linking.
I've been struggling with this, and with the AOG support team for a very long time to no avail. Anyone else see a similar issue?
Updated with response redirect code:
Login handled by react-google-login component with profile & email scopes. On success we call:
finish_auth(id_token) {
let provider = {
uri: '/api/auth/google_auth',
params: ['client_id', 'redirect_uri', 'state', 'response_type'],
name: "Google Assistant"
}
if (provider) {
let data = {};
provider.params.forEach((p) => {
data[p] = this.props.location.query[p];
});
if (id_token) data.id_token = id_token;
api.post(provider.uri, data, (res) => {
if (res.redirect) window.location = res.redirect;
else if (res.error) toastr.error(res.error);
});
} else {
toastr.error("Provider not found");
}
}
provider.uri hits this API endpoint:
def google_auth(self):
client_id = self.request.get('client_id')
redirect_uri = self.request.get('redirect_uri')
state = self.request.get('state')
id_token = self.request.get('id_token')
redir_url = user = None
if client_id == DF_CLIENT_ID:
# Part of Google Home / API.AI auth flow
if redirect_uri == "https://oauth-redirect.googleusercontent.com/r/%s" % secrets.GOOGLE_PROJECT_ID:
if not user:
ok, _email, name = self.validate_google_id_token(id_token)
if ok:
user = User.GetByEmail(_email, create_if_missing=True, name=name)
if user:
access_token = user.aes_access_token(client_id=DF_CLIENT_ID)
redir_url = 'https://oauth-redirect.googleusercontent.com/r/%s#' % secrets.GOOGLE_PROJECT_ID
redir_url += urllib.urlencode({
'access_token': access_token,
'token_type': 'bearer',
'state': state
})
self.success = True
else:
self.message = "Malformed"
else:
self.message = "Malformed"
self.set_response({'redirect': redir_url}, debug=True)
I am able to make it work after a long time. We have to enable the webhook first and we can see how to enable the webhook in the dialog flow fulfillment docs If we are going to use Google Assistant, then we have to enable the Google Assistant Integration in the integrations first. Then follow the steps mentioned below for the Account Linking in actions on google:-
Go to google cloud console -> APIsand Services -> Credentials -> OAuth 2.0 client IDs -> Web client -> Note the client ID, client secret from there -> Download JSON - from json note down the project id, auth_uri, token_uri -> Authorised Redirect URIs -> White list our app's URL -> in this URL fixed part is https://oauth-redirect.googleusercontent.com/r/ and append the project id in the URL -> Save the changes
Actions on Google -> Account linking setup 1. Grant type = Authorisation code 2. Client info 1. Fill up client id,client secrtet, auth_uri, token_uri 2. Enter the auth uri as https://www.googleapis.com/auth and token_uri as https://www.googleapis.com/token 3. Save and run 4. It will show an error while running on the google assistant, but dont worry 5. Come back to the account linking section in the assistant settings and enter auth_uri as https://accounts.google.com/o/oauth2/auth and token_uri as https://accounts.google.com/o/oauth2/token 6. Put the scopes as https://www.googleapis.com/auth/userinfo.profile and https://www.googleapis.com/auth/userinfo.email and weare good to go. 7. Save the changes.
In the hosting server(heroku)logs, we can see the access token value and through access token, we can get the details regarding the email address.
Append the access token to this link "https://www.googleapis.com/oauth2/v1/userinfo?access_token=" and we can get the required details in the resulting json page.
`accessToken = req.get("originalRequest").get("data").get("user").get("accessToken")
r = requests.get(link)
print("Email Id= " + r.json()["email"])
print("Name= " + r.json()["name"])`
Not sure which python middleware or modules you are using but
self.set_response({'redirect': redir_url}, debug=True)
seems to be setting parameters for a returning a response which isn't correct. Instead you should redirect your response to the redirect_url. For example importing the redirect module in Flask or Django like:
from flask import redirect or from django.shortcuts import redirect
then redirect like:
return redirect(redirect_url)
It appears Google has made a change that has partially solved this problem in that it is now possible to complete the implicit account linking flow outside of the simulator, in the way outlined in my question.
It seems the problem stemmed from an odd handling (on the AOG side) of the client-side redirect case used after sign in with the Google sign-in button.
From Jeff Craig in this thread:
The current workaround, where we provide the "Return to app" link
currently what we're able to provide. The issue is with the way that
redirecting to custom-scheme URIs is handled in Chrome, specifically,
with regard to the redirect happening in the context of a user action.
XHR will break that context, so what is happening is that you click
the Google Sign-In Button, which triggers an XHR to Google's servers,
and then you (most likely) do a client-side redirect back to the
redirect_url we supply, our handler executes, and isn't able to do a
JS redirect to the custom scheme URI of the app, because were outside
of the context of a direct user click.
This is more of a problem with the Implicit (response_type=token) flow
than with the authorization code (response_type=code) flow, and the
"Return to app" link is the best fallback case we currently have,
though we are always looking for better solutions here as well.
The current behavior shows the 'Return to app' link, but as of last week, this link's href is no longer about:invalid#zClosurez, but instead successfully completes the sign-in and linking process. It's an odd and confusing UX that I hope Google will improve in the future, but it was sufficient to get my app approved by the AOG team without any changes to my flow.

Realm Object Server - Error: Your request parameters did not validate

I built a small iOS application which uses Realm instead of CoreData. The app does not require a login as it only stores data entered by the user. I'm currently trying to save users data so that if a user deleted the app for example, the data will be there by default the next the app is re-installed.
Here's where I am getting confused. Can I still use Realm Mobile Platform even though the app will not require a login screen. (i.e. data will automatically be saved for users who are logged-in to their iCloud accounts).
Here's what I've done so far:
I configured Realm Object Server on an AWS EC2 instance, and I can login to the realm dashboard through the browser just fine.
I configured the cloudKit stanza in the configuration.yml file as per the authentication instructions.
In my setupRealm() func, I tried the following code but I keep getting a parameters validation error:
SyncUser.logIn(with: cloudKitCredentials,
server: serverURL) { user, error in
if let user = user {
print("in")
}
else if let error = error {
fatalError(String(describing: error))
// Error: "Your request parameters did not validate."
}
This is the error message:
Error Domain=io.realm.sync Code=3
"Your request parameters did not validate."
UserInfo={statusCode=400,
NSLocalizedDescription=Your request parameters did not validate.}:
I suspect that the my iCloud user is not being tied with the object server, but I can't seem to put the pieces together. I'd appreciate any pointers.
The server requires a restart after editing the authentication lines in the configuration.yml.

Facebook .NET SDK -> Get and Post using generated token

I am writing a Azure Service that will occasionally write to my facebook page as a status. Since the service does not have a UI component, a majority of the examples on the Facebook and Facebook .NET SDK pages are not helpful.
I created an application on facebook and then fired up the F# REPL in Visual Studio. I generated the token like so:
#r "../packages/Facebook.7.0.6/lib/net45/Facebook.dll"
#r "../packages/Newtonsoft.Json.7.0.1/lib/net45/Newtonsoft.Json.dll"
open Facebook
open Newtonsoft.Json
type Credentials = {client_id:string; client_secret:string; grant_type:string;scope:string}
let credentials = {client_id="859968674039398";
client_secret="XXXXXXXXXX";
grant_type="client_credentials";
scope="manage_pages,publish_stream,read_stream,publish_checkins,offline_access"}
let client = FacebookClient()
let tokenJson = client.Get("oauth/access_token",credentials)
type Token = {access_token:string}
let token = JsonConvert.DeserializeObject<Token>(tokenJson.ToString())
A token comes back as expected. However, when I go to use the token, I am getting errors:
let client' = FacebookClient(token.access_token)
let me = client'.Get("me")
returns
An active access token must be used to query information about the
current user.
and
let pageId = "/me"
type FacecbookPost = {title:string; message:string}
let post = {title="Test Title"; message = "Test Message"}
let postResponse = client'.Post(pageId + "/feed", post)
returns
The user hasn't authorized the application to perform this action
When I read the docs, they talk about getting the application to be approved by Facebook -> but that makes no sense in my use case b/c there is no application as defined with a human end user -> or even any other user invoking the code.
When I generate the token on Facebook Graph Api explorer with the correct permissions, I can use the token to make those GETS and POSTS. Should I just generate the token and stick it in my .config file? How long does a token last?
Thanks in advance
I think you haven't fully understood how Facebook API works.
You always need an App to perform an action (in your case the APP is 859968674039398)
In order to post on behalf a user, you will need that user to grant permissions to your App.
Your App has to be public and if you require more permissions than the basic ones, you need to go through the review process.
The access token you get from the Graph API Explorer (which is an App BTW) is only for you.
Please read the docs CBro provided.
I hope it helps.

Facebook UserId returned from Azure Mobile Services keeps changing within the same Windows Phone app

I'm a newbie to app development. I am building a Windows Phone 8.1 app and have followed the tutorial here: http://azure.microsoft.com/en-us/documentation/articles/app-service-mobile-dotnet-backend-windows-store-dotnet-get-started-users-preview/ to add authentication using Facebook. Everything seems to work fine, except that every now and again it appears to stop bringing back any data from my Azure database. Further investigation revealed that the UserId that is being shown from the code below, changes periodically (although I can't quite work out how often it changes).
// Define a member variable for storing the signed-in user.
private MobileServiceUser user;
...
var provider = "Facebook";
...
// Login with the identity provider.
user = await App.MobileService.LoginAsync(provider);
// Create and store the user credentials.
credential = new PasswordCredential(provider,
user.UserId, user.MobileServiceAuthenticationToken);
vault.Add(credential);
...
message = string.Format("You are now logged in - {0}", user.UserId);
var dialog = new MessageDialog(message);
dialog.Commands.Add(new UICommand("OK"));
await dialog.ShowAsync();
This code is identical to the code in the tutorial. The Facebook app settings (on the Facebook developers site) confirm that I am using v2.3 of their API so I should be getting app-scoped UserIds back. I have only ever logged in with one Facebook account, so I would expect the UserId to be the same each time, but they're not. The UserId is prefaced with 'sid:', which someone on the Facebook developers group on Facebook itself says stands for Session ID, which they would expect to change, but if that's the case, I can't work out where to get the actual UserId from that I can then store in my database and do useful things with. I'm sure I must be doing something basic wrong, but I have spent hours Googling this and cannot (unusually) find an answer.
Any help would be greatly appreciated.
Thanks!
So dug deeper. This is how Mobile Apps work (I was thinking from a Mobile Services perspective). The issue here is that the Gateway doesn't provide static SIDs, which is what User.userId provides. The work around to this is listed in the migration doc.
You can only get the Facebook AppId on the server.
ServiceUser user = (ServiceUser) this.User;
FacebookCredentials creds = (await user.GetIdentitiesAsync()).OfType< FacebookCredentials >().FirstOrDefault();
string mobileServicesUserId = creds.Provider + ":" + creds.UserId;
You should note, that this Id is directly connected with your Facebook App registration. If you ever want to migrate your App to a new Facebook App, you'd have to migrate them. You can also use the Facebook AppId to look up the user's global facebook Id via the Facebook Graph API, which you could use between applications. If you don't see yourself using multiple apps, etc., you can use the Facebook AppId just fine.
Hard to tell what's going on to cause you to use a SID instead of the Faceboook token (which like Facebook:10153...).
It may be faster to rip out the code and reimplement the Auth GetStarted. Maybe you missed a step or misconfigured something along the way. If you have the code hosted on github, I can try to take a look.
Another thing you can do is to not trust the user to give you their User id when you save it to a table. On your insert function, you can add it there.
function insert(item, user, request) {
item.id = user.userId;
request.execute();
}
That should, theoretically, be a valid Facebook token. Let me know if that doesn't work; can dig deeper.