Golang auth. Parsing JWT: illegal base64 data at input byte 0 - json

I am making an authentication form on my web app.
First, I send the form data to server, it produces the token and I store it in the localStorage
Then, I want to redirect from form page to the home page. Here is the JavaScript that sends the token to server so that I would be seen as an authorized user on the home page:
const xhr = new XMLHttpRequest()
xhr.open('GET', '/')
xhr.setRequestHeader('Authorization', localStorage.token)
xhr.send()
xhr.onload = () => {
if(xhr.status >= 400) {
console.log("error")
}
}
xhr.onerror = () => {
console.log("error")
}
Then I want to check the token and show the home page. Here is the Golang func for it:
func (h *Handler) Home_page(c *gin.Context) {
header := c.GetHeader("Authorization")
if header != "" {
_, err := h.services.Authorization.ParseToken(header)
if err != nil {
newErrorResponse(c, http.StatusUnauthorized, err.Error())
return
}
c.HTML(
http.StatusOK,
"home_page.gohtml",
gin.H{
"IsAuth": true,
},
)
return
}
}
ParseToken func:
func (s *AuthService) ParseToken(accessToken string) (int, error) {
token, err := jwt.ParseWithClaims(accessToken, &tokenClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("invalid signing method")
}
return []byte(signingKey), nil //signingKey is a string with random elements
})
if err != nil {
return 0, err
}
claims, ok := token.Claims.(*tokenClaims)
if !ok {
return 0, errors.New("token claims are not of type *tokenClaims")
}
return claims.UserId, nil
}
The problem is that I get this error:
ERRO[0001] illegal base64 data at input byte 0
I already checked the token on the jwt.io, it shows that the token is verified, maybe the problem is in the type of string that I am passing as a token.
Another problem is that if I don't check the error returning from token validation, the home page doesn't show with this error:
http: panic serving [::1]:50490: write tcp [::1]:8083->[::1]:50490: write: broken pipe
I am new to Golang, was struggling with this problem, though it seems typical.
I would be very thankful for any help on how to process the token or to redirect from the authentication form!
Edit: I am using the https://github.com/golang-jwt/jwt/v4

Somehow, this issue:
illegal base64 data at input byte 0
Was solved by changing the way I stored the token. I was storing in the local storage like this:
localStorage.setItem('token', data.token)
I changed it to the next line and the error disappeared:
localStorage.token = data.token
(data is the JSON with token that my server returns)
Now if I log the value of token it appears without commas. #Crowman, thank you for answer!
Edit: The second issue with broken pipe occurred, because I was not waiting for the answer on the client side. So now I changed the JS code to wait for the response but still I am struggling with how to show the html page that I receive from server using JavaScript.

Related

Golang http client always performs get requests and does not post

I'm currently experiencing a problem that really frustrates me and where i absolutely can't see anny issues with my code.
What i'm trying to achieve is to send a http POST message to a mockup of an iDrac i wrote (both softwares written in golang) to control the mockup's powerstate, but no matter what i configure for the request, the mockup always receives get requests with an empty body.
The function i create and send the request with:
func (iDrac *IDrac) SetPowerstate(powerstate string) error {
//Create reset type json string
var jsonStr = `{"ResetType":"` + powerstate + `"}`
//Create the request with auth header
req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
if reqErr != nil {
return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
}
req.Header.Set("Content-Type", "application/json; charset=UTF-8")
//Make the request
resp, doErr := http.DefaultClient.Do(req)
if doErr != nil {
return fmt.Errorf("COULD_NOT_SEND_POST_REQUEST_TO_IDRAC_API" + doErr.Error())
}
//Check if the request was successful
if resp.StatusCode != 200 {
return fmt.Errorf("COULD_NOT_CHANGE_SERVER_POWER_STATUS_OVER_IDRAC HTTP:" + resp.Status)
}
return nil
}
The helper function i use to build the request with:
func (iDrac *IDrac) buildHttpRequestWithAuthorizationHeader(method string, url string, content string) (*http.Request, error) {
req, err := http.NewRequest(method, url, bytes.NewBuffer([]byte(content)))
if err != nil {
return nil, err
}
req.Header.Add("Authorization", "Basic "+iDrac.hashedCredentials)
return req, nil
}
And finally the function where the mockup proccesses the request:
func handlePerformReset(w http.ResponseWriter, r *http.Request) {
garbagelog.Log("Handling reset perform request from " + r.RemoteAddr)
if r.Method != http.MethodPost {
garbagelog.Log("Invalid http method! Got " + r.Method + " expected POST")
w.WriteHeader(405)
return
}
if !checkAuthorization(r.BasicAuth()) {
w.WriteHeader(401)
return
}
var resetType nidrac.ResetType
err := json.NewDecoder(r.Body).Decode(&resetType)
if err != nil {
garbagelog.Log("Could not decode reset type: " + err.Error())
w.WriteHeader(422)
return
}
iDracMock.PerformResetAction(resetType.ResetType)
garbagelog.Log(">>>>SEVERSTATE: " + iDracMock.PowerState().PowerState + "<<<<")
w.WriteHeader(200)
}
The type the iDrac mock tries to convert the body to:
type ResetType struct {
ResetType string
}
It works flawlessly when i try to reach the endpoint with postman:
iDrac mockup log confirming the successful request
Postnam request configuration:
Postman request configuration
But it somehow does not work when i try making the request with go code:
iDrac mockup log saying that the http method is invalid (because it's get instead of post)
I spent two hours trying to find a solution but i somehow did not find a post with someone having the same problem that i have.
Edit: Corrected old code. The problem remains even with the silly mistake fixed:
//Create the request with auth header
req, reqErr := iDrac.buildHttpRequestWithAuthorizationHeader(http.MethodPost, "https://"+iDrac.hostAddress+apiBaseURL+apiChassisURL+apiChassisResetAction, jsonStr)
if reqErr != nil {
return fmt.Errorf("COULD_NOT_CREATE_REQUEST: " + reqErr.Error())
}
How i build the routes in the iDrac mockup:
http.Handle("/", http.HandlerFunc(handleDefault))
http.Handle("/reset", http.HandlerFunc(handleReset))
http.Handle("/redfish/v1/Systems/System.Embedded.1", http.HandlerFunc(handlePowerStateGet))
http.Handle("/redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset", http.HandlerFunc(handlePerformReset))
garbagelog.Log("Starting webserver")
err := http.ListenAndServeTLS(":443", currentConfig.CertFile, currentConfig.PrivKeyFile, nil)
if err != nil {
garbagelog.Log("Could not serve TLS: " + err.Error())
}
In both requests, the one created in my iDrac comms module and the one received by the iDrac mockup, did confirm that the requested path is:
r.URL.Path = /redfish/v1/Systems/System.Embedded.1/Actions/ComputerSystem.Reset
I found the problem:
The constants i built the urls with were defined like this:
const (
apiBaseURL = "/redfish/v1/"
apiChassisURL = "/Systems/System.Embedded.1"
apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)
Leading to a url that looks like this:
https://host/redfish/v1//Systems/System.Embedded.1/Actions/ComputerSystem.Reset
(Notice the double // between v1 and Systems)
So i've fixed it:
const (
apiBaseURL = "/redfish/v1"
apiChassisURL = "/Systems/System.Embedded.1"
apiChassisResetAction = "/Actions/ComputerSystem.Reset"
)
And everything works correctly:
Test results showing that every test was successful
I thank everyone for their input for helping me not lose my mind completely.
based on your screenshot, I can see you are using a "POST" request in your postman, but in your code is "GET".
I think nothing wrong with the code but the method only :)

How can I initiate a download of an external Image (AWS Signed URL) on button press

I'm trying to download an image from an s3 Presigned URL on a button click (from the client-side Nextjs).
Here's the background:
I'm using Golang on the backend.
Nextjs React framework on the front end.
I started by trying to make a fetch call to the backend (since my AWS creds are stored accessible only via the back end). The backend would then download the image, and a download stream of mime-type "image/tiff":
Backend Handler (batch is just system-specific):
// GetDownloadBatchFile handles GET requests to download the batch output file
func GetDownloadBatchFile(ctx *atreugo.RequestCtx) error {
// Parse Batch ID
batchIntfID := ctx.UserValue("batch_id")
batchStrID, isStr := batchIntfID.(string)
if !isStr {
fmt.Println("User error. No batch ID provided")
}
batchIntID, err := strconv.Atoi(batchStrID)
if err != nil {
fmt.Println("User error. Invalid batch ID provided")
}
// Get Batch
batch, err := models.GetBatchByID(batchIntID)
if err != nil {
fmt.Println("User Error. No batch associated with provided batch ID")
}
// Get Batch Setting
batch.BatchSetting, err = models.GetBatchSettingByID(batch.BatchSettingID)
if err != nil {
fmt.Println("User Error. No batch setting associated with provided batch setting ID")
}
// Get signed url and download
req, _ := util.AWSS3Svc.GetObjectRequest(&s3.GetObjectInput{
Bucket: aws.String(batch.BatchSetting.AWSBucket),
Key: aws.String(batch.OutputFileKey),
})
// Get and Download Presigned URL
URL, err := req.Presign(time.Hour * 1)
if err != nil {
fmt.Println("Whoops, failed to generate signed s3 preview link.")
}
resp, err := http.Get(URL)
if err != nil {
fmt.Println("System Error. Unable to download image file.")
}
batchImage, err := tiff.Decode(resp.Body)
if err != nil {
fmt.Println(err)
}
ctx.Response.Header.Add("Content-Disposition", "attachment; filename=logo")
ctx.Response.Header.SetContentType("image/tiff")
err = tiff.Encode(ctx.Response.BodyWriter(), batchImage, &tiff.Options{Compression: tiff.Uncompressed}, uint32(batch.BatchSetting.DPI))
if err != nil {
return ctx.TextResponse(`ENCODE ERROR: ` + err.Error())
}
return nil
}
This would normally work great, but the problem is my file is almost 1 GB! It takes too long to download and then the response just times out.
I'm thinking I should just send the presigned URL to the front end and download from that, but I can't figure out how to download from the URL.
All ideas welcome here, and also any information about how to download a url on button press. Thanks!
I can't figure out how to download from the URL
I'm unclear on how you're trying currently trying to do this in Next.js, so I'll answer generally.
I would use the Next/image component and feed the signed URL to its src prop once it is available in the response from your backend.
import Image from "next/image";
import { useState } from "react";
// If signedSrc is available, render the image
const MyComponent = () => {
const [signedSrc, signedSrcUpdate] = useState(null);
return (
<>
<button onClick={async () => {
// fetch signed image url
const response = await fetch('https://your-api/signedimageurlendpoint';
signedSrcUpdate(response.data);
}}
{signedSrc &&
<Image
src={signedSrc}
/>
}
</>
)
}
I found a solution that worked for me. I created a function and used plain javascript to download from a readstream (I also got my file to be much smaller at only 90mb), and set the button to load during the request. It's a long wait, but it's manageable. Here's the link to how to download a readstream in js.
How to download a ReadableStream on the browser that has been returned from fetch

Getting error on PUT "Body length 0" using net/http

I am using this Go API client on my app https://github.com/heroku/docker-registry-client to interact with a docker registry using Go. The case is that internally that is having some issue when does a PUT request using the package "net/http".
When I run the following code I am getting this as error Put url: http: ContentLength=2821 with Body length 0. So it seems that net/http Client.Do() function is not getting the body I set at some point of the function. But as you can se right on the code below at some point I still have the JSON content that I want to send in a []byte.
body, err := manifest.MarshalJSON()
if err != nil {
return err
}
log.Println(string(body)) // I get the JSON data back here
req, err := http.NewRequest("PUT", url, bytes.NewReader(body))
if err != nil {
return err
}
req.Header.Set("Content-Type", manifestV2.MediaTypeManifest)
resp, err := registry.Client.Do(req)
if resp != nil {
defer resp.Body.Close()
}
return err
As far as I have digged into it, the error comes from the net/http Client.do() function (golang.org/src/net/http/client.go line 514), and I'd say the error is triggered from Request.GetBody() function (from line 591 on Client).
So still trying to go deeper and do some tests to find out what is going on here.
Any clue?
In case the error is given by the server I'll have to get something like this, but in the response body and no errors on the net/http Client.Do() call.
Content-Length: <length>
Content-Type: application/json; charset=utf-8
{
"errors:" [
{
"code": <error code>,
"message": "<error message>",
"detail": ...
},
...
]
}
Thank you so much!
Cheers
The issue is modified RoundTripper in https://github.com/heroku/docker-registry-client makes a redirect call to registry after getting authentication token.when it tries to redirect call to registry ,body is empty because http.request body is a buffer and once it is read in first call,buffer becomes empty and there is no body content to send in redirect call.
To fix it:
if req.Method == "PUT" || req.Method == "POST" {
if req.Body != nil {
reUseBody, _ = req.GetBody()
}
}
add this in https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L17 and
if req.Method == "PUT" || req.Method == "POST" {
if reUseBody != nil {
req.Body = reUseBody
}
}
in here https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L72
and change this function signature https://github.com/heroku/docker-registry-client/blob/master/registry/tokentransport.go#L71
to accept extra argument.
func (t *TokenTransport) retry(req *http.Request, token string, reUseBody io.ReadCloser)

Invalid Json web token in Go

I am trying to make a Json web token authentication system with Go however I cant seem to get the parsing of the web token working.
The error occurs in the following function.
func RequireTokenAuthentication(rw http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
authBackend := InitJWTAuthenticationBackend()
jwtString := req.Header.Get("Authorization")
token, err := jwt.Parse(jwtString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodRSA); !ok {
log.Println("Unexpected signing method")
return nil, fmt.Errorf("Unexpected signing method: %v", token.Header["alg"])
} else {
log.Println("The token has been successfully returned")
return authBackend.PublicKey, nil
}
})
log.Println(token)
log.Println(token.Valid)
if err == nil && token.Valid && !authBackend.IsInBlacklist(req.Header.Get("Authorization")) {
next(rw, req)
} else {
rw.WriteHeader(http.StatusUnauthorized)
log.P
rintln("Status unauthorized RequireTokenAuthentication")
}
}
returns the following log
[negroni] Started GET /test/hello
2016/09/13 01:34:46 &{Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NzM5NzQ4OTAsImlhdCI6MTQ3MzcxNTY5MCwic3ViIjoiIn0.mnwEwdR8nuvdLo_4Ie43me7iph2LeSj1uikokgD6VJB7isjFPShN8E7eQr4GKwuIiLTi34_i6iJRpmx9qrPugkzvsoxX44qlFi6M7FDhVySRiYbBQwTCvKCpvhnsK8BHJyEgy813aaxOMK6sKZJoaKs5JYUvnNZdNqmENYj1BM6FdbGP-oLHuR_CJK0Pym1NMhv9zLI1rpJOGu4mfj1t4tHYZAEGirPnzYMamtrK6TyEFE6Xi4voEEadq7hXvWREg6wNSQsYgww8uOaIWLy1yLbhTkPmT8zfRwLLYLqS_UuZ0xIaSWO1mF2plvOzz1WlF3ZEHLS31T1egB1XL4WTNQe <nil> map[] <nil> false}
2016/09/13 01:34:46 false
2016/09/13 01:34:46 Status unauthorized RequireTokenAuthentication
[negroni] Completed 401 Unauthorized in 71.628ms
and here is the cURL that I am using to initiate it
curl -H "Authorization: Bearer eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NzM5NzQ4OTAsImlhdCI6MTQ3MzcxNTY5MCwic3ViIjoiIn0.mnwEwdR8nuvdLo_4Ie43me7iph2LeSj1uikokgD6VJB7isjFPShN8E7eQr4GKwuIiLTi34_i6iJRpmx9qrPugkzvsoxX44qlFi6M7FDhVySRiYbBQwTCvKCpvhnsK8BHJyEgy813aaxOMK6sKZJoaKs5JYUvnNZdNqmENYj1BM6FdbGP-oLHuR_CJK0Pym1NMhv9zLI1rpJOGu4mfj1t4tHYZAEGirPnzYMamtrK6TyEFE6Xi4voEEadq7hXvWREg6wNSQsYgww8uOaIWLy1yLbhTkPmT8zfRwLLYLqS_UuZ0xIaSWO1mF2plvOzz1WlF3ZEHLS31T1egB1XL4WTNQe" http://localhost:5000/test/hello
I have also tried curl without Bearer
curl -H "Authorization:eyJhbGciOiJSUzUxMiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE0NzM5NzQ4OTAsImlhdCI6MTQ3MzcxNTY5MCwic3ViIjoiIn0.mnwEwdR8nuvdLo_4Ie43me7iph2LeSj1uikokgD6VJB7isjFPShN8E7eQr4GKwuIiLTi34_i6iJRpmx9qrPugkzvsoxX44qlFi6M7FDhVySRiYbBQwTCvKCpvhnsK8BHJyEgy813aaxOMK6sKZJoaKs5JYUvnNZdNqmENYj1BM6FdbGP-oLHuR_CJK0Pym1NMhv9zLI1rpJOGu4mfj1t4tHYZAEGirPnzYMamtrK6TyEFE6Xi4voEEadq7hXvWREg6wNSQsYgww8uOaIWLy1yLbhTkPmT8zfRwLLYLqS_UuZ0xIaSWO1mF2plvOzz1WlF3ZEHLS31T1egB1XL4WTNQe" http://localhost:5000/test/hello
The error is occurring because the token is invalid token.Valid = false I have generated it using the following process.
Here is the router
router.HandleFunc("/token-auth", controllers.Login).Methods("POST")
Here is the login controller
func Login(w http.ResponseWriter, r *http.Request) {
requestUser := new(models.User)
decoder := json.NewDecoder(r.Body)
decoder.Decode(&requestUser)
responseStatus, token := utils.Login(requestUser) //here the util file seen below is used
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(responseStatus)
w.Write(token)
}
This is the util file
func Login(requestUser *models.User) (int, []byte) {
authBackend := authentication.InitJWTAuthenticationBackend()
if authBackend.Authenticate(requestUser) {
token, err := authBackend.GenerateToken(requestUser.UUID)
if err != nil {
return http.StatusInternalServerError, []byte("")
} else {
response, _ := json.Marshal(parameters.TokenAuthentication{token})
return http.StatusOK, response
}
}
return http.StatusUnauthorized, []byte("")
}
and here is the method used to generate the token
func (backend *JWTAuthenticationBackend) GenerateToken(userUUID string) (string, error) {
token := jwt.New(jwt.SigningMethodRS512)
claims := token.Claims.(jwt.MapClaims)
claims["exp"] = time.Now().Add(time.Hour * time.Duration(settings.Get().JWTExpirationDelta)).Unix()
claims["iat"] = time.Now().Unix()
claims["sub"] = userUUID
tokenString, err := token.SignedString(backend.privateKey)
if err != nil {
panic(err)
return "", err
}
return tokenString, nil
}
How do I fix the Token Parsing system so that the token is valid?
If you need any additional information I would be more than happy to make an edit with the respective information.
Thank
The error returned by jwt.Parse() says
tokenstring should not contain 'bearer '
So if you remove "Bearer ":
jwtString = strings.Split(jwtString, "Bearer ")[1]
you get a bit further
The token has been successfully returned
however now there's a new error:
key is of invalid type
Sorry it's not a complete answer!
key is of invalid type
type in this context is referring to the dynamic data-type in Go.
For SigningMethodRSA, the public key must be of type *rsa.PublicKey which can be constructed by calling jwt.ParseRSAPublicKeyFromPEM().
The key value returned to the parser might be created with something like:
keyStruct, _ := jwt.ParseRSAPublicKeyFromPEM(myPublicKeyString)
See:
https://github.com/dgrijalva/jwt-go#signing-methods-and-key-types
https://godoc.org/github.com/dgrijalva/jwt-go#SigningMethodRSA
https://godoc.org/github.com/dgrijalva/jwt-go#ParseRSAPublicKeyFromPEM
Related:
How to generate JWT token always through invalid key type error

JSON RPC Client Go

I have a python server serving response through JSON-RPC. Here is a sample response from the server.
'{"jsonrpc": "2.0", "result": "Peer 1: local 10.10.0.2 remote 10.10.0.1 state CONNECT\\nPeer 2: local 10.10.0.18 remote 10.10.0.17 state ESTABLISHED\\nPeer 3: local 10.10.0.10 remote 10.10.0.9 state ESTABLISHED", "id": "839732f9-cf36-46ff-8b9b-6120250d9ce5"}'
Here is the request I need to send to the server:
'{"method":"echo","jsonrpc":"2.0","params":["test_params"],"id":"839732f9-cf36-46ff-8b9b-6120250d9ce5"}'
Here is my client with go language:
package main
import (
"fmt"
"log"
"net"
"net/rpc/jsonrpc"
)
type Args struct {
jsonrpc, id string
}
func main() {
conn, err := net.Dial("tcp", "11.21.22.221:8080")
if err != nil {
panic(err)
}
defer conn.Close()
args := Args{"2.0", "d87198f0-af92-49f8-9a7d-ab8bed5c4d17"}
var reply string
c := jsonrpc.NewClient(conn)
err = c.Call("echo", args, &reply)
if err != nil {
log.Fatal("error:", err)
}
fmt.Printf("Response: %d", reply)
}
But, when I run this client, it is not sending anything in the params. Instead it is sending empty params like this:
'{"method":"echo","params":[{}],"id":0}\n
Can somebody help me telling what mistake I am making? I am a newbie to go language.
Thanks.
I don't think what you are doing is possible using the client as provided by go because the private clientRequest struct is currently defined as:
type clientRequest struct {
Method string `json:"method"`
Params [1]interface{} `json:"params"`
Id uint64 `json:"id"`
}
What you pass into Call as args is stuck into Params and note how there is no "Version `json:"jsonrpc"`" inside of that struct.
AFAICT (which may be wrong, this is my first time reading through this code) you would need to implement your own ClientCodec. You could probably get away with copying most (all) of the parts out of the stdlib and add the field to the clientRequest above. ;-)