auth
1:7b9e0fc20256 Browse Files
Continue our descent to horribleness. Remove all the nonsense about "extensibility" and "clean separation of concerns", instead hardcoding connections to decisions. Remove all those "test" things that stopped passing.
access.go access_test.go authorize.go authorize_test.go config.go context.go errors.go info.go info_test.go session.go storage.go storage_test.go tokengen.go urivalidate.go urivalidate_test.go util.go util_test.go
1.1 --- a/access.go Fri Jul 18 07:13:22 2014 -0400 1.2 +++ b/access.go Fri Aug 01 23:08:38 2014 -0400 1.3 @@ -2,8 +2,10 @@ 1.4 1.5 import ( 1.6 "net/http" 1.7 + "net/url" 1.8 "time" 1.9 1.10 + "strconv" 1.11 "secondbit.org/uuid" 1.12 ) 1.13 1.14 @@ -15,58 +17,19 @@ 1.15 RefreshTokenGrant = "refresh_token" 1.16 PasswordGrant = "password" 1.17 ClientCredentialsGrant = "client_credentials" 1.18 - AssertionGrant = "assertion" 1.19 - ImplicitGrant = "__implicit" 1.20 ) 1.21 1.22 -// AccessRequest is a request for access tokens 1.23 -type AccessRequest struct { 1.24 - Code string 1.25 - Client Client 1.26 - AuthorizeData AuthorizeData 1.27 - AccessData AccessData 1.28 - RedirectURI string 1.29 - Scope string 1.30 - Username string 1.31 - Password string 1.32 - AssertionType string 1.33 - Assertion string 1.34 - 1.35 - // Token expiration in seconds. Change if different from default 1.36 - Expiration int32 1.37 - 1.38 - // Set if a refresh token should be generated 1.39 - GenerateRefresh bool 1.40 -} 1.41 - 1.42 // AccessData represents an access grant (tokens, expiration, client, etc) 1.43 type AccessData struct { 1.44 - // Client information 1.45 - Client Client 1.46 - 1.47 - // Authorize data, for authorization code 1.48 - AuthorizeData *AuthorizeData 1.49 - 1.50 - // Previous access data, for refresh token 1.51 - AccessData *AccessData 1.52 - 1.53 - // Access token 1.54 - AccessToken string 1.55 - 1.56 - // Refresh Token. Can be blank 1.57 - RefreshToken string 1.58 - 1.59 - // Token expiration in seconds 1.60 - ExpiresIn int32 1.61 - 1.62 - // Requested scope 1.63 - Scope string 1.64 - 1.65 - // Redirect URI from request 1.66 - RedirectURI string 1.67 - 1.68 - // Date created 1.69 - CreatedAt time.Time 1.70 + PreviousAuthorizeData *AuthorizeData 1.71 + PreviousAccessData *AccessData // previous access data, when refreshing 1.72 + AccessToken string 1.73 + RefreshToken string 1.74 + ExpiresIn int32 1.75 + CreatedAt time.Time 1.76 + TokenType string 1.77 + ProfileID uuid.ID 1.78 + AuthRequest 1.79 } 1.80 1.81 // IsExpired returns true if access expired 1.82 @@ -79,27 +42,16 @@ 1.83 return d.CreatedAt.Add(time.Duration(d.ExpiresIn) * time.Second) 1.84 } 1.85 1.86 -// AccessTokenGen generates access tokens 1.87 -type AccessTokenGen interface { 1.88 - GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) 1.89 -} 1.90 - 1.91 // HandleOAuth2AccessRequest is the http.HandlerFunc for handling access token requests. 1.92 func HandleOAuth2AccessRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.93 // Only allow GET or POST 1.94 if r.Method != "POST" { 1.95 - if r.Method == "GET" && !ctx.Config.AllowGetAccessRequest { 1.96 + if r.Method != "GET" || !ctx.Config.AllowGetAccessRequest { 1.97 // TODO: return error 1.98 return 1.99 } 1.100 } 1.101 1.102 - err := r.ParseForm() 1.103 - if err != nil { 1.104 - // TODO: return error 1.105 - return 1.106 - } 1.107 - 1.108 grantType := GrantType(r.Form.Get("grant_type")) 1.109 if ctx.Config.AllowedAccessTypes.Exists(grantType) { 1.110 switch grantType { 1.111 @@ -111,8 +63,6 @@ 1.112 handlePasswordRequest(w, r, ctx) 1.113 case ClientCredentialsGrant: 1.114 handleClientCredentialsRequest(w, r, ctx) 1.115 - case AssertionGrant: 1.116 - handleAssertionRequest(w, r, ctx) 1.117 default: 1.118 // TODO: return error 1.119 return 1.120 @@ -128,63 +78,69 @@ 1.121 return 1.122 } 1.123 1.124 - // generate access token 1.125 - ret := AccessRequest{ 1.126 - Code: r.Form.Get("code"), 1.127 - RedirectURI: r.Form.Get("redirect_uri"), 1.128 - GenerateRefresh: true, 1.129 - Expiration: ctx.Config.AccessExpiration, 1.130 - } 1.131 - 1.132 + code := r.Form.Get("code") 1.133 // "code" is required 1.134 - if ret.Code == "" { 1.135 + if code == "" { 1.136 // TODO: return error 1.137 return 1.138 } 1.139 1.140 // must have a valid client 1.141 - ret.Client, err = getClient(auth, ctx) 1.142 + client, err := getClient(auth, ctx) 1.143 if err != nil { 1.144 // TODO: return error 1.145 return 1.146 } 1.147 1.148 // must be a valid authorization code 1.149 - ret.AuthorizeData, err = loadAuthorize(ret.Code, ctx) 1.150 + authData, err := ctx.Tokens.GetAuthorization(code) 1.151 if err != nil { 1.152 // TODO: return error 1.153 return 1.154 } 1.155 - if ret.AuthorizeData.Client.RedirectURI == "" { 1.156 + if authData.Client.RedirectURI == "" { 1.157 // TODO: return error 1.158 return 1.159 } 1.160 - if ret.AuthorizeData.IsExpired() { 1.161 + if authData.IsExpired() { 1.162 return // TODO: return error 1.163 } 1.164 1.165 // code must be from the client 1.166 - if !ret.AuthorizeData.Client.ID.Equal(ret.Client.ID) { 1.167 + if !authData.Client.ID.Equal(client.ID) { 1.168 // TODO: return error 1.169 return 1.170 } 1.171 1.172 // check redirect uri 1.173 - if ret.RedirectURI == "" { 1.174 - ret.RedirectURI = ret.Client.RedirectURI 1.175 + redirectURI := r.Form.Get("redirect_uri") 1.176 + if redirectURI == "" { 1.177 + redirectURI = client.RedirectURI 1.178 } 1.179 - if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil { 1.180 + if err = validateURI(client.RedirectURI, redirectURI); err != nil { 1.181 // TODO: return error 1.182 return 1.183 } 1.184 - if ret.AuthorizeData.RedirectURI != ret.RedirectURI { 1.185 + if authData.RedirectURI != redirectURI { 1.186 // TODO: return error 1.187 return 1.188 } 1.189 1.190 - // set rest of data 1.191 - ret.Scope = ret.AuthorizeData.Scope 1.192 - // TODO: write ret 1.193 + data := AccessData{ 1.194 + AuthRequest: AuthRequest{ 1.195 + Client: client, 1.196 + RedirectURI: redirectURI, 1.197 + Scope: authData.Scope, 1.198 + }, 1.199 + PreviousAuthorizeData: &authData, 1.200 + } 1.201 + 1.202 + err = fillTokens(&data, true, ctx) 1.203 + if err != nil { 1.204 + // TODO: return error 1.205 + return 1.206 + } 1.207 + // TODO: write data 1.208 } 1.209 1.210 func handleRefreshTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.211 @@ -195,52 +151,62 @@ 1.212 return 1.213 } 1.214 1.215 - // generate access token 1.216 - ret := AccessRequest{ 1.217 - Code: r.Form.Get("refresh_token"), 1.218 - Scope: r.Form.Get("scope"), 1.219 - GenerateRefresh: true, 1.220 - Expiration: ctx.Config.AccessExpiration, 1.221 - } 1.222 + code := r.Form.Get("refresh_token") 1.223 1.224 // "refresh_token" is required 1.225 - if ret.Code == "" { 1.226 + if code == "" { 1.227 // TODO: return error 1.228 return 1.229 } 1.230 1.231 // must have a valid client 1.232 - ret.Client, err = getClient(auth, ctx) 1.233 + client, err := getClient(auth, ctx) 1.234 if err != nil { 1.235 // TODO: return error 1.236 return 1.237 } 1.238 1.239 // must be a valid refresh code 1.240 - ret.AccessData, err = loadRefresh(ret.Code, ctx) 1.241 + refreshData, err := ctx.Tokens.GetRefresh(code) 1.242 if err != nil { 1.243 // TODO: return error 1.244 return 1.245 } 1.246 - if ret.AccessData.Client.RedirectURI == "" { 1.247 + if refreshData.Client.RedirectURI == "" { 1.248 // TODO: return error 1.249 return 1.250 } 1.251 1.252 // client must be the same as the previous token 1.253 - if !ret.AccessData.Client.ID.Equal(ret.Client.ID) { 1.254 + if !refreshData.Client.ID.Equal(client.ID) { 1.255 // TODO: return error 1.256 return 1.257 - 1.258 } 1.259 1.260 // set rest of data 1.261 - ret.RedirectURI = ret.AccessData.RedirectURI 1.262 - if ret.Scope == "" { 1.263 - ret.Scope = ret.AccessData.Scope 1.264 + redirectURI := r.Form.Get("redirect_uri") 1.265 + if redirectURI == "" { 1.266 + redirectURI = refreshData.RedirectURI 1.267 + } 1.268 + scope := r.Form.Get("scope") 1.269 + if scope == "" { 1.270 + scope = refreshData.Scope 1.271 } 1.272 1.273 - // TODO: write ret 1.274 + data := AccessData{ 1.275 + AuthRequest: AuthRequest{ 1.276 + Client: client, 1.277 + RedirectURI: redirectURI, 1.278 + Scope: scope, 1.279 + }, 1.280 + PreviousAccessData: &refreshData, 1.281 + } 1.282 + err = fillTokens(&data, true, ctx) 1.283 + if err != nil { 1.284 + // TODO: return error 1.285 + return 1.286 + } 1.287 + // TODO: write data 1.288 } 1.289 1.290 func handlePasswordRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.291 @@ -251,32 +217,50 @@ 1.292 return 1.293 } 1.294 1.295 - // generate access token 1.296 - ret := AccessRequest{ 1.297 - Username: r.Form.Get("username"), 1.298 - Password: r.Form.Get("password"), 1.299 - Scope: r.Form.Get("scope"), 1.300 - GenerateRefresh: true, 1.301 - Expiration: ctx.Config.AccessExpiration, 1.302 - } 1.303 + username := r.Form.Get("username") 1.304 + password := r.Form.Get("password") 1.305 + scope := r.Form.Get("scope") 1.306 1.307 // "username" and "password" is required 1.308 - if ret.Username == "" || ret.Password == "" { 1.309 + if username == "" || password == "" { 1.310 // TODO: return error 1.311 return 1.312 } 1.313 1.314 // must have a valid client 1.315 - ret.Client, err = getClient(auth, ctx) 1.316 + client, err := getClient(auth, ctx) 1.317 if err != nil { 1.318 // TODO: return error 1.319 return 1.320 } 1.321 1.322 // set redirect uri 1.323 - ret.RedirectURI = ret.Client.RedirectURI 1.324 + redirectURI := r.Form.Get("redirect_uri") 1.325 + if redirectURI == "" { 1.326 + redirectURI = client.RedirectURI 1.327 + } 1.328 1.329 - // TODO: write ret 1.330 + _, err = ctx.Profiles.GetProfile(username, password) 1.331 + if err != nil { 1.332 + // TODO: return error 1.333 + return 1.334 + } 1.335 + 1.336 + data := AccessData{ 1.337 + AuthRequest: AuthRequest{ 1.338 + Client: client, 1.339 + RedirectURI: redirectURI, 1.340 + Scope: scope, 1.341 + }, 1.342 + } 1.343 + 1.344 + err = fillTokens(&data, true, ctx) 1.345 + if err != nil { 1.346 + // TODO: return error 1.347 + return 1.348 + } 1.349 + 1.350 + // TODO: write data 1.351 } 1.352 1.353 func handleClientCredentialsRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.354 @@ -287,130 +271,111 @@ 1.355 return 1.356 } 1.357 1.358 - // generate access token 1.359 - ret := AccessRequest{ 1.360 - Scope: r.Form.Get("scope"), 1.361 - GenerateRefresh: true, 1.362 - Expiration: ctx.Config.AccessExpiration, 1.363 - } 1.364 + scope := r.Form.Get("scope") 1.365 1.366 // must have a valid client 1.367 - ret.Client, err = getClient(auth, ctx) 1.368 + client, err := getClient(auth, ctx) 1.369 if err != nil { 1.370 // TODO: return error 1.371 return 1.372 } 1.373 1.374 // set redirect uri 1.375 - ret.RedirectURI = ret.Client.RedirectURI 1.376 + redirectURI := r.Form.Get("redirect_uri") 1.377 + if redirectURI == "" { 1.378 + redirectURI = client.RedirectURI 1.379 + } 1.380 1.381 - // TODO: write ret 1.382 -} 1.383 + data := AccessData{ 1.384 + AuthRequest: AuthRequest{ 1.385 + Client: client, 1.386 + RedirectURI: redirectURI, 1.387 + Scope: scope, 1.388 + }, 1.389 + } 1.390 1.391 -func handleAssertionRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 1.392 - // get client authentication 1.393 - auth, err := getClientAuth(r, ctx.Config.AllowClientSecretInParams) 1.394 + err = fillTokens(&data, true, ctx) 1.395 if err != nil { 1.396 // TODO: return error 1.397 return 1.398 } 1.399 1.400 - // generate access token 1.401 - ret := &AccessRequest{ 1.402 - Scope: r.Form.Get("scope"), 1.403 - AssertionType: r.Form.Get("assertion_type"), 1.404 - Assertion: r.Form.Get("assertion"), 1.405 - GenerateRefresh: false, // assertion should NOT generate a refresh token, per the RFC 1.406 - Expiration: ctx.Config.AccessExpiration, 1.407 - } 1.408 - 1.409 - // "assertion_type" and "assertion" is required 1.410 - if ret.AssertionType == "" || ret.Assertion == "" { 1.411 - // TODO: return error 1.412 - return 1.413 - } 1.414 - 1.415 - // must have a valid client 1.416 - ret.Client, err = getClient(auth, ctx) 1.417 - if err != nil { 1.418 - //TODO: return error 1.419 - return 1.420 - } 1.421 - 1.422 - // set redirect uri 1.423 - ret.RedirectURI = ret.Client.RedirectURI 1.424 - 1.425 - // TODO: write ret 1.426 + // TODO: write data 1.427 } 1.428 1.429 -func FinishAccessRequest(w http.ResponseWriter, r *http.Request, ar AccessRequest, ctx Context) { 1.430 - // TODO: check if authorized? 1.431 - redirectURI := r.Form.Get("redirect_uri") 1.432 - // Get redirect uri from AccessRequest if it's there (e.g., refresh token request) 1.433 - if ar.RedirectURI != "" { 1.434 - redirectURI = ar.RedirectURI 1.435 - } 1.436 - ret := AccessData{ 1.437 - Client: ar.Client, 1.438 - AuthorizeData: &ar.AuthorizeData, 1.439 - AccessData: &ar.AccessData, 1.440 - RedirectURI: redirectURI, 1.441 - CreatedAt: time.Now(), 1.442 - ExpiresIn: ar.Expiration, 1.443 - Scope: ar.Scope, 1.444 - } 1.445 - 1.446 +func fillTokens(data *AccessData, includeRefresh bool, ctx Context) error { 1.447 var err error 1.448 1.449 // generate access token 1.450 - ret.AccessToken = newToken() 1.451 - if ar.GenerateRefresh { 1.452 - ret.RefreshToken = newToken() 1.453 + data.AccessToken = newToken() 1.454 + if includeRefresh { 1.455 + data.RefreshToken = newToken() 1.456 } 1.457 1.458 // save access token 1.459 - err = saveAccess(ret, ctx) 1.460 + err = ctx.Tokens.SaveAccess(*data) 1.461 if err != nil { 1.462 - // TODO: return error 1.463 - return 1.464 + // TODO: abstract out error 1.465 + return err 1.466 } 1.467 1.468 // remove authorization token 1.469 - if ret.AuthorizeData != nil { 1.470 - err = removeAuthorize(ret.AuthorizeData.Code, ctx) 1.471 + if data.PreviousAuthorizeData != nil { 1.472 + err = ctx.Tokens.RemoveAuthorization(data.PreviousAuthorizeData.Code) 1.473 if err != nil { 1.474 // TODO: log error 1.475 } 1.476 } 1.477 1.478 // remove previous access token 1.479 - if ret.AccessData != nil { 1.480 - if ret.AccessData.RefreshToken != "" { 1.481 - err = removeRefresh(ret.AccessData.RefreshToken, ctx) 1.482 + if data.PreviousAccessData != nil { 1.483 + if data.PreviousAccessData.RefreshToken != "" { 1.484 + err = ctx.Tokens.RemoveRefresh(data.PreviousAccessData.RefreshToken) 1.485 if err != nil { 1.486 // TODO: log error 1.487 } 1.488 } 1.489 - err = removeAccess(ret.AccessData.AccessToken, ctx) 1.490 + err = ctx.Tokens.RemoveAccess(data.PreviousAccessData.AccessToken) 1.491 if err != nil { 1.492 // TODO: log error 1.493 } 1.494 } 1.495 1.496 - // output data 1.497 - //w.Output["access_token"] = ret.AccessToken 1.498 - //w.Output["token_type"] = ctx.Config.TokenType 1.499 - //w.Output["expires_in"] = ret.ExpiresIn 1.500 - //if ret.RefreshToken != "" { 1.501 - // w.Output["refresh_token"] = ret.RefreshToken 1.502 - //} 1.503 - //if ar.Scope != "" { 1.504 - // w.Output["scope"] = ar.Scope 1.505 - //} 1.506 - // TODO: write ret 1.507 + data.TokenType = ctx.Config.TokenType 1.508 + data.ExpiresIn = ctx.Config.AccessExpiration 1.509 + data.CreatedAt = time.Now() 1.510 + return nil 1.511 } 1.512 1.513 -// Helper Functions 1.514 +func (data AccessData) GetRedirect(fragment bool) (string, error) { 1.515 + u, err := url.Parse(data.RedirectURI) 1.516 + if err != nil { 1.517 + return "", err 1.518 + } 1.519 + 1.520 + // add parameters 1.521 + q := u.Query() 1.522 + q.Set("access_token", data.AccessToken) 1.523 + q.Set("token_type", data.TokenType) 1.524 + q.Set("expires_in", strconv.FormatInt(int64(data.ExpiresIn), 10)) 1.525 + if data.RefreshToken != "" { 1.526 + q.Set("refresh_token", data.RefreshToken) 1.527 + } 1.528 + if data.Scope != "" { 1.529 + q.Set("scope", data.Scope) 1.530 + } 1.531 + if len(data.ProfileID) > 0 { 1.532 + q.Set("profile", data.ProfileID.String()) 1.533 + } 1.534 + if fragment { 1.535 + u.RawQuery = "" 1.536 + u.Fragment = q.Encode() 1.537 + } else { 1.538 + u.RawQuery = q.Encode() 1.539 + } 1.540 + 1.541 + return u.String(), nil 1.542 +} 1.543 1.544 // getClient looks up and authenticates the basic auth using the given 1.545 // storage. Sets an error on the response if auth fails or a server error occurs. 1.546 @@ -419,7 +384,7 @@ 1.547 if err != nil { 1.548 return Client{}, err 1.549 } 1.550 - client, err := GetClient(id, ctx) 1.551 + client, err := ctx.Clients.GetClient(id) 1.552 if err != nil { 1.553 // TODO: abstract out errors 1.554 return Client{}, err 1.555 @@ -434,23 +399,3 @@ 1.556 } 1.557 return client, nil 1.558 } 1.559 - 1.560 -func loadRefresh(code string, ctx Context) (AccessData, error) { 1.561 - return AccessData{}, nil 1.562 -} 1.563 - 1.564 -func loadAccess(code string, ctx Context) (AccessData, error) { 1.565 - return AccessData{}, nil 1.566 -} 1.567 - 1.568 -func saveAccess(data AccessData, ctx Context) error { 1.569 - return nil 1.570 -} 1.571 - 1.572 -func removeAccess(token string, ctx Context) error { 1.573 - return nil 1.574 -} 1.575 - 1.576 -func removeRefresh(token string, ctx Context) error { 1.577 - return nil 1.578 -}
2.1 --- a/access_test.go Fri Jul 18 07:13:22 2014 -0400 2.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 2.3 @@ -1,195 +0,0 @@ 2.4 -package oauth2 2.5 - 2.6 -import ( 2.7 - "net/http" 2.8 - "net/url" 2.9 - "testing" 2.10 -) 2.11 - 2.12 -func TestAccessAuthorizationCode(t *testing.T) { 2.13 - sconfig := NewServerConfig() 2.14 - sconfig.AllowedAccessTypes = AllowedAccessType{AuthorizationCodeART} 2.15 - server := NewServer(sconfig, NewTestingStorage()) 2.16 - server.AccessTokenGen = &TestingAccessTokenGen{} 2.17 - resp := server.NewResponse() 2.18 - 2.19 - req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) 2.20 - if err != nil { 2.21 - t.Fatal(err) 2.22 - } 2.23 - req.SetBasicAuth("1234", "aabbccdd") 2.24 - 2.25 - req.Form = make(url.Values) 2.26 - req.Form.Set("grant_type", string(AuthorizationCodeART)) 2.27 - req.Form.Set("code", "9999") 2.28 - req.Form.Set("state", "a") 2.29 - req.PostForm = make(url.Values) 2.30 - 2.31 - if ar := server.HandleAccessRequest(resp, req); ar != nil { 2.32 - ar.Authorized = true 2.33 - server.FinishAccessRequest(resp, req, ar) 2.34 - } 2.35 - 2.36 - //fmt.Printf("%+v", resp) 2.37 - 2.38 - if resp.IsError && resp.InternalError != nil { 2.39 - t.Fatalf("Error in response: %s", resp.InternalError) 2.40 - } 2.41 - 2.42 - if resp.IsError { 2.43 - t.Fatalf("Should not be an error") 2.44 - } 2.45 - 2.46 - if resp.Type != DATA { 2.47 - t.Fatalf("Response should be data") 2.48 - } 2.49 - 2.50 - if d := resp.Output["access_token"]; d != "1" { 2.51 - t.Fatalf("Unexpected access token: %s", d) 2.52 - } 2.53 - 2.54 - if d := resp.Output["refresh_token"]; d != "r1" { 2.55 - t.Fatalf("Unexpected refresh token: %s", d) 2.56 - } 2.57 -} 2.58 - 2.59 -func TestAccessRefreshToken(t *testing.T) { 2.60 - sconfig := NewServerConfig() 2.61 - sconfig.AllowedAccessTypes = AllowedAccessType{REFRESH_TOKEN} 2.62 - server := NewServer(sconfig, NewTestingStorage()) 2.63 - server.AccessTokenGen = &TestingAccessTokenGen{} 2.64 - resp := server.NewResponse() 2.65 - 2.66 - req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) 2.67 - if err != nil { 2.68 - t.Fatal(err) 2.69 - } 2.70 - req.SetBasicAuth("1234", "aabbccdd") 2.71 - 2.72 - req.Form = make(url.Values) 2.73 - req.Form.Set("grant_type", string(REFRESH_TOKEN)) 2.74 - req.Form.Set("refresh_token", "r9999") 2.75 - req.Form.Set("state", "a") 2.76 - req.PostForm = make(url.Values) 2.77 - 2.78 - if ar := server.HandleAccessRequest(resp, req); ar != nil { 2.79 - ar.Authorized = true 2.80 - server.FinishAccessRequest(resp, req, ar) 2.81 - } 2.82 - 2.83 - //fmt.Printf("%+v", resp) 2.84 - 2.85 - if resp.IsError && resp.InternalError != nil { 2.86 - t.Fatalf("Error in response: %s", resp.InternalError) 2.87 - } 2.88 - 2.89 - if resp.IsError { 2.90 - t.Fatalf("Should not be an error") 2.91 - } 2.92 - 2.93 - if resp.Type != DATA { 2.94 - t.Fatalf("Response should be data") 2.95 - } 2.96 - 2.97 - if d := resp.Output["access_token"]; d != "1" { 2.98 - t.Fatalf("Unexpected access token: %s", d) 2.99 - } 2.100 - 2.101 - if d := resp.Output["refresh_token"]; d != "r1" { 2.102 - t.Fatalf("Unexpected refresh token: %s", d) 2.103 - } 2.104 -} 2.105 - 2.106 -func TestAccessPassword(t *testing.T) { 2.107 - sconfig := NewServerConfig() 2.108 - sconfig.AllowedAccessTypes = AllowedAccessType{PASSWORD} 2.109 - server := NewServer(sconfig, NewTestingStorage()) 2.110 - server.AccessTokenGen = &TestingAccessTokenGen{} 2.111 - resp := server.NewResponse() 2.112 - 2.113 - req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) 2.114 - if err != nil { 2.115 - t.Fatal(err) 2.116 - } 2.117 - req.SetBasicAuth("1234", "aabbccdd") 2.118 - 2.119 - req.Form = make(url.Values) 2.120 - req.Form.Set("grant_type", string(PASSWORD)) 2.121 - req.Form.Set("username", "testing") 2.122 - req.Form.Set("password", "testing") 2.123 - req.Form.Set("state", "a") 2.124 - req.PostForm = make(url.Values) 2.125 - 2.126 - if ar := server.HandleAccessRequest(resp, req); ar != nil { 2.127 - ar.Authorized = ar.Username == "testing" && ar.Password == "testing" 2.128 - server.FinishAccessRequest(resp, req, ar) 2.129 - } 2.130 - 2.131 - //fmt.Printf("%+v", resp) 2.132 - 2.133 - if resp.IsError && resp.InternalError != nil { 2.134 - t.Fatalf("Error in response: %s", resp.InternalError) 2.135 - } 2.136 - 2.137 - if resp.IsError { 2.138 - t.Fatalf("Should not be an error") 2.139 - } 2.140 - 2.141 - if resp.Type != DATA { 2.142 - t.Fatalf("Response should be data") 2.143 - } 2.144 - 2.145 - if d := resp.Output["access_token"]; d != "1" { 2.146 - t.Fatalf("Unexpected access token: %s", d) 2.147 - } 2.148 - 2.149 - if d := resp.Output["refresh_token"]; d != "r1" { 2.150 - t.Fatalf("Unexpected refresh token: %s", d) 2.151 - } 2.152 -} 2.153 - 2.154 -func TestAccessClientCredentials(t *testing.T) { 2.155 - sconfig := NewServerConfig() 2.156 - sconfig.AllowedAccessTypes = AllowedAccessType{CLIENT_CREDENTIALS} 2.157 - server := NewServer(sconfig, NewTestingStorage()) 2.158 - server.AccessTokenGen = &TestingAccessTokenGen{} 2.159 - resp := server.NewResponse() 2.160 - 2.161 - req, err := http.NewRequest("POST", "http://localhost:14000/appauth", nil) 2.162 - if err != nil { 2.163 - t.Fatal(err) 2.164 - } 2.165 - req.SetBasicAuth("1234", "aabbccdd") 2.166 - 2.167 - req.Form = make(url.Values) 2.168 - req.Form.Set("grant_type", string(CLIENT_CREDENTIALS)) 2.169 - req.Form.Set("state", "a") 2.170 - req.PostForm = make(url.Values) 2.171 - 2.172 - if ar := server.HandleAccessRequest(resp, req); ar != nil { 2.173 - ar.Authorized = true 2.174 - server.FinishAccessRequest(resp, req, ar) 2.175 - } 2.176 - 2.177 - //fmt.Printf("%+v", resp) 2.178 - 2.179 - if resp.IsError && resp.InternalError != nil { 2.180 - t.Fatalf("Error in response: %s", resp.InternalError) 2.181 - } 2.182 - 2.183 - if resp.IsError { 2.184 - t.Fatalf("Should not be an error") 2.185 - } 2.186 - 2.187 - if resp.Type != DATA { 2.188 - t.Fatalf("Response should be data") 2.189 - } 2.190 - 2.191 - if d := resp.Output["access_token"]; d != "1" { 2.192 - t.Fatalf("Unexpected access token: %s", d) 2.193 - } 2.194 - 2.195 - if d := resp.Output["refresh_token"]; d != "r1" { 2.196 - t.Fatalf("Unexpected refresh token: %s", d) 2.197 - } 2.198 -}
3.1 --- a/authorize.go Fri Jul 18 07:13:22 2014 -0400 3.2 +++ b/authorize.go Fri Aug 01 23:08:38 2014 -0400 3.3 @@ -5,6 +5,7 @@ 3.4 "net/url" 3.5 "time" 3.6 3.7 + "strings" 3.8 "secondbit.org/uuid" 3.9 ) 3.10 3.11 @@ -16,41 +17,27 @@ 3.12 TokenAuthRT = "token" 3.13 ) 3.14 3.15 -// Authorize request information 3.16 -type AuthorizeRequest struct { 3.17 - Type AuthorizeRequestType 3.18 +type AuthRequest struct { 3.19 Client Client 3.20 Scope string 3.21 RedirectURI string 3.22 State string 3.23 - 3.24 - // Token expiration in seconds. Change if different from default. 3.25 - // If type = TokenAuthRT, this expiration will be for the ACCESS token. 3.26 - Expiration int32 3.27 } 3.28 3.29 // Authorization data 3.30 type AuthorizeData struct { 3.31 - // Client information 3.32 - Client Client 3.33 - 3.34 // Authorization code 3.35 Code string 3.36 3.37 // Token expiration in seconds 3.38 ExpiresIn int32 3.39 3.40 - // Requested scope 3.41 - Scope string 3.42 - 3.43 - // Redirect URI from request 3.44 - RedirectURI string 3.45 - 3.46 - // State data from request 3.47 - State string 3.48 - 3.49 // Date created 3.50 CreatedAt time.Time 3.51 + 3.52 + ProfileID uuid.ID 3.53 + 3.54 + AuthRequest 3.55 } 3.56 3.57 // IsExpired is true if authorization expired 3.58 @@ -67,153 +54,217 @@ 3.59 // authorization requests 3.60 func HandleAuthorizeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 3.61 r.ParseForm() 3.62 + // create the authorization request 3.63 + redirectURI := r.Form.Get("redirect_uri") 3.64 + var err error 3.65 + if redirectURI != "" { 3.66 + redirectURI, err = url.QueryUnescape(redirectURI) 3.67 + if err != nil { 3.68 + ctx.RenderError(w, URIFormatError(redirectURI)) 3.69 + return 3.70 + } 3.71 + } 3.72 + 3.73 + state := r.Form.Get("state") 3.74 + scope := r.Form.Get("scope") 3.75 + 3.76 + // must have a valid client 3.77 + id, err := uuid.Parse(r.Form.Get("client_id")) 3.78 + if err != nil { 3.79 + ctx.RenderError(w, InvalidClientIDError(r.Form.Get("client_id"))) 3.80 + return 3.81 + } 3.82 + client, err := GetClient(id, ctx) 3.83 + if err != nil { 3.84 + if err == ClientNotFoundError { 3.85 + ctx.RenderError(w, ClientNotFoundError) 3.86 + return 3.87 + } 3.88 + if redirectURI == "" { 3.89 + ctx.RenderError(w, URIMissingError) 3.90 + return 3.91 + } 3.92 + req := AuthRequest{ 3.93 + RedirectURI: redirectURI, 3.94 + Scope: scope, 3.95 + State: state, 3.96 + } 3.97 + redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain) 3.98 + if err != nil { 3.99 + ctx.RenderError(w, URIFormatError(redirectURI)) 3.100 + return 3.101 + } 3.102 + http.Redirect(w, r, redir, http.StatusFound) 3.103 + return 3.104 + } 3.105 + if client.RedirectURI == "" { 3.106 + ctx.RenderError(w, URIMissingError) 3.107 + return 3.108 + } 3.109 + 3.110 + // check redirect uri 3.111 + if redirectURI == "" { 3.112 + redirectURI = client.RedirectURI 3.113 + } 3.114 + if err = validateURI(client.RedirectURI, redirectURI); err != nil { 3.115 + ctx.RenderError(w, NewURIMismatchError(client.RedirectURI, redirectURI)) 3.116 + return 3.117 + } 3.118 + 3.119 + req := AuthRequest{ 3.120 + Client: client, 3.121 + RedirectURI: redirectURI, 3.122 + Scope: scope, 3.123 + State: state, 3.124 + } 3.125 3.126 requestType := AuthorizeRequestType(r.Form.Get("response_type")) 3.127 if ctx.Config.AllowedAuthorizeTypes.Exists(requestType) { 3.128 switch requestType { 3.129 case CodeAuthRT: 3.130 - handleCodeRequest(w, r, ctx) 3.131 + req.handleCodeRequest(w, r, ctx) 3.132 return 3.133 case TokenAuthRT: 3.134 - handleTokenRequest(w, r, ctx) 3.135 + req.handleTokenRequest(w, r, ctx) 3.136 return 3.137 } 3.138 } 3.139 - // TODO: return error 3.140 + redir, err := req.GetErrorRedirect(ErrorInvalidRequest, "Invalid response type.", ctx.Config.DocumentationDomain) 3.141 + if err != nil { 3.142 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 3.143 + return 3.144 + } 3.145 + http.Redirect(w, r, redir, http.StatusFound) 3.146 } 3.147 3.148 -func handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 3.149 - // create the authorization request 3.150 - unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri")) 3.151 - if err != nil { 3.152 - unescapedURI = "" 3.153 - } 3.154 - ret := &AuthorizeRequest{ 3.155 - Type: CodeAuthRT, 3.156 - State: r.Form.Get("state"), 3.157 - Scope: r.Form.Get("scope"), 3.158 - RedirectURI: unescapedURI, 3.159 - Expiration: ctx.Config.AuthorizationExpiration, 3.160 - } 3.161 +func (req AuthRequest) handleCodeRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 3.162 3.163 - // must have a valid client 3.164 - id, err := uuid.Parse(r.Form.Get("client_id")) 3.165 - if err != nil { 3.166 - // TODO: return error 3.167 + if r.Method == "GET" { 3.168 + ctx.RenderConfirmation(w) 3.169 return 3.170 - } 3.171 - ret.Client, err = GetClient(id, ctx) 3.172 - if err != nil { 3.173 - // TODO: return error 3.174 - return 3.175 - } 3.176 - if ret.Client.RedirectURI == "" { 3.177 - // TODO: return error 3.178 + } else if r.Method != "POST" { 3.179 + ctx.RenderError(w, InvalidMethodError) 3.180 return 3.181 } 3.182 3.183 - // check redirect uri 3.184 - if ret.RedirectURI == "" { 3.185 - ret.RedirectURI = ret.Client.RedirectURI 3.186 - } 3.187 - if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil { 3.188 - // TODO: return error 3.189 + if err := validateSession(r); err == ErrorNotAuthenticated { 3.190 + ctx.RenderLogin(w) 3.191 + return 3.192 + } else if err != nil { 3.193 + ctx.RenderError(w, err) 3.194 return 3.195 } 3.196 3.197 - // TODO: do redirect with ret data 3.198 -} 3.199 - 3.200 -func handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 3.201 - // create the authorization request 3.202 - unescapedURI, err := url.QueryUnescape(r.Form.Get("redirect_uri")) 3.203 - if err != nil { 3.204 - unescapedURI = "" 3.205 - } 3.206 - ret := &AuthorizeRequest{ 3.207 - Type: TokenAuthRT, 3.208 - State: r.Form.Get("state"), 3.209 - Scope: r.Form.Get("scope"), 3.210 - RedirectURI: unescapedURI, 3.211 - // this type will generate a token directly, use access token expiration instead. 3.212 - Expiration: ctx.Config.AccessExpiration, 3.213 - } 3.214 - 3.215 - // must have a valid client 3.216 - id, err := uuid.Parse(r.Form.Get("client_id")) 3.217 - if err != nil { 3.218 - // TODO: return error 3.219 - return 3.220 - } 3.221 - ret.Client, err = GetClient(id, ctx) 3.222 - if err != nil { 3.223 - // TODO: return error 3.224 - return 3.225 - } 3.226 - if ret.Client.RedirectURI == "" { 3.227 - // TODO: return error 3.228 + if r.FormValue("approved") != "true" { 3.229 + redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain) 3.230 + if err != nil { 3.231 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 3.232 + return 3.233 + } 3.234 + http.Redirect(w, r, redir, http.StatusFound) 3.235 return 3.236 } 3.237 3.238 - // check redirect uri 3.239 - if ret.RedirectURI == "" { 3.240 - ret.RedirectURI = ret.Client.RedirectURI 3.241 - } 3.242 - if err = ValidateURI(ret.Client.RedirectURI, ret.RedirectURI); err != nil { 3.243 - // TODO: return error 3.244 + data := AuthorizeData{AuthRequest: req} 3.245 + 3.246 + data.ExpiresIn = ctx.Config.AuthorizationExpiration 3.247 + data.Code = newToken() 3.248 + data.CreatedAt = time.Now() 3.249 + 3.250 + err := ctx.Tokens.SaveAuthorization(data) 3.251 + if err != nil { 3.252 + redir, err := req.GetErrorRedirect(ErrorServerError, "Internal server error.", ctx.Config.DocumentationDomain) 3.253 + if err != nil { 3.254 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 3.255 + return 3.256 + } 3.257 + http.Redirect(w, r, redir, http.StatusFound) 3.258 + return 3.259 } 3.260 3.261 - // TODO: redirect with ret information 3.262 + redir, err := data.GetRedirect() 3.263 + if err != nil { 3.264 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 3.265 + return 3.266 + } 3.267 + http.Redirect(w, r, redir, http.StatusFound) 3.268 } 3.269 3.270 -func FinishAuthorizeRequest(w http.ResponseWriter, r *http.Request, ar *AuthorizeRequest, ctx Context) { 3.271 - // TODO: check if authorized? 3.272 - if ar.Type == TokenAuthRT { 3.273 - // TODO: w.SetRedirectFragment(true) was called... 3.274 +func (req AuthRequest) handleTokenRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 3.275 3.276 - // generate token directly 3.277 - ret := AccessRequest{ 3.278 - Code: "", 3.279 - Client: ar.Client, 3.280 - RedirectURI: ar.RedirectURI, 3.281 - Scope: ar.Scope, 3.282 - GenerateRefresh: false, // per the RFC, should NOT generate a refresh token in this case 3.283 - Expiration: ar.Expiration, 3.284 - } 3.285 - // TODO: ret.type was implicit 3.286 - // TODO: ret.Authorized was true 3.287 - FinishAccessRequest(w, r, ret, ctx) 3.288 - } else { 3.289 - // generate authorization token 3.290 - ret := AuthorizeData{ 3.291 - Client: ar.Client, 3.292 - CreatedAt: time.Now(), 3.293 - ExpiresIn: ar.Expiration, 3.294 - RedirectURI: ar.RedirectURI, 3.295 - State: ar.State, 3.296 - Scope: ar.Scope, 3.297 - Code: newToken(), 3.298 - } 3.299 + if r.Method == "GET" { 3.300 + ctx.RenderConfirmation(w) 3.301 + return 3.302 + } else if r.Method != "POST" { 3.303 + ctx.RenderError(w, InvalidMethodError) 3.304 + return 3.305 + } 3.306 3.307 - // save authorization token 3.308 - err := saveAuthorize(ret, ctx) 3.309 + if err := validateSession(r); err == ErrorNotAuthenticated { 3.310 + ctx.RenderLogin(w) 3.311 + return 3.312 + } else if err != nil { 3.313 + ctx.RenderError(w, err) 3.314 + return 3.315 + } 3.316 + 3.317 + if r.FormValue("approved") != "true" { 3.318 + redir, err := req.GetErrorRedirect(ErrorAccessDenied, "Request was not authorized.", ctx.Config.DocumentationDomain) 3.319 if err != nil { 3.320 - // TODO: return error 3.321 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 3.322 return 3.323 } 3.324 + http.Redirect(w, r, redir, http.StatusFound) 3.325 + return 3.326 + } 3.327 3.328 - // TODO: redirect with ret.Code and ret.State 3.329 + data := AccessData{AuthRequest: req} 3.330 + 3.331 + err := fillTokens(&data, false, ctx) 3.332 + if err != nil { 3.333 + ctx.RenderError(w, InternalServerError) 3.334 + return 3.335 } 3.336 + 3.337 + redir, err := data.GetRedirect(true) 3.338 + if err != nil { 3.339 + ctx.RenderError(w, URIFormatError(req.RedirectURI)) 3.340 + return 3.341 + } 3.342 + http.Redirect(w, r, redir, http.StatusFound) 3.343 } 3.344 3.345 -func loadAuthorize(code string, ctx Context) (AuthorizeData, error) { 3.346 - return AuthorizeData{}, nil 3.347 +func (data AuthorizeData) GetRedirect() (string, error) { 3.348 + u, err := url.Parse(data.RedirectURI) 3.349 + if err != nil { 3.350 + return "", err 3.351 + } 3.352 + 3.353 + // add parameters 3.354 + q := u.Query() 3.355 + q.Set("code", data.Code) 3.356 + q.Set("state", data.State) 3.357 + u.RawQuery = q.Encode() 3.358 + 3.359 + return u.String(), nil 3.360 } 3.361 3.362 -func saveAuthorize(ret AuthorizeData, ctx Context) error { 3.363 - return nil 3.364 +func (req AuthRequest) GetErrorRedirect(code, description, uriBase string) (string, error) { 3.365 + u, err := url.Parse(req.RedirectURI) 3.366 + if err != nil { 3.367 + return "", err 3.368 + } 3.369 + 3.370 + // add parameters 3.371 + q := u.Query() 3.372 + q.Set("error", code) 3.373 + q.Set("error_description", description) 3.374 + q.Set("error_uri", strings.Join([]string{ 3.375 + strings.TrimRight(uriBase, "/"), 3.376 + strings.TrimLeft(code, "/"), 3.377 + }, "/")) 3.378 + q.Set("state", req.State) 3.379 + u.RawQuery = q.Encode() 3.380 + 3.381 + return u.String(), nil 3.382 } 3.383 - 3.384 -func removeAuthorize(code string, ctx Context) error { 3.385 - return nil 3.386 -}
4.1 --- a/authorize_test.go Fri Jul 18 07:13:22 2014 -0400 4.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 4.3 @@ -1,88 +0,0 @@ 4.4 -package oauth2 4.5 - 4.6 -import ( 4.7 - "net/http" 4.8 - "net/url" 4.9 - "testing" 4.10 -) 4.11 - 4.12 -func TestAuthorizeCode(t *testing.T) { 4.13 - sconfig := NewServerConfig() 4.14 - sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{CODE} 4.15 - server := NewServer(sconfig, NewTestingStorage()) 4.16 - server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{} 4.17 - resp := server.NewResponse() 4.18 - 4.19 - req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil) 4.20 - if err != nil { 4.21 - t.Fatal(err) 4.22 - } 4.23 - req.Form = make(url.Values) 4.24 - req.Form.Set("response_type", string(CODE)) 4.25 - req.Form.Set("client_id", "1234") 4.26 - req.Form.Set("state", "a") 4.27 - 4.28 - if ar := server.HandleAuthorizeRequest(resp, req); ar != nil { 4.29 - ar.Authorized = true 4.30 - server.FinishAuthorizeRequest(resp, req, ar) 4.31 - } 4.32 - 4.33 - //fmt.Printf("%+v", resp) 4.34 - 4.35 - if resp.IsError && resp.InternalError != nil { 4.36 - t.Fatalf("Error in response: %s", resp.InternalError) 4.37 - } 4.38 - 4.39 - if resp.IsError { 4.40 - t.Fatalf("Should not be an error") 4.41 - } 4.42 - 4.43 - if resp.Type != REDIRECT { 4.44 - t.Fatalf("Response should be a redirect") 4.45 - } 4.46 - 4.47 - if d := resp.Output["code"]; d != "1" { 4.48 - t.Fatalf("Unexpected authorization code: %s", d) 4.49 - } 4.50 -} 4.51 - 4.52 -func TestAuthorizeToken(t *testing.T) { 4.53 - sconfig := NewServerConfig() 4.54 - sconfig.AllowedAuthorizeTypes = AllowedAuthorizeType{TOKEN} 4.55 - server := NewServer(sconfig, NewTestingStorage()) 4.56 - server.AuthorizeTokenGen = &TestingAuthorizeTokenGen{} 4.57 - server.AccessTokenGen = &TestingAccessTokenGen{} 4.58 - resp := server.NewResponse() 4.59 - 4.60 - req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil) 4.61 - if err != nil { 4.62 - t.Fatal(err) 4.63 - } 4.64 - req.Form = make(url.Values) 4.65 - req.Form.Set("response_type", string(TOKEN)) 4.66 - req.Form.Set("client_id", "1234") 4.67 - req.Form.Set("state", "a") 4.68 - 4.69 - if ar := server.HandleAuthorizeRequest(resp, req); ar != nil { 4.70 - ar.Authorized = true 4.71 - server.FinishAuthorizeRequest(resp, req, ar) 4.72 - } 4.73 - 4.74 - //fmt.Printf("%+v", resp) 4.75 - 4.76 - if resp.IsError && resp.InternalError != nil { 4.77 - t.Fatalf("Error in response: %s", resp.InternalError) 4.78 - } 4.79 - 4.80 - if resp.IsError { 4.81 - t.Fatalf("Should not be an error") 4.82 - } 4.83 - 4.84 - if resp.Type != REDIRECT || !resp.RedirectInFragment { 4.85 - t.Fatalf("Response should be a redirect with fragment") 4.86 - } 4.87 - 4.88 - if d := resp.Output["access_token"]; d != "1" { 4.89 - t.Fatalf("Unexpected access token: %s", d) 4.90 - } 4.91 -}
5.1 --- a/config.go Fri Jul 18 07:13:22 2014 -0400 5.2 +++ b/config.go Fri Aug 01 23:08:38 2014 -0400 5.3 @@ -53,6 +53,9 @@ 5.4 5.5 // If true allows access request using GET, else only POST - default false 5.6 AllowGetAccessRequest bool 5.7 + 5.8 + // The base path of documentation 5.9 + DocumentationDomain string 5.10 } 5.11 5.12 // NewServerConfig returns a new ServerConfig with default configuration
6.1 --- a/context.go Fri Jul 18 07:13:22 2014 -0400 6.2 +++ b/context.go Fri Aug 01 23:08:38 2014 -0400 6.3 @@ -1,5 +1,19 @@ 6.4 package oauth2 6.5 6.6 +import "io" 6.7 + 6.8 type Context struct { 6.9 - Config ServerConfig 6.10 + Config ServerConfig 6.11 + Clients ClientStore 6.12 + Tokens TokenStore 6.13 + Profiles ProfileStore 6.14 } 6.15 + 6.16 +func (c Context) RenderError(w io.Writer, err error) { 6.17 +} 6.18 + 6.19 +func (c Context) RenderConfirmation(w io.Writer) { 6.20 +} 6.21 + 6.22 +func (c Context) RenderLogin(w io.Writer) { 6.23 +}
7.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 7.2 +++ b/errors.go Fri Aug 01 23:08:38 2014 -0400 7.3 @@ -0,0 +1,45 @@ 7.4 +package oauth2 7.5 + 7.6 +import "errors" 7.7 + 7.8 +const ( 7.9 + ErrorServerError = "server_error" 7.10 + ErrorInvalidRequest = "invalid_request" 7.11 + ErrorAccessDenied = "access_denied" 7.12 +) 7.13 + 7.14 +var ( 7.15 + ClientNotFoundError = errors.New("Client not found.") 7.16 + URIMissingError = errors.New("Redirect URI missing.") 7.17 + InvalidMethodError = errors.New("Invalid request method.") 7.18 + InternalServerError = errors.New("Internal server error.") 7.19 + ErrorNotAuthenticated = errors.New("Not authenticated.") 7.20 +) 7.21 + 7.22 +type URIFormatError string 7.23 + 7.24 +func (err URIFormatError) Error() string { 7.25 + return "Invalid URI format: " + string(err) 7.26 +} 7.27 + 7.28 +type InvalidClientIDError string 7.29 + 7.30 +func (err InvalidClientIDError) Error() string { 7.31 + return "Invalid client ID: " + string(err) 7.32 +} 7.33 + 7.34 +type URIMismatchError struct { 7.35 + uri string 7.36 + mismatch string 7.37 +} 7.38 + 7.39 +func (err URIMismatchError) Error() string { 7.40 + return "Supplied redirect URI " + err.mismatch + " does not match the redirect in the database (" + err.uri + ")" 7.41 +} 7.42 + 7.43 +func NewURIMismatchError(uri, mismatch string) error { 7.44 + return URIMismatchError{ 7.45 + uri: uri, 7.46 + mismatch: mismatch, 7.47 + } 7.48 +}
8.1 --- a/info.go Fri Jul 18 07:13:22 2014 -0400 8.2 +++ b/info.go Fri Aug 01 23:08:38 2014 -0400 8.3 @@ -1,59 +1,37 @@ 8.4 package oauth2 8.5 8.6 -import "net/http" 8.7 - 8.8 -// InfoRequest is a request for information about some AccessData 8.9 -type InfoRequest struct { 8.10 - Code string // Code to look up 8.11 - AccessData AccessData // AccessData associated with Code 8.12 -} 8.13 +import ( 8.14 + "net/http" 8.15 + "time" 8.16 +) 8.17 8.18 // HandleInfoRequest is an http.HandlerFunc for server information 8.19 // NOT an RFC specification. 8.20 func HandleInfoRequest(w http.ResponseWriter, r *http.Request, ctx Context) { 8.21 r.ParseForm() 8.22 8.23 - // generate info request 8.24 - ret := InfoRequest{ 8.25 - Code: r.Form.Get("code"), 8.26 - } 8.27 + code := r.Form.Get("code") 8.28 8.29 - if ret.Code == "" { 8.30 + if code == "" { 8.31 // TODO: return error 8.32 return 8.33 } 8.34 8.35 - var err error 8.36 - 8.37 // load access data 8.38 - ret.AccessData, err = loadAccess(ret.Code, ctx) 8.39 + accessData, err := ctx.Tokens.GetAccess(code) 8.40 if err != nil { 8.41 // TODO: return error 8.42 return 8.43 } 8.44 - if ret.AccessData.Client.RedirectURI == "" { 8.45 + if accessData.Client.RedirectURI == "" { 8.46 // TODO: return error 8.47 return 8.48 } 8.49 - if ret.AccessData.IsExpired() { 8.50 + if accessData.IsExpired() { 8.51 // TODO: return error 8.52 return 8.53 } 8.54 - // TODO: write ret 8.55 + 8.56 + accessData.ExpiresIn = int32(accessData.CreatedAt.Add(time.Duration(accessData.ExpiresIn)*time.Second).Sub(time.Now()) / time.Second) 8.57 + // TODO: write accessData 8.58 } 8.59 - 8.60 -// FinishInfoRequest finalizes the request handled by HandleInfoRequest 8.61 -func FinishInfoRequest(w http.ResponseWriter, r *http.Request, ir *InfoRequest, ctx Context) { 8.62 - // output data 8.63 - //w.Output["client_id"] = ir.AccessData.Client.Id 8.64 - //w.Output["access_token"] = ir.AccessData.AccessToken 8.65 - //w.Output["token_type"] = s.Config.TokenType 8.66 - //w.Output["expires_in"] = ir.AccessData.CreatedAt.Add(time.Duration(ir.AccessData.ExpiresIn)*time.Second).Sub(time.Now()) / time.Second 8.67 - //if ir.AccessData.RefreshToken != "" { 8.68 - // w.Output["refresh_token"] = ir.AccessData.RefreshToken 8.69 - //} 8.70 - //if ir.AccessData.Scope != "" { 8.71 - // w.Output["scope"] = ir.AccessData.Scope 8.72 - //} 8.73 - // TODO: write output 8.74 -}
9.1 --- a/info_test.go Fri Jul 18 07:13:22 2014 -0400 9.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 9.3 @@ -1,42 +0,0 @@ 9.4 -package oauth2 9.5 - 9.6 -import ( 9.7 - "net/http" 9.8 - "net/url" 9.9 - "testing" 9.10 -) 9.11 - 9.12 -func TestInfo(t *testing.T) { 9.13 - sconfig := NewServerConfig() 9.14 - server := NewServer(sconfig, NewTestingStorage()) 9.15 - resp := server.NewResponse() 9.16 - 9.17 - req, err := http.NewRequest("GET", "http://localhost:14000/appauth", nil) 9.18 - if err != nil { 9.19 - t.Fatal(err) 9.20 - } 9.21 - req.Form = make(url.Values) 9.22 - req.Form.Set("code", "9999") 9.23 - 9.24 - if ar := server.HandleInfoRequest(resp, req); ar != nil { 9.25 - server.FinishInfoRequest(resp, req, ar) 9.26 - } 9.27 - 9.28 - //fmt.Printf("%+v", resp) 9.29 - 9.30 - if resp.IsError && resp.InternalError != nil { 9.31 - t.Fatalf("Error in response: %s", resp.InternalError) 9.32 - } 9.33 - 9.34 - if resp.IsError { 9.35 - t.Fatalf("Should not be an error") 9.36 - } 9.37 - 9.38 - if resp.Type != DATA { 9.39 - t.Fatalf("Response should be data") 9.40 - } 9.41 - 9.42 - if d := resp.Output["access_token"]; d != "9999" { 9.43 - t.Fatalf("Unexpected authorization code: %s", d) 9.44 - } 9.45 -}
10.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 10.2 +++ b/session.go Fri Aug 01 23:08:38 2014 -0400 10.3 @@ -0,0 +1,7 @@ 10.4 +package oauth2 10.5 + 10.6 +import "net/http" 10.7 + 10.8 +func validateSession(r *http.Request) error { 10.9 + return nil 10.10 +}
11.1 --- a/storage.go Fri Jul 18 07:13:22 2014 -0400 11.2 +++ b/storage.go Fri Aug 01 23:08:38 2014 -0400 11.3 @@ -1,39 +1,28 @@ 11.4 package oauth2 11.5 11.6 -// Storage interface 11.7 -type Storage interface { 11.8 +import "secondbit.org/uuid" 11.9 11.10 - // GetClient loads the client by id (client_id) 11.11 - GetClient(id string) (*Client, error) 11.12 +type ClientStore interface { 11.13 + GetClient(id uuid.ID) (Client, error) 11.14 + CreateClient(name, logo, redirectURI string, owner uuid.ID) (Client, error) 11.15 + UpdateClient(client *Client, name, logo, redirectURI *string) error 11.16 + RemoveClient(id uuid.ID, ctx Context) error 11.17 + ListClients(id uuid.ID, page, num int, ctx Context) ([]Client, error) 11.18 +} 11.19 11.20 - // SaveAuthorize saves authorize data. 11.21 - SaveAuthorize(*AuthorizeData) error 11.22 +type TokenStore interface { 11.23 + SaveAuthorization(AuthorizeData) error 11.24 + GetAuthorization(code string) (AuthorizeData, error) 11.25 + RemoveAuthorization(code string) error 11.26 11.27 - // LoadAuthorize looks up AuthorizeData by a code. 11.28 - // Client information MUST be loaded together. 11.29 - // Optionally can return error if expired. 11.30 - LoadAuthorize(code string) (*AuthorizeData, error) 11.31 - 11.32 - // RemoveAuthorize revokes or deletes the authorization code. 11.33 - RemoveAuthorize(code string) error 11.34 - 11.35 - // SaveAccess writes AccessData. 11.36 - // If RefreshToken is not blank, it must save in a way that can be loaded using LoadRefresh. 11.37 - SaveAccess(*AccessData) error 11.38 - 11.39 - // LoadAccess retrieves access data by token. Client information MUST be loaded together. 11.40 - // AuthorizeData and AccessData DON'T NEED to be loaded if not easily available. 11.41 - // Optionally can return error if expired. 11.42 - LoadAccess(token string) (*AccessData, error) 11.43 - 11.44 - // RemoveAccess revokes or deletes an AccessData. 11.45 + SaveAccess(AccessData) error 11.46 + GetAccess(token string) (AccessData, error) 11.47 RemoveAccess(token string) error 11.48 11.49 - // LoadRefresh retrieves refresh AccessData. Client information MUST be loaded together. 11.50 - // AuthorizeData and AccessData DON'T NEED to be loaded if not easily available. 11.51 - // Optionally can return error if expired. 11.52 - LoadRefresh(token string) (*AccessData, error) 11.53 - 11.54 - // RemoveRefresh revokes or deletes refresh AccessData. 11.55 + GetRefresh(token string) (AccessData, error) 11.56 RemoveRefresh(token string) error 11.57 } 11.58 + 11.59 +type ProfileStore interface { 11.60 + GetProfile(username, password string) (uuid.ID, error) 11.61 +}
12.1 --- a/storage_test.go Fri Jul 18 07:13:22 2014 -0400 12.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 12.3 @@ -1,147 +0,0 @@ 12.4 -package oauth2 12.5 - 12.6 -import ( 12.7 - "errors" 12.8 - "strconv" 12.9 - "time" 12.10 -) 12.11 - 12.12 -type TestingStorage struct { 12.13 - clients map[string]*Client 12.14 - authorize map[string]*AuthorizeData 12.15 - access map[string]*AccessData 12.16 - refresh map[string]string 12.17 -} 12.18 - 12.19 -func NewTestingStorage() *TestingStorage { 12.20 - r := &TestingStorage{ 12.21 - clients: make(map[string]*Client), 12.22 - authorize: make(map[string]*AuthorizeData), 12.23 - access: make(map[string]*AccessData), 12.24 - refresh: make(map[string]string), 12.25 - } 12.26 - 12.27 - r.clients["1234"] = &Client{ 12.28 - Id: "1234", 12.29 - Secret: "aabbccdd", 12.30 - RedirectUri: "http://localhost:14000/appauth", 12.31 - } 12.32 - 12.33 - r.authorize["9999"] = &AuthorizeData{ 12.34 - Client: r.clients["1234"], 12.35 - Code: "9999", 12.36 - ExpiresIn: 3600, 12.37 - CreatedAt: time.Now(), 12.38 - RedirectUri: "http://localhost:14000/appauth", 12.39 - } 12.40 - 12.41 - r.access["9999"] = &AccessData{ 12.42 - Client: r.clients["1234"], 12.43 - AuthorizeData: r.authorize["9999"], 12.44 - AccessToken: "9999", 12.45 - ExpiresIn: 3600, 12.46 - CreatedAt: time.Now(), 12.47 - } 12.48 - 12.49 - r.access["r9999"] = &AccessData{ 12.50 - Client: r.clients["1234"], 12.51 - AuthorizeData: r.authorize["9999"], 12.52 - AccessData: r.access["9999"], 12.53 - AccessToken: "9999", 12.54 - RefreshToken: "r9999", 12.55 - ExpiresIn: 3600, 12.56 - CreatedAt: time.Now(), 12.57 - } 12.58 - 12.59 - r.refresh["r9999"] = "9999" 12.60 - 12.61 - return r 12.62 -} 12.63 - 12.64 -func (s *TestingStorage) GetClient(id string) (*Client, error) { 12.65 - if c, ok := s.clients[id]; ok { 12.66 - return c, nil 12.67 - } 12.68 - return nil, errors.New("Client not found") 12.69 -} 12.70 - 12.71 -func (s *TestingStorage) SetClient(id string, client *Client) error { 12.72 - s.clients[id] = client 12.73 - return nil 12.74 -} 12.75 - 12.76 -func (s *TestingStorage) SaveAuthorize(data *AuthorizeData) error { 12.77 - s.authorize[data.Code] = data 12.78 - return nil 12.79 -} 12.80 - 12.81 -func (s *TestingStorage) LoadAuthorize(code string) (*AuthorizeData, error) { 12.82 - if d, ok := s.authorize[code]; ok { 12.83 - return d, nil 12.84 - } 12.85 - return nil, errors.New("Authorize not found") 12.86 -} 12.87 - 12.88 -func (s *TestingStorage) RemoveAuthorize(code string) error { 12.89 - delete(s.authorize, code) 12.90 - return nil 12.91 -} 12.92 - 12.93 -func (s *TestingStorage) SaveAccess(data *AccessData) error { 12.94 - s.access[data.AccessToken] = data 12.95 - if data.RefreshToken != "" { 12.96 - s.refresh[data.RefreshToken] = data.AccessToken 12.97 - } 12.98 - return nil 12.99 -} 12.100 - 12.101 -func (s *TestingStorage) LoadAccess(code string) (*AccessData, error) { 12.102 - if d, ok := s.access[code]; ok { 12.103 - return d, nil 12.104 - } 12.105 - return nil, errors.New("Access not found") 12.106 -} 12.107 - 12.108 -func (s *TestingStorage) RemoveAccess(code string) error { 12.109 - delete(s.access, code) 12.110 - return nil 12.111 -} 12.112 - 12.113 -func (s *TestingStorage) LoadRefresh(code string) (*AccessData, error) { 12.114 - if d, ok := s.refresh[code]; ok { 12.115 - return s.LoadAccess(d) 12.116 - } 12.117 - return nil, errors.New("Refresh not found") 12.118 -} 12.119 - 12.120 -func (s *TestingStorage) RemoveRefresh(code string) error { 12.121 - delete(s.refresh, code) 12.122 - return nil 12.123 -} 12.124 - 12.125 -// Predictable testing token generation 12.126 - 12.127 -type TestingAuthorizeTokenGen struct { 12.128 - counter int64 12.129 -} 12.130 - 12.131 -func (a *TestingAuthorizeTokenGen) GenerateAuthorizeToken(data *AuthorizeData) (ret string, err error) { 12.132 - a.counter++ 12.133 - return strconv.FormatInt(a.counter, 10), nil 12.134 -} 12.135 - 12.136 -type TestingAccessTokenGen struct { 12.137 - acounter int64 12.138 - rcounter int64 12.139 -} 12.140 - 12.141 -func (a *TestingAccessTokenGen) GenerateAccessToken(data *AccessData, generaterefresh bool) (accesstoken string, refreshtoken string, err error) { 12.142 - a.acounter++ 12.143 - accesstoken = strconv.FormatInt(a.acounter, 10) 12.144 - 12.145 - if generaterefresh { 12.146 - a.rcounter++ 12.147 - refreshtoken = "r" + strconv.FormatInt(a.rcounter, 10) 12.148 - } 12.149 - return 12.150 -}
13.1 --- a/tokengen.go Fri Jul 18 07:13:22 2014 -0400 13.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 13.3 @@ -1,11 +0,0 @@ 13.4 -package oauth2 13.5 - 13.6 -import ( 13.7 - "encoding/base64" 13.8 - 13.9 - "code.google.com/p/go-uuid/uuid" 13.10 -) 13.11 - 13.12 -func newToken() string { 13.13 - return base64.StdEncoding.EncodeToString([]byte(uuid.New())) 13.14 -}
14.1 --- a/urivalidate.go Fri Jul 18 07:13:22 2014 -0400 14.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 14.3 @@ -1,39 +0,0 @@ 14.4 -package oauth2 14.5 - 14.6 -import ( 14.7 - "errors" 14.8 - "fmt" 14.9 - "net/url" 14.10 - "strings" 14.11 -) 14.12 - 14.13 -// ValidateURI validates that redirectURI is contained in baseURI 14.14 -func ValidateURI(baseURI string, redirectURI string) error { 14.15 - if baseURI == "" || redirectURI == "" { 14.16 - return errors.New("urls cannot be blank.") 14.17 - } 14.18 - 14.19 - // parse base url 14.20 - base, err := url.Parse(baseURI) 14.21 - if err != nil { 14.22 - return err 14.23 - } 14.24 - 14.25 - // parse passed url 14.26 - redirect, err := url.Parse(redirectURI) 14.27 - if err != nil { 14.28 - return err 14.29 - } 14.30 - 14.31 - // must not have fragment 14.32 - if base.Fragment != "" || redirect.Fragment != "" { 14.33 - return errors.New("url must not include fragment.") 14.34 - } 14.35 - 14.36 - // check if urls match 14.37 - if base.Scheme == redirect.Scheme && base.Host == redirect.Host && len(redirect.Path) >= len(base.Path) && strings.HasPrefix(redirect.Path, base.Path) { 14.38 - return nil 14.39 - } 14.40 - 14.41 - return errors.New(fmt.Sprintf("urls don't validate: %s / %s\n", baseURI, redirectURI)) 14.42 -}
15.1 --- a/urivalidate_test.go Fri Jul 18 07:13:22 2014 -0400 15.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 15.3 @@ -1,27 +0,0 @@ 15.4 -package oauth2 15.5 - 15.6 -import ( 15.7 - "testing" 15.8 -) 15.9 - 15.10 -func TestURIValidate(t *testing.T) { 15.11 - // V1 15.12 - if err := ValidateUri("http://localhost:14000/appauth", "http://localhost:14000/appauth"); err != nil { 15.13 - t.Errorf("V1: %s", err) 15.14 - } 15.15 - 15.16 - // V2 15.17 - if err := ValidateUri("http://localhost:14000/appauth", "http://localhost:14000/app"); err == nil { 15.18 - t.Error("V2 should have failed") 15.19 - } 15.20 - 15.21 - // V3 15.22 - if err := ValidateUri("http://www.google.com/myapp", "http://www.google.com/myapp/interface/implementation"); err != nil { 15.23 - t.Errorf("V3: %s", err) 15.24 - } 15.25 - 15.26 - // V4 15.27 - if err := ValidateUri("http://www.google.com/myapp", "http://www2.google.com/myapp"); err == nil { 15.28 - t.Error("V4 should have failed") 15.29 - } 15.30 -}
16.1 --- a/util.go Fri Jul 18 07:13:22 2014 -0400 16.2 +++ b/util.go Fri Aug 01 23:08:38 2014 -0400 16.3 @@ -3,8 +3,12 @@ 16.4 import ( 16.5 "encoding/base64" 16.6 "errors" 16.7 + "fmt" 16.8 "net/http" 16.9 + "net/url" 16.10 "strings" 16.11 + 16.12 + "code.google.com/p/go-uuid/uuid" 16.13 ) 16.14 16.15 var ( 16.16 @@ -61,3 +65,38 @@ 16.17 16.18 return CheckBasicAuth(r) 16.19 } 16.20 + 16.21 +func newToken() string { 16.22 + return base64.StdEncoding.EncodeToString([]byte(uuid.New())) 16.23 +} 16.24 + 16.25 +// validateURI validates that redirectURI is contained in baseURI 16.26 +func validateURI(baseURI string, redirectURI string) error { 16.27 + if baseURI == "" || redirectURI == "" { 16.28 + return errors.New("urls cannot be blank.") 16.29 + } 16.30 + 16.31 + // parse base url 16.32 + base, err := url.Parse(baseURI) 16.33 + if err != nil { 16.34 + return err 16.35 + } 16.36 + 16.37 + // parse passed url 16.38 + redirect, err := url.Parse(redirectURI) 16.39 + if err != nil { 16.40 + return err 16.41 + } 16.42 + 16.43 + // must not have fragment 16.44 + if base.Fragment != "" || redirect.Fragment != "" { 16.45 + return errors.New("url must not include fragment.") 16.46 + } 16.47 + 16.48 + // check if urls match 16.49 + if base.Scheme == redirect.Scheme && base.Host == redirect.Host && len(redirect.Path) >= len(base.Path) && strings.HasPrefix(redirect.Path, base.Path) { 16.50 + return nil 16.51 + } 16.52 + 16.53 + return errors.New(fmt.Sprintf("urls don't validate: %s / %s\n", baseURI, redirectURI)) 16.54 +}
17.1 --- a/util_test.go Fri Jul 18 07:13:22 2014 -0400 17.2 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 17.3 @@ -1,96 +0,0 @@ 17.4 -package oauth2 17.5 - 17.6 -import ( 17.7 - "net/http" 17.8 - "net/url" 17.9 - "testing" 17.10 -) 17.11 - 17.12 -const ( 17.13 - badAuthValue = "Digest XHHHHHHH" 17.14 - goodAuthValue = "Basic dGVzdDp0ZXN0" 17.15 -) 17.16 - 17.17 -func TestBasicAuth(t *testing.T) { 17.18 - r := &http.Request{Header: make(http.Header)} 17.19 - 17.20 - // Without any header 17.21 - if b, err := CheckBasicAuth(r); b != nil || err != nil { 17.22 - t.Errorf("Validated basic auth without header") 17.23 - } 17.24 - 17.25 - // with invalid header 17.26 - r.Header.Set("Authorization", badAuthValue) 17.27 - b, err := CheckBasicAuth(r) 17.28 - if b != nil || err == nil { 17.29 - t.Errorf("Validated invalid auth") 17.30 - return 17.31 - } 17.32 - 17.33 - // with valid header 17.34 - r.Header.Set("Authorization", goodAuthValue) 17.35 - b, err = CheckBasicAuth(r) 17.36 - if b == nil || err != nil { 17.37 - t.Errorf("Could not extract basic auth") 17.38 - return 17.39 - } 17.40 - 17.41 - // check extracted auth data 17.42 - if b.Username != "test" || b.Password != "test" { 17.43 - t.Errorf("Error decoding basic auth") 17.44 - } 17.45 -} 17.46 - 17.47 -func TestGetClientAuth(t *testing.T) { 17.48 - 17.49 - urlWithSecret, _ := url.Parse("http://host.tld/path?client_id=xxx&client_secret=yyy") 17.50 - urlWithEmptySecret, _ := url.Parse("http://host.tld/path?client_id=xxx&client_secret=") 17.51 - urlNoSecret, _ := url.Parse("http://host.tld/path?client_id=xxx") 17.52 - 17.53 - headerNoAuth := make(http.Header) 17.54 - headerBadAuth := make(http.Header) 17.55 - headerBadAuth.Set("Authorization", badAuthValue) 17.56 - headerOKAuth := make(http.Header) 17.57 - headerOKAuth.Set("Authorization", goodAuthValue) 17.58 - 17.59 - var tests = []struct { 17.60 - header http.Header 17.61 - url *url.URL 17.62 - allowQueryParams bool 17.63 - expectAuth bool 17.64 - }{ 17.65 - {headerNoAuth, urlWithSecret, true, true}, 17.66 - {headerNoAuth, urlWithSecret, false, false}, 17.67 - {headerNoAuth, urlWithEmptySecret, true, true}, 17.68 - {headerNoAuth, urlWithEmptySecret, false, false}, 17.69 - {headerNoAuth, urlNoSecret, true, false}, 17.70 - {headerNoAuth, urlNoSecret, false, false}, 17.71 - 17.72 - {headerBadAuth, urlWithSecret, true, true}, 17.73 - {headerBadAuth, urlWithSecret, false, false}, 17.74 - {headerBadAuth, urlWithEmptySecret, true, true}, 17.75 - {headerBadAuth, urlWithEmptySecret, false, false}, 17.76 - {headerBadAuth, urlNoSecret, true, false}, 17.77 - {headerBadAuth, urlNoSecret, false, false}, 17.78 - 17.79 - {headerOKAuth, urlWithSecret, true, true}, 17.80 - {headerOKAuth, urlWithSecret, false, true}, 17.81 - {headerOKAuth, urlWithEmptySecret, true, true}, 17.82 - {headerOKAuth, urlWithEmptySecret, false, true}, 17.83 - {headerOKAuth, urlNoSecret, true, true}, 17.84 - {headerOKAuth, urlNoSecret, false, true}, 17.85 - } 17.86 - 17.87 - for _, tt := range tests { 17.88 - w := new(Response) 17.89 - r := &http.Request{Header: tt.header, URL: tt.url} 17.90 - r.ParseForm() 17.91 - auth := getClientAuth(w, r, tt.allowQueryParams) 17.92 - if tt.expectAuth && auth == nil { 17.93 - t.Errorf("Auth should not be nil for %v", tt) 17.94 - } else if !tt.expectAuth && auth != nil { 17.95 - t.Errorf("Auth should be nil for %v", tt) 17.96 - } 17.97 - } 17.98 - 17.99 -}