http.Request has undefined fields when deploying to appengine - go

I'm working on a web app and I depend on the following code for authentication (I'm using github.com/dgrijalva/jwt-go package):
func ValidateProtectedPage(protectedPage http.HandlerFunc) http.HandlerFunc {
return http.HandlerFunc(func(res http.ResponseWriter, req *http.Request) {
// If no Auth cookie is set then return a 404 not found
cookie, err := req.Cookie("Auth")
if err != nil {
Forbidden(res)
return
}
// Return a Token using the cookie
token, err := jwt.ParseWithClaims(cookie.Value, &Claims{}, func(token *jwt.Token) (interface{}, error) {
// Make sure token's signature wasn't changed
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, fmt.Errorf("Unexpected siging method")
}
return []byte(util.Secret), nil
})
if err != nil {
Forbidden(res)
return
}
// Grab the tokens claims and pass it into the original request
if claims, ok := token.Claims.(*Claims); ok && token.Valid {
ctx := context.WithValue(req.Context(), "Auth", *claims)
protectedPage(res, req.WithContext(ctx))
} else {
Forbidden(res)
}
})}
The issue is, that I'm trying to deploy my app to appengine, and I get the following errors:
planilla-go/controllers/auth.go:45: req.Context undefined (type *http.Request has no field or method Context)
planilla-go/controllers/auth.go:46: req.WithContext undefined (type *http.Request has no field or method WithContext)
From what I read this is due to incompatibilities between appengine's requests and those from go's library, but I couldn't find a workaround yet
Is there a way to wrap or convert the http.Request to use appengine's instead?

Appengine only supports Go 1.6. A bit baffling since it renders my app useless without a major compat overhaul, but that's the current state of the platform

Related

How to get the decoded token for jwt auth0 and gofiber?

Im using next.js auth0 and a custom golang api backend and I'm
having trouble getting the decoded token on the backend side.
On the frontend side I followed this tutorial -
https://auth0.com/docs/quickstart/webapp/nextjs/01-login
and I managed to send the accessToken to my backend API successfully
on the backend side I followed this tutorial -
https://auth0.com/docs/quickstart/backend/golang/01-authorization
The middleware has successfully verified the token
Example middleware from auth0 implementation
func EnsureValidToken(next http.Handler) http.Handler {
// EnsureValidToken is a middleware that will check the validity of our JWT.
err := godotenv.Load()
if err != nil {
log.Fatal("Error loading .env file")
}
issuerURL, err := url.Parse("https://" + os.Getenv("AUTH0_DOMAIN") + "/")
if err != nil {
log.Fatalf("Failed to parse the issuer url: %v", err)
}
provider := jwks.NewCachingProvider(issuerURL, 5*time.Minute)
jwtValidator, err := validator.New(
provider.KeyFunc,
validator.RS256,
issuerURL.String(),
[]string{os.Getenv("AUTH0_AUDIENCE")},
validator.WithCustomClaims(
func() validator.CustomClaims {
return &CustomClaims{}
},
),
validator.WithAllowedClockSkew(time.Minute),
)
if err != nil {
log.Fatalf("Failed to set up the jwt validator")
}
errorHandler := func(w http.ResponseWriter, r *http.Request, err error) {
log.Printf("Encountered error while validating JWT: %v", err)
w.Header().Set("Content-Type", "application/json")
w.WriteHeader(http.StatusUnauthorized)
w.Write([]byte(`{"message":"Failed to validate JWT."}`))
}
middleware := jwtmiddleware.New(
jwtValidator.ValidateToken,
jwtmiddleware.WithErrorHandler(errorHandler),
)
return middleware.CheckJWT(next)
}
Example token
I'm using https://docs.gofiber.io/ to handle the HTTP methods
Main function
func main() {
// This is to translate the net/http -> fiber http
var ensureValidToken = adaptor.HTTPMiddleware(EnsureValidToken)
app := fiber.New()
app.Use(cors.New())
app.Use(logger.New())
// routes
app.Use(ensureValidToken)
app.Get("/api/books", getAll)
app.Listen(":8080")
}
func getAll(c *fiber.Ctx) error {
token := c.Context().Value(jwtmiddleware.ContextKey{}).(*validator.ValidatedClaims)
// The above code will always panic, I'm assuming that it already stored in the context since it passes the validation
}
Panic example
panic: interface conversion: interface {} is nil, not
*validator.ValidatedClaims
I dig deeper into the auth0 golang implementation, it does store in the context, I think the translation between http.Request to fiber HTTP failed
r = r.Clone(context.WithValue(r.Context(), ContextKey{}, validToken))
Seems like more people have faced the same issue when they used the gofiber adaptor. The way others have solved it was to create their own implementation of HTTPMiddleware middleware adaptor with the only change being that they set the context to the fiber.Ctx.
You can find an the thread on the gofiber/adaptor github page here: https://github.com/gofiber/adaptor/issues/27#issuecomment-1120428400
I got the same panic in the gin framework, I resolved the panic error by changing the code snippet to c.Request.Context().Value() but this is not available in fiber framework. If you want the decoded jwt token either you can get it from the header of the fiber context and decode it appropriately inside the controller, and pass the token you get from the header to the below function and decode.
import (
extract "github.com/golang-jwt/jwt"
"fmt"
)
func Extractor(tokenString string) {
token, _, err := new(extract.Parser).ParseUnverified(tokenString, extract.MapClaims{})
if err != nil {
fmt.Printf("Error %s", err)
}
if claims, ok := token.Claims.(extract.MapClaims); ok {
// obtains claims
subId := fmt.Sprint(claims["sub"])
fmt.Println(subId)
}
}
Implement your logic after this and pass the values you needed to the next handler.

How to access below json values in go fiber?

This is the code I wrote. When I used token.Claims method it returns below result. And I need to access email value also for check whether who is the user. When I used token.Claims.Email it throws an error.
func User(c *fiber.Ctx) error {
cookie := c.Cookies("jwt")
claims := jwt.MapClaims{}
token, _ := jwt.ParseWithClaims(cookie, claims, func(token *jwt.Token) (interface{}, error) {
return []byte(SecretKey), nil
})
return c.JSON(token.Claims)
}
This is the postman response. How to access below email address
Just by checking out your code I'm guessing that you are using this golang-jwt package.
After calling jwt.ParseWithClaims you should be able to access your claims over the already created jwt.MapClaims object named claims in your code.
Example without Fiber, only with golang-jwt, but logic stays the same:
package main
import (
"fmt"
"github.com/golang-jwt/jwt/v4"
)
// Created on https://jwt.io/
const tokenString = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJlbWFpbCI6InRlc3RAbWFpbC5jaCJ9.Iv6oZWIX7Rrtag4d6h3-eJ3xdXLwoZ9PbotcvbjOvhI"
// Just as an example, make sure to pick a stronger key
const key = "1234567890"
func main() {
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(tokenString, claims, keyFunc)
if err != nil {
panic(err)
}
fmt.Println(token.Valid)
email, ok := claims["email"].(string)
if !ok {
panic("Couldn't parse email as string")
}
fmt.Println(email)
}
func keyFunc(*jwt.Token) (interface{}, error) {
return []byte(key), nil
}

How can I add a custom Authentication Bearer token to go oauth2 Exchange function?

We have an oauth2 endpoint that seems to require a client_credentials token to use as a Bearer for the initial code to token exchange process. I have successfully obtained the token for that.
However, in go's current implementation of oauth2 client lib, the Exchange() function (see: Exchange which eventually calls RetrieveToken)
It doesn't add an "Authentication: Bearer " header with a token I can slip in during the Exchange. It can however add a Basic auth header. Our implementation does not currently support basic auth though.
If possible, I'd like to make it add the header without modifying the source code to to oauth2 package.
It would appear if I call oauth2.RegisterBrokenAuthHeaderProvider it might skip the attempt to add a basic auth header which is needed in my case since that'll not work.
Then finally the call to do the http request which is setup as a POST.
Code:
func RetrieveToken(ctx context.Context, clientID, clientSecret, tokenURL string, v url.Values) (*Token, error) {
hc, err := ContextClient(ctx)
if err != nil {
return nil, err
}
bustedAuth := !providerAuthHeaderWorks(tokenURL)
if bustedAuth {
if clientID != "" {
v.Set("client_id", clientID)
}
if clientSecret != "" {
v.Set("client_secret", clientSecret)
}
}
req, err := http.NewRequest("POST", tokenURL, strings.NewReader(v.Encode()))
if err != nil {
return nil, err
}
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if !bustedAuth {
req.SetBasicAuth(url.QueryEscape(clientID), url.QueryEscape(clientSecret))
}
r, err := ctxhttp.Do(ctx, hc, req)
if err != nil {
return nil, err
}
// rest of code ...
}
the ctxhttp.Do can take a context. I've been reading about these but I don't understand how they work. Can I use them to add my header or any other headers I might require?
I'm not sure if this is the most optimal way of doing it, but it seems to work. This is the redirect callback handler that is called as a result of visiting the an auth endpoint.
// Create a custom tokenSource type
type tokenSource struct{ token *oauth2.Token }
func (t *tokenSource) Token() (*oauth2.Token, error) {
fmt.Println("TokenSource!", t.token)
return t.token, nil
}
func handleCallback(w http.ResponseWriter, r *http.Request) {
state := r.FormValue("state")
if state != oauthStateString {
fmt.Printf("invalid oauth state, expected '%s', got '%s'\n", oauthStateString, state)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
code := r.FormValue("code")
// Create a tokenSource and fill in our application Bearer token
ts := &tokenSource{
token: &oauth2.Token{
AccessToken: appToken.AccessToken,
TokenType: appToken.TokenType,
},
}
tr := &oauth2.Transport{
Source: ts,
}
ctx := context.WithValue(context.Background(), oauth2.HTTPClient, &http.Client{
Transport: tr,
})
// myConfig defined previously and in the usual way. Look at the examples.
token, err := myConfig.Exchange(ctx, code)
if err != nil {
fmt.Printf("Code exchange failed with '%s'\n", err)
http.Redirect(w, r, "/", http.StatusTemporaryRedirect)
return
}
fmt.Printf("token: %+v", token)
// token can now be used.
}

How to decode a JWT token in Go?

I am currently working on a Go application. I receive a JWT token from the client side and I need to decode that token and obtain the relevant information: user, name, etc.
I was checking the libraries that are available to handle JWT tokens and I came down to dgrijalva/jwt-go, but I don't see how to accomplish my goal in a simple way.
I have the token and I need to decode the info into a map or at least a json. How can I do it?
Function jwt.ParseWithClaims accept an interface of jwt.Claims as the second argument. Besides struct-based custom claims, the package also provides map-based claims, i.e. jwt.MapClaims.
So, you can simply decode the token into a MapClaims, e.g.
tokenString := "<YOUR TOKEN STRING>"
claims := jwt.MapClaims{}
token, err := jwt.ParseWithClaims(tokenString, claims, func(token *jwt.Token) (interface{}, error) {
return []byte("<YOUR VERIFICATION KEY>"), nil
})
// ... error handling
// do something with decoded claims
for key, val := range claims {
fmt.Printf("Key: %v, value: %v\n", key, val)
}
Disclaimer: I am not affiliated to the library. I'm just a user, find it useful and would like to share.
It's 2019. I would like to suggest an alternate library that does pretty good job on JWT using JWS and/or JWE.
Here is the few examples on how to use the library:
import (
"gopkg.in/square/go-jose.v2/jwt"
"gopkg.in/square/go-jose.v2"
)
...
var claims map[string]interface{} // generic map to store parsed token
// decode JWT token without verifying the signature
token, _ := jwt.ParseSigned(tokenString)
_ = token.UnsafeClaimsWithoutVerification(&claims)
// decode JWT token and verify signature using JSON Web Keyset
token, _ := jwt.ParseSigned(tokenString)
jwks := &jose.JSONWebKeySet { // normally you can obtain this from an endpoint exposed by authorization server
Keys: []jose.JSONWebKey { // just an example
{
Key: publicKey,
Algorithm: jose.RS256, // should be the same as in the JWT token header
KeyID: "kid", // should be the same as in the JWT token header
},
},
}
_ = jwt.Claims(jwks, &claims)
Noted that, claims can be a struct that contains default JWT fields and also customized fields that are inside the token
E.g.:
import (
"github.com/mitchellh/mapstructure"
"gopkg.in/square/go-jose.v2/jwt"
)
...
type CustomClaims struct {
*jwt.Claims
// additional claims apart from standard claims
extra map[string]interface{}
}
func (cc *CustomClaims) UnmarshalJSON(b []byte) error {
var rawClaims map[string]interface{}
if err := json.Unmarshal(b, &rawClaims); err != nil {
return nil
}
var claims jwt.Claims
var decoderResult mapstructure.Metadata
decoder, err := mapstructure.NewDecoder(&mapstructure.DecoderConfig{
Result: &claims,
Metadata: &decoderResult,
TagName: "json",
})
if err != nil {
return err
}
if err := decoder.Decode(rawClaims); err != nil {
return err
}
cc.Claims = &claims
cc.extra = make(map[string]interface{})
for _, k := range decoderResult.Unused {
cc.extra[k] = rawClaims[k]
}
return nil
}
I also built a command line tool that uses the library to perform various encoding/decoding activities. It might be a useful reference on the usage of the library too.
Since both the question and answers mention the JWT library github.com/dgrijalva/jwt-go, please note that this library has been unmaintained for a long time now.
As of June 2021 there is a community fork golang-jwt/jwt, officially blessed by Dave Grijalva, the original author.
This also means that the library import path has changed. Note that the current major version v3 is not on Go modules, therefore you will still see v3.x.x+incompatible in your go.mod.
Edit: since August 2021 version v4 of golang-jwt/jwt is available. This finally supports Go modules. The new version is backward-compatible with previous versions, so in order to migrate simply replace the old import path with:
github.com/golang-jwt/jwt/v4
then update your modules as needed — see also the migration guide for details.
The fork most notably fixes an important security issue with the original library. Before the fix, the library didn't properly handle multiple aud in the JWT claims, making it actually not compliant with the JWT spec.
Apart from that, the main API is still the same. For example to parse a JWT with HMAC verification:
tokenString := /* raw JWT string*/
token, err := jwt.Parse(tokenString, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(/* your JWT secret*/), nil
})
if err != nil {
// handle err
}
// validate the essential claims
if !token.Valid {
// handle invalid tokebn
}
To parse a JWT with custom claims, you can define your own struct type and embed jwt.StandardClaims into it:
type MyClaims struct {
jwt.StandardClaims
MyField string `json:"my_field"`
}
tokenString := /* raw JWT string*/
// pass your custom claims to the parser function
token, err := jwt.ParseWithClaims(tokenString, &MyClaims{}, func(token *jwt.Token) (interface{}, error) {
if _, ok := token.Method.(*jwt.SigningMethodHMAC); !ok {
return nil, errors.New("unexpected signing method")
}
return []byte(/* your JWT secret*/), nil
})
// type-assert `Claims` into a variable of the appropriate type
myClaims := token.Claims.(*MyClaims)
A valid alternative to this library is lestrrat-go/jwx. The API is slightly different, but also very easy to use:
tokenString := /* raw JWT string*/
// parse and verify signature
tok, err := jwt.Parse(tokenString, jwt.WithVerify(jwa.HS256, []byte(/* your JWT secret */)))
if err != nil {
// handle err
}
// validate the essential claims
if err := jwt.Validate(tok); err != nil {
// handle err
}
If you want to get claims from jwt token without validation
import "github.com/dgrijalva/jwt-go"
...
token, err := jwt.Parse(tokenStr, nil)
if token == nil {
return nil, err
}
claims, _ := token.Claims.(jwt.MapClaims)
// claims are actually a map[string]interface{}
Note: code compares token with nil, not the err. The err will be keyFunc can't be nil.
Use github.com/dgrijalva/jwt-go go liabary for the implementation. we can extract JWT token information from the api request according to the following way.
When post the JWT token from the using post request. you must extract JWT information in routing section.
func RequireTokenAuthentication(inner http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token, err := jwt.ParseFromRequest(
r,
func(token *jwt.Token) (interface{}, error) {
return VERIFICATION.PublicKey, nil
})
if err != nil || !token.Valid) {
log.Debug("Authentication failed " + err.Error())
w.WriteHeader(http.StatusForbidden)
return
} else {
r.Header.Set("username", token.Claims["username"].(string))
r.Header.Set("userid", strconv.FormatFloat((token.Claims["userid"]).(float64), 'f', 0, 64))
}
inner.ServeHTTP(w, r)
})
}
VERIFICATION.PublicKey : The key for verification(get public key from public.key file in your system)
Any Issue happen.Please let me know. I can give you help.

Go Nil Dereference Error When Testing HTTP Request

I am trying to test a HTTP request in my Go library. Object which makes the call accepts a HTTP client object via dependency injection so in my test I am mocking the HTTP client like this:
func TestMyObject(t *testing.T) {
server := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(200)
w.Header().Set("Content-Type", "application/json")
fmt.Fprintln(w, mockJSONResponse)
}))
defer server.Close()
// Make a transport that reroutes all traffic to the example server
transport := &http.Transport{
Proxy: func(req *http.Request) (*url.URL, error) {
return url.Parse(server.URL)
},
}
// Make a http.Client with the transport
httpClient := &http.Client{Transport: transport}
// I am passing the httpClient to my object
}
Here is how the HTTP request is made within my object:
// Make - makes a prepared HTTP request
func (ir *MyObject) Make() *http.Response {
if ir.Err != nil {
return nil
}
ir.resp, ir.Err = ir.Client.Do(ir.req)
runtime.SetFinalizer(ir, func(ir *MyObject) {
ir.resp.Body.Close()
})
ir.logReqRes()
ir.checkErrorResponse()
return ir.resp
}
I am getting nil pointer dereference error though:
panic: runtime error: invalid memory address or nil pointer dereference [recovered]
panic: runtime error: invalid memory address or nil pointer dereference
In this function (I am trying to log the response):
// Logs request and response
func (ir *MyObject) logReqRes() {
log.Print("AAAAAAA")
log.Print(ir.resp)
log.Print("AAAAAAA")
if reqInfo, err := httputil.DumpRequest(ir.req, true); err == nil {
log.Print("Logging request:")
log.Print(string(reqInfo))
}
if respInfo, err := httputil.DumpResponse(ir.resp, true); err == nil {
log.Print("Logging response:")
log.Print(string(respInfo))
}
}
As you can see, ir.resp is nil for some reason. Any ideas?
Ok. I found an issue. Better logging of errors actually showed that my mistake was very simple.
I was passing this as a URL to mocked HTTP client:
host/api/v1/tokens/
Which was resulting in this error:
Post host/api/v1/tokens/: unsupported protocol scheme
Apparently the URL must include a proper HTTP scheme (http://) even when you are using a test server.

Resources