Help with api and basic http auth


(Kevin Hoyt) #1

HI,

I’m trying to build an api that will eventually receive json data from the client and save to a file, however I’m stuck trying to get basic auth working. The middleware is giving me a problem. I keep re-reading the info I can find on it but I’m missing something. Can someone get a look at this code and explain what I’m doing wrong?

‘’’
package main

import (
“log”
“net/http”
“fmt”
)

func apitest(w http.ResponseWriter, r *http.Request) http.Handler{
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {

})

}

func basicAuth(h http.Handler) http.Handler{
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
user, pass, _ := r.BasicAuth()
var myUser string = “kevin”
var myPass string = “kevin”

    if myUser != user || myPass != pass {
        w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
        http.Error(w, "Unauthorized.", http.StatusUnauthorized)
        return
    }

    h.ServeHTTP(w, r)
})

}

func main() {
http.HandleFunc("/test", basicAuth(apitest))

err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
if err != nil {
    log.Fatal("ListenAndServe: ", err)
}

}
‘’’


(Johan Dahl) #2

Hi

Your code is a bit hard to read. Post your code like this:

I think you will need to use something else to for authentication if you are aiming for rest API. Http simple is not really suited for this.


(Kevin Hoyt) #3

Hi Johan,

ok, sorry about that. here you go:

package main

import (
    "log"
    "net/http"
	"fmt"
)

func apitest(w http.ResponseWriter, r *http.Request) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
 
    })
}


func basicAuth(h http.Handler) http.Handler{
    return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
        user, pass, _ := r.BasicAuth()
        var myUser string = "kevin"
		var myPass string = "kevin"
		
        if myUser != user || myPass != pass {
            w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
            http.Error(w, "Unauthorized.", http.StatusUnauthorized)
            return
        }

        h.ServeHTTP(w, r)
    })
}

func main() {
    http.HandleFunc("/test", basicAuth(apitest))
	
    err := http.ListenAndServeTLS(":443", "server.crt", "server.key", nil)
    if err != nil {
        log.Fatal("ListenAndServe: ", err)
    }
}

(Kevin Hoyt) #4

Hi Johan,

This api is for use with another company that is requiring http basic auth.

Thanks,

Kevin


(Johan Dahl) #5

Hi again

I got errors because apitest didn’t implement httpServe method. I rewrote it slightly to just use handleFuncs instead. And I changed it to http (just because I was lazy and didn’t want to generate certificate and key. But now it works:


package main

import (
	"io"
	"log"
	"net/http"
)

func apitest(w http.ResponseWriter, r *http.Request) {
	io.WriteString(w, "Hello world")
}

func basicAuth(hf http.HandlerFunc) http.HandlerFunc {
	return func(w http.ResponseWriter, r *http.Request) {
		user, pass, _ := r.BasicAuth()
		var myUser string = "kevin"
		var myPass string = "kevin"

		if myUser != user || myPass != pass {
			w.Header().Set("WWW-Authenticate", `Basic realm="Restricted"`)
			http.Error(w, "Unauthorized.", http.StatusUnauthorized)
			return
		}

		hf(w, r)
	}
}

func main() {
	http.HandleFunc("/test", basicAuth(apitest))

	err := http.ListenAndServe(":8080", nil)
	if err != nil {
		log.Fatal("ListenAndServe: ", err)
	}
}

Two more notes:

var myUser string = "kevin"
var myPass string = "kevin"

should be written without specifing string. Go vet will complain about this. And on this line

http.Error(w, "Unauthorized.", http.StatusUnauthorized)

Can you replace “Unauthorized.” with the a call to http.StatusText like this:

http.Error(w, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)

(Kevin Hoyt) #6

Johan,

Thank you for correcting it. I want to make sure I’m understanding this properly. Changing the basicauth var to a handler , is that because it’s being called as part of HandleFunc in the main? Then the apitest function is because that’s what is getting passed in the anon function, right?

Thanks,

Kevin


(Johan Dahl) #7

Hi. I’m not sure if I know what you mean. You can use http.Handler or http.HandleFunc but http.Handler is an interface which must implement ServeHTTP(w, r) interface. So usually it is simpler to just use HandlerFunc. I guess Handler is more handy (pun intended) then you want an object with state for example. I don’t think I answered your question :confused:


(Kevin Hoyt) #8

Johan,

Thanks, I was looking to make sure to understand the functions better, I think I’ll just have to mess with it more.

Thanks,

Kevin


(Nuclear Squid!!) #9

Hi, I have recently worked with a project that used HTTP basic auth to gate access to the app’s pages.

I found it works for my purposes, but with two^H^H^Hthree caveats:

  1. Keep in mind HTTP basic auth is not secure! Place your service behind an HTTP reverse proxy so TLS will protect the auth info (which is sent in HTTP headers).
  2. Logging out of HTTP basic auth sessions is difficult in modern browsers. They cache HTTP headers aggressively, requiring Javascript hacks to clear the state. Don’t ask me more about it, I barely understand it myself :slight_smile: But there’s discussion below:
  3. This doesn’t support multiple ‘users’ (different roles etc.). Use a more advanced account mechanism if you need that.

Ref: https://stackoverflow.com/a/14329930/1012159

My complete example:

https://play.golang.org/p/2rRaQY3Iu3f

Note one should explicitly refresh (CTRL-R) the page after clicking ‘logout’ to verify the session is indeed de-authenticated.

-Russ


(Kevin Hoyt) #10

Hi Russ,

Thank you for that. I’ll get a look at it!

Thanks,

Kevin