How to specify authorizer custom error codes when using serverless - serverless

I have an authorizer for my api. It returns a 401 status code on failed authorization. I want to return custom status code (410) as I am using 401 else where in the application. I want a different behavior on client side when it gets a status code of 410
functions:
authorizerFunc:
handler: authorize.handler
# Roles API
getRoles:
handler: allrolesget.handler
events:
- http:
path: /
method: get
cors: true
integration: lambda
authorizer:
name: authorizerFunc
resultTtlInSeconds: 0
identitySource: method.request.header.Authorization

You can't change status code of custom authorizer. What you can do - remove custom authorizer and make custom authorization in lambda - there you have full control of what status code should be returned.

Related

Create AWS Policies-serverless framework

I am trying to create policies using serverless framework. The idea is to access S3 services, depending on the user's company.
I tried to deploy my serverless.yaml with the policy:
- PolicyName: IAM_AWS_S3
PolicyDocument:
Version: "2012-10-17"
Statement:
- Effect: Allow
Action: '*'
Resource:
- !Sub 'arn:aws:s3:${AWS::AccountId}-${aws:PrincipalTag/company}'
- !Sub 'arn:aws:s3:${AWS::AccountId}-${aws:PrincipalTag/company}/*'
but I get this error:
CREATE_FAILED: AuthenticatedRole (AWS::IAM::Role) The policy failed
legacy parsing (Service: AmazonIdentityManagement; Status Code: 400;
Error Code: MalformedPolicyDocument; Request ID:
da38iiii; Proxy: null)
So, here is my question, is it possible to create a policy before I have a user? can PrincipalTag/company be null?
Thanks in advance
It is not possible to use PropertyTag for this issue due to I needed to use it in DynamoDB too.
I just create the policies through a Lambda.
Take into account these answers:
IAM Policy with `aws:ResourceTag` not supported
Use tags inside IAM policy resource

Ballerina Oauth2 authenticated endpoint returning a 406

I am trying to call a 3rd party service that uses Oauth2 Password Credentials to get an authentication token. Ballerina is returning the following messages.
2020-04-23 15:07:35,414 ERROR [ballerina/oauth2] - Received an invalid response with status-code: 406; and payload: {"fault":{"faultstring":"Raising fault. Fault name : RF.Raise-406-Exception","detail":{"errorcode":"steps.raisefault.RaiseFault"}}}
2020-04-23 15:07:35,418 ERROR [ballerina/oauth2] - Failed to generate OAuth2 token. : error {ballerina/oauth2}Error message=Received an invalid response with status-code: 406; and payload: {"fault":{"faultstring":"Raising fault. Fault name : RF.Raise-406-Exception","detail":{"errorcode":"steps.raisefault.RaiseFault"}}}
error {ballerina/http}AuthenticationFailed message=Failed to prepare request at bearer auth handler. cause=error {ballerina/auth}Error message=Failed to generate OAuth2 token. cause=error {ballerina/oauth2}Error message=Received an invalid response with status-code: 406; and payload: {"fault":{"faultstring":"Raising fault. Fault name : RF.Raise-406-Exception","detail":{"errorcode":"steps.raisefault.RaiseFault"}}}
It's the 406 code that is confusing me as I have set both the content type & accept headers to "application/json" which is what the service requires.
However, the second message says "Failed to generate OAuth2 token" so could it be the call to get the oauth token that is returning the 406? If so how do I set the accept header on the token service call?
Using Ballerina I have called the token endpoint and successfully got a token but if I try to call a service using a PasswordGrantConfig those are the errors I get. I've tried everything I can think of and have successfully got other services using ClientCredentialsGrantConfig to work.
Any help gratefully received.
The relevant code is below. The three sections below are parts of the code in 3 different .bal files.
// configure the Oauth2 Config
import ballerina/config;
import ballerina/http;
import ballerina/oauth2;
public function getOauth2Handler() returns http:BearerAuthHandler {
oauth2:PasswordGrantConfig passwordGrantConfig = {
tokenUrl: config:getAsString("experian.authentication.tokenUrl"),
username: config:getAsString("experian.authentication.username"),
password: config:getAsString("experian.authentication.password"),
clientId: config:getAsString("experian.authentication.clientId"),
clientSecret: config:getAsString("experian.authentication.clientSecret"),
credentialBearer: http:AUTH_HEADER_BEARER
};
oauth2:OutboundOAuth2Provider oauth2Provider = new (passwordGrantConfig);
return new (oauth2Provider);
}
// Configure the API Client
http:ClientConfiguration delphiSelectClientConfig = {
auth: {
authHandler: experian:getOauth2Handler()
}
};
experian:DelphiSelectClientConfig delphiSelectConfig = {
serviceUrl: config:getAsString("experian.services.delphi-select.serviceUrl"),
clientConfig: delphiSelectClientConfig
};
experian:DelphiSelectClient delphiSelectClient = new (delphiSelectConfig);
// Call the endpoint using the Oath2 configuration
import ballerina/http;
import ballerina/io;
public type DelphiSelectClientConfig record {
string serviceUrl;
http:ClientConfiguration clientConfig;
};
//==============================
//============Client============
//==============================
public type DelphiSelectClient client object {
public http:Client clientEp;
public http:ClientConfiguration config;
public function __init(DelphiSelectClientConfig config) {
http:Client httpEp = new (config.serviceUrl, {auth: config.clientConfig.auth});
self.clientEp = httpEp;
self.config = config.clientConfig;
}
public remote function newApplication() returns #untainted json|error {
io:println("In newApplication function");
http:Request request = new;
json requestBody = newApplicationBody; // get test data from json in another file
request.setJsonPayload(requestBody);
var response = check self.clientEp->post("/application", request);
var payload = check response.getJsonPayload();
return payload;
}
};
I have also modified my test code to call the token EP and deliberately set accept to an unacceptable value, for example, "text/csv". In this case I get the same error response. However setting accept to "*/*" does work. Final test; accept of "" (empty) also fails so I suspect that the BearerAuthHandler is not setting any value for accept.
So can I force the BearerAuthHandler to set an accept of "application/json"?
Thanks.
See picture below.
Also, the example in the Oath2 spec you referenced shows a content-type value being set. Even a value of “*/*” would work but I suspect Ballerina leaves it blank.
I have raised the GitHub issue Need to be able to set http header values for OutboundOAuth2Provider
The main objective of http:OutboundAuthHandler objects are to prepare the http:Request with authentication information that needs to be authenticated with external endpoint you are calling to.
The http:BearerAuthHandler is responsible for adding Authorization header with the value of Bearer <token>. "token" is prepared with the provided information. So, there is no option to force http:BearerAuthHandler to set any header for the request.
But in this case, if the API successfully respond if there is Accept header with the value of application/json, you can simply add that header to the http:Request before calling the POST request as follow:
request.addHeader("Accept", "application/json");

Protect API can be accessed by Ajex without Login to Active Directory

I followed this example (https://learn.microsoft.com/en-us/azure/api-management/api-management-howto-protect-backend-with-aad) and selected "OAuth 2.0" under "Setting > User authorization" for the Echo API.
Then, I created an html file with and trigger an ajax call to the Echo API "https://{myapi}.azure-api.net/echo/resource-cached?param1=sample"
I assume that I should received an error something like access denied or no authorization because this API is supposed to be protected by my OAuth Server (according to the example, it is my AD). However, this ajax call return Status code : 200. I don't need to login my AD but can consume the API.
$(document).ready(function(){
$("button").click(function(){
$.ajax({
url: "https://{myapi}.azure-api.net/echo/resource?param1=sample"
,headers: {
'Cache-Control':'no-cache',
'Ocp-Apim-Trace':'true',
'Ocp-Apim-Subscription-Key':'xxxxxxxxxxxxxxxxxxxxxx'
}
,success: function(result){
$("#div1").html(JSON.stringify(result));
}
,error : function(result) {
alert(JSON.stringify(result));
}
});
});
});
I have created another API named "demo" manually with mock response. This API returns an json {'sampleField' : 'test'}. My Ajex call receives the json without issu even I have applied "OAuth 2.0" as "User Authorization" in that API.
Is there anything I missed or I misunderstood how it works?
Finally, JWT is required to block it.

Testing Doorkeeper oAuth2 for Zapier App

Question
How would we test a Doorkeeper oauth2 implementation for a Zapier cli app?
Background
I have a Rails 3 app. I am trying to create a Zapier client for the application and I decided to use OAuth. Thus I configured doorkeeper to generate a JWT. All looks good, I am able to authorize and get token using the redirects.
I am not sure how to test the app purely through the console. Wouldn't it require some way to authorize using username/password?
I got an app generated from the template with some minor differences.
it('can fetch an access token', (done) => {
const bundle = {
inputData: {
// In production, Zapier passes along whatever code your API set in the query params when it redirects
// the user's browser to the `redirect_uri`
code: 'one_time_code',
subdomain: 'ducks'
},
environment: {
CLIENT_ID: process.env.CLIENT_ID,
CLIENT_SECRET: process.env.CLIENT_SECRET
}
};
appTester(App.authentication.oauth2Config.getAccessToken, bundle)
.then((result) => {
result.access_token.should.eql('a_token');
result.refresh_token.should.eql('a_refresh_token');
done();
})
.catch(done);
});
results in something like this:
1) oauth2 app can fetch an access token:
Got 401 calling POST https://ducks.<domain>.com/oauth/token, triggering auth refresh.
What happened:
Starting POST request to https://ducks.<domain>.com/oauth/token
Received 401 code from https://ducks.<domain>.com/oauth/token after 1425ms
Received content "{"error":"invalid_request","error_description":"The request is missing a required parameter, include"
Got 401 calling POST https://ducks.<domain>.com/oauth/token, triggering auth refresh.
Which should be because the user is not logged in the request made in the test console...
How can I make the user login? Or should the tests be changed?

Microsoft Graph API access token validation failure

I use this URL to get id_token:
https://login.microsoftonline.com/common/oauth2/authorize?
response_type=id_token%20code&
client_id=MY_CLIENT_GUID_ID_IN_HERE&
redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fauth%2Fopenid%2Freturn&nonce=alfaYYCTxBK8oypM&
state=6DnAi0%2FICAWaH14e
and this return result like this
http://localhost:3000/auth/openid/return?
code=AAA_code_in_here&
id_token=eyJ0eXAi_xxxx_yyyy_in_here&
state=6DnAi0%2FICAWaH14e&
session_state=xxxx_guid_xxxxx
and then i use the id_token to query Graph (use POST man)
i have see this post InvalidAuthenticationToken and CompactToken issues - Microsoft Graph using PHP Curl but make no sense.
OATH 2.0 requires multiple steps. The first request returns an OAUTH Code. The next step is converting that OATUH code into a Bearer Token. This is the step you are missing here.
I would also recommend using the v2 Endpoint which is a lot easier to work with (particularly with Graph). I wrote a v2 Endpoint Primer that walks through the process and may be helpful as well.
You can't use the token directly, there is one more step to exchange the code you get from the response url into token.
Here is my C# code (using Microsoft.IdentityModel.Clients.ActiveDirectory)
public static AuthenticationResult ExchangeCodeForToken(string InTenantName, string InUserObjId, string InRedirectUri, string InApplicationAzureClientID, string InApplicationAzureClientAppKey)
{
Check.Require(!string.IsNullOrEmpty(InTenantName), "InTenantName must be provided");
Check.Require(!string.IsNullOrEmpty(InUserObjId), "InUserObjId must be provided");
if (CanCompleteSignIn) //redirect from sign-in
{
var clientCredential = new ClientCredential(InApplicationAzureClientID, InApplicationAzureClientAppKey);
var authContext = new AuthenticationContext(Globals.GetLoginAuthority(InTenantName), (TokenCache)new ADALTokenCache(InUserObjId)); //Login Authority is https://login.microsoftonline.com/TenantName
return authContext.AcquireTokenByAuthorizationCode(VerificationCode, new Uri(InRedirectUri), clientCredential, Globals.AZURE_GRAPH_API_RESOURCE_ID); //RESOURCE_ID is "https://graph.microsoft.com/"
}
return null;
}
I had this issue today when I was playing with graph API, the problem in my case was how I was generating the token.
I used postman for generating the token wherein the Auth URL section I was adding the resource = client_id whereas it should be the graph URL. After making that change I was able to make the call via postman.
In order for the above to work, please make sure your application in Azure has delegated permissions to access the Graph API.
To receive the access token and use it for profile requests, you don't need anything from server-side, you can implement the oAuth2 just from the client side.
Use the following URL for login:
https://login.microsoftonline.com/common/oauth2/authorize?client_id=YOUR_CLIENT_ID&resource=https://graph.microsoft.com&response_type=token&redirect_uri=YOUR_REDIRECT_URI&scope=User.ReadBasic.All
After successful login, user will redirected to the page with access_token parameter. Then use the following AJAX call to fetch user info:
var token = login_window.location.href.split('access_token=').pop().split('&')[0];
$.ajax({
url: "https://graph.microsoft.com/v1.0/me",
type: "GET",
beforeSend: function(xhr){xhr.setRequestHeader('Authorization', 'Bearer '+token);},
success: function(data) {
alert('Hi '+data.displayName);
console.log(data);
}
});
Note that you may need to enable oauth2AllowImplicitFlow:true setting from your Azure Active Directory application manifest file.
Set "oauth2AllowImplicitFlow": false to "oauth2AllowImplicitFlow": true.
Lastly, ensure that your app has required permissions for Microsoft Graph which are sign in users and View users' basic profile
An updated answer to get access with new applications:
Register your app in the app registration portal.
Authorization request example:
https://login.microsoftonline.com/{tenant}/oauth2/v2.0/authorize?client_id=6731de76-14a6-49ae-97bc-6eba6914391e&response_type=code&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F&response_mode=query&scope=offline_access%20user.read%20mail.read&state=12345
Authorization response will look like this:
https://localhost/myapp/?code=M0ab92efe-b6fd-df08-87dc-2c6500a7f84d&state=12345
Get a token
POST /{tenant}/oauth2/v2.0/token HTTP/1.1
Host: https://login.microsoftonline.com
Content-Type: application/x-www-form-urlencoded
client_id=6731de76-14a6-49ae-97bc-6eba6914391e
&scope=user.read%20mail.read
&code=OAAABAAAAiL9Kn2Z27UubvWFPbm0gLWQJVzCTE9UkP3pSx1aXxUjq3n8b2JRLk4OxVXr...
&redirect_uri=http%3A%2F%2Flocalhost%2Fmyapp%2F
&grant_type=authorization_code
&client_secret=JqQX2PNo9bpM0uEihUPzyrh // NOTE: Only required for web apps
Use the access token to call Microsoft Graph
GET https://graph.microsoft.com/v1.0/me
Authorization: Bearer eyJ0eXAiO ... 0X2tnSQLEANnSPHY0gKcgw
Host: graph.microsoft.com
Source:
https://learn.microsoft.com/en-us/graph/auth-v2-user?context=graph/api/1.0
You can also get an access token without a user, see here:
https://learn.microsoft.com/en-us/graph/auth-v2-service

Resources