Lambda Authorizer not returning proper error message with callback() in node.js - node.js

I have created a Lambda Authorizer on a AWS API Gateway, which calls a Lambda Function. Following is the code in the Lambda Function written in Node.js 8.0 code.
exports.handler = function(event, context, callback) {
var token = event.authorizationToken;
switch (token.toLowerCase()) {
case 'allow':
callback(null, generatePolicy('user', 'Allow', event.methodArn));
break;
case 'deny':
callback(null, generatePolicy('user', 'Deny', event.methodArn));
break;
case 'unauthorized':
callback("Unauthorized"); // Return a 401 Unauthorized response
break;
default:
callback("Error: Invalid token");
}
};
// Help function to generate an IAM policy
var generatePolicy = function(principalId, effect, resource) {
var authResponse = {};
authResponse.principalId = principalId;
if (effect && resource) {
var policyDocument = {};
policyDocument.Version = '2012-10-17';
policyDocument.Statement = [];
var statementOne = {};
statementOne.Action = 'execute-api:Invoke';
statementOne.Effect = effect;
statementOne.Resource = resource;
policyDocument.Statement[0] = statementOne;
authResponse.policyDocument = policyDocument;
}
// Optional output with custom properties of the String, Number or Boolean type.
authResponse.context = {
"stringKey": "stringval",
"numberKey": 123,
"booleanKey": true
};
return authResponse;
}
(The above sample code if from the web site https://markpollmann.com/lambda-authorizer/)
If I save and Test this function by passing an invalid value for authorizationToken, I get expected result which is below.
Response:
{
"errorMessage": "Error: Invalid token"
}
Request ID:
"e93567c0-fcbb-4cb1-b0b3-28e9c1b30162"
But If I call this API from Postman, by passing the value in the header, I get the following response. I am getting this error for any value in the header i.e, deny, allow, unauthorized, wrong etc.
{
"message": null
}
The status message in postman shows "500 Internal Server Error". Following is the detail from header section in postman.
content-length →16
content-type →application/json
date →Fri, 08 Mar 2019 14:07:57 GMT
status →500
x-amz-apigw-id →W89kFDRDoEFxYg=
x-amzn-errortype →AuthorizerConfigurationException
x-amzn-requestid →92f31d11-41ab-11e9-9c36-97d38d96f31b
I do not understand why the API is returning the above response and error message, while the Lambda test is working fine.
I have already gone through the following two threads in SO, but the answers/comments couldn't help in my case.
AWS API Gateway with custom authorizer returns AuthorizerConfigurationException
AWS API Gateway Custom Authorizer AuthorizerConfigurationException

I have understood the reason why I was getting message = null for the invalid input. The default block in the switch case was using parameter "Error: Invalid token" in the callback() method. API Gateway only identifies "Allow", "Deny" and "Unauthorized" as valid values. These values are also case sensitive. If any string values other than these values are passed to the callback() method, then API Gateway will return message=null to the client.

Related

Mobile app performing an HTTP Post to Azure Function using Bearer Token and Function Key returns Unauthorized

I'm using a mobile app and am receiving an Unauthorized response when attempting to post to an Azure Function and providing a function key.
Error:
StatusCode: 401, ReasonPhrase: 'Unauthorized'
Code:
let postToAsync (baseAddress:string) (resource:string) (payload:Object) =
async {
let tokenSource = new CancellationTokenSource(TimeSpan(0,0,30));
let token = tokenSource.Token;
try
let tokens = resource.Split("?code=")
let functionKey = tokens.[1]
use client = httpClient baseAddress
client.DefaultRequestHeaders.Add("x-functions-key", functionKey)
client.DefaultRequestHeaders.Accept.Add(MediaTypeWithQualityHeaderValue("application/json"))
let json = JsonConvert.SerializeObject(payload)
let content = new StringContent(json, Encoding.UTF8, "application/json")
let! response = client.PostAsync(resource.Replace($"?code={functionKey}",""), content, token) |> Async.AwaitTask
Debug.WriteLine $"\n\n{baseAddress}{resource}\nSuccess: {response.IsSuccessStatusCode}\n\n"
return response
with ex -> ...
} |> Async.StartAsTask
Note:
My Azure Function's AuthorizationLevel is set to Function.
I can call the function successfully when I publish it manually from Visual Studio.
However, when I deploy the function using Pulumi, I receive an Unauthorized response. I believe this is because Pulumi constrains me to add access policies for each Function App.
Versioning:
<TargetFramework>net6.0</TargetFramework>
<AzureFunctionsVersion>v4</AzureFunctionsVersion>
oauth2/v2.0:
I think the following link provides a clue to why I'm observing the issue. However, I still don't know how to resolve it.
Connectivity
I launched Log Stream and observed that the URL is correct:
Access Control:
Please note that the difference between the Function App that I created without using Pulumi, which lets me post successfully, versus the Function App that was generated using Pulumi, is an Access Policy per Function App with Pulumi.
public static class AccessPolicies
{
public static void Build(string policyName, string functionName, Dictionary<string, CustomResource> registry)
{
var resourceGroup = registry[nameof(ResourceGroup)] as ResourceGroup;
var keyVault = registry[nameof(KeyVault)] as KeyVault;
var functionApp = registry[functionName] as FunctionApp;
var result = new AccessPolicy(policyName, new AccessPolicyArgs {
KeyVaultId = keyVault.Id,
TenantId = TenantId.Value,
ObjectId = functionApp.Identity.Apply(v => v.PrincipalId ?? "11111111-1111-1111-1111-111111111111"),
KeyPermissions = new[] { "Get", },
SecretPermissions = new[] { "Get", },
});
registry.Add($"{policyName}-{functionName}", result);
}
}
}
I tried to reproduce the same in my environment via Postman and got below results:
I have one function app with http function named srifunction like below:
I generated one bearer token with same scope as you like below:
POST https://login.microsoftonline.com/<tenantID>/oauth2/v2.0/token
grant_type:client_credentials
client_id: <appID>
client_secret: <secret_value>
scope: https://management.azure.com/.default
Response:
When I used the above token to call function, I got 401 Unauthorized error same as you like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
Authorization: Bearer <token>
If you pass function key in token value, you will still get 401 Unauthorized error like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
Authorization: Bearer <function key>
To call function using function key, you need to include key value
in x-functions-key header instead of Bearer token.
When I included the above header, I am able to call the function successfully like below:
POST https://<funcappName>.azurewebsites.net/api/<function_name>
x-functions-key: <function key>

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

Xero API 403 - AuthenticationUnsuccessful error

I am using the Xero Api with Nodejs and the xero-node library.
I have completed the oAuth flow and saved the token to the database. The issue i am now having is continually getting a 403 forbidden error when attempting to get anything from Xero be that Contacts, Accounts or Users. Sample code is below
I can get tenants ok without an issue however anything else doesn't work. I have checked the scopes to make sure when I am setting up the client they are correct which they are.
var getStuff = async(tokenSet) => {
await xero.setTokenSet(tokenSet);
const tenants = await xero.updateTenants();
const xeroTenantId = tenants[0].id // {String} Xero identifier for Tenant
const ifModifiedSince = new Date("2020-02-06T12:17:43.202-08:00");
const where = 'IsSubscriber==true'; // {String} Filter by an any element
const order = 'LastName ASC'; // {String} Order by an any element
console.log(tenants);
try {
const response = await xero.accountingApi.getUsers(xeroTenantId, ifModifiedSince, where, order);
console.log(response.body || response.response.statusCode)
}
catch (err) {
/// console.log(err);
console.log(`There was an ERROR! \n Status Code: ${err.response.statusCode}.`);
console.log(`ERROR: \n ${JSON.stringify(err.response.body, null, 2)}`);
}
}
Which scopes have been added to the access token you are passing through? You can decode your token here https://jwt.io/
Also - you need to pass the ‘tenant.tenantId’ to the function. I believe the tenant.id actually relates to the connection id which is coming back from the /connections endpoint.
My hunch is that is the issue. Also curious how that’s working, as updateTenants() should have the empty function call on the end. Is that working for you?

Azure DevOps REST API call to Accounts-endpoint retrieves TF400813 error

I'm trying to read all accounts my user is associated with. The Documentation claims that this should be possible by calling:
GET https://app.vssps.visualstudio.com/_apis/accounts?api-version=5.1
Because the docs are kind of confusing it could be that I have to call
GET https://app.vssps.visualstudio.com/_apis/accounts?ownerId={GUID}&api-version=5.1
instead.
I'm using OAuth-Authentication. In order to get it work I created an ASP.NET Core application. I created an app registration in DevOps and I retrieve an OAuth token with the following scopes without any problem:
vso.auditlog
vso.connected_server
vso.dashboards
vso.entitlements
vso.environment_manage
vso.graph
vso.identity
vso.loadtest
vso.machinegroup_manage
vso.memberentitlementmanagement
vso.profile
vso.project
vso.securefiles_read
vso.security_manage
vso.taskgroups_read
vso.tokenadministration
vso.tokens
vso.variablegroups_read
vso.wiki
According to the docs only vso.profile should be necessary for this request.
However the result I receive is always:
HttpRequestException: Response status code does not indicate success: 401 (TF400813: The user '{AZURE_TENANT_ID}\{MY_MAIL_ADDRESS}' is not authorized to access this resource.).
Other requests are working just fine e.g.:
GET https://dev.azure.com/{organization}/{project}/_apis/build/builds?api-version=5.1
The accounts-request is special because it can be sent without setting the context to a specific organisation or project. I guess this the reason for the different results.
EDIT
After trying ot today using the both URLs mentioned above I now get 400 as the response status code. This is a sample Bearer-Token I got after I decoded it:
{
"nameid": "1340eb0b-cabf-476c-a950-a070c34ca367",
"scp": "vso.profile",
"aui": "a2d8bdf0-9406-415a-aa79-bee9e2600c37",
"appid": "e1bea2a2-****-****-****-************",
"iss": "app.vstoken.visualstudio.com",
"aud": "app.vstoken.visualstudio.com",
"nbf": 1587395030,
"exp": 1587398630
}
Here is some simplified C# code I use:
HttpClient client = _clientFactory.CreateClient("DevOps");
var token = await _authHelper.GetTokenAsync(tokenType);
client.DefaultRequestHeaders.Add("Authorization", $"Bearer { token.AccessToken}");
var uri = "https://app.vssps.visualstudio.com/_apis/accounts?api-version = 5.1";
try
{
var response = await client.GetAsync(uri);
response.EnsureSuccessStatusCode();
var content = await response.Content.ReadAsStringAsync();
return content;
}
catch (Exception ex)
{
// ex.Message = Response status code does not indicate success: 400 (Bad Request).
// ...
throw;
}

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

Resources