How can I make a request with a bearer token in Go - http

I need to make a GET request to an API with a bearer token in the authorization request. How can I do this in Go? I have the following code, but I haven't had success.
package main
import (
"io/ioutil"
"log"
"net/http"
)
func main() {
url := "https://api.globalcode.com.br/v1/publico/eventos"
resp, err := http.Get(url)
resp.Header.Add("Bearer", "token")
if err != nil {
log.Println("Erro ao realizar request.\n[ERRO] -", err)
}
body, _ := ioutil.ReadAll(resp.Body)
log.Println(string([]byte(body)))
}

For control over HTTP client headers, redirect policy, and other settings, create a Client:
package main
import (
"io/ioutil"
"log"
"net/http"
)
func main() {
url := "https://api.globalcode.com.br/v1/publico/eventos"
// Create a Bearer string by appending string access token
var bearer = "Bearer " + <ACCESS TOKEN HERE>
// Create a new request using http
req, err := http.NewRequest("GET", url, nil)
// add authorization header to the req
req.Header.Add("Authorization", bearer)
// Send req using http Client
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
log.Println("Error on response.\n[ERROR] -", err)
}
defer resp.Body.Close()
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
log.Println("Error while reading the response bytes:", err)
}
log.Println(string([]byte(body)))
}
The Client's Transport typically has internal state (cached TCP
connections), so Clients should be reused instead of created as
needed. Clients are safe for concurrent use by multiple goroutines.
A Client is higher-level than a RoundTripper (such as Transport) and
additionally handles HTTP details such as cookies and redirects.
For more information on Client and Transport check golang spec for net/http package

I had to add a client.CheckRedirect Function(seen below) in order to pass the Bearer token to the API.
bearer := "Bearer " + token
req, err := http.NewRequest("GET", url, bytes.NewBuffer(nil))
req.Header.Set("Authorization", bearer)
req.Header.Add("Accept", "application/json")
client := &http.Client{}
client.CheckRedirect = func(req *http.Request, via []*http.Request) error {
for key, val := range via[0].Header {
req.Header[key] = val
}
return err
}
resp, err := client.Do(req)
if err != nil {
log.Println("Error on response.\n[ERRO] -", err)
} else {
defer resp.Body.Close()
data, _ := ioutil.ReadAll(resp.Body)
fmt.Println(string(data))
}
}

I made a super-little-basic library for execute basic request like:
package main
import (
request "github.com/alessiosavi/Requests"
)
func main(){
// Create a key-value list of headers
headers := request.CreateHeaderList(`Accept`, `application/json`, "Authorization", "Bearer "+IAMToken)
resp :=request.SendRequest(`http://your_site.com`, `GET`, headers, nil))
}
Here you can find the request implementation:
https://github.com/alessiosavi/Requests/blob/e7ca66bde738b6224fba2b6f146a8dbee67d3323/Requests.go
Here you can find how i use the library for Bearer Auth and other auth type:
https://github.com/alessiosavi/GoCloudant/blob/a8ad3a7990f04ea728bb327d6faea6af3e5455ca/cloudant.go

Related

How to make a HTTP request from server to client using grpc in golang

Problem Statement
I have a client (which dials to the server) and server (that listens for incoming requests) written in golang and with the RPC calls defined. I am trying to initiate an HTTP request on the server side which would in turn execute the RPC call for streaming and send a JSON response back to the user
Challenge
I was able to handle both grpc and HTTP requests on different ports but having issues with passing parameters from the HTTP request onto the RPC call on the server side
Server Code
log.Println("Listening for connections from client ........")
lis, err := net.Listen("tcp", ":9000")
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := testApi.Server{}
grpcServer := grpc.NewServer()
testApi.RegisterTestApiServiceServer(grpcServer, &s)
if err := grpcServer.Serve(lis); err != nil {
log.Fatalf("failed to serve: %s", err)
}
func main() {
go runGrpc()
log.Printf("*------ Waiting for requests from users ------*")
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/exchangeId/{test_id}", ConnectAndExchange).Methods("GET")
log.Fatal(http.ListenAndServe(":8080", router))
}
func ConnectAndExchange(w http.ResponseWriter, r *http.Request){
vars := mux.Vars(r)
test_id, _ := strconv.Atoi(vars["test_id"])
log.Println("Test id request from user : ", test_id)
func (s * Server) ConnectAndStream(channelStream TestApiService_ConnectAndStreamServer) error {
// Question: This Id has to come from http request above- test_id
var id int32 = 1234566
// id := a.ConnectAndExchange
log.Println("Id from sam user ", id)
// var id int32 = 1234566
for i := 1; i <= 2; i++ {
id += 1
log.Println("Speed Server is sending data : ", id)
channelStream.Send(&Input{Id: id})
}
for i := 1; i <= 2; i++ {
log.Println("now time to receive")
client_response, err := channelStream.Recv()
log.Println("Response from samd client : ", client_response.Id)
if err != nil {
log.Println("Error while receiving from samd : ", err)
}
}
return nil
}
I am stuck with being able to pass the test_id from the curl request to the RPC call as above. Any input is greatly appreciated
Note
Client - Dials in and connects to the server and starts receiving and sending data (bi-directional streaming)
Both the Http and GRPC client are part of the same server application. So why call the RPC method from the Http handler? The Http handler should have access to the same backend functionality.
Your question is slightly unclear but if you are trying to have your client establish a GRPC connection to the server via the HTTP handler this will not work. The GRPC connection established in this situation is between the server and its self.
Edit - thanks for the clarification. Now I understand better the flow that you are trying to achieve. Your http handler method can make the outgoing grpc call to the server and return the response back via the http.ResponseWriter
For simplicity I have used the hello world example on https://github.com/grpc/grpc-go/tree/master/examples/helloworld
Running the code sample below and hitting http://localhost:1000/exchangeId/Test will show the output
Starting
*------ Waiting for http requests from users on port 1000 ------*
server listening at 127.0.0.1:1001
Test id request from user : Test
Server Received: Test
Greeting: Hello Test
Code sample:
import (
"context"
"log"
"net"
"net/http"
"time"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
pb "google.golang.org/grpc/examples/helloworld/helloworld"
"github.com/gorilla/mux"
)
var (
grpcserver = "localhost:1001"
)
func main() {
log.Print("Starting")
go StartGrpcServer()
log.Printf("*------ Waiting for http requests from users on port 1000 ------*")
router := mux.NewRouter().StrictSlash(true)
router.HandleFunc("/exchangeId/{test_id}", ConnectAndExchange).Methods("GET")
log.Fatal(http.ListenAndServe(":1000", router))
}
type server struct {
pb.UnimplementedGreeterServer
}
// SayHello implements helloworld.GreeterServer
func (s *server) SayHello(ctx context.Context, in *pb.HelloRequest) (*pb.HelloReply, error) {
log.Printf("Server Received: %v", in.GetName())
return &pb.HelloReply{Message: "Hello " + in.GetName()}, nil
}
func StartGrpcServer() {
lis, err := net.Listen("tcp", grpcserver)
if err != nil {
log.Fatalf("failed to listen: %v", err)
}
s := grpc.NewServer()
pb.RegisterGreeterServer(s, &server{})
log.Printf("server listening at %v", lis.Addr())
if err := s.Serve(lis); err != nil {
log.Fatalf("failed to serve: %v", err)
}
}
func ConnectAndExchange(w http.ResponseWriter, r *http.Request) {
vars := mux.Vars(r)
test_id := vars["test_id"]
log.Println("Test id request from user : ", test_id)
// Set up a connection to the server.
conn, err := grpc.Dial(grpcserver, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
c := pb.NewGreeterClient(conn)
// Contact the server and print out its response.
ctx, cancel := context.WithTimeout(context.Background(), time.Second)
defer cancel()
resp, err := c.SayHello(ctx, &pb.HelloRequest{Name: test_id})
if err != nil {
log.Fatalf("could not greet: %v", err)
}
log.Printf("Greeting: %s", resp.GetMessage())
w.Write([]byte(resp.GetMessage()))
}

Logging All HTTP Request and Response from done through an HTTP Client

I have the following simple http.Client:
import (
"net/http"
"log"
)
...
func main() {
...
link = "http://example.com"
method = "GET"
req, _ := http.NewRequest(method, link, nil)
client := &http.Client{}
myZapLogger.Info("Sending a %s request to %s\n", method, link)
resp, err := client.Do(req)
if err != nil {
myZapLogger.Error(..., err) // I'm logging rather than fatal-ing or so
} else {
myZapLogger.Info("Received a %d on request X", resp.StatusCode)
}
...
}
...
I was looking for a way to do the above for each request through a hook (or so), so that it's triggered automatically each time. I can write a function the encloses all that, but in a case where I'm passing an http client to some other package, I wouldn't be able to control/log such requests that way (e.g. aws-go-sdk).
Is there a way to do this through contexts or attaching hooks to the client?
Thanks
eudore's comment answers the question; I'll just put it into code:
type MyRoundTripper struct {}
func (t MyRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
// Do work before the request is sent
resp, err := http.DefaultTransport.RoundTrip(req)
if err != nil {
return resp, err
}
// Do work after the response is received
return resp, err
}
To use it, you'll just pass it to your HTTP Client:
rt := MyRoundTripper{}
client := http.Client{Transport: rt}

How to perform a GET request with application/x-www-form-urlencoded content-type in Go?

Basically, I need to implement the following method in Go - https://api.slack.com/methods/users.lookupByEmail.
I tried doing it like this:
import (
"bytes"
"encoding/json"
"errors"
"io/ioutil"
"net/http"
)
type Payload struct {
Email string `json:"email,omitempty"`
}
// assume the following code is inside some function
client := &http.Client{}
payload := Payload{
Email: "octocat#github.com",
}
body, err := json.Marshal(payload)
if err != nil {
return "", err
}
req, err := http.NewRequest("GET", "https://slack.com/api/users.lookupByEmail", bytes.NewReader(body))
if err != nil {
return "", err
}
req.Header.Add("Authorization", "Bearer "+token)
req.Header.Add("Content-Type", "application/x-www-form-urlencoded")
resp, err := client.Do(req)
if err != nil {
return "", err
}
defer resp.Body.Close()
if resp.StatusCode != 200 {
t, _ := ioutil.ReadAll(resp.Body)
return "", errors.New(string(t))
}
responseData, err := ioutil.ReadAll(resp.Body)
if err != nil {
return "", err
}
return string(responseData), nil
But I get an error that "email" field is missing, which is obvious because this content-type does not support JSON payload:
{"ok":false,"error":"invalid_arguments","response_metadata":{"messages":["[ERROR] missing required field: email"]}} (type: string)
I couldn't find how to include a post form with the GET request - there is no available post form argument neither to http.NewRequest, nor to http.Client.Get; http.Client.PostForm issues a POST request but GET is needed in this case. Also, I think I have to use http.NewRequest here (unless another approach exists) because I need to set the Authorization header.
You misunderstand the application/x-www-form-urlencoded header, you should pass an URL parameters here. Check out an example:
import (
...
"net/url"
...
)
data := url.Values{}
data.Set("email", "foo#bar.com")
data.Set("token", "SOME_TOKEN_GOES_HERE")
r, _ := http.NewRequest("GET", "https://slack.com/api/users.lookupByEmail", strings.NewReader(data.Encode()))
r.Header.Add("Content-Type", "application/x-www-form-urlencoded")
r.Header.Add("Content-Length", strconv.Itoa(len(data.Encode())))

How to send JSON inside JSON in POST

I am trying to send the following data via a POST HTTP request to an API:
{
"client_interface":{
"source_address":source,
"destination_address":destn,
"message":encrypted_msg,
"business_event_url":settings.Message_CallbackURL
},
"server_interface":{
"message_id":msg_id
}
}
The API is responding with the following error:
{
"Meta":{
"Requestid":12301343169471000
},
"Error":{
"Message":"Request body contains badly-formed JSON (at position 51)",
"Param":""
}
}
CODE:
apiUrl := "http://example.com"
tr := &http.Transport{
TLSClientConfig: &tls.Config{InsecureSkipVerify : true},
}
jsonStr := []byte(`{
"client_interface": {
"source_address": source,
"destination_address": destn,
"message": encrypted_msg,
"business_event_url": settings.Message_CallbackURL
},
"server_interface": {
"message_id": msg_id
}
}`)
req, err := http.NewRequest("POST", apiUrl, bytes.NewBuffer(jsonStr))
fmt.Println("req..........",req)
if err!=nil{
log.Println("err in http req..............",err)
}
req.Header.Set("Content-Type", "application/json")
req.Header.Set("requestid", msg_id)
req.Header.Set("Authorization", "Bearer "+conn_token)
client := &http.Client{Transport: tr}
resp, err := client.Do(req)
if resp!=nil{
body, _ := ioutil.ReadAll(resp.Body)
}
Using struct :
package main
import (
"fmt"
"net/http"
"io/ioutil"
"bytes"
//"crypto/tls"
"encoding/json"
)
type client_interface struct {
source_address string `json:"string"`
destination_address uint64 `json:"uint64"`
message string `json:"string"`
business_event_url string `json:"string"`
}
type server_interface struct {
message_id uint64 `json:"uint64"`
}
type data struct {
client_interface client_interface `json:"client_interface"`
server_interface server_interface `json:"server_interface"`
}
func main() {
url := "https://example.com"
fmt.Println("URL:>", url)
client_interface := client_interface{}
server_interface := server_interface{}
client_interface.source_address="1"
client_interface.destination_address=1111111111
client_interface.message="khsjhdjks"
client_interface.business_event_url="http://callbackurl-hdfc"
server_interface.message_id=8210993557215399651
fmt.Println("server_interface..........",server_interface)
fmt.Println("client_interface..........",client_interface)
body1 := &data{
client_interface: client_interface,
server_interface: server_interface,
}
fmt.Println("body1..........",body1)
t,e:=json.Marshal(body1)
fmt.Println("t..........",t)
fmt.Println("e..........",e)
req, err := http.NewRequest("POST", url, bytes.NewReader(t))
fmt.Println("req......",req)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("requestid", "8210993557215399651")
req.Header.Set("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJhcGk6Ly90cC1kZXYtdGFubGEtYXBpIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwLyIsImlhdCI6MTU3NTg5MTI3NCwibmJmIjoxNTc1ODkxMjc0LCJleHAiOjE1NzU4OTUxNzQsImFjciI6IjEiLCJhaW8iOiI0MlZnWU9EY3JjenlhZXIxdkRMRDVlNHVtWUxha1UrRUplOVYrZGVlRFgrOTNUMytNRGNBIiwiYW1yIjpbInB3ZCJdLCJhcHBpZCI6IjFmMjI1N2ZlLWIzYjktNGQ2Ny05M2YyLWRjNjM2N2Q2MGM4MCIsImFwcGlkYWNyIjoiMCIsImlwYWRkciI6IjE0LjE0My4xODcuMjUwIiwibmFtZSI6ImhkZmMuMTEiLCJvaWQiOiIzOGQxMGFlNS01OGYyLTQ0NjUtYTFkOC04YTc0NDAzYjc5MmEiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiIzNDdUd0ZwYUw5MDhmOXlNRWlGOWNHMU84THFQYmJxZk45VzhyQWVEX1prIiwidGlkIjoiY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwIiwidW5pcXVlX25hbWUiOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJuS05TTXRsT3VFeXMtQjRIOGJ3TEFRIiwidmVyIjoiMS4wIn0.F5H9WCOktau3JaqNyWM91A5jFpJ9eJE99fBWvqDq9kOfCk3OCJnHFKXtIaIA7MoqbxWpNZt1yWpVKuw8gd2Lg_9nfUvvXts2DJHVQN0EqQmFUyWTzhdLW8ZVi6E9RtXK2aEWrI2TVceL5C2wbYOQYfvV4LzjTuNbs6k_20cQ0nD6oO1Id16VVFQWy9yKvpDzsTrvlQdFBZeohIfyL9XWKa8DOk0gxe4bjC7OFmuMsF3FZE5XPaQPHOJ3ejlZJiApml2TlRHnvLpkn1biE3NTAu9aO2lE262lyLg8ZaU0sbPuQaS8P797a-outxLvKEMh07895mA9g6vMxEdRV9X2eA")
client := &http.Client{}
resp, err := client.Do(req)
fmt.Println("err.............",err)
if err != nil {
panic(err)
}
defer resp.Body.Close()
fmt.Println("response Status:", resp.Status)
fmt.Println("response Headers:", resp.Header)
body, _ := ioutil.ReadAll(resp.Body)
fmt.Println("response Body:", string(body))
}
First of all: please use gofmt
Your first code can't work because golang doesn't substitute variables inside string. It's better to use structs.
With structs it's not working because you named struct fields from lower case latter, it means "private" fields in go. So encoding/json package can't access them and just skip them. Use Capital first letters.
Another fix is about 'json:"tag"' - here tag means encoded field name, not type. So instead of 'json:"string"' you should use 'json:"message_id"' or so. You can specify type like this 'json:"field_name,type"' or like this 'json:",type"' but encoding/json guess type on his own.
(I used wrong quotes in tags because of markdown)
I used netcat -l 5000 to listen on 5000 port on localhost and print everything to the terminal. Then I changed url to http://localhost:5000 (not https) to send request to myself.
You need to restart netcat each time to work.
And I made logging a bit more readable.
Also it's CamelCase naming convention in go.
Changed your code a little bit
package main
import (
"bytes"
"flag"
"fmt"
"io/ioutil"
"net/http"
//"crypto/tls"
"encoding/json"
"github.com/nikandfor/tlog"
)
type ClientInterface struct {
SourceAddress string `json:"source_address"`
DestinationAddress uint64 `json:"destination_address"`
Message string `json:"message"`
BusinessEventURL string `json:"business_event_url"`
}
type ServerInterface struct {
MessageID uint64 `json:"message_id"`
}
type Data struct {
ClientInterface ClientInterface `json:"client_interface"`
ServerInterface ServerInterface `json:"server_interface"`
}
var (
// use command line flag. so run like so:
// go run ./file.go -addr https://example.com
addr = flag.String("addr", "http://localhost:5000", "address to send data to")
)
func main() {
flag.Parse() // DO NOT FORGET TO PARSE FLAGS
fmt.Println("URL:>", *addr)
clientInterface := ClientInterface{
SourceAddress: "1",
DestinationAddress: 8886121111,
Message: "khsjhdjks",
BusinessEventURL: "http://callbackurl-hdfc",
}
serverInterface := ServerInterface{
MessageID: 8210993557215399651,
}
tlog.Printf("server_interface %+v", serverInterface)
tlog.Printf("client_interface %+v", clientInterface)
body1 := &Data{
ClientInterface: clientInterface,
ServerInterface: serverInterface,
}
tlog.Printf("body %+v", body1)
t, err := json.Marshal(body1)
if err != nil {
panic(err)
}
tlog.Printf("marshalled: %s", t)
req, err := http.NewRequest("POST", *addr, bytes.NewReader(t))
tlog.Printf("req %v", req)
req.Header.Set("Content-Type", "application/json")
req.Header.Set("requestid", "8210993557215399651")
req.Header.Set("Authorization", "Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJhcGk6Ly90cC1kZXYtdGFubGEtYXBpIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwLyIsImlhdCI6MTU3NTg5MTI3NCwibmJmIjoxNTc1ODkxMjc0LCJleHAiOjE1NzU4OTUxNzQsImFjciI6IjEiLCJhaW8iOiI0MlZnWU9EY3JjenlhZXIxdkRMRDVlNHVtWUxha1UrRUplOVYrZGVlRFgrOTNUMytNRGNBIiwiYW1yIjpbInB3ZCJdLCJhcHBpZCI6IjFmMjI1N2ZlLWIzYjktNGQ2Ny05M2YyLWRjNjM2N2Q2MGM4MCIsImFwcGlkYWNyIjoiMCIsImlwYWRkciI6IjE0LjE0My4xODcuMjUwIiwibmFtZSI6ImhkZmMuMTEiLCJvaWQiOiIzOGQxMGFlNS01OGYyLTQ0NjUtYTFkOC04YTc0NDAzYjc5MmEiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiIzNDdUd0ZwYUw5MDhmOXlNRWlGOWNHMU84THFQYmJxZk45VzhyQWVEX1prIiwidGlkIjoiY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwIiwidW5pcXVlX25hbWUiOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJuS05TTXRsT3VFeXMtQjRIOGJ3TEFRIiwidmVyIjoiMS4wIn0.F5H9WCOktau3JaqNyWM91A5jFpJ9eJE99fBWvqDq9kOfCk3OCJnHFKXtIaIA7MoqbxWpNZt1yWpVKuw8gd2Lg_9nfUvvXts2DJHVQN0EqQmFUyWTzhdLW8ZVi6E9RtXK2aEWrI2TVceL5C2wbYOQYfvV4LzjTuNbs6k_20cQ0nD6oO1Id16VVFQWy9yKvpDzsTrvlQdFBZeohIfyL9XWKa8DOk0gxe4bjC7OFmuMsF3FZE5XPaQPHOJ3ejlZJiApml2TlRHnvLpkn1biE3NTAu9aO2lE262lyLg8ZaU0sbPuQaS8P797a-outxLvKEMh07895mA9g6vMxEdRV9X2eA")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
tlog.Printf("response Status: %v", resp.Status)
tlog.Printf("response Headers: %v", resp.Header)
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
tlog.Printf("response Body: %s", string(body))
}
$ nc -l 5000
POST / HTTP/1.1
Host: localhost:5000
User-Agent: Go-http-client/1.1
Content-Length: 92
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyIsImtpZCI6IkJCOENlRlZxeWFHckdOdWVoSklpTDRkZmp6dyJ9.eyJhdWQiOiJhcGk6Ly90cC1kZXYtdGFubGEtYXBpIiwiaXNzIjoiaHR0cHM6Ly9zdHMud2luZG93cy5uZXQvY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwLyIsImlhdCI6MTU3NTg5MTI3NCwibmJmIjoxNTc1ODkxMjc0LCJleHAiOjE1NzU4OTUxNzQsImFjciI6IjEiLCJhaW8iOiI0MlZnWU9EY3JjenlhZXIxdkRMRDVlNHVtWUxha1UrRUplOVYrZGVlRFgrOTNUMytNRGNBIiwiYW1yIjpbInB3ZCJdLCJhcHBpZCI6IjFmMjI1N2ZlLWIzYjktNGQ2Ny05M2YyLWRjNjM2N2Q2MGM4MCIsImFwcGlkYWNyIjoiMCIsImlwYWRkciI6IjE0LjE0My4xODcuMjUwIiwibmFtZSI6ImhkZmMuMTEiLCJvaWQiOiIzOGQxMGFlNS01OGYyLTQ0NjUtYTFkOC04YTc0NDAzYjc5MmEiLCJzY3AiOiJ1c2VyX2ltcGVyc29uYXRpb24iLCJzdWIiOiIzNDdUd0ZwYUw5MDhmOXlNRWlGOWNHMU84THFQYmJxZk45VzhyQWVEX1prIiwidGlkIjoiY2JhYThhYmItZTcwZi00YmI4LWIwNDQtZmZiZjAwNzk0NzkwIiwidW5pcXVlX25hbWUiOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1cG4iOiJoZGZjLjExQFRhbmxhUHJvZHVjdC5vbm1pY3Jvc29mdC5jb20iLCJ1dGkiOiJuS05TTXRsT3VFeXMtQjRIOGJ3TEFRIiwidmVyIjoiMS4wIn0.F5H9WCOktau3JaqNyWM91A5jFpJ9eJE99fBWvqDq9kOfCk3OCJnHFKXtIaIA7MoqbxWpNZt1yWpVKuw8gd2Lg_9nfUvvXts2DJHVQN0EqQmFUyWTzhdLW8ZVi6E9RtXK2aEWrI2TVceL5C2wbYOQYfvV4LzjTuNbs6k_20cQ0nD6oO1Id16VVFQWy9yKvpDzsTrvlQdFBZeohIfyL9XWKa8DOk0gxe4bjC7OFmuMsF3FZE5XPaQPHOJ3ejlZJiApml2TlRHnvLpkn1biE3NTAu9aO2lE262lyLg8ZaU0sbPuQaS8P797a-outxLvKEMh07895mA9g6vMxEdRV9X2eA
Content-Type: application/json
Requestid: 8210993557215399651
Accept-Encoding: gzip
{"client_interface":{"source_address":"1","destination_address":8886121111,"message":"khsjhdjks","business_event_url":"http://callbackurl-hdfc"},"server_interface":{"message_id":8210993557215399651}}
Is it what you've expected?
And the last. I strongly suggest you to read https://golang.org/doc/effective_go.html

How do you do a HTTP POST with digest authentication in Golang?

I am trying to use the Gerrit API that requires digest authentication. After reading up some I know I am supposed to make a request, get a 401, then use the realm and nonce and maybe other headers to then create the actual request authentication using MD5. I have found some examples on digest but they all seem to be the server side, not the client side.
I mostly followed what Wikipedia said about how to make a request then looked at the details of a verbose curl request to figure out the parts curl -v --digest --user username:password http://url.com/api. Here are the parts. You need to make a request, receive a 401 unauthorized, then compute an authorization header using MD5 sums based on the nonce and realm in the headers of the unauthorized request.
import (
"bytes"
"crypto/md5"
"crypto/rand"
"encoding/hex"
"fmt"
"io"
"io/ioutil"
"log"
"net/http"
"strings"
)
func digestPost(host string, uri string, postBody []byte) bool {
url := host + uri
method := "POST"
req, err := http.NewRequest(method, url, nil)
req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusUnauthorized {
log.Printf("Recieved status code '%v' auth skipped", resp.StatusCode)
return true
}
digestParts := digestParts(resp)
digestParts["uri"] = uri
digestParts["method"] = method
digestParts["username"] = "username"
digestParts["password"] = "password"
req, err = http.NewRequest(method, url, bytes.NewBuffer(postBody))
req.Header.Set("Authorization", getDigestAuthrization(digestParts))
req.Header.Set("Content-Type", "application/json")
resp, err = client.Do(req)
if err != nil {
panic(err)
}
defer resp.Body.Close()
if resp.StatusCode != http.StatusOK {
body, err := ioutil.ReadAll(resp.Body)
if err != nil {
panic(err)
}
log.Println("response body: ", string(body))
return false
}
return true
}
func digestParts(resp *http.Response) map[string]string {
result := map[string]string{}
if len(resp.Header["Www-Authenticate"]) > 0 {
wantedHeaders := []string{"nonce", "realm", "qop"}
responseHeaders := strings.Split(resp.Header["Www-Authenticate"][0], ",")
for _, r := range responseHeaders {
for _, w := range wantedHeaders {
if strings.Contains(r, w) {
result[w] = strings.Split(r, `"`)[1]
}
}
}
}
return result
}
func getMD5(text string) string {
hasher := md5.New()
hasher.Write([]byte(text))
return hex.EncodeToString(hasher.Sum(nil))
}
func getCnonce() string {
b := make([]byte, 8)
io.ReadFull(rand.Reader, b)
return fmt.Sprintf("%x", b)[:16]
}
func getDigestAuthrization(digestParts map[string]string) string {
d := digestParts
ha1 := getMD5(d["username"] + ":" + d["realm"] + ":" + d["password"])
ha2 := getMD5(d["method"] + ":" + d["uri"])
nonceCount := 00000001
cnonce := getCnonce()
response := getMD5(fmt.Sprintf("%s:%s:%v:%s:%s:%s", ha1, d["nonce"], nonceCount, cnonce, d["qop"], ha2))
authorization := fmt.Sprintf(`Digest username="%s", realm="%s", nonce="%s", uri="%s", cnonce="%s", nc="%v", qop="%s", response="%s"`,
d["username"], d["realm"], d["nonce"], d["uri"], cnonce, nonceCount, d["qop"], response)
return authorization
}

Resources