auth

Paddy 2014-11-02 Parent:03c9890f99c5 Child:cef5111af5c7

60:0cc717e02c9b Go to Latest

auth/http.go

Fill out redirects. Actually redirect the user when obtaining a grant. Check if the redirect_uri being passed when obtaining a grant is _actually a URL_. If it's not, return the right error. Add a test for a redirect_uri that isn't a valid URL.

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