Documentation ¶
Overview ¶
Package digest provides HTTP Digest authentication.
You may use Apache style htdigest files, a simple map of userXrealm->MD5(user:realm:password) or your own implementation of a user lookup function (if perhaps you use a database).
The API is designed for HTTP request routers like Martini. It provides a handler, which will do nothing to the HTTP response for valid authentication. For invalid authentication it will make an http.Error(), which will usually be a 401 (Unauthorized) to request authentication from a user.
You might use digest like this if you were getting passwords from your own source:
m := martini.Classic() ... myUserStore := auth.NewSimpleUserStore( map[string]string{ "foo:mortal": "3791e8e14a10b3666ba15d9e78e4b359", // pw is 'bar' "Mufasa:[email protected]": "939e7578ed9e3c518a452acee763bce9", // pw is 'Circle Of Life' }) ... digester := auth.NewDigestHandler( "mortal", nil, nil, myUserStore ) m.Use( digester.ServeHTTP ) // this will force authentication of all requests, you can be more specific.
If you want to use htdigest files (because I know you thought about passing credentials on the command line or in environment variables and you should know better):
m := martini.Classic() ... // Read file: hint, the nil is standing in for a malformed line reporter function. myUserFile,err := auth.NewHtdigestUserStore("path/to/my/htdigest/file", nil) if err != nil { log.Fatalf("Unable to load password file '%s': %s", digestfile, err.Error()) } ... digester := auth.NewDigestHandler( "My Realm", nil, nil, myUserFile ) ... m.Post("/my-sensitive-uri", digester.ServeHTTP, mySensitiveHandler) // just protect this one, notice chained handlers.
You will have noticed that both New*Handler calls included a pair of nils. If you don't like the way nonces are created, tracked, and expired, then you will want to replace one or both of these with your own implementations of Nonce and NonceStore. Otherwise, ignore those interfaces. The default is a 64 bit random nonce which will last for about 100 uses before going stale. The NonceStore expires nonces which are unused for about 5 to 10 minutes.
Index ¶
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type BadLineHandler ¶
type BadLineHandler func(err error)
A BadLineHandler is used to notice bad lines in an htdigest file. If not nil, it will be called for each bad line with a descriptive error. Think about what you do with these, they will sometimes contain hashed passwords.
type Digest ¶
The Digest interface exposes a ServeHTTP method from net/http. On successful authentication, nothing happens. On a failure or missing credentials then it will invoke http.Error(), afterwhich point you should consider the request finished.
func NewDigestHandler ¶
func NewDigestHandler(realm string, nonces NonceStore, nonceMaker NonceMaker, users UserStore) Digest
Create a new Digest.
realm - Your HTTP Digest realm. Must match that used in your UserStore.Lookup() function or htdigest file.
nonces - Pass nil for the default NonceStore, or implement your own.
nonceMaker - Pass nil for the default, or implement your own.
users - A UserStore. A simple one that uses a map, and a slightly more complicated one that reads Apache style htdigest files are included for you to choose from. Or write your own.
type HtdigestUserStore ¶
type HtdigestUserStore struct {
// contains filtered or unexported fields
}
Use an htdigest file from apache's htdigest command as a source of user authentication As provided it will read the file once. You may invoke .Reload() if you wish to track changes, perhaps in response to fsnotify.
func NewHtdigestUserStore ¶
func NewHtdigestUserStore(filename string, onbad BadLineHandler) (*HtdigestUserStore, error)
Create a new UserStore loaded from an Apache style htdigest file.
func (*HtdigestUserStore) Reload ¶
func (us *HtdigestUserStore) Reload(onbad BadLineHandler) error
Reload the htdigest's file. If there is an error, the old data will be kept instead. This function is thread safe.
func (*HtdigestUserStore) ReloadOn ¶
func (us *HtdigestUserStore) ReloadOn(when os.Signal, onbad BadLineHandler)
Reload the htdigest's file on a signal. If there is an error, the old data will be kept instead. Typically you would use syscall.SIGHUP for the value of "when"
type Nonce ¶
type Nonce interface { // Return the nonce string to be passed to the client Value() string // This is used when a nonce is expiring to get a new one in order // to chain in a sane manner. Next() Nonce // Returns true iff the nonce should be marked as stale to force a replacement, // ideally without ever rejecting a request. Stale() bool // Check is a particular 'nc' is acceptable at this time, and consume it if it // is. The function is not idempotent! AcceptCounter(uint) bool // Mark this Nonce as worn out. It should get a Next() in place and // after an interval, identify itself as Stale() in future calls. // The interval is to allow any inflight pipelined requests to clear. Expire() }
Each nonce created is tracked with a Nonce. The default lasts about 100 'nc' uses before going stale.
type NonceStore ¶
A NonceStore keeps track of currently valid nonces. It needs to handle expiration.
type UserStore ¶
type UserStore interface { // Look up a user in a realm. Valid is xxx,true,nil // bool false means no such user. // error not nil means something when wrong in the store Lookup(user string, realm string) (string, bool, error) }
func NewSimpleUserStore ¶
Create a simple UserStore. You will pass in a map of the form { "username:realm": "md5(username:realm:password)", ... } It will be used to answer .Lookup() queries. The map is NOT copied. You could alter it if you wished to track new accounts or password changes, though I suggest you make your own implementation of UserStore instead of going that route. Spare your code readers.