Using this https://github.com/praveen001/go-passport
Path with passsport middleware:
app.Put("/api/auth/login", p.Authenticate("local", MyHandler))
The passport authenticate function:
// Authenticate calls `Strategy.Authenticate` method of registered strategies, and checks the `passport.Result` returned by it.
//
// The result is stored in the request context with `passport.CtxKey` as key.
//
func (p *Passport) Authenticate(name string, h http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
s, ok := p.Options.strategies[name]
if !ok {
w.WriteHeader(404)
return
}
s.Authenticate(w, r, func(res *Result) {
res.StrategyName = name
ctx := context.WithValue(r.Context(), CtxKey, res)
h.ServeHTTP(w, r.WithContext(ctx))
})
}
}
So this attach the response from my auth method and returns a http.HandleFunc which I can define myself (MyHandler).
func MyHandler(w http.ResponseWriter, r *http.Request) {
// Where is the data attached from the Authenticate func?
w.Header().Add("Content-Type", "application/json")
w.WriteHeader(401)
io.WriteString(w, `{"status":"ok"}`)
}
I dont understand where I can reach the attached data from the Authenticate func. The comment says: "//The result is stored in the request context with passport.CtxKey as key".
I don't find it in the r *http.Request.
The http.Request object has a Context() method which gives you the context. You access the key through that.
E.g.
ctx := r.Context()
value := ctx.Value(passport.CtxKey)
Related
I'm beginner with Go, ruby my previous language. I started with Mux for build an API. Now I'm learning authentication using JWT to protect the API. Firstly I try to handle authentication in a function with single file main.go
func ListPosts(w http.ResponseWriter, r *http.Request){
// verify authentication here
// other stuff to get list of posts by a user authentication here
}
func handleRequests() {
myRouter := mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/posts", ListPosts).Methods("GET")
log.Fatal(http.ListenAndServe(":8081", myRouter))
}
func main() {
handleRequests()
}
Everything is working well, the above implementation is repeating the authentication every I create a new endpoint, so I'm trying to make it DRY
Here is my main.go now :
func handleRequests() {
myRouter := mux.NewRouter().StrictSlash(true)
myRouter.HandleFunc("/posts", jwt.Authenticate(controllers.ListPosts)).Methods("GET")
log.Fatal(http.ListenAndServe(":8081", myRouter))
}
func main() {
handleRequests()
}
this is the jwt package I have made :
package jwt
func Authenticate(handler http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
// checking header Authorization here
token, err := ValidateToken(r)
if err != nil {
// checking signature invalid here
// checking expire token here
// response error here
}
if !token.Valid {
// response error here
}
getClaim := token.Claims.(*Token)
// I'm using https://github.com/Kamva/mgm (mongo)
user := &models.User{}
coll := mgm.Coll(user)
// Find and decode the doc to a user model here
handler.ServeHTTP(w, r)
return
}
}
The authentication is working well, and now I want to get a user authentication in controllers package, do I need to put an UID of user on header and find a user with it? I believe that make a redudant, because I have find a user on Authenticate function.
package controllers
func ListPosts(w http.ResponseWriter, r *http.Request) {
// should I find a user with r.Header["UID"] here ?
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode({})
}
I have a requirement of informing user the allowed methods for specific endpoint. This information will be shown in case there is 405 response from server (I'm using gorilla/mux).
I've trying using custom handler by mux for 405, but I can't find any info in the Request object and ResponseWriter.
After reading the docs and SO, I can't find any. May I know if anyone has been doing same thing before?
Code is below. I only allow GET apparently.
router.HandleFunc("/users/{id}",).Methods(http.MethodGet)
In my handler for 405, the response header is empty apparently. There is no info on allowed methods for this endpoint.
func MethodNotAllowedHandler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
logrus.Debugln("Header Writer: ", w.Header())
})
Thanks!
You can modify your MethodNotAllowed handler such below;
It simply walks through the routes and applies routematch function. If path is matched and method is not, it returns mux.ErrMethodMismatch error. Then you can obtain allowed methods for the route and send it in the headers
func notAllowedHandler(x *mux.Router) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
//next.ServeHTTP(w, r)
x.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
var routeMatch mux.RouteMatch
if route.Match(r, &routeMatch) || routeMatch.MatchErr == mux.ErrMethodMismatch {
m, _ := route.GetMethods()
w.Header().Set("Access-Control-Allow-Methods", strings.Join(m, ", "))
}
return nil
})
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
})
}
Edit: I forgot to mention. You need to pass router parameter to your custom method not allowed handler;
You can find the complete example below;
func main() {
r := mux.NewRouter()
r.HandleFunc("/test", func(rw http.ResponseWriter, r *http.Request) {}).Methods(http.MethodPost)
r.HandleFunc("/test2", func(rw http.ResponseWriter, r *http.Request) {}).Methods(http.MethodPut)
r.MethodNotAllowedHandler = notAllowedHandler(r)
http.ListenAndServe(":8089", r)
}
func notAllowedHandler(x *mux.Router) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
x.Walk(func(route *mux.Route, router *mux.Router, ancestors []*mux.Route) error {
var routeMatch mux.RouteMatch
if route.Match(r, &routeMatch) || routeMatch.MatchErr == mux.ErrMethodMismatch {
m, _ := route.GetMethods()
w.Header().Set("Access-Control-Allow-Methods", strings.Join(m, ", "))
}
return nil
})
http.Error(w, http.StatusText(http.StatusMethodNotAllowed), http.StatusMethodNotAllowed)
})
}
I am designing my handlers to return a http.Handler. Here's the design of my handlers:
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})
}
My middleware is designed to accept an http.Handler and then call the handler once the middleware has finished performing its operations. Here's the design of my middleware:
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
next.ServeHTTP(w, r)
})
}
Considering the design of my middleware and handlers, what is the proper way of passing information from the middleware to the handler? The information that I am trying to pass from my middleware to the handlers is a JSON web token parsed from the request body. If I do not pass the parsed JWT to the handler, then I will need to parse the JWT again in my handlers. Parsing the request body for a JWT in both the middleware and handler seems wasteful. Just in case this information is relevant, I am using the standard net/http library with gorilla mux.
Since you're already using Gorilla take a look at the context package.
(This is nice if you don't want to change your method signatures.)
import (
"github.com/gorilla/context"
)
...
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
// Parse body/get token.
context.Set(r, "token", token)
next.ServeHTTP(w, r)
})
}
...
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := context.Get(r, "token")
})
}
Update
The Gorilla context package is now in maintenance mode
per the repo:
Note gorilla/context, having been born well before context.Context existed, does not play well with the shallow copying of the request that http.Request.WithContext (added to net/http Go 1.7 onwards) performs.
Using gorilla/context may lead to memory leaks under those conditions, as the pointers to each http.Request become "islanded" and will not be cleaned up when the response is sent.
You should use the http.Request.Context() feature in Go 1.7.
The proper way to pass request scoped data would now be the context package in the standard library.
https://golang.org/pkg/context/
You can access it with request.Context on an http.Request.
A first approach, similar to the question, is in codemodus/chain by Daved.
Package chain aids the composition of Handler wrapper chains that carry request-scoped data.
It uses the notion of Context, coupled with a Context handler:
func ctxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
// ...
if s, ok := getMyString(ctx); ok {
// s = "Send this down the line."
}
// ...
}
Another approach: You can have a look at "Custom Handlers and Avoiding Globals in Go Web Applications", by Matt Silverlock (elithrar). (full example here)
The idea is to define ServeHTTP on a type which include the relevant context.
// We've turned our original appHandler into a struct with two fields:
// - A function type similar to our original handler type (but that now takes an *appContext)
// - An embedded field of type *appContext
type appHandler struct {
*appContext
h func(*appContext, http.ResponseWriter, *http.Request) (int, error)
}
// Our ServeHTTP method is mostly the same, and also has the ability to
// access our *appContext's fields (templates, loggers, etc.) as well.
func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Updated to pass ah.appContext as a parameter to our handler type.
status, err := ah.h(ah.appContext, w, r)
if err != nil {
log.Printf("HTTP %d: %q", status, err)
switch status {
case http.StatusNotFound:
http.NotFound(w, r)
// And if we wanted a friendlier error page, we can
// now leverage our context instance - e.g.
// err := ah.renderTemplate(w, "http_404.tmpl", nil)
case http.StatusInternalServerError:
http.Error(w, http.StatusText(status), status)
default:
http.Error(w, http.StatusText(status), status)
}
}
}
In the appContext struct, you would put any data you want to pass around.
Hopefully, this is an easy way to earn some rep. This seems very simple, so I must be doing something wrong and just cant see it.
I have a simple middleware which a transaction id and adds it to the request and response headers.
func HandleTransactionID(fn http.HandlerFunc) http.HandlerFunc {
return func(w http.ResponseWriter, req *http.Request) {
tid := uuid.NewV4()
req.Header.Set(TransIDHeader, TransIDPrefix + tid.String())
w.Header().Set(TransIDHeader, TransIDPrefix + tid.String())
fn(w, req)
}
}
In my unit tests, I've confirmed the response header is successfully set, but it doesn't appear the the request header is being set. I would assume that it is possible to modify the request headers, so ?
const (
WriteTestHeader = "WriterTransHeader"
RequestTestHeader = "ReqTransHeader"
)
func recorderFunc(w http.ResponseWriter, req *http.Request){
w.Header().Set(WriteTestHeader, w.Header().Get(TransIDHeader))
w.Header().Set(RequestTestHeader, req.Header.Get(TransIDHeader))
}
func TestHandleTransactionID(t *testing.T) {
recorder := httptest.NewRecorder()
req := httptest.NewRequest("GET", "/foo", nil)
middleware.HandleTransactionID(recorderFunc)(recorder, req)
if req.Header.Get(RequestTestHeader) == "" {
t.Error("request header is nil")
}
if recorder.Header().Get(WriteTestHeader) == "" {
t.Error("response header is nil")
}
if req.Header.Get(RequestTestHeader) != recorder.Header().Get(WriteTestHeader) {
t.Errorf("header value mismatch: %s != %s",
req.Header.Get(RequestTestHeader),
recorder.Header().Get(WriteTestHeader))
}
}
In your test, req.Header.Get(RequestTestHeader) will always remain an empty string because you are not setting the Key as 'RequestTestHeader' in the request header but in the ResponseWriter w.Header().Set(RequestTestHeader, req.Header.Get(TransIDHeader))
On an unrelated note, It would be considered idomatic Go, to have your middleware function signature using the http.Handler interface, func HandleTransactionID(fn http.Handler) http.Handler.
I am designing my handlers to return a http.Handler. Here's the design of my handlers:
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
})
}
My middleware is designed to accept an http.Handler and then call the handler once the middleware has finished performing its operations. Here's the design of my middleware:
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
next.ServeHTTP(w, r)
})
}
Considering the design of my middleware and handlers, what is the proper way of passing information from the middleware to the handler? The information that I am trying to pass from my middleware to the handlers is a JSON web token parsed from the request body. If I do not pass the parsed JWT to the handler, then I will need to parse the JWT again in my handlers. Parsing the request body for a JWT in both the middleware and handler seems wasteful. Just in case this information is relevant, I am using the standard net/http library with gorilla mux.
Since you're already using Gorilla take a look at the context package.
(This is nice if you don't want to change your method signatures.)
import (
"github.com/gorilla/context"
)
...
func Middleware(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
// Middleware operations
// Parse body/get token.
context.Set(r, "token", token)
next.ServeHTTP(w, r)
})
}
...
func Handler() http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
token := context.Get(r, "token")
})
}
Update
The Gorilla context package is now in maintenance mode
per the repo:
Note gorilla/context, having been born well before context.Context existed, does not play well with the shallow copying of the request that http.Request.WithContext (added to net/http Go 1.7 onwards) performs.
Using gorilla/context may lead to memory leaks under those conditions, as the pointers to each http.Request become "islanded" and will not be cleaned up when the response is sent.
You should use the http.Request.Context() feature in Go 1.7.
The proper way to pass request scoped data would now be the context package in the standard library.
https://golang.org/pkg/context/
You can access it with request.Context on an http.Request.
A first approach, similar to the question, is in codemodus/chain by Daved.
Package chain aids the composition of Handler wrapper chains that carry request-scoped data.
It uses the notion of Context, coupled with a Context handler:
func ctxHandler(ctx context.Context, w http.ResponseWriter, r *http.Request) {
// ...
if s, ok := getMyString(ctx); ok {
// s = "Send this down the line."
}
// ...
}
Another approach: You can have a look at "Custom Handlers and Avoiding Globals in Go Web Applications", by Matt Silverlock (elithrar). (full example here)
The idea is to define ServeHTTP on a type which include the relevant context.
// We've turned our original appHandler into a struct with two fields:
// - A function type similar to our original handler type (but that now takes an *appContext)
// - An embedded field of type *appContext
type appHandler struct {
*appContext
h func(*appContext, http.ResponseWriter, *http.Request) (int, error)
}
// Our ServeHTTP method is mostly the same, and also has the ability to
// access our *appContext's fields (templates, loggers, etc.) as well.
func (ah appHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
// Updated to pass ah.appContext as a parameter to our handler type.
status, err := ah.h(ah.appContext, w, r)
if err != nil {
log.Printf("HTTP %d: %q", status, err)
switch status {
case http.StatusNotFound:
http.NotFound(w, r)
// And if we wanted a friendlier error page, we can
// now leverage our context instance - e.g.
// err := ah.renderTemplate(w, "http_404.tmpl", nil)
case http.StatusInternalServerError:
http.Error(w, http.StatusText(status), status)
default:
http.Error(w, http.StatusText(status), status)
}
}
}
In the appContext struct, you would put any data you want to pass around.