auth

Paddy 2014-11-02 Parent:0cc717e02c9b Child:dd75d24475c0

61:cef5111af5c7 Go to Latest

auth/http.go

Make static error messages HTML safe. We may, at some point, want to use links or HTML elements in these static error messages. Since they're hardcoded, let's pass them as safe HTML strings.

History
paddy@51 1 package auth
paddy@51 2
paddy@51 3 import (
paddy@61 4 "html/template"
paddy@51 5 "net/http"
paddy@60 6 "net/url"
paddy@60 7 "time"
paddy@56 8
paddy@56 9 "code.secondbit.org/uuid"
paddy@51 10 )
paddy@51 11
paddy@60 12 const (
paddy@60 13 getGrantTemplateName = "get_grant"
paddy@60 14 defaultGrantExpiration = 600 // default to ten minute grant expirations
paddy@60 15 )
paddy@51 16
paddy@57 17 // GetGrantHandler presents and processes the page for asking a user to grant access
paddy@57 18 // to their data. See RFC 6749, Section 4.1.
paddy@51 19 func GetGrantHandler(w http.ResponseWriter, r *http.Request, context Context) {
paddy@56 20 if r.URL.Query().Get("client_id") == "" {
paddy@56 21 w.WriteHeader(http.StatusBadRequest)
paddy@56 22 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 23 "error": template.HTML("Client ID must be specified in the request."),
paddy@56 24 })
paddy@56 25 return
paddy@56 26 }
paddy@56 27 clientID, err := uuid.Parse(r.URL.Query().Get("client_id"))
paddy@56 28 if err != nil {
paddy@56 29 w.WriteHeader(http.StatusBadRequest)
paddy@56 30 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 31 "error": template.HTML("client_id is not a valid Client ID."),
paddy@56 32 })
paddy@56 33 return
paddy@56 34 }
paddy@56 35 client, err := context.GetClient(clientID)
paddy@56 36 if err != nil {
paddy@59 37 if err == ErrClientNotFound {
paddy@59 38 w.WriteHeader(http.StatusBadRequest)
paddy@59 39 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 40 "error": template.HTML("The specified Client couldn’t be found."),
paddy@59 41 })
paddy@59 42 } else {
paddy@59 43 w.WriteHeader(http.StatusInternalServerError)
paddy@59 44 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 45 "internal_error": template.HTML(err.Error()),
paddy@59 46 })
paddy@59 47 }
paddy@56 48 return
paddy@56 49 }
paddy@56 50 // whether a redirect URI is valid or not depends on the number of endpoints
paddy@56 51 // the client has registered
paddy@56 52 numEndpoints, err := context.CountEndpoints(clientID)
paddy@56 53 if err != nil {
paddy@56 54 w.WriteHeader(http.StatusInternalServerError)
paddy@56 55 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 56 "internal_error": template.HTML(err.Error()),
paddy@56 57 })
paddy@56 58 return
paddy@56 59 }
paddy@56 60 redirectURI := r.URL.Query().Get("redirect_uri")
paddy@56 61 var validURI bool
paddy@58 62 if redirectURI != "" {
paddy@58 63 // BUG(paddy): We really should normalize URIs before trying to compare them.
paddy@58 64 validURI, err = context.CheckEndpoint(clientID, redirectURI)
paddy@56 65 if err != nil {
paddy@56 66 w.WriteHeader(http.StatusInternalServerError)
paddy@56 67 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 68 "internal_error": template.HTML(err.Error()),
paddy@56 69 })
paddy@56 70 return
paddy@56 71 }
paddy@56 72 } else if redirectURI == "" && numEndpoints == 1 {
paddy@56 73 // if we don't specify the endpoint and there's only one endpoint, the
paddy@56 74 // request is valid, and we're redirecting to that one endpoint
paddy@56 75 validURI = true
paddy@56 76 endpoints, err := context.ListEndpoints(clientID, 1, 0)
paddy@56 77 if err != nil {
paddy@56 78 w.WriteHeader(http.StatusInternalServerError)
paddy@56 79 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 80 "internal_error": template.HTML(err.Error()),
paddy@56 81 })
paddy@56 82 return
paddy@56 83 }
paddy@56 84 if len(endpoints) != 1 {
paddy@56 85 validURI = false
paddy@56 86 } else {
paddy@56 87 redirectURI = endpoints[0].URI.String()
paddy@56 88 }
paddy@56 89 } else {
paddy@56 90 validURI = false
paddy@56 91 }
paddy@56 92 if !validURI {
paddy@56 93 w.WriteHeader(http.StatusBadRequest)
paddy@56 94 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 95 "error": template.HTML("The redirect_uri specified is not valid."),
paddy@56 96 })
paddy@56 97 return
paddy@56 98 }
paddy@60 99 scope := r.URL.Query().Get("scope")
paddy@60 100 state := r.URL.Query().Get("state")
paddy@60 101 redirectURL, err := url.Parse(redirectURI)
paddy@60 102 if err != nil {
paddy@60 103 w.WriteHeader(http.StatusBadRequest)
paddy@60 104 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@61 105 "error": template.HTML("The redirect_uri specified is not valid."),
paddy@60 106 })
paddy@60 107 return
paddy@60 108 }
paddy@56 109 if r.URL.Query().Get("response_type") != "code" {
paddy@60 110 redirectURL.Query().Add("error", "invalid_request")
paddy@60 111 redirectURL.Query().Add("state", state)
paddy@60 112 http.Redirect(w, r, redirectURL.String(), http.StatusFound)
paddy@60 113 return
paddy@56 114 }
paddy@56 115 if r.Method == "POST" {
paddy@56 116 // TODO: CSRF protection
paddy@56 117 if r.PostFormValue("grant") == "approved" {
paddy@60 118 code := uuid.NewID().String()
paddy@60 119 grant := Grant{
paddy@60 120 Code: code,
paddy@60 121 Created: time.Now(),
paddy@60 122 ExpiresIn: defaultGrantExpiration,
paddy@60 123 ClientID: clientID,
paddy@60 124 Scope: scope,
paddy@60 125 RedirectURI: redirectURI,
paddy@60 126 State: state,
paddy@60 127 }
paddy@60 128 err := context.SaveGrant(grant)
paddy@60 129 if err != nil {
paddy@60 130 redirectURL.Query().Add("error", "server_error")
paddy@60 131 redirectURL.Query().Add("state", state)
paddy@60 132 http.Redirect(w, r, redirectURL.String(), http.StatusFound)
paddy@60 133 return
paddy@60 134 }
paddy@60 135 redirectURL.Query().Add("code", code)
paddy@60 136 redirectURL.Query().Add("state", state)
paddy@60 137 http.Redirect(w, r, redirectURL.String(), http.StatusFound)
paddy@60 138 return
paddy@56 139 }
paddy@60 140 redirectURL.Query().Add("error", "access_denied")
paddy@60 141 redirectURL.Query().Add("state", state)
paddy@60 142 http.Redirect(w, r, redirectURL.String(), http.StatusFound)
paddy@60 143 return
paddy@56 144 }
paddy@51 145 w.WriteHeader(http.StatusOK)
paddy@56 146 context.Render(w, getGrantTemplateName, map[string]interface{}{
paddy@56 147 "client": client,
paddy@56 148 })
paddy@51 149 }