How to access secured backend API using API Gateway? - amazon-web-services

I have created a backend spring-boot REST api which is deployed on the EC2 that is authenticated with JWT. So, I first curl to get the Bearer token:
curl -iH "Content-Type: application/json" -X POST -d '{"username":"myusername", "password":"mypassword"}' http://123.45.6782.910:8080/login
Then make the REST call to access my REST resource
curl -H "Authorization: eyJhbGzd9.NYHXPv-vXUIoNr7qtA" http://123.45.6782.910:8080/categories/pets/
This all works fine.
Now, I want to use API Gateway to access the /categories/pets/ Resource.
I have setup GET - Method execution's Method request, Integration Request sections. But, when I try to Test the setup, I get 403.
{
"timestamp": 1498392625274,
"status": 403,
"error": "Forbidden",
"message": "Access Denied",
"path": "/categories/pets/"
}
I think this is expected because I am directly trying to access the backend api without the bearer token. I want to know how can I do the POST on http://123.45.6782.910:8080/login to get the Bearer token and then make the call to /categories/pets/ ?
UPDATE: As per #KaHouIeong suggestion, I created a POST endpoint /login on the API gateway to get the bearer token, When I test is in the test console in the API Gateway, I am getting the Authorization →Bearer eyJhbGzd9 but when I try it from postman, I am getting the status 200 OK but not the Authorization →Bearer eyJhbGzd9 token.
content-length →0
content-type →application/json
status →200
via →1.1 swfbfbbaf3fb6c32bdccb152354539e473d.cloudfront.net (CloudFront)
x-amz-cf-id →K9V3XUxHOretrza0kCM5dk_G5eZgePrtrBziyVTxptrePD7wjsWqk-l0kCQQ==
x-amzn-requestid →5ac81024-5c27-11e7-af9a-9f3c8494c542
x-amzn-trace-id →Root=1-5953e77f-ed76d15b5bfre9374c9

Your client should send a request to /login and get the bearer token from the response, then re-use the token to access your /categories/pets/ API.
The workflow should be same as you hit your EC2 backend directly.
For your API setup on API Gateway, you need to setup a /login resource and point to the http://123.45.6782.910:8080/login
Then, you need to setup the integration response header mapping to map the Authorization header back to the method response, then API Gateway will pass through the header to the client.
integration.response.header.Authorization -> method.response.header.Authorization

Related

unable to retrieve the applications and unable to create the subscription for the API

I am using wso2 API manager 3.1.0
I am using REST APIs for creating the subscriptions for the application. I am using the REST API
https://x.x.x.x:9443/apis/api/am/store/v1.0/subscriptions
I am using basic auth for the authentication of the API. My request body is as follows.
{
"applicationId": "6451faaa-65aa-48dc-8655-2ffb623fc441",
"apiId": "ed94f936-7e8a-4c62-92e6-92991aca4348",
"throttlingPolicy": "Unlimited"
}
When I send this request I am getting the 200 OK response but the content of the 200 OK is the management console login page. This is one issue I am facing. Another issue is a very basic one- I am trying to get the applications created in the system for that I am using below API
https://x.x.x.x:9443/api/am/store/v1.0/applications?limit=25&offset=0
again with basic authorization. for this request I am getting the 404 response. Same response I get if I change the authentication to Oauth token.
I tried these APIs with multiple tenant users credentials but for all I am getting the same response.
UPDATE:
I have obtained the access token like below:
1. curl --insecure -X POST -H "Authorization: Basic YWRtaW46YWRtaW4=" -H "Content-Type: application/json" -d #payload.json https://localhost:9443/client-registration/v0.16/register
I got the response
{"clientId":"MPPomfxrQ_ZReRlHz0x70pU6yLca","clientName":"rest_api_store","callBackURL":"www.google.lk","clientSecret":"dQrYXtwMG8fB4Qhu7NfC1uqLAkwa","isSaasApplication":true,"appOwner":"admin","jsonString":"{\"grant_types\":\"password refresh_token\",\"redirect_uris\":\"www.google.lk\",\"client_name\":\"rest_api_store\"}","jsonAppAttribute":"{}","tokenType":null}
2. curl -k -d "grant_type=password&username=admin&password=admin&scope=apim:subscribe" -H "Authorization: Basic TVBQb21meHJRX1pSZVJsSHoweDcwcFU2eUxjYTpkUXJZWHR3TUc4ZkI0UWh1N05mQzF1cUxBa3dh" https://10.57.8.36:9443/oauth2/token
and I got the response
{"access_token":"194d3a3c-8f37-3459-a909-f1f871f096dc","refresh_token":"be7bfcb5-970d-3a8b-8f0a-d69fb7ea53fb","scope":"apim:subscribe","token_type":"Bearer","expires_in":3600}
using the obtained token if I place the API request I get still 404

Using nested authentication with Google IAP

I deployed a 3rd party webapp which uses basic authentication for access on Google Cloud Run. I additionally wanted to protect the endpoint by allowing only Google-authenticated users of the organization access. Both these methods use the Authorization header of the request and I cannot get it to work.
I tried following this post, providing both credentials in one field. My reasoning was, that GCP should select the strongest credential mechanism it supports - bearer - and leave the basic credentials for the webap. I have no idea if the webapp could have dealt with it because Google's reverse proxy already barred my access.
curl "-HAuthorization: bearer ${bearer_token}, basic ${base64_userpw}" https://my-google-endpoint.com
-> 401 Unauthorized
I also tried Proxy-Authorization with no different result.
curl "-HProxy-Authorization: bearer ${bearer_token}" "-HAuthorization: basic ${base64_userpw}" https://my-google-endpoint.com
Is there a way to get nested authentication to work with Google's reverse proxy? I was able to get past the reversed proxy by only supplying the bearer but naturally hit the wall at the service. With deactivated authentication on proxy side I was able to authenticate with the service using the basic credentials.
P.S.:
I am not using a browser to access the webapp but command line tools.
You cannot mix Authorization mechanisms with IAP. Everything after the bearer keyword is considered the token string.
One solution is to change your Basic Authorization HTTP header from using Authorization to a custom HTTP header. Something like X-MyApp-Authorization. Then your app processes the custom header to handle the Basic Authorization mechanism.
[Update 2021-08-17]
My original answer is partially wrong. Google's solution is currently broken.
Cloud Run is behind Google Cloud IAP. The client making a request can use two HTTP Authorization headers:
Authorization: <application authorization token>
Proxy-Authorization: Bearer <IDENTITY_TOKEN>
If a valid ID token is found in a Proxy-Authorization header, IAP authorizes the request with it. After authorizing the request, IAP passes the Authorization header to your application without processing the content.
Authenticating from Proxy-Authorization Header
This means the OP was on the right track using the Proxy-Authorization header. However, this feature does not work.
Create an Identity Token:
Use curl to verify that the token works with a Cloud Run endpoint that requires the Invoker role:
curl -H "Authorization: Bearer $TOKEN" $endpoint
That works. Now try the Proxy-Authorization header:
curl -H "Proxy-Authorization: Bearer $TOKEN" $endpoint
That fails with a 403.
Now try both headers:
curl -H "Proxy-Authorization: Bearer $TOKEN" -H "Authorization: Bearer $ANOTHER_TOKEN" $endpoint
That fails with 401 "The access token could not be verified"
gcloud auth print-identity-token
I am using documented methods to use two authorization headers, but this feature does not work.
The PHP SDK did not have the proxy-authorization header support added until June 25, 2021. I created a test application from Google's example. That also failed with the same errors.
June 25, 2021 Patch
Does it happen to work if you send two Authorization headers, like curl -H "Authorization: bearer foo" -H "Authorization: basic bar" ?
--Matthew, Google Cloud IAP engineering

Introspection endpoint is not getting called

I am looking to create an API using the Django REST Framework which will authenticate using a separate authentication server by means of its introspection endpoint. The authorization flow should look something like the following.
The client provides either user credentials or a refresh token to the token endpoint on our authentication server.
If the provided credentials or refresh token are valid, the authentication server responds with an access token and a refresh token.
The client then sends the access token to the API when requesting a resource.
The API verifies the provided access token using the introspection endpoint on our authentication server.
The authentication server responds letting the API know if the access token is valid.
If the access token is valid, the API responds to the client with the requested resources.
Step 4 is the part I'm after, and the Django OAuth Toolkit looks like it provides an option for exactly this. In the section about setting up a separate resource server it states that it allows the application to verify access tokens by use of an introspection endpoint.
So I followed the setup for the Django OAuth Toolkit, and pointed the RESOURCE_SERVER_INTROSPECTION_URL toward the introspection endpoint on our authentication server. Then I acquired an access token from our authentication server and provided it to the API as an Authorization header, but I get the following response.
Content-Type: application/json
WWW-Authenticate: Bearer realm="api",error="invalid_token",error_description="The access token is invalid."
Vary: Accept
Allow: GET, HEAD, OPTIONS
Content-Length: 58
{
"detail": "Authentication credentials were not provided."
}
If I don't provide a token I get the same response body, but no WWW-Authenticate header. The strange part is that the introspection endpoint never receives a POST request, which it should be sending to verify the access token.
So did I misread the documentation, or am I doing something wrong? Why isn't this working as I expect?

Cannot authenticate into google datastore through REST Api

I tried to access Google's Datastore through their REST Api. It says that they allow authentication through the API-key. However it doesn't seems that I can get it to work any where. I copied the snippet generated from their Try this API page.
curl --request POST \
'https://datastore.googleapis.com/v1/projects/PROJECT_ID:runQuery?key=[YOUR_API_KEY]' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--data '{"query":{"filter":{"compositeFilter":{"op":"AND","filters":[{"propertyFilter":{"property":{"name":"id"},"op":"EQUAL","value":{"stringValue":"ID"}}}]}},"kind":[{"name":"NAME"}]},"partitionId":{"namespaceId":"NAMESPACE_ID","projectId":"PROJECT_ID"}}' \
--compressed
But it keeps returning me an 401 error.
{
"error": {
"code": 401,
"message": "Request is missing required authentication credential. Expected OAuth 2 access token, login cookie or other valid authentication credential. See https://developers.google.com/identity/sign-in/web/devconsole-project.",
"status": "UNAUTHENTICATED"
}
}
It looks like it require me to use OAuth instead, which is not what their documentation says. Anyone experienced something similar?
You are using an API key which is incorrect.
This link details which services support API Keys. Cloud Datastore is not one of them.
Using API Keys
You want to use an Access Token which is derived from Service Account credentials.
Review this document.
Using OAuth 2.0
The steps to generate an Access Token:
Load the service account credentials json file.
Extract the client_email, private_key and private_key_id.
Create a json payload.
Call the authorization URL: https://www.googleapis.com/oauth2/v4/token
This returns a json object. Extract the access_token.
Use the access_token instead of an API Key.
There are examples on the Internet in various languages. The link will get you started. The process appears complicated, and it is, but once you understand it, generating Access Tokens is easy and they can be reused until they expire (typically 60 minutes which you control).
This document on Google Cloud Storage authentication is the same for Cloud Datastore. The key is understanding "Authorization: Bearer" which is a header you need to include with your curl request.
Authentication

Why can I invoke my API Gateway endpoint from terminal but not from Braintree's console?

I have an API Gateway that invokes a lambda function. I deployed the gateway and am succesfully invoking the function from my terminal by sending a POST request to the endpoint. I do this like so:
curl -H "Content-Type: application/json" -X POST -d "{\"bt_signature\":\"curl\",\"bt_payload\":\"abcsdsddsd\"}" https://*myendpointurl*
When I enter this endpoint in my Braintree console though as a webhook url though and click "check url" i get the following error pop up:
Last delivery unsuccessful. Invalid server response 400
It is weird that it works when I invoke it from my terminal but not here. Futhermore when i enter my endpoint for the method in my web browser it turns up with this error message:
{"message":"Missing Authentication Token"}
I have double checked I am using the write endpoint for this specific POST method. It's just weird that it works from terminal but not as a webhook url in braintree. Ideas?
The reason you get "Missing Authentication Token" when hitting from the browser is because the browser will make a GET request, whereas your API is setup for POST method. API Gateway will respond with a "Missing Authentication Token" when you make requests for non-existent resources/methods. (It's possible Braintree is also generating a GET request internally, but I would recommend you check with their support for confirmation.)
Hope this helps,
Ritisha.