Alexa Reminders API 401 response - amazon

So, I am using Amazon Alexa Reminders API as shown here.
Here is my method for sending requests to API:
public static void sendReminder(String accessToken, String reminderText, long offsetInSec) {
CloseableHttpClient client = HttpClients.createDefault();
HttpPost post = new HttpPost("https://api.amazonalexa.com/v1/alerts/reminders");
post.addHeader("Authorization", "Bearer " + accessToken);
post.addHeader("Content-Type", "application/json");
TimeZone tz = TimeZone.getTimeZone("UTC");
DateFormat df = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm'Z'");
df.setTimeZone(tz);
String nowAsISO = df.format(new Date());
String jsonContent = "{ \"requestTime\" : \"" + nowAsISO + "\", \"trigger\": { \"type\" : \"SCHEDULED_RELATIVE\", \"offsetInSeconds\" : \"" + offsetInSec + "\" }, \"alertInfo\": { \"spokenInfo\": { \"content\": [{ \"locale\": \"en-US\", \"text\": \"" + reminderText + "\" }] } }, \"pushNotification\" : { \"status\" : \"ENABLED\" } }";
HttpEntity entity = null;
try {
byte[] bytes = jsonContent.getBytes("UTF-8");
entity = new ByteArrayEntity(bytes);
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
post.setEntity(entity);
try {
CloseableHttpResponse response = client.execute(post);
System.out.println(response);
} catch (IOException e) {
e.printStackTrace();
}
}
And I execute it like this:
RemindersToolkit.sendReminder(session.getUser().getAccessToken(), "text", 1);
Skill also has permission for reminders:
But when the method is executed, I get the following response:
HttpResponseProxy{HTTP/1.1 401 Unauthorized [Content-Type: application/json, Connection: keep-alive, Server: Server, Date: Tue, 22 Jan 2019 00:21:21 GMT, Vary: Accept-Encoding,User-Agent, x-amz-rid: 8YMCM10GKVGTT71JQH3N, X-Cache: Error from cloudfront, Via: 1.1 05a90e634e0872685ad69ee9a4e0eba5.cloudfront.net (CloudFront), X-Amz-Cf-Id: J5CtMnkUTv1hd6p-7-tob7mCb-4DM7y_LxhEiMLt5x3qEqmzhwbx_Q==] org.apache.http.client.entity.DecompressingEntity#6df97b55}
According to Amazon on this page, 401 UNAUTHORIZED means Token is valid but does not have appropriate permissions.
Maybe some of you guys had the same problem or could help me figure out how to solve mine?
Thanks

Got it worked, as pointed out in this answer, granting permission in the skill is not enough, the end user using that skill also has to grant the permission. Ask the user for the permission through permission card when encountered with the unauthorized response.

The issue was that I was using the old Alexa SDK.
I had to download current version of the SDK and there is a different key (not accessToken I've used), which can be obtained directly from the Intent object and can then be used to send requests

Related

Unable to programmatically add/update Azure Function Key

When attempting to programmatically add/update a function key, I receive the following error:
StatusCode: 401, ReasonPhrase: 'Unauthorized'
Code:
Executing the following code results in the error described above.
static void FunctionKey(string resourceGroupName, string functionAppName, string functionName, NameValuePair kv)
{
var resource = $"subscriptions/{SubscriptionId.Value}/resourceGroups/{resourceGroupName}/providers/Microsoft.Web/sites/{functionAppName}/functions/{functionName}/keys/{kv.Name}?api-version=2022-03-01";
var httpClient = new HttpClient() { BaseAddress = new Uri("https://management.azure.com/") };
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", AuthToken.Value);
httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
var json = #"{
""Properties"": {
""Name"": ""ApiKey"",
""Value"": ""some_value""
}
}";
using (var content = new StringContent(json, Encoding.UTF8, "application/json"))
{
var response = httpClient.PostAsync(resource, content).Result;
if (!response.IsSuccessStatusCode)
throw new Exception($"Error: Failed to register function key for {functionName}");
}
}
Research:
I was successful when performing this task in the the documentation emulator.
I tried to reproduce the same in my environment via Postman and got below results:
When I ran the below query without including bearer token, I got same error with 401 Unauthorized like below:
PUT https://management.azure.com/subscriptions/<subID>/resourceGroups/<rgname>/providers/Microsoft.Web/sites/<funcappname>/functions/<funcname>/keys/<keyname>?api-version=2022-03-01
{
"Properties":
{
"Name": "keyname",
"Value": "xxxxxxxxxxxx"
}
}
Response:
After passing the token, I'm able to create function key successfully like below:
When I checked the same portal, srikey appeared under function keys like below:
In your case, you are using httpClient.PostAsync which means
POST method.
When I used POST method for below query, I too got 404 Not found error like below:
POST https://management.azure.com/subscriptions/<subID>/resourceGroups/<rgname>/providers/Microsoft.Web/sites/<funcappname>/functions/<funcname>/keys/<keyname>?api-version=2022-03-01
{
"Properties":
{
"Name": "keyname",
"Value": "xxxxxxxxxxxx"
}
}
Response:
To resolve the error, make sure to use PUT method by changing httpClient.PostAsync method to httpClient.PutAsync method.
Reference:
HttpClient.PutAsync Method (System.Net.Http) | Microsoft

Authentication errors using Azure blob storage and Azure CDN

I'm having intermittent 403 errors trying to access a blob storage, via Azure CDN, with the symmetric access key. It seems that sometimes there's a header added for "Range", in the format of "bytes=xxx". The full error message is below:
{'Date': 'Mon, 12 Dec 2022 13:07:40 GMT', 'Content-Type': 'application/xml', 'Content-Length': '697', 'Connection': 'keep-alive', 'x-ms-request-id': '3f89c2c1-e01e-0050-132a-0eeb42000000', 'x-ms-error-code': 'AuthenticationFailed', 'x-azure-ref': '20221212T130740Z-6rfkrgx8qt0shbtz3x46rwnhrn0000000630000000002ayd', 'X-Cache': 'TCP_MISS'}
<?xml version="1.0" encoding="utf-8"?><Error><Code>AuthenticationFailed</Code><Message>Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature.
RequestId:3f89c2c1-e01e-0050-132a-0eeb42000000
Time:2022-12-12T13:07:40.7638741Z</Message><AuthenticationErrorDetail>The MAC signature found in the HTTP request 'xxxxxx=' is not the same as any computed signature. Server used following string to sign: 'GET
bytes=0-8388607
x-ms-date:Mon, 12 Dec 2022 13:07:36 GMT
x-ms-version:2020-04-08
/deviceimage2zgjscikl7kny/images/data-prod-1.1.packer'.</AuthenticationErrorDetail></Error>
I was able to reproduce the error by generating the MAC signature in Python, but I saw it originally using the Go SDK and az CLI.
We added a rule at the CDN to Bypass caching, and it seems to have improved the situation (problem happens less frequently), but we are still seeing it on occasion.
Has anyone else experienced this? And is there a workaround?
Trying to access a blob storage with an access key, via Azure CDN
I tried in my environment and got below results:
Initially, I got a same when I tried to access blob storage with CDN using Postman.
Postman:
The above error states that signature and date is incorrect. So, we can't pass directly storage access key. You need to create a signature string that represents the given request, sign the string with the HMAC-SHA256 algorithm (using your storage key to sign), and encode the result in base 64.
For creating signature, I used below .NET code:
using System.Globalization;
using System.Net;
using System.Security.Cryptography;
class Program
{
static void Main(string[] args)
{
ListBlobs();
Console.WriteLine("done");
Console.ReadLine();
}
static void ListBlobs()
{
string Account = "venkat123";
string Key = "<Storage account key>";
string Container = "test";
string apiversion = "2021-06-08";
DateTime dt = DateTime.UtcNow;
string StringToSign = String.Format("GET\n"
+ "\n" // content encoding
+ "\n" // content language
+ "\n" // content length
+ "\n" // content md5
+ "\n" // content type
+ "\n" // date
+ "\n" // if modified since
+ "\n" // if match
+ "\n" // if none match
+ "\n" // if unmodified since
+ "\n" // range
+ "x-ms-date:" + dt.ToString("R") + "\nx-ms-version:" + apiversion + "\n" // headers
+ "/{0}/{1}\ncomp:list\nrestype:container", Account, Container);
string auth = SignThis(StringToSign, Key, Account);
Console.WriteLine($"the date is: {dt.ToString("R")}");
Console.WriteLine($"the auth token is: {auth}");
Console.WriteLine("*********");
string method = "GET";
string urlPath = string.Format("https://{0}.blob.core.windows.net/{1}?restype=container&comp=list", Account, Container);
Uri uri = new Uri(urlPath);
HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
request.Method = method;
request.Headers.Add("x-ms-date", dt.ToString("R"));
request.Headers.Add("x-ms-version", apiversion);
request.Headers.Add("Authorization", auth);
Console.WriteLine("***list all the blobs in the specified container, in xml format***");
using (HttpWebResponse response = (HttpWebResponse)request.GetResponse())
{
using (StreamReader reader = new StreamReader(response.GetResponseStream()))
{
Console.WriteLine(reader.ReadToEnd());
}
}
}
private static String SignThis(String StringToSign, string Key, string Account)
{
String signature = string.Empty;
byte[] unicodeKey = Convert.FromBase64String(Key);
using (HMACSHA256 hmacSha256 = new HMACSHA256(unicodeKey))
{
Byte[] dataToHmac = System.Text.Encoding.UTF8.GetBytes(StringToSign);
signature = Convert.ToBase64String(hmacSha256.ComputeHash(dataToHmac));
}
String authorizationHeader = String.Format(
CultureInfo.InvariantCulture,
"{0} {1}:{2}",
"SharedKey",
Account,
signature);
return authorizationHeader;
}
}
Console:
Above executed code, date and signature which I copied and used in postman, and it worked successfully.
Postman:

Why a 400 response from Azure endpoint for Postman but works fine with the Alexa console?

I am new to developing Alexa skills so I am using a sample I found on the web as a C# endpoint hosted on Azure. It works correctly with the Alexa console but when I try to test the same endpoint with the Postman app, I get a 400 error.
When I use the Alexa console, it displays the JSON input that it sends to the endpoint and the JSON output that it receives from the endpoint. If I copy the JSON input and paste it into Postman and send it to the same endpoint, I get a 400 error. Obviously, I am missing something.
The following are my two source files and the JSON input.
RollTheDice.cs
public static class RollTheDice
{
[FunctionName("RollTheDice")]
public static async Task<HttpResponseMessage> Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = null)]HttpRequestMessage req, TraceWriter log)
{
log.Info("C# HTTP trigger function processed a request.");
var speechlet = new RollTheDiceSpeechlet();
return await speechlet.GetResponseAsync(req);
}
}
RollTheDiceSpeechlet.cs
public class RollTheDiceSpeechlet : SpeechletBase, ISpeechletWithContext
{
public SpeechletResponse OnIntent(IntentRequest intentRequest, Session session, Context context)
{
try
{
// Default to 6 sides if not specified
if (!int.TryParse(intentRequest.Intent.Slots["DiceType"].Value, out int numSides))
numSides = 6;
var rollResults = new Random().Next(Math.Max(1, numSides - 1)) + 1; // Account for random returning '0'
return new SpeechletResponse
{
ShouldEndSession = false,
OutputSpeech = new PlainTextOutputSpeech { Text = $"I rolled a {numSides} sided die and got a {rollResults}." }
};
}
catch (Exception ex)
{
return new SpeechletResponse
{
ShouldEndSession = false,
OutputSpeech = new PlainTextOutputSpeech { Text = ex.Message }
};
}
}
public SpeechletResponse OnLaunch(LaunchRequest launchRequest, Session session, Context context)
{
return new SpeechletResponse
{
ShouldEndSession = false,
OutputSpeech = new PlainTextOutputSpeech { Text = "Welcome to the Roll the Dice. Ask me to roll the dice." }
};
}
public void OnSessionEnded(SessionEndedRequest sessionEndedRequest, Session session, Context context)
{
return;
}
public void OnSessionStarted(SessionStartedRequest sessionStartedRequest, Session session, Context context)
{
return;
}
}
JSON Input
Again, everything works fine but when I test it with Postman I get a 404 error.
The endpoint is C# serverless function that I developed in Visual Studio 201.
When I run it locally, I copy/paste the URL in the Postman app and send a post. See attached screenshots.
As the error suggest you are missing Signature and SignatureCertChainUrl headers. These helps to protect your endpoint and verify that incoming requests were sent by Alexa. Any requests coming from other sources should be rejected. When you test it via Test Console these headers are included and you get successful response.
Headers:
Signature
SignatureCertChainUrl
There are two parts to validating incoming requests:
Check the request signature to verify the authenticity of the request.
Check the request timestamp to ensure that the request is not an old request.
More information on verifying that the request was sent by Alexa here

403 Forbidden error, while access the ClientRepresentation in keycloack

We use keycloak API in our application. When we try to retrieve the Client list of the realm it pass 403 forbidden error. Highly appreciate your comments for avoid this matter.
String authServer = UriUtils.getOrigin(httpRequest.getRequestURL().toString()) + AUTH_CONTEXT_PATH;
String token = httpRequest.getHeader("Authorization").replaceAll("Bearer ", "");
String realmClientsUrl = authServer+"/admin/realms/testrealm/clients/"+getClientRepresentationId(authServer,realm,token);
ClientRequest request = getClientRequest(realmClientsUrl,token);
ClientResponse<String> response;
ClientRepresentation clientRepresentation = null;
try{
response = request.get(String.class);
validateResponse(response,"CLIENT_REPRESENTATION");
clientRepresentation = response.getEntity(ClientRepresentation.class);
return clientRepresentation;
} catch (Exception e) {
e.printStackTrace();
}
Error which passed,
java.lang.Exception: ErrorStage:CLIENT_REPRESENTATION_ID,HTTP responseCode:403,StatusIno=Forbidden
You might get this error if the logged in user doesn't have the relevant Client Roles access. Add the client role access as "View Client" under realm-management.

Need Working Example of Nest REST API without using Firebase API

I'm struggling trying to find a working example of writing data to the Nest Thermostat API using plain rest. Attempting to write a C# app and cannot use Firebase. The multiple Curl examples posted so far do not work. I have a valid auth_token and can read data without issues. Finding the correct post url is elusive. Can anyone assist?
Examples like
curl -v -X PUT "https://developer-api.nest.com/structures/g-9y-2xkHpBh1MGkVaqXOGJiKOB9MkoW1hhYyQk2vAunCK8a731jbg?auth=<AUTH_TOKEN>" -H "Content-Type: application/json" -d '{"away":"away"}'
don't change any data.
Two things. First, follow redirects with -L. Second, put directly to the away data location, like
curl -v -L -X PUT "https://developer-api.nest.com/structures/g-9y-2xkHpBh1MGkVaqXOGJiKOB9MkoW1hhYyQk2vAunCK8a731jbg/away?auth=<AUTH_TOKEN>" -H "Content-Type: application/json" -d '"away"'
The PUT overwrites all data at a location. The previous command would logically be setting the structure's data to just {"away":"away"}.
user3791884,
Any luck with your C# PUT? Here is C# code that works:
using System.Net.Http;
private async void changeAway()
{
using (HttpClient http = new HttpClient())
{
string url = "https://developer-api.nest.com/structures/" + structure.structure_id + "/?auth=" + AccessToken;
StringContent content = new StringContent("{\"away\":\"home\"}"); // derived from HttpContent
HttpResponseMessage rep = await http.PutAsync(new Uri(url), content);
if (null != rep)
{
Debug.WriteLine("http.PutAsync2=" + rep.ToString());
}
}
}
Debug.WriteLine writes this to the Output window:
"http.PutAsync2=StatusCode: 200, ReasonPhrase: 'OK', Version: 1.1, Content: System.Net.Http.StreamContent, Headers:
{
Access-Control-Allow-Origin: *
Cache-Control: no-cache, max-age=0, private
Content-Length: 15
Content-Type: application/json; charset=UTF-8
}"
These two methods return a valid structure of my data.
1/ command line
curl -v -k -L -X GET "https://developer-api.nest.com/structures/Za6hCZpmt4g6mBTaaA96yuY87lzLtsucYjbxW_b_thAuJJ7oUOelKA/?auth=c.om2...AeiE"
2/ C#
private bool getStructureInfo()
{
bool success = false;
try
{
// Create a new HttpWebRequest Object to the devices URL.
HttpWebRequest myHttpWebRequest=(HttpWebRequest)WebRequest.Create("https://developer-api.nest.com/structures/?auth=" + AccessToken);
// Define the request access method.
myHttpWebRequest.Method = "GET";
myHttpWebRequest.MaximumAutomaticRedirections=3;
myHttpWebRequest.AllowAutoRedirect=true;
myHttpWebRequest.Credentials = CredentialCache.DefaultCredentials;
using(HttpWebResponse myHttpWebResponse = (HttpWebResponse)myHttpWebRequest.GetResponse())
{
if (null != myHttpWebResponse)
{
// Store the response.
Stream sData = myHttpWebResponse.GetResponseStream();
// Pipes the stream to a higher level stream reader with the required encoding format.
StreamReader readStream = new StreamReader (sData, Encoding.UTF8);
Debug.WriteLine("Response Structure stream received.");
string data = readStream.ReadToEnd();
Debug.WriteLine(data);
readStream.Close();
success = deserializeStructure(data);
}
}
}
catch (Exception ex)
{
Debug.WriteLine("getStructure Exception=" + ex.ToString());
}
return success;
}

Resources