Unable to complete oauth web workflow for GitHub in golang - http

I'm trying to implement oauth-workflow for GitHub in golang and using https://github.com/franela/goreq to perform http(s) requests.
There is a section in which GitHub returns a code and you have to make a POST request to https://github.com/login/oauth/access_token with code, client_id and client_secret.
package main
import "fmt"
import "github.com/franela/goreq"
type param struct {
code string
client_id string
client_secret string
}
func main() {
params := param {code: "XX", client_id:"XX", client_secret: "XX"}
req := goreq.Request{
Method : "POST",
Uri : "https://github.com/login/oauth/access_token",
Body : params,
}
req.AddHeader("Content-Type", "application/json")
req.AddHeader("Accept", "application/json")
res, _ := req.Do()
fmt.Println(res.Body.ToString())
}
It is giving 404 with {"error":"Not Found"} message always.
While using Python, I'm getting the correct results with the same input data.

You are generating empty JSON objects. Your struct fields should start in capitals for the JSON encoder to be able to encode them.
type goodparam struct {
Code string `json:"code"`
ClientId string `json:"client_id"`
ClientSecret string `json:"client_secret"`
}
See this in action.

You should double check your 'client_secret' and 'client_id' (must be right because you get the code) if it is correct, apparently Github returns HTTP status code 404 if it is wrong.

Related

Sending a Post Request from Ballerina

I want to send a post request using ballerina to get a access token from the Choreo Dev Portal. I am able to do it using postman. But unable to make it work in Ballerina code level. it gives 415 - unsupported media type error. Need some Help in Ballerina
import ballerina/http;
import ballerina/io;
import ballerina/url;
public function main() returns error? {
final http:Client clientEndpoint = check new ("https://sts.choreo.dev");
http:Request request = new();
string payload = string`grant_type=urn:ietf:params:oauth:grant-type:token-exchange&
subject_token=*******&
subject_token_type=urn:ietf:params:oauth:token-type:jwt&
requested_token_type=urn:ietf:params:oauth:token-type:jwt`;
string encodedPayload = check url:encode(payload, "UTF-8");
io:print(encodedPayload);
request.setTextPayload(encodedPayload);
request.addHeader("Authorization","Basic *****");
request.addHeader("Content-Type","application/x-www-form-urlencoded");
io:print(request.getTextPayload());
json resp = check clientEndpoint->post("/oauth2/token",request);
io:println(resp.toJsonString());
}
I was expecting an access token from Choreo Devportal for the particular application.
import ballerina/http;
import ballerina/io;
import ballerina/mime;
public function main() returns error? {
// Creates a new client with the backend URL.
final http:Client clientEndpoint = check new ("https://sts.choreo.dev");
json response = check clientEndpoint->post("/oauth2/token",
{
"grant_type": "urn:ietf:params:oauth:grant-type:token-exchange",
"subject_token_type": "urn:ietf:params:oauth:token-type:jwt",
"requested_token_type":"urn:ietf:params:oauth:token-type:jwt",
"subject_token":"****"
},
{
"Authorization": "Basic ****"
},
mime:APPLICATION_FORM_URLENCODED
);
io:println(response.toString());
}
This is the recommended way to send the post request with the form URL encoded payload.
Change the Content-type header setting method from addHeader() to setHeader().
The request.setTextPayload(encodedPayload); will set the Content-type as text/plain as the default content type header.
Later request.addHeader("Content-Type","application/x-www-form-urlencoded"); is executed. The addHeader() method will append the new value to the same header in addition to the previously added text/plain. But the setHeader() will replace the previously set header which is the correct way in this scenario.
However better way is to pass the Content-type as the second param of setXXXPayload() method.
request.setTextPayload(encodedPayload, "application/x-www-form-urlencoded");

how can i send a post request via Business Central?

I would like to create a PostRequest in my Business Central Extension that authenticates me in my web service and returns me a token. I send my username and password in the body of the request to my web service and I also receive the token in JSON format in the body.I want to create the post request using HttpClient.
I use the following link as a template: https://learn.microsoft.com/en-us/dynamics365/business-central/dev-itpro/developer/methods-auto/httpcontent/httpcontent-data-type
procedure sendPostRequest(uri: Text)
var
client: HttpClient;
content: HttpContent;
contentHeaders: HttpHeaders;
response: HttpResponseMessage;
request: HttpRequestMessage;
begin
content.GetHeaders(contentHeaders);
contentHeaders.Clear();
contentHeaders.Add('Content-Type', 'application/json');
request.Content:= content;
request.SetRequestUri(uri);
request.Method := 'POST';
end;
procedure SetURLsToDefault(var MessagingServiceSetup: Record "Messaging Service Setup WMR")
begin
MessagingServiceSetup."Service URL" := '202.212.127:8800';
end;
And I have a couple of questions:
1) the basic url is 202.212.127:8800 for my API gateway. To be able to authenticate myself I have to access 202.212.127:8800/authenticate. Is there a method in which you can create urls?
2) how do I get my username and password in the content?
3) and how do I get the token and can I save it in the field?
can someone tell me how to get the PostRequest up and running?
Common method to create different URLs is like this:
Create a setup table
Create fields like "Base Url", User, Pass etc.
I propose this pattern for your code:
SendRequest(Method; Url; Body)
Begin
...
Couple of functions (Your Api and Auth):
Authenticate()
begin
Method = 'post';
Url = SetupTable."Base Url" + '/authenticate';
Body = (Use AL Json stack and incorporate your user pass)
SendRequest(Method; Url; Body);
end;
Function1()
begin
Method = 'get';
Url = SetupTable."Base Url" + '/apiPath-Function1';
Body = '';
SendRequest(Method; Url; Body);
end
Function2()
begin
Method = 'post';
Url = SetupTable."Base Url" + '/apiPath-Function2';
Body = (Use AL Json stack and incorporate your body structure);
SendRequest(Method; Url; Body);
end;
To get your user pass into the content you need to check the documentation of the Api you're trying to call. It's usually described in details, it can be a simple header for basic authentication or a complex Jwt.
For receiving a token, again you need to check your Api documentation first, but essentially after making a Rest call (like: client.Send(RequestMessage, ResponseMessage); inside your SendRequest method), you get a response back and you can use AL Json stack to carve information out.
This is a fine article on how to proceed:
https://jackmallender.com/2019/03/04/interacting-with-rest-apis-using-json-from-within-business-central-part-1-an-introduction-to-the-httpclient-data-type/
Basically a string could work as an url. Depends on what you want. It is good practice to have a setup for your web service calls, so I am with Babak. You can set up a table in which you store the links, credentials - whatsoever.
and 4) I suggest Waldos Rest App for web service calls. you can download the source here: https://github.com/waldo1001/waldo.restapp
It encapsulated the calls, has helper functions for json handling as well. Using the "REST Helper" Codeunit. You can break down your call to:
local procedure DoCallWebservice(URI: Text; User: Text; Pass: Text; var Token: Text);
var
RESTHelper: Codeunit "REST Helper WLD";
begin
RRESTHelper.Initialize('GET', URI);
RESTHelper.SetContentType('application/json');
RESTHelper.AddBody('{"user":"USERNAME","pass":"PASSWORD"}');
if RESTHelper.Send() then
Token := RESTHelper.GetResponseContentAsText();
end;
Obviously, you need to parse the response (JSONHelper) to your needs. Look at the code of the codeunit, it's more or less self explanatory.

Sending headers to post request

I have this python code that does not work as expected.
import requests
import json
API_ENDPOINT = "https://lkokpdvhc4.execute-api.us-east-1.amazonaws.com/mycall"
data = {'mnumber':'9819838466'}
r = requests.post(url = API_ENDPOINT, data = json.dumps(data))
print (r.text)
This will return an error:
{"stackTrace": [["/var/task/index.py", 5, "handler", "return
mydic[code]"]], "errorType": "KeyError", "errorMessage": "''"}
When I test the API using Amazon console's gateway, I get the expected output (i.e. string like "mumbai"). It means this is client side issue. I have confirmed this by using "postman" as well that returns the same error as mentioned above. How do I send correct headers to post request?
You can create a dictionary with the headers such as
headers = {
"Authorization": "Bearer 12345",
"Content-Type": "application/json",
"key" : "value"
}
Then at the point of making the request pass it as a keyword argument to the request method i.e .post() or .get() or .put
This will be
response = requests.post(API_ENDPOINT, data=json.dumps(data), headers=headers)

Elm: Function `send` expecting Http.Request String but it is Http.Request Bool

I'm setting up a super simple http call to an endpoint on my server which returns a JSON response - an object with a success prop which is a boolean. Here is the relevant code:
getData : Model -> Cmd Msg
getData { userId, data } =
let
url =
"/get-data?userId=" ++ userId ++ "&data=" ++ data
request =
Http.get url decodeGetData
in
Http.send GetDataResult request
decodeGetData : Decode.Decoder Bool
decodeGetData =
Decode.at [ "success" ] Decode.bool
I'm getting the following error from the compiler:
Http.send GetDataResult request
^^^^^^^
Function `send` is expecting the 2nd argument to be:
Http.Request String
But it is:
Http.Request Bool
What's going wrong here? How do I set up Http.send to expect a Bool instead of a string? I know that the basic setup of my request is correct because my code compiles if I change the decodeGetData function to:
decodeGetData : Decode.Decoder String
decodeGetData =
Decode.at [ "success" ] Decode.string
In this case I can successfully make the http request, but then I get an error because the success prop on the response is a boolean instead of a string.
Any pointers? Cheers!
The code you pasted in all looks good, which leads me to think that the problem lies in a piece of code you don't have listed. Namely, the Msg constructor GetDataResult should have a single parameter of type Result Http.Error Bool. The compiler error you received would occur if the signature were instead Result Http.Error String.

How to send HTTP data and get response with Synapse (Delphi)

I think the title was clear enough.
I want to know how to send HTTP POST request with parameters/arguments and receive HTML response back - using Synapse library for Delphi.
Try to use HttpPostURL function.
function HttpPostURL(const URL, URLData: string; const Data: TStream): Boolean;
URL - target URL
URLData - URL parameters; must be encoded e.g. using EncodeURLElement function
Data - target stream, where the response will be stored
The following example uses testing POST server where send two POST parameters. Note using of EncodeURLElement function for encoding parameter data. If the POST succeed the server response is saved into the file.
uses HTTPSend, Synacode;
procedure TForm1.Button1Click(Sender: TObject);
var URL: string;
Params: string;
Response: TMemoryStream;
begin
Response := TMemoryStream.Create;
try
URL := 'http://posttestserver.com/post.php?dump&html';
Params := 'parameter1=' + EncodeURLElement('data1') + '&' +
'parameter2=' + EncodeURLElement('data2');
if HttpPostURL(URL, Params, Response) then
Response.SaveToFile('c:\response.txt');
finally
Response.Free;
end;
end;

Resources