403 Forbidden when accessing Kroger API from Azure-Hosted .Net Core 3.1 app - azure

Problem: I need to determine the nature of this failure so I can know how to further troubleshoot. I have come up with a few hypothesis:
- could be a firewall/proxy configuration within Azure
- could be a misconfiguration with Kroger's API
- could be a public certificate rejection from Azure app
- could be something totally unrelated to any of above
Details: I’m attempting to connect to Kroger's developer API. The following code has been simplified for this post. (I have been using IHttpClientFactory previously to generate my HTTPClient). This works locally, but once it’s deployed to an Azure web service, I am presented with a 403 message (which appears to be coming from Azure and not the external API):
You don't have permission to access http://api.kroger.com on this server.
This same code works within Azure for other 3rd party APIs over HTTPS, so the suspicion I have had is that this error comes from not using correct client certificates, so Azure attempts to call over http?
I have tried many things to resolve this, including uploading a public certificate that I downloaded from https://api.kroger.com to my azure app service. It appears to pull the certificate correctly, but the request still fails with the same message.
Relevant code is below (Without using client certificate):
using(var client = _requestFactory.CreateClient()))
{
client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("basic", _clientId);
client.DefaultRequestHeaders.Add("Accept", "application/json");
var newRequest = new Dictionary<string, string>
{
{ "grant_type", "client_credentials" },
{ "scope", "product.compact" }
};
var response = await client.PostAsync($"https://api.kroger.com/v1/connect/oauth2/token", new FormUrlEncodedContent(newRequest));
return Ok(await response.Content.ReadAsStringAsync());
}
Below is the full response from the server.
{
"version": "1.1",
"content": {
"headers": [{
"key": "Content-Type",
"value": ["text/html"]
}, {
"key": "Content-Length",
"value": ["299"]
}, {
"key": "Expires",
"value": ["Sat, 08 Feb 2020 19:18:55 GMT"]
}]
},
"statusCode": 403,
"reasonPhrase": "Forbidden",
"headers": [{
"key": "Server",
"value": ["AkamaiGHost"]
}, {
"key": "Mime-Version",
"value": ["1.0"]
}, {
"key": "Date",
"value": ["Sat, 08 Feb 2020 19:18:55 GMT"]
}, {
"key": "Connection",
"value": ["close"]
}, {
"key": "Set-Cookie",
"value": ["akaalb_Digital_ALB_API=~op=KT_Digital_API_KCVG_F5:api-kcvg|~rv=47~m=api-kcvg:0|~os=75b4a9ec926d2a9e67035451773cec6c~id=63ba4b3e2a027e4d53b693e2fded5ac3; path=/; HttpOnly; Secure; SameSite=None"]
}],
"trailingHeaders": [],
"requestMessage": {
"version": "1.1",
"content": {
"headers": [{
"key": "Content-Type",
"value": ["application/x-www-form-urlencoded"]
}, {
"key": "Content-Length",
"value": ["51"]
}]
},
"method": {
"method": "POST"
},
"requestUri": "https://api.kroger.com/v1/connect/oauth2/token",
"headers": [{
"key": "Authorization",
"value": ["basic {removed}"]
}, {
"key": "Accept",
"value": ["application/json"]
}, {
"key": "Request-Context",
"value": ["appId={removed}"]
}, {
"key": "Request-Id",
"value": ["|{removed}"]
}, {
"key": "traceparent",
"value": ["{removed}"]
}],
"properties": {}
},
"isSuccessStatusCode": false
}

"headers": [{
"key": "Server",
"value": ["AkamaiGHost"]
}
Almost certainly Akamai issue. I have seen 403 errors due to header order or even user-Agent header that Akamai does not like.

Related

Azure Devops extension : refresh token for service endpoint with oauth2

I'm currently building an extension and i added a service endpoint used to fetch some information from our server.
Our server uses Azure AD to authenticate, i successfully Authorize the service connection in DevOps and the information are correctly fetched. However the token used expires at some point and i would like to know how i could refresh it.
The documentation about this feature is kinda lacking and i'm a bit lost
here is the manifest json :
{
"id": "service-endpoint",
"description": "Service endpoint to get game information",
"type": "ms.vss-endpoint.service-endpoint-type",
"targets": [ "ms.vss-endpoint.endpoint-types" ],
"properties": {
"name": "portal",
"displayName": "Portal Access",
"dataSources": [
{
"name": "AccessToken",
"endpointUrl": "{{{configuration.Url}}}/token",
"requestVerb": "Post",
"requestContent": "grant_type=authorization_code&code={{{#uriDataEncode 1 AuthorizationCode}}}{{{/uridataencode}}}&client_id={{{#uriDataEncode 1 configuration.ClientId}}}{{{/uridataencode}}}&client_secret={{{#uriDataEncode 1 configuration.ClientSecret}}}{{{/uridataencode}}}&redirect_uri={{{#uriDataEncode 1 RedirectUrl}}}{{{/uridataencode}}}",
"resultSelector": "jsonpath:$",
"headers": [
{
"name": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
]
},
{
"name": "Game",
"endpointUrl": "{{{endpoint.url}}}/Studio/studios/games",
"requestVerb": "Get",
"resultSelector": "jsonpath:$.[*]",
"headers": [
{
"name": "Content-Type",
"value": "application/json"
}
]
},
{
"name": "RefreshToken",
"endpointUrl": "{{{configuration.Url}}}/token",
"requestVerb": "Post",
"requestContent": "grant_type=refresh_token&refresh_token={{{#uriDataEncode 1 RefreshToken}}}{{{/uridataencode}}}&client_id={{{#uriDataEncode 1 configuration.ClientId}}}{{{/uridataencode}}}&client_secret={{{#uriDataEncode 1 configuration.ClientSecret}}}{{{/uridataencode}}}",
"resultSelector": "jsonpath:$",
"headers": [
{
"name": "Content-Type",
"value": "application/x-www-form-urlencoded"
}
]
}
],
"authenticationSchemes": [
{
"displayName": "i18n:OAuth2",
"type": "ms.vss-endpoint.endpoint-auth-scheme-oauth2",
"headers": [
{
"name": "Authorization",
"value": "Bearer {{{endpoint.AccessToken}}}"
}
],
"authorizationUrl": "{{{configuration.Url}}}/authorize?client_id={{{configuration.ClientId}}}&response_type=code&redirect_uri={{{RedirectUrl}}}&scope=api://03105a38-d4dd-4fa1-8d6a-d1ef5c918574/API.Access",
"dataSourceBindings": [
{
"target": "AccessToken",
"dataSourceName": "AccessToken",
"resultTemplate": "{\"AccessToken\" : \"{{{access_token}}}\", \"RefreshToken\" : \"{{{refresh_token}}}\", \"ExpiresIn\" : \"{{{expires_in}}}\", \"TokenType\" : \"{{{token_type}}}\", \"Scope\" : \"{{{scope}}}\", \"Error\" : \"{{{error}}}\", \"ErrorDescription\" : \"{{{error_description}}}\"}"
},
{
"target": "RefreshToken",
"dataSourceName": "RefreshToken",
"resultTemplate": "{\"AccessToken\" : \"{{{access_token}}}\", \"RefreshToken\" : \"{{{refresh_token}}}\", \"ExpiresIn\" : \"{{{expires_in}}}\", \"TokenType\" : \"{{{token_type}}}\", \"Scope\" : \"{{{scope}}}\", \"Error\" : \"{{{error}}}\", \"ErrorDescription\" : \"{{{error_description}}}\"}"
}
]
}
],
"helpMarkDown": ""
Thanks in advance !
I tried to reproduce the same in my environment and got the below results:
Note that, to get the refresh token add the offline_access API permission:
I generated access token by using below parameters:
GET
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:client_id
grant_type:authorization_code
code:code
redirect_uri:redirect_uri
code_verifier:S256
scope:499b84ac-1321-427f-aa17-267ca6975798/user_impersonation offline_access
client_secret:client_secret
Response:
To refresh the access token, use the below parameters:
GET
https://login.microsoftonline.com/TenantID/oauth2/v2.0/token
client_id:client_id
grant_type:refresh_token
scope:scope
client_secret:client_secret
refresh_token:refreshtoken
Access token will be refreshed successfully like below:

Azure Logic App not able create client certificate authentication with converted base64 encoded pfx

I want to get the token information for ADP Client through Azure Logic App. I have the Client Certificate from ADP so I decided to use HTTP trigger from Logic App and selected authentication type "Client Certificate".
Since I cant directly use certificate in Logic app so I converted certificate into base64Encoded .pfx format, and certificate is not having any password.
below is the sample code for the request
{
"definition": {
"$schema": "https://schema.management.azure.com/providers/Microsoft.Logic/schemas/2016-06-01/workflowdefinition.json#",
"actions": {},
"contentVersion": "1.0.0.0",
"outputs": {},
"triggers": {
"HTTP": {
"inputs": {
"authentication": {
"pfx": "convertedbase64string",
"type": "ClientCertificate"
},
"body": "grant_type=client_credentials&client_id=ClientId&client_secret=client_secret",
"headers": {
"content-type": "application/x-www-form-urlencoded"
},
"method": "POST",
"uri": "https://accounts.adp.com/auth/oauth/v2/token"
},
"recurrence": {
"frequency": "Month",
"interval": 15
},
"type": "Http"
}
}
},
"kind": "Stateful"
}
above request returns me bad request, can anyone help me what is going wrong here?
For converting into base64 I used below steps in power shell
$pfx_cert = get-content 'C:\sample\adpcertificate.pfx' -Encoding Byte
$output =[Convert]::ToBase64String($pfx_cert)
$output
I tried same request with client certificate using postman which is working fine, but not able to get succeed with Logic App.
Any help is much appreciated.
There are only few differences between the headers sent from Postman and the Logic App. The main difference is that Postman also sends the accept-header: "Accept": "*/*" and leaves out alle the x-ms-* headers from the logic app.
I created a Logic App with http-trigger, which I post to from Postman and Logic App to inspect the changes:
With Postman
{
"headers": {
"Connection": "keep-alive",
"Accept": "*/*",
"Accept-Encoding": "br,gzip,deflate",
"Host": "....westeurope.logic.azure.com:443",
"User-Agent": "PostmanRuntime/7.28.4",
"Postman-Token": "...-baea-4e89-9bf6-490a63968b5d",
"Content-Length": "76",
"Content-Type": "application/x-www-form-urlencoded"
},
"body": {
"$content-type": "application/x-www-form-urlencoded",
"$content": "Z3JhbnRfdHlwZT1jbGllbnRfY3JlZGVudGlhbHMmY2xpZW50X2lkPUNsaWVudElkJmNsaWVudF9zZWNyZXQ9Y2xpZW50X3NlY3JldA==",
"$formdata": [
{
"key": "grant_type",
"value": "client_credentials"
},
{
"key": "client_id",
"value": "ClientId"
},
{
"key": "client_secret",
"value": "client_secret"
}
]
}
}
With Logic App
{
"headers": {
"Connection": "Keep-Alive",
"Accept-Encoding": "gzip,deflate",
"Accept-Language": "en",
"Host": "...westeurope.logic.azure.com",
"User-Agent": "azure-logic-apps/1.0,(workflow ...; version ...)",
"x-ms-trigger-callback-url": "https://....westeurope.logic.azure.com/ <...>",
"x-ms-trigger-type": "Http",
"x-ms-workflow-id": "...",
"x-ms-workflow-version": "...",
"x-ms-workflow-name": "myworkflowname",
"x-ms-workflow-system-id": "/locations/westeurope/scaleunits/...",
"x-ms-workflow-run-id": "...",
"x-ms-workflow-operation-name": "HTTP",
"x-ms-execution-location": "westeurope",
"x-ms-workflow-subscription-id": "...",
"x-ms-workflow-resourcegroup-name": "..",
"x-ms-tracking-id": "...",
"x-ms-correlation-id": "...",
"x-ms-client-request-id": "...",
"x-ms-activity-vector": "...",
"Content-Length": "76",
"Content-Type": "application/x-www-form-urlencoded"
},
"body": {
"$content-type": "application/x-www-form-urlencoded",
"$content": "Z3JhbnRfdHlwZT1jbGllbnRfY3JlZGVudGlhbHMmY2xpZW50X2lkPUNsaWVudElkJmNsaWVudF9zZWNyZXQ9Y2xpZW50X3NlY3JldA==",
"$formdata": [
{
"key": "grant_type",
"value": "client_credentials"
},
{
"key": "client_id",
"value": "ClientId"
},
{
"key": "client_secret",
"value": "client_secret"
}
]
}
}
Solution
My solution would be to manually add the Accept-Header in the post request in the Logic App.
"headers": {
"Accept": "*/*",
// ...
},
I sadly don't have an ADP account to verify this, but I've seen other APIs break when no accept header is sent.

Multiple authorization using swagger OpenAPI 3.0 with Express and JSON format

The project has two authorization systems, basic auth and bearer. I need for each request after clicking on the "try it out" and "execute" buttons to attach to the request the Authorization headers, in which there will be a Basic line and a jwt header, in which there will be a bearer token. The problem is that I can attach these headers individually, but not together. There is a feeling that both authorizations want to write to the Authorization header and one of them overwrites the other, even though I explicitly indicated the header names in the schema.
My schemas:
{
"securitySchemes": {
"Bearer": {
"in": "header",
"name": "jwt",
"type": "http",
"scheme": "bearer"
},
"basicAuth": {
"type": "http",
"scheme": "basic"
}
}
}
and how I use it:
{
"/channel/base-list": {
"get": {
"tags": [
"CMS Channel"
],
"security": [
{
"Bearer": [],
"basicAuth": []
}
],
"summary": "Get _id and title of all channels",
"produces": [
"application/json"
],
"parameters": [
{
"in": "query",
"name": "count",
"required": false,
"schema": {
"type": "Integer"
},
"default": 25,
"example": 10
},
{
"in": "query",
"name": "search",
"required": false,
"schema": {
"type": "String"
},
"description": "Channel name"
}
],
"responses": {
"200": {
"description": "A list of channels",
"content": {
"application/json": {
"schema": {
"$ref": "#/definitions/get-channel-base-list"
}
}
}
}
}
}
}
}
I use swagger-ui-express for node.JS and OpenAPI 3.0
A request can contain only one Authorization header, and the Authorization header can only contain a single set of credentials (i.e. either Basic or Bearer, but not both). Your use case is not supported by the HTTP protocol.

How to send to Amazon's Alexa Event Gateway?

I am trying to test sending an event to the Amazon's Event Gateway for my Alexa Smart Home skill using Postman but I keep receiving an 'invalid access token exception.' I have read the Amazon's documentation on this but apparently I am missing something.
When I enable my skill, my Smart Home Lambda receives the AcceptGrant.
{
"directive": {
"header": {
"namespace": "Alexa.Authorization",
"name": "AcceptGrant",
"messageId": "b2862179-bc56-4bb2-ac05-ce55c7a3e977",
"payloadVersion": "3"
},
"payload": {
"grant": {
"type": "OAuth2.AuthorizationCode",
"code": "ANSVjPzpTDBsdfoRSyrs"
},
"grantee": {
"type": "BearerToken",
"token": "Atza|IwEB..."
}
}
}
}
My lambda sends a POST to 'https://api.amazon.com/auth/o2/token' to receive the Access and Refresh tokens. It then stores those tokens. Next, my Lamdba responds with the following:
{
"event": {
"header": {
"namespace": "Alexa.Authorization",
"name": "AcceptGrant.Response",
"messageId": "b2862179-bc56-4bb2-ac05-ce55c7a3e977",
"payloadVersion": "3"
},
"payload": {}
}
}
I then get a message web page that I have successfully linked my skill - all is good.
Next, I try to send an event to Amazon's Alexa event gateway using the Postman app. I put the Access token (I also tried the Refresh token) in the header as a 'BearerToken' type and the in the 'scope' of the 'endpoint' object.
POST https://api.amazonalexa.com/v3/events?Content-Type=application/json&charset=UTF-8
with a header that specifies a Bearer Token (Access token received earlier) and a body that contains the following:
{
"event": {
"header": {
"messageId": "abc-123-def-456",
"namespace": "Alexa",
"name": "ChangeReport",
"payloadVersion": "3"
},
"endpoint": {
"scope": {
"type": "BearerToken",
"token": "<access token>"
},
"endpointId": "MySmartSwitch-001"
},
"payload": {
"change": {
"cause": {
"type": "RULE_TRIGGER"
},
"properties": [
{
"namespace": "Alexa.ModeController",
"name": "mode",
"value": "Backup",
"timeOfSample": "2020-01-02T09:30:00ZZ",
"uncertaintyInMilliseconds": 50
}
]
}
}
},
"context": {
"properties": [
{
"namespace": "Alexa.PowerController",
"name": "powerState",
"value": "ON",
"timeOfSample": "2020-01-02T09:30:00Z",
"uncertaintyInMilliseconds": 60000
},
{
"namespace": "Alexa.EndpointHealth",
"name": "connectivity",
"value": {
"value": "OK"
},
"timeOfSample": "2020-01-02T09:30:00Z",
"uncertaintyInMilliseconds": 0
}
]
}
}
The response received is '401 Unauthorized'
{
"header": {
"namespace": "System",
"name": "Exception",
"messageId": "95bd23c3-76e6-472b-9c6d-74d436e1eb61"
},
"payload": {
"code": "INVALID_ACCESS_TOKEN_EXCEPTION",
"description": "Access token is not valid."
}
}
I figured out the issue. I was mistakenly sending parameters: Content-Type=application/json and charset=UTF-8 as well including them in the header - my bad. You just need to include them in the header.

Proxy Breaks When Forwarding To GitHub Pages

We configured a function proxy for our website approximately 2 months ago and got everything working as expected. Last night around 8:00-8:30pm EST the proxy stopped working, receiving "Internal server error" 500 messages when accessing it's endpoints. We haven't changed anything on our end so I don't know why this all of a sudden started.
We proxy our domain to various endpoints. The endpoint that stopped working are proxies to pages we are hosting on GitHub pages. Proxies to other services such as other Azure App service instances are still working.
I ran a Proxy-Trace-Enabled: true request to the proxy and find the following error in the trace log:
"backend": [
{
"source": "forward-request",
"timestamp": "2018-01-31T01:45:36.4810022Z",
"elapsed": "00:00:00.0037370",
"data": {
"message": "Request is being forwarded to the backend service.",
"request": {
"method": "GET",
"url": "https://xxxxxxxxxx.github.io/xxxxxxxxxx/",
"headers": [
{
"name": "Cache-Control",
"value": "no-cache"
},
{
"name": "Accept",
"value": "*/*"
},
{
"name": "Accept-Encoding",
"value": "gzip"
},
{
"name": "Cookie",
"value": "__cfduid=xxxxxxxxxx"
},
{
"name": "Max-Forwards",
"value": "10"
},
{
"name": "User-Agent",
"value": "PostmanRuntime/7.1.1"
},
{
"name": "CF-IPCountry",
"value": "US"
},
{
"name": "X-Forwarded-For",
"value": "xxxxxxxxxx, xxxxxxxxxx, xxxxxxxxxx"
},
{
"name": "CF-RAY",
"value": "xxxxxxxxxx-MIA"
},
{
"name": "X-Forwarded-Proto",
"value": "https"
},
{
"name": "CF-Visitor",
"value": "{\"scheme\":\"https\"}"
},
{
"name": "Postman-Token",
"value": "xxxxxxxxxx"
},
{
"name": "CF-Connecting-IP",
"value": "xxxxxxxxxx"
},
{
"name": "X-WAWS-Unencoded-URL",
"value": "/"
},
{
"name": "X-Original-URL",
"value": "/"
},
{
"name": "X-ARR-LOG-ID",
"value": "xxxxxxxxxx"
},
{
"name": "DISGUISED-HOST",
"value": "xxxxxxxxxx.com"
},
{
"name": "X-SITE-DEPLOYMENT-ID",
"value": "xxxxxxxxxx"
},
{
"name": "WAS-DEFAULT-HOSTNAME",
"value": "xxxxxxxxxx.azurewebsites.net"
},
{
"name": "Content-Length",
"value": "0"
}
]
}
}
},
{
"source": "forward-request",
"timestamp": "2018-01-31T01:45:36.5122512Z",
"elapsed": "00:00:00.0363283",
"data": {
"messages": [
"Error occured while calling backend service.",
"The request was aborted: Could not create SSL/TLS secure channel."
]
}
}
],
I am not sure why there are "The request was aborted: Could not create SSL/TLS secure channel." errors since I can access the GitHub pages version of the website that it is proxying to without issue (no SSL issues). We've had to disable use of the proxy for now and change our DNS to point directly to the GitHub page until we can resolve this.
What's going on here?
Why did this break all of a sudden with no changes on our end?
Maybe GitHub suddenly switched to TLS 1.2-only and walked away?
$ curl --tlsv1.0 -vki https://microsoft.github.io
...
* gnutls_handshake() failed: Error in protocol version
$ curl --tlsv1.1 -vki https://microsoft.github.io
...
* gnutls_handshake() failed: Error in protocol version
$ curl --tlsv1.2 -vki https://microsoft.github.io
* SSL connection using TLS1.2 / ECDHE_RSA_AES_128_GCM_SHA256
...
* subject: C=US,ST=California,L=San Francisco,O=GitHub, Inc.,
CN=www.github.com
* issuer: C=US,O=DigiCert Inc,OU=www.digicert.com,
CN=DigiCert SHA2 High Assurance Server CA
...
HTTP/1.1 200 OK
I don't know if you can tell Functions Proxy to use a particular TLS version for outbound connections, you know, the equivalent of
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;

Resources