How to check authentication for hundreds and thousands of API endpoints? - api

I am currently building a web application in golang (with Gorilla) and have implemented a handful of API endpoints. However, I noticed that every time I implement a function like
func CreateUserHandler(w http.ResponseWriter, r *http.Request) {}
I have to add the function below to the body of handler functions to check if request is authorized:
func checkAuthorizedUser (r * http.Request) error {
uid, err := CheckRequestUser (r.Cookie("uid"))
if err != nil {
return errors.New("Can't find cookie value for uid")
}
if !IsValidUser (uid.Value) {
return errors.New("Not a valid user")
}
return nil
}
What happens to me right now is that I have to add checkAuthorizedUser() to every handler function, and I have already have a lot of handler functions so far. I wonder if there is a better way to check whether a client is authorized to access certain endpoint other than explicitly checking authentication in every handler function.

Gorilla has a router you can use. You can then wrap the router with authentication checking. Something like this would work:
func checkPermissions(h http.Handler) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
authCheck := true //implement the actual checking
if authCheck {
w.WriteError(w, 400, "error")
return
}
h.ServeHttp(w, r)
}
}
func main() {
r := mux.NewRouter()
r.HandleFunc("/", HomeHandler)
r.HandleFunc("/products", ProductsHandler)
r.HandleFunc("/articles", ArticlesHandler)
http.Handle("/", checkPermissions(r))
}
Supporting links:
https://godoc.org/github.com/gorilla/mux#NewRouter
https://github.com/gorilla/mux

Related

Can't access mux params when testing API endpoint with custom handlers

I have a custom handler for my API endpoints like this:
type HTTPError struct {
Error error
Message string
Code int
}
type endpointREST func(http.ResponseWriter, *http.Request) *HTTPError
func (fn endpointREST) ServeHTTP(w http.ResponseWriter, r *http.Request) {
if e := fn(w, r); e != nil {
http.Error(w, e.Message, e.Code)
}
}
my example route look like this:
func GetShare(w http.ResponseWriter, r *http.Request) *HTTPError {
vars := mux.Vars(r)
fmt.Println(r.URL) // http://127.0.0.1:36455/share/5713d228-a042-446d-a5e4-183b19fa832a
fmt.Println(vars) // -->> always empty map when testing
return nil
}
These routes work well (manual, using Postman) after setting them up with
router := mux.NewRouter().StrictSlash(true)
handler := cors.Default().Handler(router)
router.Handle("/share/{id}", endpointREST(GetShare)).Methods("GET")
log.Fatal(http.ListenAndServe(":6969", handler))
The Problem is, that i can't test the API this way, since mux.Vars(r) will always return an empty map in the testing environment.
This is my testing code:
func TestGetShare(t *testing.T) {
Reset()
router := mux.NewRouter()
ts := httptest.NewServer(router)
router.Handle("/share/{id}", endpointREST(GetShare)).Methods("GET")
defer ts.Close()
t.Run("unauthorized", func(t *testing.T) {
req, _ := http.NewRequest("GET", ts.URL + "/share/5713d228-a042-446d-a5e4-183b19fa832a", nil)
res, _ := http.DefaultClient.Do(req)
assert.Equal(t, http.StatusUnauthorized, res.StatusCode)
})
}
I suggest you tu use the SetURLVars test helper func:
// SetURLVars sets the URL variables for the given request, to be accessed via
// mux.Vars for testing route behaviour. Arguments are not modified, a shallow
// copy is returned.
//
// This API should only be used for testing purposes; it provides a way to
// inject variables into the request context. Alternatively, URL variables
// can be set by making a route that captures the required variables,
// starting a server and sending the request to that server.
func SetURLVars(r *http.Request, val map[string]string) *http.Request {
return requestWithVars(r, val)
}

gRPC authorization approach

I work on go grpc service and implementing authorization. Literally, have to allow or forbid access to gprc methods base on JWT claims.
I do JWT parsing on grpc.UnaryServerInterceptor level - extracting claims and populate context with value, unauthenticated if there is no jwt or it is incorrect.
func (s *Server) GetSomething(ctx context.Context, req *GetSomething Request) (*GetSomething Response, error) {
if hasAccessTo(ctx, req.ID) {
//some work here
}
}
func hasAccessTo(ctx context.Context, string id) {
value := ctx.Value(ctxKey).(MyStruct)
//some work here
}
So I wonder if there is some common practice for authorization/authentication to avoid boilerplate code in each grpc server method?
You can call a to a UnaryInterceptor like so if you want to verify the jwt on every request
// middleware for each rpc request. This function verifies the client has the correct "jwt".
func authInterceptor(ctx context.Context, req interface{}, _ *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) {
meta, ok := metadata.FromIncomingContext(ctx)
if !ok {
return nil, status.Error(codes.Unauthenticated, "INTERNAL_SERVER_ERROR")
}
if len(meta["jwt"]) != 1 {
return nil, status.Error(codes.Unauthenticated, "INTERNAL_SERVER_ERROR")
}
// if code here to verify jwt is correct. if not return nil and error by accessing meta["jwt"][0]
return handler(ctx, req) // go to function.
}
In your context from the client use the metadata to pass the jwt string and verify.
In Your main function remember to register it like so
// register server
myService := grpc.NewServer(
grpc.UnaryInterceptor(authInterceptor), // use auth interceptor middleware
)
pb.RegisterTheServiceServer(myService, &s)
reflection.Register(myService)
Your client would need to call your server like this:
// create context with token and timeout
ctx, cancel := context.WithTimeout(metadata.NewOutgoingContext(context.Background(), metadata.New(map[string]string{"jwt": "myjwtstring"})), time.Second*1)
defer cancel()

Testing golang middleware that modifies the request

I have some middleware that adds a context with a request id to a request.
func AddContextWithRequestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var ctx context.Context
ctx = NewContextWithRequestID(ctx, r)
next.ServeHTTP(w, r.WithContext(ctx))
})}
How do I write a test for this ?
To test that, you need to run that handler passing in a request, and using a custom next handler that checks that the request was indeed modified.
You can create that handler as follows:
(I am assuming your NewContextWithRequestID adds a "reqId" key to the request with a "1234" value, you should of course modify the assertions as needed)
// create a handler to use as "next" which will verify the request
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
val := r.Context().Value("reqId")
if val == nil {
t.Error("reqId not present")
}
valStr, ok := val.(string)
if !ok {
t.Error("not string")
}
if valStr != "1234" {
t.Error("wrong reqId")
}
})
You can then use that handler as your next one:
// create the handler to test, using our custom "next" handler
handlerToTest := AddContextWithRequestID(nextHandler)
And then invoke that handler:
// create a mock request to use
req := httptest.NewRequest("GET", "http://testing", nil)
// call the handler using a mock response recorder (we'll not use that anyway)
handlerToTest.ServeHTTP(httptest.NewRecorder(), req)
Putting everything together as a working test, that'd be the code below.
Note: I fixed a small bug in your original "AddContextWithRequestID", as the ctx value started with a nil value when you just declared it with no initialization.
import (
"net/http"
"context"
"testing"
"net/http/httptest"
)
func NewContextWithRequestID(ctx context.Context, r *http.Request) context.Context {
return context.WithValue(ctx, "reqId", "1234")
}
func AddContextWithRequestID(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
var ctx = context.Background()
ctx = NewContextWithRequestID(ctx, r)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
func TestIt(t *testing.T) {
// create a handler to use as "next" which will verify the request
nextHandler := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
val := r.Context().Value("reqId")
if val == nil {
t.Error("reqId not present")
}
valStr, ok := val.(string)
if !ok {
t.Error("not string")
}
if valStr != "1234" {
t.Error("wrong reqId")
}
})
// create the handler to test, using our custom "next" handler
handlerToTest := AddContextWithRequestID(nextHandler)
// create a mock request to use
req := httptest.NewRequest("GET", "http://testing", nil)
// call the handler using a mock response recorder (we'll not use that anyway)
handlerToTest.ServeHTTP(httptest.NewRecorder(), req)
}

how to login to use your google calendar using go?

I am new to GO and APIs and I am making a back end using GO.
the user should be able to login using his/her google account and modifies his calendar.
I opened the sample on this link Google Quickstart
but the way I get the client is by the keys google gives it to me
how should I make the user login and get his calendar
You'll need to do something like:
import (
"crypto/rand"
"encoding/base64"
"encoding/gob"
"golang.org/x/oauth2/google"
"golang.org/x/oauth2"
calendar "google.golang.org/api/calendar/v3"
"github.com/gorilla/sessions"
)
var conf oauth2.Config
func init() {
gob.Register(&oauth2.Token{})
}
func getLoginURL(state string) string {
// State can be some kind of random generated hash string.
// See relevant RFC: http://tools.ietf.org/html/rfc6749#section-10.12
return conf.AuthCodeURL(state)
}
func randToken() string {
b := make([]byte, 32)
rand.Read(b)
return base64.StdEncoding.EncodeToString(b)
}
func Login(w http.ResponseWriter, r *http.Request) {
conf = &oauth2.Config{
ClientID: "your-client-id",
ClientSecret: "your-client-secret",
RedirectUrl: "https://www.yoursite.com/auth",
Endpoint: google.Endpoint,
Scopes: []string{"https://www.googleapis.com/auth/calendar"}
}
state := randToken()
sess, _ := session.Get(r, "session")
sess.Values["state"] = state
sess.Save(r, w)
http.Redirect(w, r, conf.AuthCodeURL(state), http.StatusFound)
}
func Auth(w http.ResponseWriter, r *http.Request) {
sess, _ := session.Get(r, "session")
state = sess.Values["state"]
if state != r.URL.Query().Get("state") {
http.Error(w, "authorization failed", http.StatusUnauthorized)
return
}
tok, _ := conf.Exchange(oauth2.NoContext, c.QueryParam("code"))
sess.Values["token"] = tok
sess.Save(r, w)
http.Redirect(w, r, "https://www.yoursite.com/profile", http.StatusFound)
}
func GetClient(r *http.Request) *http.Client {
sess, _ := session.Get(r, "session")
tok, _ := sess.Values["token"].(*oauth2.Token)
client := conf.Client(oauth2.NoContext, tok)
return client
}
func Calendar(w http.ResponseWriter, r *http.Request) {
client := GetClient(r)
calendarService, _ = calendar.New(client)
//do stuff
}
So, you send them to your Login handler, this generates a random key, and sends it (and the user) to google to have them login and authorize you to access their calendars, which will then redirect them to you Auth handler. This will make sure that the state key they sent back matches the one you sent, and if so, will get the token from Google. You then save it to the session. When you want to get their client, you fetch the token from your session, and use it to exchange for a new Client, which you then use to create your Calendar service.
I haven't checked the code exactly, but I tried to make a minimal example from my app which actually uses basically this code, so it should work (aside from probably missing an import or typos or some really minor stuff).

methods for interface mocking in go

If I have a handlerfunc like the one below. What is the best way to "mock" or inject a interface that wraps some object for testing?
func GetOrCreateUser(w http.ResponseWriter, r *http.Request) {
user := GetUserFromContext(r.Context())
if created :=user.GetOrCreate(); created {
smtp.SendEmail(...)
return
} else {
w.Write([]byte("Hello User!"))
}
}
The only way that I have come by seems to be to do this:
type Mailer interface { SendMail() }
func HandlerWithMailer(m Mailer) http.handlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
user := GetUserFromContext(r.Context())
if created := user.GetOrCreate(); created {
m.SendEmail(...)
return
} else {
w.Write([]byte("Hello User!"))
}
}
}
Then calling the mailer like this in the router (using httprouter):
m := mailer.New() // assuming this returns a new mailer
r := httprouter.New()
r.GET("/mailer", HandlerWithMailer(m))
Which could then be tested by making a struct that implements the interface and returns whatever I want, which can be called in the tests. I know this works, but I am wondering if this is the preferred method of testing in Go and if there is any other way to go about accomplishing this.
I would call my handlers from a struct like so:
type Handlers struct {
mailer *Mailer
}
func(h *Handlers) GetOrCreateUser(w http.ResponseWriter, r *http.Request) {
user := GetUserFromContext(r.Context())
if created :=user.GetOrCreate(); created {
h.mailer.SendEmail(...)
return
} else {
w.Write([]byte("Hello User!"))
}
}
This way you can instansiate the struct with the implementation of Mailer you want to use.
m := mailer.New() // assuming this returns a new mailer
h := Handlers{&m}
r := httprouter.New()
r.GET("/mailer", h.HandlerWithMailer)
and in your tests
m := mockMailer.New() // assuming this returns a new mailer
h := Handlers{&m}
r := httprouter.New()
r.GET("/mailer", h.HandlerWithMailer)