auth
auth/http.go
Actually validate grant requests. Write the logic to validate grant requests and stub out the rendering/error handling/redirecting locations. Finally, we get to the good stuff: implementing the specification. Write some tests to verify that granting requests works the way we think it does.
| paddy@51 | 1 package auth |
| paddy@51 | 2 |
| paddy@51 | 3 import ( |
| paddy@51 | 4 "net/http" |
| paddy@56 | 5 |
| paddy@56 | 6 "code.secondbit.org/uuid" |
| paddy@51 | 7 ) |
| paddy@51 | 8 |
| paddy@51 | 9 const getGrantTemplateName = "get_grant" |
| paddy@51 | 10 |
| paddy@51 | 11 func GetGrantHandler(w http.ResponseWriter, r *http.Request, context Context) { |
| paddy@56 | 12 if r.URL.Query().Get("client_id") == "" { |
| paddy@56 | 13 w.WriteHeader(http.StatusBadRequest) |
| paddy@56 | 14 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 15 "error": "Client ID must be specified in the request.", |
| paddy@56 | 16 }) |
| paddy@56 | 17 return |
| paddy@56 | 18 } |
| paddy@56 | 19 clientID, err := uuid.Parse(r.URL.Query().Get("client_id")) |
| paddy@56 | 20 if err != nil { |
| paddy@56 | 21 w.WriteHeader(http.StatusBadRequest) |
| paddy@56 | 22 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 23 "error": "client_id is not a valid Client ID.", |
| paddy@56 | 24 }) |
| paddy@56 | 25 return |
| paddy@56 | 26 } |
| paddy@56 | 27 client, err := context.GetClient(clientID) |
| paddy@56 | 28 if err != nil { |
| paddy@56 | 29 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 30 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 31 "internal_error": err, |
| paddy@56 | 32 }) |
| paddy@56 | 33 return |
| paddy@56 | 34 } |
| paddy@56 | 35 // whether a redirect URI is valid or not depends on the number of endpoints |
| paddy@56 | 36 // the client has registered |
| paddy@56 | 37 numEndpoints, err := context.CountEndpoints(clientID) |
| paddy@56 | 38 if err != nil { |
| paddy@56 | 39 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 40 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 41 "internal_error": err, |
| paddy@56 | 42 }) |
| paddy@56 | 43 return |
| paddy@56 | 44 } |
| paddy@56 | 45 redirectURI := r.URL.Query().Get("redirect_uri") |
| paddy@56 | 46 var validURI bool |
| paddy@56 | 47 if redirectURI != "" && numEndpoints > 1 { |
| paddy@56 | 48 // if there's more than one registered endpoint, we need to match the |
| paddy@56 | 49 // entire thing, character for character. So use strict checking. |
| paddy@56 | 50 validURI, err = context.CheckEndpoint(clientID, redirectURI, true) |
| paddy@56 | 51 if err != nil { |
| paddy@56 | 52 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 53 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 54 "internal_error": err, |
| paddy@56 | 55 }) |
| paddy@56 | 56 return |
| paddy@56 | 57 } |
| paddy@56 | 58 } else if redirectURI != "" && numEndpoints == 1 { |
| paddy@56 | 59 // if there's exactly one endpoint, we can match only the prefix of it, |
| paddy@56 | 60 // so don't use strict checking. |
| paddy@56 | 61 validURI, err = context.CheckEndpoint(clientID, redirectURI, false) |
| paddy@56 | 62 if err != nil { |
| paddy@56 | 63 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 64 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 65 "internal_error": err, |
| paddy@56 | 66 }) |
| paddy@56 | 67 return |
| paddy@56 | 68 } |
| paddy@56 | 69 } else if redirectURI == "" && numEndpoints == 1 { |
| paddy@56 | 70 // if we don't specify the endpoint and there's only one endpoint, the |
| paddy@56 | 71 // request is valid, and we're redirecting to that one endpoint |
| paddy@56 | 72 validURI = true |
| paddy@56 | 73 endpoints, err := context.ListEndpoints(clientID, 1, 0) |
| paddy@56 | 74 if err != nil { |
| paddy@56 | 75 w.WriteHeader(http.StatusInternalServerError) |
| paddy@56 | 76 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 77 "internal_error": err, |
| paddy@56 | 78 }) |
| paddy@56 | 79 return |
| paddy@56 | 80 } |
| paddy@56 | 81 if len(endpoints) != 1 { |
| paddy@56 | 82 validURI = false |
| paddy@56 | 83 } else { |
| paddy@56 | 84 redirectURI = endpoints[0].URI.String() |
| paddy@56 | 85 } |
| paddy@56 | 86 } else { |
| paddy@56 | 87 validURI = false |
| paddy@56 | 88 } |
| paddy@56 | 89 if !validURI { |
| paddy@56 | 90 w.WriteHeader(http.StatusBadRequest) |
| paddy@56 | 91 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 92 "error": "The redirect_uri specified is not valid.", |
| paddy@56 | 93 }) |
| paddy@56 | 94 return |
| paddy@56 | 95 } |
| paddy@56 | 96 if r.URL.Query().Get("response_type") != "code" { |
| paddy@56 | 97 // TODO: redirect error |
| paddy@56 | 98 } |
| paddy@56 | 99 //scope := r.URL.Query().Get("scope") |
| paddy@56 | 100 //state := r.URL.Query().Get("state") |
| paddy@56 | 101 if r.Method == "POST" { |
| paddy@56 | 102 // TODO: CSRF protection |
| paddy@56 | 103 if r.PostFormValue("grant") == "approved" { |
| paddy@56 | 104 // TODO: redirect |
| paddy@56 | 105 } else { |
| paddy@56 | 106 // TODO: redirect error |
| paddy@56 | 107 } |
| paddy@56 | 108 } |
| paddy@51 | 109 w.WriteHeader(http.StatusOK) |
| paddy@56 | 110 context.Render(w, getGrantTemplateName, map[string]interface{}{ |
| paddy@56 | 111 "client": client, |
| paddy@56 | 112 }) |
| paddy@51 | 113 } |