"Signature is Invalid" message for AppCenter Webhook request - github-api

I am trying to write a proxy service between GitHub API and AppCenter API and when I am trying to reroute a standard GitHub API message I am getting a "Signature is invalid" response.
Here's what I am attempting to send:
{
"ref": "refs/heads/xxxx",
"before": "xxxxxxxxxxxxx",
"after": "xxxxxxxxxxxxx",
"created": false,
"deleted": false,
"forced": false,
"base_ref": null,
"compare": "https://github.com/xxxxx",
"commits": [{
"content": "xxxx"
}],
"head_commit": {
"content": "xxxx"
},
"repository": {
"content": "xxxx"
},
"pusher": {
"content": "xxxx"
},
"organization": {
"content": "xxxx"
},
"sender": {
"content": "xxxx"
}
}
AppCenter's endpoint is:
https://api.appcenter.ms/v0.1/public/apps/xxxxxxxxxxx/hooks
Request's headers are:
{
"content-type": "application/json",
"User-Agent": "GitHub-Hookshot/xxxxxxxx",
"X-GitHub-Delivery": "xxxxxxxxxxxxxx",
"X-GitHub-Event": "push",
"X-Hub-Signature": "sha1=xxxxxxxxxxx"
}
Here's the response I am getting:
{
"id": "xxxxxxxxxxxxxxxx",
"message": "Signature is invalid"
}
I've yet to receive a sensible answer from AppCenter, hope somebody already had a similar experience and can answer.
Thanks

I think the issue is the value of X-Hub-Signature. From Validating payloads from Github, Github uses a HMAC SHA1 between the payload data & the secret string you have put in the webhook section of your repository :
The signature format (without brackets):
sha1={HMAC-SHA1(secret, payload)}
A few examples to compute the signature :
using ruby (from here) :
signature = 'sha1=' + OpenSSL::HMAC.hexdigest(OpenSSL::Digest.new('sha1'), ENV['SECRET_TOKEN'], payload_body)
using javascript (from here) :
var signature = 'sha1=' + CryptoJS.HmacSHA1(payload, environment.secret).toString(CryptoJS.enc.Hex)

Related

new klaviyo node SDK

I'm trying to use Klaviyo for the first time, and the natural starting point seems to be their new Node SDK. Unfortunately, their documentation seems to be an odd combination of extremely detailed, and laking any details.
I've set up my account and gotten my API key, but am failing to get a simple registration to succeed.
I thought I could do this:
import { ConfigWrapper, Profiles } from 'klaviyo-api';
import logger from '../utils/logger';
import requireEnvVariable from '../utils/requireEnvVariable';
export async function subscribe(profile) {
const apiKey = requireEnvVariable('KLAVIYO_PK');
ConfigWrapper(apiKey);
const user = Profiles.createProfile(profile)
.then(data => logger.debug({data}, 'Got profile data'))
.catch(error => {
logger.warn(error, 'Klaviyo error');
return error;
});
return true;
}
For profile, I'm passing in
{
"email": "me#example.com",
"phone_number": "",
"first_name": "Bob",
"last_name": "Smith"
}
but it responds with an error:
WARN: Klaviyo error
env: "development"
err: {
"type": "Error",
"message": "Bad Request",
"status": 400,
"response": {
"req": {
"method": "POST",
"url": "https://a.klaviyo.com/api/profiles/",
"data": {
"email": "me#example.com",
"phone_number": "",
"first_name": "Bob",
"last_name": "Smith"
},
"headers": {
"authorization": "Klaviyo-API-Key <my private key>",
"revision": "2022-10-17",
"user-agent": "klaviyo-api-node/1.0.2",
"content-type": "application/json",
"accept": "application/json"
}
},
"header": {
"date": "Wed, 28 Dec 2022 23:34:14 GMT",
"content-type": "application/vnd.api+json",
"content-length": "211",
"connection": "close",
"cf-ray": "780e1b056c3728f5-ORD",
"allow": "GET, POST, HEAD, OPTIONS",
"vary": "Cookie, Accept-Encoding",
"cf-cache-status": "DYNAMIC",
"cid": "UDiE82",
"ratelimit-limit": "700, 75;w=1, 700;w=60",
"ratelimit-remaining": "699",
"ratelimit-reset": "44",
"x-robots-tag": "noindex, nofollow",
"server": "cloudflare"
},
"status": 400,
"text": "{\"errors\":[{\"id\":\"5648779b-3e1a-4ccf-80c4-dc19b8b32a2c\",\"status\":400,\"code\":\"invalid\",\"title\":\"Invalid input.\",\"detail\":\"The payload provided in the request is invalid.\",\"source\":{\"pointer\":\"/data\"},\"meta\":{}}]}"
}
}
The various examples under Optional Parameters made me think that the above would Just Work; however, the docs also say, "For example values / data types, as well as whether parameters are required/optional, please reference the corresponding API Reference link," and "This SDK is a thin wrapper around our API. See our API Reference for full documentation on API behavior."
So looking at the Create Profile API Reference, I tried both
const user = Profiles.createProfile({attributes: profile})
and
const user = Profiles.createProfile({type: 'profile', attributes: profile})
but they both end up with the same error (except the reported data is updated appropriately, e.g.,
"status": 400,
"response": {
"req": {
"method": "POST",
"url": "https://a.klaviyo.com/api/profiles/",
"data": {
"type": "profile",
"attributes": {
"email": "me#example.com",
"phone_number": "",
"first_name": "Bob",
"last_name": "Smith"
}
},
What's the right way to use the new SDK? Or should I ignore this, and go back to creating manual fetch calls against their API?

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:

Swagger-ui 2.0 path do not get Bearer Authorization from top

when I check on swagger , my path "/customers" says "no headers authorization" whereas I have entered a Bearer token into the top right "Authorize" input. It is like the customers "security" prop is not linked to general securityDefinitions. In web Swagger, the padlock near GET /Customers is grey and open, when I click on it it does not show any "available authorization".(Postman version is working), I can't find why, could you help me please ?
In my swagger-ui (2.0)json file I got :
"securityDefinitions": {
"Bearer": {
"type": "apiKey",
"name": "Authorization",
"in": "header",
"description": "Enter your bearer token in the format **Bearer <token>**"
}
},
then below on the second path I entered "security" :
"/customers": {
"get": {
"tags": ["Customer Search"],
"summary": "find a customer by email, name, tel, ...",
"security": [{ "bearerAuth": [] }],
"parameters": [
{
"name": "email",
"in": "query",
"description": "email",
"required": true,
"type": "string"
},
{
"name": "realmCode",
"in": "query",
"description": "realmCode",
"required": true,
"type": "string"
}
],
"responses": {
"200": {
"description": "send customer json file"
}
}
Here is the beginning of my node Express JS code for my route :
router.get("/customers", async (req: any, res: any) => {
if (!req.headers.authorization)
return res.status(401).json({ error: "no headers authorization" });
else {
```
OK I found it :
"security": [{ "Bearer": [] }],
instead of "bearerAuth" :)

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

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.

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.

Resources