How to properly read errors from golang oauth2 - go

token, err := googleOauthConfig.Exchange(context.Background(), code)
if err != nil {
fmt.Fprintf(w, "Err: %+v", err)
}
The output of the fprintf is:
Err: oauth2: cannot fetch token: 401 Unauthorized
Response: {"error":"code_already_used","error_description":"code_already_used"}
I want to check if "error" = "code_already_used". For the life of me, I can't sort out how.
How do I check/return/read "error" or "error_description" of err?
I've looked at the oauth2 code and it's a bit above me.
// retrieveToken takes a *Config and uses that to retrieve an *internal.Token.
// This token is then mapped from *internal.Token into an *oauth2.Token which is returned along
// with an error..
func retrieveToken(ctx context.Context, c *Config, v url.Values) (*Token, error) {
tk, err := internal.RetrieveToken(ctx, c.ClientID, c.ClientSecret, c.Endpoint.TokenURL, v)
if err != nil {
if rErr, ok := err.(*internal.RetrieveError); ok {
return nil, (*RetrieveError)(rErr)
}
return nil, err
}
return tokenFromInternal(tk), nil
}
How guess I'm trying to see the (*RetrieveError) part. Right?
THANK YOU!

The expression:
(*RetrieveError)(rErr)
converts therErr's type from *internal.RetrieveError to *RetrieveError. And since RetrieveError is declared in the oauth2 package, you can type assert the error you receive to *oauth2.RetrieveError to get the details. The details are contained in that type's Body field as a slice of bytes.
Since a slice of bytes is not the best format to be inspected and in your case it seems like the bytes contain a json object you can make your life easier by predefining a type into which you can unmarshal those details.
That is:
type ErrorDetails struct {
Error string `json:"error"`
ErrorDescription string `json:"error_description"`
}
token, err := googleOauthConfig.Exchange(context.Background(), code)
if err != nil {
fmt.Fprintf(w, "Err: %+v", err)
if rErr, ok := err.(*oauth2.RetrieveError); ok {
details := new(ErrorDetails)
if err := json.Unmarshal(rErr.Body, details); err != nil {
panic(err)
}
fmt.Println(details.Error, details.ErrorDescription)
}
}

Can do like this.
arr := strings.Split(err.Error(), "\n")
str := strings.Replace(arr[1], "Response: ", "", 1)
var details ErrorDetails
var json = jsoniter.ConfigCompatibleWithStandardLibrary
err := json.Unmarshal([]byte(str), &details)
if err == nil {
beego.Debug(details.Error)
beego.Debug(details.ErrorDescription)
}

Related

Gmail API shows empty body when getting message

When I send a request to get the email body, the Gmail API returns everything but the body data on the payload object.
Things I've tried so far
The "Watch" method is already implemented and working fine
As you can see from the screenshot, the response shows the "snipped", which means that the message get is working, but the body data and the "raw" field is still empty.
I am using the history id correctly (saving the current one to use for subsequent requests)
upgrade all the dependencies to the latest stable version
Am I missing anything?
func GetEmail(srv *gmail.Service, historyId uint64) (string, string) {
hist := getHistory(srv, historyId)
for _, h := range hist.History {
for _, m := range h.MessagesAdded {
id := m.Message.Id
mailContent, err := srv.Users.Messages.Get("me", id).Format("full").Do()
if err != nil {
log.Println("error when getting mail content: ", err)
}
if mailContent != nil {
if mailContent.Payload != nil {
payload := mailContent.Payload.Body
data, err := b64.RawURLEncoding.DecodeString(payload.Data)
if err != nil {
log.Println("error b64 decoding: ", err)
}
body := string(data)
if len(body) > 0 {
subject := getSubject(mailContent)
log.Println("subject ", subject)
return body, subject
}
}
}
}
}
return "No email to process, something's wrong - GetEmail func", ""
}
If you want the RAW message data then you need to use Format("RAW")
func GetEmail(srv *gmail.Service, messageId string) {
gmailMessageResposne, err := srv.Users.Messages.Get("me", messageId).Format("RAW").Do()
if err != nil {
log.Println("error when getting mail content: ", err)
}
if gmailMessageResposne != nil {
decodedData, err := base64.RawURLEncoding.DecodeString(gmailMessageResposne.Raw)
if err != nil {
log.Println("error b64 decoding: ", err)
}
fmt.Printf("- %s\n", decodedData)
}
}
Good question that was fun 😁
How to read a gmail email body with Go?

Golang BoltDB Delete Key Seemingly Not Working

CentOS 7, Github boltdb/bolt version 1.3.1, go version go1.17.7 linux/amd64
This issue may go to a misunderstanding of how BoltDB works, or maybe I have a bug, or maybe there is an issue. I've used BoltDB before, and have had very good results. Though, I didn't explicly look for this issue. What I'm seeing is that I try to delete a key from a bucket, and the key and its value are deleted in the active db.Update, but it's still there after that db.Update is ended. Looking for any explanation of what might be going on. Seems like this functionality couldn't possibly be broken.
I am using a BoltDB bucket for storing a temporary token associated with an email address for creating a new account. Want to be tidy and clean up old data right away (expired tokens, misused tokens, etc). Pretty standard stuff. The structure for the temporary token is (the key is the temporary token, a 10 digit random character string):
(Temporary Token is the Bucket key)
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
The user enters an email address in a web form and hits 'submit'. That creates a call to the REST service that creates an entry in the temporary token table, like:
"BpLnfgDsc2" => foo#bar.com, 1645650084
The service emails a URL that has the temporary token embedded, and that link takes the user to a form that allows them to put in their email address (again to verify) and new password (twice). Hitting Submit then results in the following code being called from within a web handler:
func checkTokenValid(emailAddress string, tempToken string) error {
var tempTokenData tempTokenStruct
var tempTokenBytes []byte
tempTokenBytes = []byte(tempToken)
db, err := bolt.Open(USER_DB_NAME, 0644, nil)
if err != nil {
return err
}
defer db.Close()
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket := tx.Bucket([]byte("temptokens"))
// The bucket hasn't been created, so there are no stored tokens
if tempTokenBucket == nil {
return errors.New("Not Authorized (1): Please request a new password new/change email from the login page.")
}
// There is no matching token stored in the bucket, so this is an invalid request
tempTokenJSON := tempTokenBucket.Get(tempTokenBytes)
//[I've put a printf here: A]
if tempTokenJSON == nil {
return errors.New("Not Authorized (2): Please request a new password new/change email from the login page.")
}
jsonConvertErr := json.Unmarshal(tempTokenJSON, &tempTokenData)
if jsonConvertErr != nil {
tempTokenBucket.Delete(tempTokenBytes)
return errors.New("Not Authorized (3): Please request a new password new/change email from the login page.")
}
// Check to see if the time is expired, if so, remove the key and indicate error
if tempTokenData.TokenExpiryTime < time.Now().Unix() {
tempTokenBucket.Delete(tempTokenBytes)
//[I've put a printf here: B]
return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")
}
// Check to see if the email addresses match
if emailAddress != tempTokenData.EmailAddress {
tempTokenBucket.Delete(tempTokenBytes)
return errors.New("Not Authorized (5): Please request a new password new/change email from the login page.")
}
tempTokenBucket.Delete(tempTokenBytes)
return nil
})
// This is test code to see if the key was in fact deleted
db.Update(func(tx *bolt.Tx) error {
tempTokenBucket := tx.Bucket([]byte("temptokens"))
tempTokenJSON := tempTokenBucket.Get(tempTokenBytes)
// [I've put a printf here: C]
return nil
})
return err
}
I'm testing with a timed-out token (4), so the idea is that when it encounters that timed out token, it wants to delete this now invalid token from the bucket.
At the A location, it prints:
First Get call token BpLnfgDsc2 is {"emailaddress":"foo#bar.com","tokenexpirytime":1645650084}
At the B location I put code in that does a .Get, it prints out (looks to be deleted):
Before the DB Close (4), after deleting, token BpLnfgDsc2 is
At the C location, it prints (looks to be back):
After the DB Close, token BpLnfgDsc2 is {"emailaddress":"foo#bar.com","tokenexpirytime":1645650084}
There are no errors returned for anything. I've repeated this many times, putting fmt.Printfs everywhere to see what's going on. The results are the same, the key doesn't seem to be getting deleted. After this sits, I 'vi -b' the DB file, and the key, value is still there. Running after it sits, it still sees the key value there. I'm confused, and any pointers will be appreciated.
Update: The basic bolt functionality of Put/Get/Delete/Get works as per this test code (should be obvious):
package main
import "fmt"
import "encoding/json"
import "github.com/boltdb/bolt"
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed (Temporary Token is the DB key)
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
func main() {
var tempToken tempTokenStruct
tempToken.EmailAddress = "foo#bar.com"
tempToken.TokenExpiryTime = 1234567890
tempTokenDataJSON, jsonMarshalError := json.Marshal(tempToken)
if jsonMarshalError != nil {
fmt.Printf("JSON Marshal Error: %s\n", jsonMarshalError.Error())
return
}
tempTokenKey := []byte("foo")
db, err := bolt.Open("test.db", 0644, nil)
if err != nil {
fmt.Printf("Error opening Database\n")
return
}
defer db.Close()
// Put a key in the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbPutError := tempTokenBucket.Put(tempTokenKey, []byte(tempTokenDataJSON))
return dbPutError
})
if err != nil {
fmt.Printf("Error putting key value pair into table: %s\n", err.Error())
}
// Check if the key/value is there after putting it in
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" just after putting it in there\n", tempTokenKey, valueGet)
return nil
})
// Delete that key from the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbDeleteError := tempTokenBucket.Delete(tempTokenKey)
return dbDeleteError
})
if err != nil {
fmt.Printf("Error Deleting key from bucket: %s\n", err.Error())
}
// Check if the key/value is there after deleting it
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" after the delete\n", tempTokenKey, valueGet)
return nil
})
if err != nil {
fmt.Printf("Error getting key from table: %s\n", err.Error())
}
}
Prints out:
Value for Token: "foo" is "{"emailaddress":"foo#bar.com","tokenexpirytime":1234567890}" just after putting it in there
Value for Token: "foo" is "" after the delete
So, not sure why the other code doesn't work. Almost as if the delete is using a different key, but the key is the same across the other code.
I believe that the behaviour of db.Update with a non-nil return value is the confusion here. As per the docs
Inside the closure, you have a consistent view of the database. You commit the transaction by returning nil at the end. You can also rollback the transaction at any point by returning an error.
You are returning an error with:
return errors.New("Not Authorized (4): Please request a new password new/change email from the login page.")
This means that all operations within that db.Update( are rolled back. This can be replicated in your simple example with a small change (return fmt.Errorf("RETURNING ERROR HERE")):
package main
import "fmt"
import "encoding/json"
import "github.com/boltdb/bolt"
type tempTokenStruct struct {
EmailAddress string `json:"emailaddress"` // Email Address to be changed (Temporary Token is the DB key)
TokenExpiryTime int64 `json:"tokenexpirytime"` // Expiry Time for token in Epoch time
}
func main() {
var tempToken tempTokenStruct
tempToken.EmailAddress = "foo#bar.com"
tempToken.TokenExpiryTime = 1234567890
tempTokenDataJSON, jsonMarshalError := json.Marshal(tempToken)
if jsonMarshalError != nil {
fmt.Printf("JSON Marshal Error: %s\n", jsonMarshalError.Error())
return
}
tempTokenKey := []byte("foo")
db, err := bolt.Open("test.db", 0644, nil)
if err != nil {
fmt.Printf("Error opening Database\n")
return
}
defer db.Close()
// Put a key in the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
dbPutError := tempTokenBucket.Put(tempTokenKey, []byte(tempTokenDataJSON))
return dbPutError
})
if err != nil {
fmt.Printf("Error putting key value pair into table: %s\n", err.Error())
}
// Check if the key/value is there after putting it in
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" just after putting it in there\n", tempTokenKey, valueGet)
return nil
})
// Delete that key from the table
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
tempTokenBucket.Delete(tempTokenKey)
return fmt.Errorf("RETURNING ERROR HERE") // CHANGED HERE
})
if err != nil {
fmt.Printf("Error Deleting key from bucket: %s\n", err.Error())
}
// Check if the key/value is there after deleting it
err = db.Update(func(tx *bolt.Tx) error {
tempTokenBucket, err := tx.CreateBucketIfNotExists([]byte("temptokens"))
if err != nil {
return err
}
valueGet := tempTokenBucket.Get(tempTokenKey)
fmt.Printf("Value for Token: \"%s\" is \"%s\" after the delete\n", tempTokenKey, valueGet)
return nil
})
if err != nil {
fmt.Printf("Error getting key from table: %s\n", err.Error())
}
}
The output is now:
Value for Token: "foo" is "{"emailaddress":"foo#bar.com","tokenexpirytime":1234567890}" just after putting it in there
Error Deleting key from bucket: RETURNING ERROR HERE
Value for Token: "foo" is "{"emailaddress":"foo#bar.com","tokenexpirytime":1234567890}" after the delete
This appears to match what you are seeing in your main code. The fix is relatively simple - don't return an error if you want changes to be committed.

json.Marshal for http post request with echo

I have two golang servers running on localhost.
They are using different ports.
I want to create a post request on one that sends a JSON object to the other one.
I am using the echo framework (if this matters)
The error I am getting is when I try to marshal the object for the post object:
2-valued json.Marshal(data) (value of type ([]byte, error)) where single value is expected
server 1:
type SendEmail struct {
SenderName string `json:"senderName,omitempty" bson:"senderName,omitempty" validate:"required,min=3,max=128"`
SenderEmail string `json:"senderEmail" bson:"senderEmail" validate:"required,min=10,max=128"`
Subject string `json:"subject" bson:"subject" validate:"required,min=10,max=128"`
RecipientName string `json:"recipientName" bson:"recipientName" validate:"required,min=3,max=128"`
RecipientEmail string `json:"recipientEmail" bson:"recipientEmail" validate:"required,min=10,max=128"`
PlainTextContent string `json:"plainTextContent" bson:"plainTextContent" validate:"required,min=10,max=512"`
}
func resetPassword(c echo.Context) error {
email := c.Param("email")
if email == "" {
return c.String(http.StatusNotFound, "You have not supplied a valid email")
}
data := SendEmail{
RecipientEmail: email,
RecipientName: email,
SenderEmail: “test#test”,
SenderName: “name”,
Subject: "Reset Password",
PlainTextContent: "Here is your code to reset your password, if you did not request this email then please ignore.",
}
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", json.Marshal(data))
if err != nil {
fmt.Println(err)
}
defer req.Body.Close()
return c.JSON(http.StatusOK, email)
}
server 2:
e.GET("/", defaultRoute)
func defaultRoute(c echo.Context) (err error) {
u := SendEmail{}
if err = c.Bind(u); err != nil {
return
}
return c.JSON(http.StatusOK, u)
}
It's always nice to meet a Gopher. A few things you might want to know, Go supports multi-value returns in that a function can return more than one value.
byteInfo, err := json.Marshal(data) // has two values returned
// check if there was an error returned first
if err != nil{
// handle your error here
}
Now the line below in your code
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", json.Marshal(data))
Will become this
// error here
req, err := http.NewRequest("POST", "127.0.0.1:8081/", bytes.NewBuffer(byteInfo))
And you can continue with the rest of your code. Happy Coding!
json.Marshal returns []byte and error which means you're passing 4 values to http.NewRequest.
You should call json.Marshal first and then use the result for http.NewRequest.
body, err := json.Marshal(data)
if err != nil {
// deal with error
}
req, err := http.NewRequest("POST", "127.0.0.1:8081/", body)

Map response to a struct using Golang

I am attempting to map a response from an API to a struct using Golang.
The JSON that comes back when I view the link in the browser is below:
{
"GBP": 657.54
}
And I just want to map it to a simple struct like so:
type Price struct {
Name string
Value float64
}
Here is my current code.
func FetchCoinPrice(fsym string, tsyms string) Price {
url := fmt.Sprintf("https://min-api.cryptocompare.com/data/price?fsym=" + fsym + "&tsyms=" + tsyms)
fmt.Println("Requesting data from " + url)
price := Price{}
// getting the data using http
request, err := http.Get(url)
if err != nil {
log.Fatal(err.Error())
}
// Read the response body using ioutil
body, err := ioutil.ReadAll(request.Body)
if err != nil {
log.Fatal(err.Error())
}
defer request.Body.Close()
if request.StatusCode == http.StatusOK {
json.Unmarshal(body, &price)
}
return price
}
At the moment all I receive is an empty struct, I know the link is bringing back the correct data and I've tested it in my browser.
The mapping doesn't work that way. Instead, you should use a map:
data := []byte(`{
"GBP": 657.54
}`)
priceMap := map[string]float64{}
err := json.Unmarshal(data, &priceMap)
// Check your errors!
if err != nil {
log.Fatal(err.Error())
}
fmt.Println(priceMap)
This will print:
map[GBP:657.54]
You can then iterate over the map and build the struct you mentioned above, or just access the entry directly if you know the currency. eg: priceMap["GBP"]
You should really check your errors, especially if you're not getting the output you expect from Unmarshal.
The problem is that the unmarshaler cannot guess that keys in a JSON object should correspond to some value in a struct. Golang JSON mapping simply doesn't work that way.
However, you can make your "Price" type implement json.Unmarshaler to deserialize a message into a map of floats (map[string]float64) then ensure the shape is right and populate the struct accordingly:
func (p *Price) UnmarshalJSON(bs []byte) error {
kvs := map[string]float64{}
err := json.Unmarshal(bs, &kvs)
if err != nil {
return err
}
if len(kvs) != 1 {
return fmt.Errorf("expected 1 key, got %d", len(kvs))
}
for name, value := range kvs {
p.Name, p.Value = name, value
}
return nil
}
func main() {
jsonstr := `[{"GBP":657.54},{"USD":123.45}]`
ps := []Price{}
err := json.Unmarshal([]byte(jsonstr), &ps)
if err != nil {
panic(err)
}
// ps=[]main.Price{
// main.Price{Name:"GBP", Value:657.54},
// main.Price{Name:"USD", Value:123.45}
// }
}

How to judge unmarshal json interface{} type in golang?

I want to judge json type,but it always return "I don't know about type map[string]interface {}!",How to resolve it.
=========================================================================
type getRemoteCardInfo struct {
Code int
Msg string
Data []*remoteCardInfo
}
type remoteCardInfo struct {
Sn string
RemoteCardIp string
RemoteCardMac string
}
func Get_json_data(url string) (interface{}, error) {
client := &http.Client{}
req, err := http.NewRequest("GET", url, nil)
req.Header.Add("X-MYCMDB-Auth-Token", "sPf98SMBWzOZJEJB8KWltbJyKvFYPauu")
if err != nil {
return nil, err
}
resp, _ := client.Do(req)
if resp.StatusCode != http.StatusOK {
resp.Body.Close()
return nil, fmt.Errorf("query failed: %s", resp.Status)
}
var result interface{}
body, err := ioutil.ReadAll(resp.Body)
if err := json.Unmarshal(body, &result); err != nil {
log.Fatalf("JSON unmarshaling failed: %s", err)
}
resp.Body.Close()
fmt.Println(result)
return result, nil
}
func main() {
jsondata, err := Get_json_data(DHCPURL)
if err != nil {
log.Fatal(err)
}
switch v := jsondata.(type) {
case getRemoteCardInfo:
fmt.Println("aaaa")
default:
fmt.Printf("I don't know about type %T!\n", v)
}
The go JSON unmarshaler doesn't know about types, as you can tell by the fact that it stores the result into an interface{} value:
func Unmarshal(data []byte, v interface{}) error
// "v" can be any type -------^
So it's up to you to use the unmarshaler to populate your structure and determine if the result is valid or not.
In your example it looks like you're trying to unmarshal a remoteCardInfo from an HTTP response. To do this you should unmarshal into an empty remoteCardInfo struct and determine if the required fields were populated.
For example, suppose you expect a JSON document like so:
{
"sn": "123",
"ip": "0.0.0.0",
"mac": "ff:ff:ff:ff:ff:ff"
}
Then you should define your "remoteCardInfo" struct as below:
type remoteCardInfo struct {
Sn string `json:"sn"`
RemoteCardIp string `json:"ip"`
RemoteCardMac string `json:"mac"`
}
And then unmarshal and validate it like so:
func getRemoteCardInfo(bs []byte) (*remoteCardInfo, error) {
rci := remoteCardInfo{}
err := json.Unmarshal(bs, &rci)
if err != nil {
return nil, err
}
// Validate the expected fields
if rci.Sn == "" {
return nil, fmt.Errorf(`missing "sn"`)
}
if rci.RemoteCardIp == "" {
return nil, fmt.Errorf(`missing "ip"`)
}
if rci.RemoteCardMac == "" {
return nil, fmt.Errorf(`missing "mac"`)
}
return &rci, nil
}
Of course, you can validate the fields any way you like but the main thing to remember is that the unmarshaler only does the job of ensuring that the input byte array is a valid JSON document and populates the fields from the document into the fields defined by the value.
It cannot tell you what "type" of object the JSON document represents.

Resources