auth
auth/request.go
Implement an endpoint for token information. Implement an endpoint that allows us to look up information on a token. We strip the refresh token before the response is sent to avoid leaking the response token.
| paddy@99 | 1 package auth |
| paddy@99 | 2 |
| paddy@104 | 3 import ( |
| paddy@104 | 4 "encoding/json" |
| paddy@104 | 5 "log" |
| paddy@104 | 6 "net/http" |
| paddy@104 | 7 |
| paddy@104 | 8 "bitbucket.org/ww/goautoneg" |
| paddy@104 | 9 ) |
| paddy@104 | 10 |
| paddy@99 | 11 const ( |
| paddy@99 | 12 requestErrAccessDenied = "access_denied" |
| paddy@99 | 13 requestErrInsufficient = "insufficient" |
| paddy@99 | 14 requestErrOverflow = "overflow" |
| paddy@99 | 15 requestErrInvalidValue = "invalid_value" |
| paddy@99 | 16 requestErrInvalidFormat = "invalid_format" |
| paddy@99 | 17 requestErrMissing = "missing" |
| paddy@99 | 18 requestErrNotFound = "not_found" |
| paddy@104 | 19 requestErrConflict = "conflict" |
| paddy@99 | 20 requestErrActOfGod = "act_of_god" |
| paddy@99 | 21 ) |
| paddy@99 | 22 |
| paddy@104 | 23 var ( |
| paddy@104 | 24 actOfGodResponse = response{Errors: []requestError{requestError{Slug: requestErrActOfGod}}} |
| paddy@104 | 25 invalidFormatResponse = response{Errors: []requestError{requestError{Slug: requestErrInvalidFormat, Field: "/"}}} |
| paddy@104 | 26 |
| paddy@104 | 27 encoders = []string{"application/json"} |
| paddy@104 | 28 ) |
| paddy@104 | 29 |
| paddy@104 | 30 type response struct { |
| paddy@108 | 31 Errors []requestError `json:"errors,omitempty"` |
| paddy@108 | 32 Logins []Login `json:"logins,omitempty"` |
| paddy@108 | 33 Profiles []Profile `json:"profiles,omitempty"` |
| paddy@108 | 34 Clients []Client `json:"clients,omitempty"` |
| paddy@108 | 35 Endpoints []Endpoint `json:"endpoints,omitempty"` |
| paddy@159 | 36 Sessions []Session `json:"sessions,omitempty"` |
| paddy@167 | 37 Tokens []Token `json:"tokens,omitempty"` |
| paddy@104 | 38 } |
| paddy@104 | 39 |
| paddy@99 | 40 type requestError struct { |
| paddy@99 | 41 Slug string `json:"error,omitempty"` |
| paddy@99 | 42 Field string `json:"field,omitempty"` |
| paddy@99 | 43 Param string `json:"param,omitempty"` |
| paddy@99 | 44 Header string `json:"header,omitempty"` |
| paddy@99 | 45 } |
| paddy@104 | 46 |
| paddy@104 | 47 func negotiate(h http.Handler) http.Handler { |
| paddy@104 | 48 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| paddy@110 | 49 if r.Header.Get("Accept") != "" { |
| paddy@110 | 50 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders) |
| paddy@110 | 51 if contentType == "" { |
| paddy@110 | 52 w.WriteHeader(http.StatusNotAcceptable) |
| paddy@110 | 53 w.Write([]byte("Unsupported content type requested: " + r.Header.Get("Accept"))) |
| paddy@110 | 54 return |
| paddy@110 | 55 } |
| paddy@104 | 56 } |
| paddy@104 | 57 h.ServeHTTP(w, r) |
| paddy@104 | 58 }) |
| paddy@104 | 59 } |
| paddy@104 | 60 |
| paddy@104 | 61 func encode(w http.ResponseWriter, r *http.Request, status int, resp response) { |
| paddy@104 | 62 contentType := goautoneg.Negotiate(r.Header.Get("Accept"), encoders) |
| paddy@104 | 63 w.Header().Set("content-type", contentType) |
| paddy@104 | 64 w.WriteHeader(status) |
| paddy@104 | 65 var err error |
| paddy@104 | 66 switch contentType { |
| paddy@104 | 67 case "application/json": |
| paddy@104 | 68 enc := json.NewEncoder(w) |
| paddy@104 | 69 err = enc.Encode(resp) |
| paddy@110 | 70 default: |
| paddy@110 | 71 enc := json.NewEncoder(w) |
| paddy@110 | 72 err = enc.Encode(resp) |
| paddy@104 | 73 } |
| paddy@104 | 74 if err != nil { |
| paddy@104 | 75 log.Println(err) |
| paddy@104 | 76 } |
| paddy@104 | 77 } |
| paddy@104 | 78 |
| paddy@133 | 79 func decode(r *http.Request, target interface{}) error { |
| paddy@133 | 80 defer r.Body.Close() |
| paddy@133 | 81 switch r.Header.Get("Content-Type") { |
| paddy@133 | 82 case "application/json": |
| paddy@133 | 83 dec := json.NewDecoder(r.Body) |
| paddy@133 | 84 return dec.Decode(target) |
| paddy@133 | 85 default: |
| paddy@133 | 86 dec := json.NewDecoder(r.Body) |
| paddy@133 | 87 return dec.Decode(target) |
| paddy@133 | 88 } |
| paddy@133 | 89 } |
| paddy@133 | 90 |
| paddy@104 | 91 func wrap(context Context, f func(w http.ResponseWriter, r *http.Request, context Context)) http.Handler { |
| paddy@104 | 92 return negotiate(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { |
| paddy@165 | 93 w.Header().Set("Access-Control-Allow-Origin", "*") |
| paddy@165 | 94 w.Header().Set("Access-Control-Allow-Headers", r.Header.Get("Access-Control-Request-Headers")) |
| paddy@165 | 95 w.Header().Set("Access-Control-Allow-Credentials", "true") |
| paddy@165 | 96 if r.Method == "OPTIONS" { |
| paddy@165 | 97 w.Header().Set("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, PATCH, OPTIONS") |
| paddy@165 | 98 w.Header().Set("Allow", "GET, POST, PUT, DELETE, PATCH, OPTIONS") |
| paddy@165 | 99 w.WriteHeader(http.StatusOK) |
| paddy@165 | 100 return |
| paddy@165 | 101 } |
| paddy@104 | 102 f(w, r, context) |
| paddy@104 | 103 })) |
| paddy@104 | 104 } |