auth
2014-07-18
Child:7b9e0fc20256
auth/authorize.go
Start rewriting the repo. This code originally was a carbon copy of https://github.com/RangelReale/osin, but I am methodically stripping out the extensible nature of it for a simpler interface, while simultaneously bringing the style into line with the Ducky style.
| paddy@0 | 1 package oauth2 |
| paddy@0 | 2 |
| paddy@0 | 3 import ( |
| paddy@0 | 4 "net/http" |
| paddy@0 | 5 "net/url" |
| paddy@0 | 6 "time" |
| paddy@0 | 7 |
| paddy@0 | 8 "secondbit.org/uuid" |
| paddy@0 | 9 ) |
| paddy@0 | 10 |
| paddy@0 | 11 // AuthorizeRequestType is the type for OAuth param `response_type` |
| paddy@0 | 12 type AuthorizeRequestType string |
| paddy@0 | 13 |
| paddy@0 | 14 const ( |
| paddy@0 | 15 CodeAuthRT AuthorizeRequestType = "code" |
| paddy@0 | 16 TokenAuthRT = "token" |
| paddy@0 | 17 ) |
| paddy@0 | 18 |
| paddy@0 | 19 // Authorize request information |
| paddy@0 | 20 type AuthorizeRequest struct { |
| paddy@0 | 21 Type AuthorizeRequestType |
| paddy@0 | 22 Client Client |
| paddy@0 | 23 Scope string |
| paddy@0 | 24 RedirectURI string |
| paddy@0 | 25 State string |
| paddy@0 | 26 |
| paddy@0 | 27 // Token expiration in seconds. Change if different from default. |
| paddy@0 | 28 // If type = TokenAuthRT, this expiration will be for the ACCESS token. |
| paddy@0 | 29 Expiration int32 |
| paddy@0 | 30 } |
| paddy@0 | 31 |
| paddy@0 | 32 // Authorization data |
| paddy@0 | 33 type AuthorizeData struct { |
| paddy@0 | 34 // Client information |
| paddy@0 | 35 Client Client |
| paddy@0 | 36 |
| paddy@0 | 37 // Authorization code |
| paddy@0 | 38 Code string |
| paddy@0 | 39 |
| paddy@0 | 40 // Token expiration in seconds |
| paddy@0 | 41 ExpiresIn int32 |
| paddy@0 | 42 |
| paddy@0 | 43 // Requested scope |
| paddy@0 | 44 Scope string |
| paddy@0 | 45 |
| paddy@0 | 46 // Redirect URI from request |
| paddy@0 | 47 RedirectURI string |
| paddy@0 | 48 |
| paddy@0 | 49 // State data from request |
| paddy@0 | 50 State string |
| paddy@0 | 51 |
| paddy@0 | 52 // Date created |
| paddy@0 | 53 CreatedAt time.Time |
| paddy@0 | 54 } |
| paddy@0 | 55 |
| paddy@0 | 56 // IsExpired is true if authorization expired |
| paddy@0 | 57 func (d *AuthorizeData) IsExpired() bool { |
| paddy@0 | 58 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second).Before(time.Now()) |
| paddy@0 | 59 } |
| paddy@0 | 60 |
| paddy@0 | 61 // ExpireAt returns the expiration date |
| paddy@0 | 62 func (d *AuthorizeData) ExpireAt() time.Time { |
| paddy@0 | 63 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second) |
| paddy@0 | 64 } |
| paddy@0 | 65 |
| paddy@0 | 66 // HandleAuthorizeRequest is the main http.HandlerFunc for handling |
| paddy@0 | 67 // authorization requests |
| paddy@0 | 68 func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { |
| paddy@0 | 69 r.ParseForm() |
| paddy@0 | 70 |
| paddy@0 | 71 requestType := AuthorizeRequestType(r.Form.Get("response_type")) |
| paddy@0 | 72 if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) { |
| paddy@0 | 73 switch requestType { |
| paddy@0 | 74 case CodeAuthRT: |
| paddy@0 | 75 handleCodeRequest(w, r, ctx) |
| paddy@0 | 76 return |
| paddy@0 | 77 case TokenAuthRT: |
| paddy@0 | 78 handleTokenRequest(w, r, ctx) |
| paddy@0 | 79 return |
| paddy@0 | 80 } |
| paddy@0 | 81 } |
| paddy@0 | 82 // TODO: return error |
| paddy@0 | 83 } |
| paddy@0 | 84 |
| paddy@0 | 85 func handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { |
| paddy@0 | 86 // create the authorization request |
| paddy@0 | 87 unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri")) |
| paddy@0 | 88 if err != nil { |
| paddy@0 | 89 unescapedURI = "" |
| paddy@0 | 90 } |
| paddy@0 | 91 ret := &AuthorizeRequest{ |
| paddy@0 | 92 Type: CodeAuthRT, |
| paddy@0 | 93 State: r.Form.Get("state"), |
| paddy@0 | 94 Scope: r.Form.Get("scope"), |
| paddy@0 | 95 RedirectURI: unescapedURI, |
| paddy@0 | 96 Expiration: ctx.Config.AuthorizationExpiration, |
| paddy@0 | 97 } |
| paddy@0 | 98 |
| paddy@0 | 99 // must have a valid client |
| paddy@0 | 100 id, err := uuid.Parse(r.Form.Get("client_id")) |
| paddy@0 | 101 if err != nil { |
| paddy@0 | 102 // TODO: return error |
| paddy@0 | 103 return |
| paddy@0 | 104 } |
| paddy@0 | 105 ret.Client, err = GetClient(id, ctx) |
| paddy@0 | 106 if err != nil { |
| paddy@0 | 107 // TODO: return error |
| paddy@0 | 108 return |
| paddy@0 | 109 } |
| paddy@0 | 110 if ret.Client.RedirectURI == "" { |
| paddy@0 | 111 // TODO: return error |
| paddy@0 | 112 return |
| paddy@0 | 113 } |
| paddy@0 | 114 |
| paddy@0 | 115 // check redirect uri |
| paddy@0 | 116 if ret.RedirectURI == "" { |
| paddy@0 | 117 ret.RedirectURI = ret.Client.RedirectURI |
| paddy@0 | 118 } |
| paddy@0 | 119 if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil { |
| paddy@0 | 120 // TODO: return error |
| paddy@0 | 121 return |
| paddy@0 | 122 } |
| paddy@0 | 123 |
| paddy@0 | 124 // TODO: do redirect with ret data |
| paddy@0 | 125 } |
| paddy@0 | 126 |
| paddy@0 | 127 func handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) { |
| paddy@0 | 128 // create the authorization request |
| paddy@0 | 129 unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri")) |
| paddy@0 | 130 if err != nil { |
| paddy@0 | 131 unescapedURI = "" |
| paddy@0 | 132 } |
| paddy@0 | 133 ret := &AuthorizeRequest{ |
| paddy@0 | 134 Type: TokenAuthRT, |
| paddy@0 | 135 State: r.Form.Get("state"), |
| paddy@0 | 136 Scope: r.Form.Get("scope"), |
| paddy@0 | 137 RedirectURI: unescapedURI, |
| paddy@0 | 138 // this type will generate a token directly, use access token expiration instead. |
| paddy@0 | 139 Expiration: ctx.Config.AccessExpiration, |
| paddy@0 | 140 } |
| paddy@0 | 141 |
| paddy@0 | 142 // must have a valid client |
| paddy@0 | 143 id, err := uuid.Parse(r.Form.Get("client_id")) |
| paddy@0 | 144 if err != nil { |
| paddy@0 | 145 // TODO: return error |
| paddy@0 | 146 return |
| paddy@0 | 147 } |
| paddy@0 | 148 ret.Client, err = GetClient(id, ctx) |
| paddy@0 | 149 if err != nil { |
| paddy@0 | 150 // TODO: return error |
| paddy@0 | 151 return |
| paddy@0 | 152 } |
| paddy@0 | 153 if ret.Client.RedirectURI == "" { |
| paddy@0 | 154 // TODO: return error |
| paddy@0 | 155 return |
| paddy@0 | 156 } |
| paddy@0 | 157 |
| paddy@0 | 158 // check redirect uri |
| paddy@0 | 159 if ret.RedirectURI == "" { |
| paddy@0 | 160 ret.RedirectURI = ret.Client.RedirectURI |
| paddy@0 | 161 } |
| paddy@0 | 162 if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil { |
| paddy@0 | 163 // TODO: return error |
| paddy@0 | 164 } |
| paddy@0 | 165 |
| paddy@0 | 166 // TODO: redirect with ret information |
| paddy@0 | 167 } |
| paddy@0 | 168 |
| paddy@0 | 169 func FinishAuthorizeRequest(w http.ResponseWriter, r *http.Request, ar *AuthorizeRequest, ctx Context) { |
| paddy@0 | 170 // TODO: check if authorized? |
| paddy@0 | 171 if ar.Type == TokenAuthRT { |
| paddy@0 | 172 // TODO: w.SetRedirectFragment(true) was called... |
| paddy@0 | 173 |
| paddy@0 | 174 // generate token directly |
| paddy@0 | 175 ret := AccessRequest{ |
| paddy@0 | 176 Code: "", |
| paddy@0 | 177 Client: ar.Client, |
| paddy@0 | 178 RedirectURI: ar.RedirectURI, |
| paddy@0 | 179 Scope: ar.Scope, |
| paddy@0 | 180 GenerateRefresh: false, // per the RFC, should NOT generate a refresh token in this case |
| paddy@0 | 181 Expiration: ar.Expiration, |
| paddy@0 | 182 } |
| paddy@0 | 183 // TODO: ret.type was implicit |
| paddy@0 | 184 // TODO: ret.Authorized was true |
| paddy@0 | 185 FinishAccessRequest(w, r, ret, ctx) |
| paddy@0 | 186 } else { |
| paddy@0 | 187 // generate authorization token |
| paddy@0 | 188 ret := AuthorizeData{ |
| paddy@0 | 189 Client: ar.Client, |
| paddy@0 | 190 CreatedAt: time.Now(), |
| paddy@0 | 191 ExpiresIn: ar.Expiration, |
| paddy@0 | 192 RedirectURI: ar.RedirectURI, |
| paddy@0 | 193 State: ar.State, |
| paddy@0 | 194 Scope: ar.Scope, |
| paddy@0 | 195 Code: newToken(), |
| paddy@0 | 196 } |
| paddy@0 | 197 |
| paddy@0 | 198 // save authorization token |
| paddy@0 | 199 err := saveAuthorize(ret, ctx) |
| paddy@0 | 200 if err != nil { |
| paddy@0 | 201 // TODO: return error |
| paddy@0 | 202 return |
| paddy@0 | 203 } |
| paddy@0 | 204 |
| paddy@0 | 205 // TODO: redirect with ret.Code and ret.State |
| paddy@0 | 206 } |
| paddy@0 | 207 } |
| paddy@0 | 208 |
| paddy@0 | 209 func loadAuthorize(code string, ctx Context) (AuthorizeData, error) { |
| paddy@0 | 210 return AuthorizeData{}, nil |
| paddy@0 | 211 } |
| paddy@0 | 212 |
| paddy@0 | 213 func saveAuthorize(ret AuthorizeData, ctx Context) error { |
| paddy@0 | 214 return nil |
| paddy@0 | 215 } |
| paddy@0 | 216 |
| paddy@0 | 217 func removeAuthorize(code string, ctx Context) error { |
| paddy@0 | 218 return nil |
| paddy@0 | 219 } |