github.com/anuvu/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/oauth_manager.go (about) 1 package gateway 2 3 import ( 4 "bytes" 5 "encoding/base64" 6 "encoding/json" 7 "errors" 8 "math" 9 "net/http" 10 "net/url" 11 "time" 12 13 "github.com/lonelycode/osin" 14 uuid "github.com/satori/go.uuid" 15 "golang.org/x/crypto/bcrypt" 16 17 "strconv" 18 19 "github.com/TykTechnologies/tyk/config" 20 "github.com/TykTechnologies/tyk/headers" 21 "github.com/TykTechnologies/tyk/storage" 22 "github.com/TykTechnologies/tyk/user" 23 ) 24 25 /* 26 Sample Oaut Flow: 27 ----------------- 28 29 1. Request to /authorize 30 2. Tyk extracts all relevant data and pre-screens client_id, client_secret and redirect_uri 31 3. Instead of proxying the request it redirects the user to the login page on the resource with the client_id & secret as a POST (basically passed through) 32 4. Resource presents approve / deny window to user 33 5. If approve is clicked, resource pings oauth/authorise which is the actual authorize endpoint (requires admin key), 34 this returns oauth details to resource as well as redirect URI which it can then redirec to 35 6. User is redirected to redirect URI with auth_code 36 7. Client makes auth request for bearer token 37 8. Client API makes all calls with bearer token 38 39 Effort required by Resource Owner: 40 1. Create a login & approve/deny page 41 2. Send an API request to Tyk to generate an auth_code 42 3. Create endpoint to accept key change notifications 43 */ 44 45 // OAuthClient is a representation within an APISpec of a client 46 type OAuthClient struct { 47 ClientID string `json:"id"` 48 ClientSecret string `json:"secret"` 49 ClientRedirectURI string `json:"redirecturi"` 50 MetaData interface{} `json:"meta_data,omitempty"` 51 PolicyID string `json:"policyid"` 52 Description string `json:"description"` 53 } 54 55 func (oc *OAuthClient) GetId() string { 56 return oc.ClientID 57 } 58 59 func (oc *OAuthClient) GetSecret() string { 60 return oc.ClientSecret 61 } 62 63 func (oc *OAuthClient) GetRedirectUri() string { 64 return oc.ClientRedirectURI 65 } 66 67 func (oc *OAuthClient) GetUserData() interface{} { 68 return oc.MetaData 69 } 70 71 func (oc *OAuthClient) GetPolicyID() string { 72 return oc.PolicyID 73 } 74 75 func (oc *OAuthClient) GetDescription() string { 76 return oc.Description 77 } 78 79 // OAuthNotificationType const to reduce risk of collisions 80 type OAuthNotificationType string 81 82 // Notification codes for new and refresh codes 83 const ( 84 newAccessToken OAuthNotificationType = "new" 85 refreshAccessToken OAuthNotificationType = "refresh" 86 ) 87 88 // NewOAuthNotification is a notification sent to a 89 // web-hook when an access request or a refresh request comes in. 90 type NewOAuthNotification struct { 91 AuthCode string `json:"auth_code"` 92 NewOAuthToken string `json:"new_oauth_token"` 93 RefreshToken string `json:"refresh_token"` 94 OldRefreshToken string `json:"old_refresh_token"` 95 NotificationType OAuthNotificationType `json:"notification_type"` 96 } 97 98 // OAuthHandlers are the HTTP Handlers that manage the Tyk OAuth flow 99 type OAuthHandlers struct { 100 Manager OAuthManager 101 } 102 103 func (o *OAuthHandlers) generateOAuthOutputFromOsinResponse(osinResponse *osin.Response) []byte { 104 105 // TODO: Might need to clear this out 106 if osinResponse.Output["state"] == "" { 107 log.Debug("Removing state") 108 delete(osinResponse.Output, "state") 109 } 110 111 redirect, err := osinResponse.GetRedirectUrl() 112 if err == nil { 113 // Hack to inject redirect into response 114 osinResponse.Output["redirect_to"] = redirect 115 } 116 117 respData, err := json.Marshal(&osinResponse.Output) 118 if err != nil { 119 return nil 120 } 121 return respData 122 } 123 124 func (o *OAuthHandlers) notifyClientOfNewOauth(notification NewOAuthNotification) { 125 log.Info("[OAuth] Notifying client host") 126 go o.Manager.API.NotificationsDetails.SendRequest(false, 0, notification) 127 } 128 129 // HandleGenerateAuthCodeData handles a resource provider approving an OAuth request from a client 130 func (o *OAuthHandlers) HandleGenerateAuthCodeData(w http.ResponseWriter, r *http.Request) { 131 // On AUTH grab session state data and add to UserData (not validated, not good!) 132 sessionJSONData := r.FormValue("key_rules") 133 if sessionJSONData == "" { 134 log.Warning("Authorise request is missing key_rules in params, policy will be required!") 135 } 136 137 // Handle the authorisation and write the JSON output to the resource provider 138 resp := o.Manager.HandleAuthorisation(r, true, sessionJSONData) 139 code := http.StatusOK 140 msg := o.generateOAuthOutputFromOsinResponse(resp) 141 if resp.IsError { 142 code = resp.ErrorStatusCode 143 log.Error("[OAuth] OAuth response marked as error: ", resp) 144 } 145 w.WriteHeader(code) 146 w.Write(msg) 147 } 148 149 // HandleAuthorizePassthrough handles a Client Auth request, first it checks if the client 150 // is OK (otherwise it blocks the request), then it forwards on to the resource providers approval URI 151 func (o *OAuthHandlers) HandleAuthorizePassthrough(w http.ResponseWriter, r *http.Request) { 152 // Extract client data and check 153 resp := o.Manager.HandleAuthorisation(r, false, "") 154 if resp.IsError { 155 log.Error("There was an error with the request: ", resp) 156 // Something went wrong, write out the error details and kill the response 157 doJSONWrite(w, resp.ErrorStatusCode, apiError(resp.StatusText)) 158 return 159 } 160 if r.Method == "GET" { 161 var buffer bytes.Buffer 162 buffer.WriteString(o.Manager.API.Oauth2Meta.AuthorizeLoginRedirect) 163 buffer.WriteString("?client_id=") 164 buffer.WriteString(r.FormValue("client_id")) 165 buffer.WriteString("&redirect_uri=") 166 buffer.WriteString(r.FormValue("redirect_uri")) 167 buffer.WriteString("&response_type=") 168 buffer.WriteString(r.FormValue("response_type")) 169 w.Header().Add("Location", buffer.String()) 170 } else { 171 w.Header().Add("Location", o.Manager.API.Oauth2Meta.AuthorizeLoginRedirect) 172 } 173 w.WriteHeader(307) 174 175 } 176 177 // HandleAccessRequest handles the OAuth 2.0 token or refresh access request, and wraps Tyk's own and Osin's OAuth handlers, 178 // returns a response to the client and notifies the provider of the access request (in order to track identity against 179 // OAuth tokens without revealing tokens before they are requested). 180 func (o *OAuthHandlers) HandleAccessRequest(w http.ResponseWriter, r *http.Request) { 181 w.Header().Set(headers.ContentType, headers.ApplicationJSON) 182 183 // Handle response 184 resp := o.Manager.HandleAccess(r) 185 msg := o.generateOAuthOutputFromOsinResponse(resp) 186 if resp.IsError { 187 // Something went wrong, write out the error details and kill the response 188 w.WriteHeader(resp.ErrorStatusCode) 189 w.Write(msg) 190 return 191 } 192 193 // Ping endpoint with o_auth key and auth_key 194 authCode := r.FormValue("code") 195 oldRefreshToken := r.FormValue("refresh_token") 196 log.Debug("AUTH CODE: ", authCode) 197 newOauthToken := "" 198 if resp.Output["access_token"] != nil { 199 newOauthToken = resp.Output["access_token"].(string) 200 } 201 log.Debug("TOKEN: ", newOauthToken) 202 refreshToken := "" 203 if resp.Output["refresh_token"] != nil { 204 refreshToken = resp.Output["refresh_token"].(string) 205 } 206 log.Debug("REFRESH: ", refreshToken) 207 log.Debug("Old REFRESH: ", oldRefreshToken) 208 209 notificationType := newAccessToken 210 if oldRefreshToken != "" { 211 notificationType = refreshAccessToken 212 } 213 214 newNotification := NewOAuthNotification{ 215 AuthCode: authCode, 216 NewOAuthToken: newOauthToken, 217 RefreshToken: refreshToken, 218 OldRefreshToken: oldRefreshToken, 219 NotificationType: notificationType, 220 } 221 222 o.notifyClientOfNewOauth(newNotification) 223 224 w.WriteHeader(http.StatusOK) 225 w.Write(msg) 226 } 227 228 // OAuthManager handles and wraps osin OAuth2 functions to handle authorise and access requests 229 type OAuthManager struct { 230 API *APISpec 231 OsinServer *TykOsinServer 232 } 233 234 // HandleAuthorisation creates the authorisation data for the request 235 func (o *OAuthManager) HandleAuthorisation(r *http.Request, complete bool, session string) *osin.Response { 236 resp := o.OsinServer.NewResponse() 237 238 if ar := o.OsinServer.HandleAuthorizeRequest(resp, r); ar != nil { 239 // Since this is called by the Reource provider (proxied API), we assume it has been approved 240 ar.Authorized = true 241 242 if complete { 243 ar.UserData = session 244 o.OsinServer.FinishAuthorizeRequest(resp, r, ar) 245 } 246 } 247 if resp.IsError && resp.InternalError != nil { 248 log.Error(resp.InternalError) 249 } 250 251 return resp 252 } 253 254 // JSONToFormValues if r has header Content-Type set to application/json this 255 // will decode request body as json to map[string]string and adds the key/value 256 // pairs in r.Form. 257 func JSONToFormValues(r *http.Request) error { 258 if r.Header.Get("Content-Type") == "application/json" { 259 var o map[string]string 260 err := json.NewDecoder(r.Body).Decode(&o) 261 if err != nil { 262 return err 263 } 264 if len(o) > 0 { 265 if r.Form == nil { 266 r.Form = make(url.Values) 267 } 268 for k, v := range o { 269 r.Form.Set(k, v) 270 } 271 } 272 273 } 274 return nil 275 } 276 277 // HandleAccess wraps an access request with osin's primitives 278 func (o *OAuthManager) HandleAccess(r *http.Request) *osin.Response { 279 resp := o.OsinServer.NewResponse() 280 // we are intentionally ignoring errors, because this is called again by 281 // osin.We are only doing this to ensure r.From is properly initialized incase 282 // r.ParseForm was success 283 r.ParseForm() 284 if err := JSONToFormValues(r); err != nil { 285 log.Errorf("trying to set url values decoded from json body :%v", err) 286 } 287 var username string 288 if ar := o.OsinServer.HandleAccessRequest(resp, r); ar != nil { 289 290 var session *user.SessionState 291 if ar.Type == osin.PASSWORD { 292 username = r.Form.Get("username") 293 password := r.Form.Get("password") 294 searchKey := "apikey-" + storage.HashKey(o.API.OrgID+username) 295 log.Debug("Getting: ", searchKey) 296 297 var err error 298 session, err = o.OsinServer.Storage.GetUser(searchKey) 299 if err != nil { 300 log.Warning("Attempted access with non-existent user (OAuth password flow).") 301 } else { 302 var passMatch bool 303 if session.BasicAuthData.Hash == user.HashBCrypt { 304 err := bcrypt.CompareHashAndPassword([]byte(session.BasicAuthData.Password), []byte(password)) 305 if err == nil { 306 passMatch = true 307 } 308 } 309 310 if session.BasicAuthData.Hash == user.HashPlainText && 311 session.BasicAuthData.Password == password { 312 passMatch = true 313 } 314 315 if passMatch { 316 log.Info("Here we are") 317 ar.Authorized = true 318 // not ideal, but we need to copy the session state across 319 pw := session.BasicAuthData.Password 320 hs := session.BasicAuthData.Hash 321 322 session.BasicAuthData.Password = "" 323 session.BasicAuthData.Hash = "" 324 asString, _ := json.Marshal(session) 325 ar.UserData = string(asString) 326 327 session.BasicAuthData.Password = pw 328 session.BasicAuthData.Hash = hs 329 330 //log.Warning("Old Keys: ", session.OauthKeys) 331 } 332 } 333 } else { 334 // Using a manual flow 335 ar.Authorized = true 336 } 337 338 // Does the user have an old OAuth token for this client? 339 if session != nil && session.OauthKeys != nil { 340 log.Debug("There's keys here bill...") 341 oldToken, foundKey := session.OauthKeys[ar.Client.GetId()] 342 if foundKey { 343 log.Info("Found old token, revoking: ", oldToken) 344 345 o.API.SessionManager.RemoveSession(oldToken, false) 346 } 347 } 348 349 log.Debug("[OAuth] Finishing access request ") 350 o.OsinServer.FinishAccessRequest(resp, r, ar) 351 352 new_token, foundNewToken := resp.Output["access_token"] 353 if username != "" && foundNewToken { 354 log.Debug("Updating token data in key") 355 if session.OauthKeys == nil { 356 session.OauthKeys = make(map[string]string) 357 } 358 session.OauthKeys[ar.Client.GetId()] = new_token.(string) 359 log.Debug("New token: ", new_token.(string)) 360 log.Debug("Keys: ", session.OauthKeys) 361 362 // add oauth-client user_fields to session's meta 363 if userData := ar.Client.GetUserData(); userData != nil { 364 var ok bool 365 session.MetaData, ok = userData.(map[string]interface{}) 366 if !ok { 367 log.WithField("oauthClientID", ar.Client.GetId()). 368 Error("Could not set session meta_data from oauth-client fields, type mismatch") 369 } else { 370 // set session alias to developer email as we do it for regular API keys created for developer 371 if devEmail, found := session.MetaData[keyDataDeveloperEmail].(string); found { 372 session.Alias = devEmail 373 // we don't need it in meta-data as we set it to alias 374 delete(session.MetaData, keyDataDeveloperEmail) 375 } 376 } 377 } 378 379 keyName := generateToken(o.API.OrgID, username) 380 381 log.Debug("Updating user:", keyName) 382 err := o.API.SessionManager.UpdateSession(keyName, session, session.Lifetime(o.API.SessionLifetime), false) 383 if err != nil { 384 log.Error(err) 385 } 386 } 387 388 } 389 if resp.IsError && resp.InternalError != nil { 390 log.Error("ERROR: ", resp.InternalError) 391 } 392 393 return resp 394 } 395 396 // These enums fix the prefix to use when storing various OAuth keys and data, since we 397 // delegate everything to the osin framework 398 const ( 399 prefixAuth = "oauth-authorize." 400 prefixClient = "oauth-clientid." 401 prefixAccess = "oauth-access." 402 prefixRefresh = "oauth-refresh." 403 prefixClientset = "oauth-clientset." 404 405 prefixClientTokens = "oauth-client-tokens." 406 ) 407 408 // swagger:model 409 type OAuthClientToken struct { 410 Token string `json:"code"` 411 Expires int64 `json:"expires"` 412 } 413 414 type ExtendedOsinClientInterface interface { 415 osin.Client 416 GetDescription() string 417 } 418 419 type ExtendedOsinStorageInterface interface { 420 osin.Storage 421 422 // Create OAuth clients 423 SetClient(id string, client osin.Client, ignorePrefix bool) error 424 425 // Custom getter to handle prefixing issues in Redis 426 GetClientNoPrefix(id string) (osin.Client, error) 427 428 GetClientTokens(id string) ([]OAuthClientToken, error) 429 GetPaginatedClientTokens(id string, page int) ([]OAuthClientToken, int, error) 430 431 GetExtendedClient(id string) (ExtendedOsinClientInterface, error) 432 433 // Custom getter to handle prefixing issues in Redis 434 GetExtendedClientNoPrefix(id string) (ExtendedOsinClientInterface, error) 435 436 GetClients(filter string, ignorePrefix bool) ([]ExtendedOsinClientInterface, error) 437 438 DeleteClient(id string, ignorePrefix bool) error 439 440 // GetUser retrieves a Basic Access user token type from the key store 441 GetUser(string) (*user.SessionState, error) 442 443 // SetUser updates a Basic Access user token type in the key store 444 SetUser(string, *user.SessionState, int64) error 445 } 446 447 // TykOsinServer subclasses osin.Server so we can add the SetClient method without wrecking the lbrary 448 type TykOsinServer struct { 449 osin.Server 450 Config *osin.ServerConfig 451 Storage ExtendedOsinStorageInterface 452 AuthorizeTokenGen osin.AuthorizeTokenGen 453 AccessTokenGen osin.AccessTokenGen 454 } 455 456 // TykOsinNewServer creates a new server instance, but uses an extended interface so we can SetClient() too. 457 func TykOsinNewServer(config *osin.ServerConfig, storage ExtendedOsinStorageInterface) *TykOsinServer { 458 459 overrideServer := TykOsinServer{ 460 Config: config, 461 Storage: storage, 462 AuthorizeTokenGen: &osin.AuthorizeTokenGenDefault{}, 463 AccessTokenGen: accessTokenGen{}, 464 } 465 466 overrideServer.Server.Config = config 467 overrideServer.Server.Storage = storage 468 overrideServer.Server.AuthorizeTokenGen = overrideServer.AuthorizeTokenGen 469 overrideServer.Server.AccessTokenGen = accessTokenGen{} 470 471 return &overrideServer 472 } 473 474 // TODO: Refactor this to move prefix handling into a checker method, then it can be an unexported setting in the struct. 475 // RedisOsinStorageInterface implements osin.Storage interface to use Tyk's own storage mechanism 476 type RedisOsinStorageInterface struct { 477 store storage.Handler 478 sessionManager SessionHandler 479 } 480 481 func (r *RedisOsinStorageInterface) Clone() osin.Storage { 482 return r 483 } 484 485 func (r *RedisOsinStorageInterface) Close() {} 486 487 // GetClient will retrieve client data 488 func (r *RedisOsinStorageInterface) GetClient(id string) (osin.Client, error) { 489 key := prefixClient + id 490 491 log.Info("Getting client ID:", id) 492 493 clientJSON, err := r.store.GetKey(key) 494 if err != nil { 495 log.Errorf("Failure retreiving client ID key %q: %v", key, err) 496 return nil, err 497 } 498 499 client := new(OAuthClient) 500 if err := json.Unmarshal([]byte(clientJSON), &client); err != nil { 501 log.Error("Couldn't unmarshal OAuth client object: ", err) 502 } 503 504 return client, nil 505 } 506 507 // GetClientNoPrefix will retrieve client data, but not assign a prefix - this is an unfortunate hack, 508 // but we don't want to change the signature in Osin for GetClient to support the odd Redis prefixing 509 func (r *RedisOsinStorageInterface) GetClientNoPrefix(id string) (osin.Client, error) { 510 511 key := id 512 513 clientJSON, err := r.store.GetKey(key) 514 515 if err != nil { 516 log.Error("Failure retrieving client ID key: ", err) 517 return nil, err 518 } 519 520 client := new(OAuthClient) 521 if err := json.Unmarshal([]byte(clientJSON), client); err != nil { 522 log.Error("Couldn't unmarshal OAuth client object: ", err) 523 } 524 525 return client, nil 526 } 527 528 func (r *RedisOsinStorageInterface) GetExtendedClient(id string) (ExtendedOsinClientInterface, error) { 529 osinClient, err := r.GetClient(id) 530 if err != nil { 531 log.WithError(err).Error("Failure retrieving client ID key") 532 return nil, err 533 } 534 535 return osinClient.(*OAuthClient), err 536 } 537 538 // GetExtendedClientNoPrefix custom getter to handle prefixing issues in Redis, 539 func (r *RedisOsinStorageInterface) GetExtendedClientNoPrefix(id string) (ExtendedOsinClientInterface, error) { 540 osinClient, err := r.GetClientNoPrefix(id) 541 if err != nil { 542 log.WithError(err).Error("Failure retrieving client ID key") 543 return nil, err 544 } 545 return osinClient.(*OAuthClient), err 546 } 547 548 // GetClients will retrieve a list of clients for a prefix 549 func (r *RedisOsinStorageInterface) GetClients(filter string, ignorePrefix bool) ([]ExtendedOsinClientInterface, error) { 550 key := prefixClient + filter 551 if ignorePrefix { 552 key = filter 553 } 554 555 var clientJSON map[string]string 556 if !config.Global().Storage.EnableCluster { 557 clientJSON = r.store.GetKeysAndValuesWithFilter(key) 558 } else { 559 keyForSet := prefixClientset + prefixClient // Org ID 560 var err error 561 if clientJSON, err = r.store.GetSet(keyForSet); err != nil { 562 return nil, err 563 } 564 } 565 566 theseClients := []ExtendedOsinClientInterface{} 567 for _, clientJSON := range clientJSON { 568 client := new(OAuthClient) 569 if err := json.Unmarshal([]byte(clientJSON), &client); err != nil { 570 log.Error("Couldn't unmarshal OAuth client object: ", err) 571 return nil, err 572 } 573 theseClients = append(theseClients, client) 574 } 575 576 return theseClients, nil 577 } 578 579 // GetPaginatedClientTokens returns all tokens associated with the given id. 580 // It returns the tokens, the total number of pages of the tokens after 581 // pagination and an error if any 582 func (r *RedisOsinStorageInterface) GetPaginatedClientTokens(id string, page int) ([]OAuthClientToken, int, error) { 583 key := prefixClientTokens + id 584 585 // use current timestamp as a start score so all expired tokens won't be picked 586 nowTs := time.Now().Unix() 587 startScore := strconv.FormatInt(nowTs, 10) 588 589 log.Info("Getting client tokens sorted list:", key) 590 591 tokens, scores, err := r.store.GetSortedSetRange(key, startScore, "+inf") 592 if err != nil { 593 return nil, 0, err 594 } 595 596 // clean up expired tokens in sorted set (remove all tokens with score up to current timestamp minus retention) 597 if config.Global().OauthTokenExpiredRetainPeriod > 0 { 598 cleanupStartScore := strconv.FormatInt(nowTs-int64(config.Global().OauthTokenExpiredRetainPeriod), 10) 599 go r.store.RemoveSortedSetRange(key, "-inf", cleanupStartScore) 600 } 601 602 itemsPerPage := 100 603 604 tokenNumber := len(tokens) 605 606 if tokenNumber == 0 { 607 return []OAuthClientToken{}, 0, nil 608 } 609 610 startIdx := (page - 1) * itemsPerPage 611 endIdx := startIdx + itemsPerPage 612 613 if tokenNumber < startIdx { 614 startIdx = tokenNumber 615 } 616 617 if tokenNumber < endIdx { 618 endIdx = tokenNumber 619 } 620 621 totalPages := int(math.Ceil(float64(len(tokens)) / float64(itemsPerPage))) 622 623 tokens = tokens[startIdx:endIdx] 624 625 // convert sorted set data and scores into reply struct 626 tokensData := make([]OAuthClientToken, len(tokens)) 627 for i := range tokens { 628 tokensData[i] = OAuthClientToken{ 629 Token: tokens[i], 630 Expires: int64(scores[i]), // we store expire timestamp as a score 631 } 632 } 633 634 return tokensData, totalPages, nil 635 } 636 637 func (r *RedisOsinStorageInterface) GetClientTokens(id string) ([]OAuthClientToken, error) { 638 key := prefixClientTokens + id 639 640 // use current timestamp as a start score so all expired tokens won't be picked 641 nowTs := time.Now().Unix() 642 startScore := strconv.FormatInt(nowTs, 10) 643 644 log.Info("Getting client tokens sorted list:", key) 645 646 tokens, scores, err := r.store.GetSortedSetRange(key, startScore, "+inf") 647 if err != nil { 648 return nil, err 649 } 650 651 // clean up expired tokens in sorted set (remove all tokens with score up to current timestamp minus retention) 652 if config.Global().OauthTokenExpiredRetainPeriod > 0 { 653 cleanupStartScore := strconv.FormatInt(nowTs-int64(config.Global().OauthTokenExpiredRetainPeriod), 10) 654 go r.store.RemoveSortedSetRange(key, "-inf", cleanupStartScore) 655 } 656 657 if len(tokens) == 0 { 658 return []OAuthClientToken{}, nil 659 } 660 661 // convert sorted set data and scores into reply struct 662 tokensData := make([]OAuthClientToken, len(tokens)) 663 for i := range tokens { 664 tokensData[i] = OAuthClientToken{ 665 Token: tokens[i], 666 Expires: int64(scores[i]), // we store expire timestamp as a score 667 } 668 } 669 670 return tokensData, nil 671 } 672 673 // SetClient creates client data 674 func (r *RedisOsinStorageInterface) SetClient(id string, client osin.Client, ignorePrefix bool) error { 675 clientDataJSON, err := json.Marshal(client) 676 677 if err != nil { 678 log.Error("Couldn't marshal client data: ", err) 679 return err 680 } 681 682 key := prefixClient + id 683 684 if ignorePrefix { 685 key = id 686 } 687 688 log.Debug("CREATING: ", key) 689 690 r.store.SetKey(key, string(clientDataJSON), 0) 691 692 log.Debug("Storing copy in set") 693 694 keyForSet := prefixClientset + prefixClient // Org ID 695 r.store.AddToSet(keyForSet, string(clientDataJSON)) 696 return nil 697 } 698 699 // DeleteClient Removes a client from the system 700 func (r *RedisOsinStorageInterface) DeleteClient(id string, ignorePrefix bool) error { 701 key := prefixClient + id 702 if ignorePrefix { 703 key = id 704 } 705 706 // Get the raw vals: 707 clientJSON, err := r.store.GetKey(key) 708 if err == nil { 709 log.Debug("Removing from set") 710 keyForSet := prefixClientset + prefixClient // Org ID 711 r.store.RemoveFromSet(keyForSet, clientJSON) 712 } 713 714 r.store.DeleteKey(key) 715 716 // delete list of tokens for this client 717 r.store.DeleteKey(prefixClientTokens + id) 718 719 return nil 720 } 721 722 // SaveAuthorize saves authorisation data to Redis 723 func (r *RedisOsinStorageInterface) SaveAuthorize(authData *osin.AuthorizeData) error { 724 authDataJSON, err := json.Marshal(&authData) 725 if err != nil { 726 return err 727 } 728 key := prefixAuth + authData.Code 729 log.Debug("Saving auth code: ", key) 730 r.store.SetKey(key, string(authDataJSON), int64(authData.ExpiresIn)) 731 732 return nil 733 } 734 735 // LoadAuthorize loads auth data from redis 736 func (r *RedisOsinStorageInterface) LoadAuthorize(code string) (*osin.AuthorizeData, error) { 737 key := prefixAuth + code 738 log.Debug("Loading auth code: ", key) 739 authJSON, err := r.store.GetKey(key) 740 741 if err != nil { 742 log.Error("Failure retreiving auth code key: ", err) 743 return nil, err 744 } 745 746 authData := osin.AuthorizeData{Client: new(OAuthClient)} 747 if err := json.Unmarshal([]byte(authJSON), &authData); err != nil { 748 log.Error("Couldn't unmarshal OAuth auth data object (LoadAuthorize): ", err) 749 return nil, err 750 } 751 752 return &authData, nil 753 } 754 755 // RemoveAuthorize removes authorisation keys from redis 756 func (r *RedisOsinStorageInterface) RemoveAuthorize(code string) error { 757 key := prefixAuth + code 758 r.store.DeleteKey(key) 759 return nil 760 } 761 762 // SaveAccess will save a token and it's access data to redis 763 func (r *RedisOsinStorageInterface) SaveAccess(accessData *osin.AccessData) error { 764 authDataJSON, err := json.Marshal(accessData) 765 if err != nil { 766 return err 767 } 768 769 key := prefixAccess + storage.HashKey(accessData.AccessToken) 770 log.Debug("Saving ACCESS key: ", key) 771 772 // Overide default ExpiresIn: 773 if oauthTokenExpire := config.Global().OauthTokenExpire; oauthTokenExpire != 0 { 774 accessData.ExpiresIn = oauthTokenExpire 775 } 776 777 r.store.SetKey(key, string(authDataJSON), int64(accessData.ExpiresIn)) 778 779 // add code to list of tokens for this client 780 sortedListKey := prefixClientTokens + accessData.Client.GetId() 781 log.Debug("Adding ACCESS key to sorted list: ", sortedListKey) 782 r.store.AddToSortedSet( 783 sortedListKey, 784 storage.HashKey(accessData.AccessToken), 785 float64(accessData.CreatedAt.Unix()+int64(accessData.ExpiresIn)), // set score as token expire timestamp 786 ) 787 788 // Create a user.SessionState object and register it with the authmanager 789 var newSession user.SessionState 790 791 // ------ 792 checkPolicy := true 793 if accessData.UserData != nil { 794 checkPolicy = false 795 err := json.Unmarshal([]byte(accessData.UserData.(string)), &newSession) 796 if err != nil { 797 log.Info("Couldn't decode user.SessionState from UserData, checking policy: ", err) 798 checkPolicy = true 799 } 800 } 801 802 if checkPolicy { 803 // defined in JWT middleware 804 sessionFromPolicy, err := generateSessionFromPolicy(accessData.Client.GetPolicyID(), "", false) 805 if err != nil { 806 return errors.New("Couldn't use policy or key rules to create token, failing") 807 } 808 809 newSession = sessionFromPolicy 810 } 811 812 // ------ 813 814 // Set the client ID for analytics 815 newSession.OauthClientID = accessData.Client.GetId() 816 817 // Override timeouts so that we can be in sync with Osin 818 newSession.Expires = time.Now().Unix() + int64(accessData.ExpiresIn) 819 820 // Use the default session expiry here as this is OAuth 821 r.sessionManager.UpdateSession(accessData.AccessToken, &newSession, int64(accessData.ExpiresIn), false) 822 823 // Store the refresh token too 824 if accessData.RefreshToken != "" { 825 accessDataJSON, err := json.Marshal(accessData) 826 if err != nil { 827 return err 828 } 829 key := prefixRefresh + accessData.RefreshToken 830 log.Debug("Saving REFRESH key: ", key) 831 refreshExpire := int64(1209600) // 14 days 832 if oauthRefreshExpire := config.Global().OauthRefreshExpire; oauthRefreshExpire != 0 { 833 refreshExpire = oauthRefreshExpire 834 } 835 r.store.SetKey(key, string(accessDataJSON), refreshExpire) 836 log.Debug("STORING ACCESS DATA: ", string(accessDataJSON)) 837 return nil 838 } 839 840 return nil 841 } 842 843 // LoadAccess will load access data from redis 844 func (r *RedisOsinStorageInterface) LoadAccess(token string) (*osin.AccessData, error) { 845 key := prefixAccess + storage.HashKey(token) 846 log.Debug("Loading ACCESS key: ", key) 847 accessJSON, err := r.store.GetKey(key) 848 849 if err != nil { 850 // Fallback to unhashed value for backward compatibility 851 key = prefixAccess + token 852 accessJSON, err = r.store.GetKey(key) 853 854 if err != nil { 855 log.Error("Failure retreiving access token by key: ", err) 856 return nil, err 857 } 858 } 859 860 accessData := osin.AccessData{Client: new(OAuthClient)} 861 if err := json.Unmarshal([]byte(accessJSON), &accessData); err != nil { 862 log.Error("Couldn't unmarshal OAuth auth data object (LoadAccess): ", err) 863 return nil, err 864 } 865 866 return &accessData, nil 867 } 868 869 // RemoveAccess will remove access data from Redis 870 func (r *RedisOsinStorageInterface) RemoveAccess(token string) error { 871 key := prefixAccess + token 872 r.store.DeleteKey(key) 873 874 // remove the access token from central storage too 875 r.sessionManager.RemoveSession(token, false) 876 877 return nil 878 } 879 880 // LoadRefresh will load access data from Redis 881 func (r *RedisOsinStorageInterface) LoadRefresh(token string) (*osin.AccessData, error) { 882 key := prefixRefresh + token 883 log.Debug("Loading REFRESH key: ", key) 884 accessJSON, err := r.store.GetKey(key) 885 886 if err != nil { 887 log.Error("Failure retreiving access token by key: ", err) 888 return nil, err 889 } 890 891 // new interface means having to make this nested... ick. 892 accessData := osin.AccessData{Client: new(OAuthClient)} 893 if err := json.Unmarshal([]byte(accessJSON), &accessData); err != nil { 894 log.Error("Couldn't unmarshal OAuth auth data object (LoadRefresh): ", err, 895 "; Decoding: ", accessJSON) 896 return nil, err 897 } 898 899 return &accessData, nil 900 } 901 902 // RemoveRefresh will remove a refresh token from redis 903 func (r *RedisOsinStorageInterface) RemoveRefresh(token string) error { 904 key := prefixRefresh + token 905 r.store.DeleteKey(key) 906 return nil 907 } 908 909 // accessTokenGen is a modified authorization token generator that uses the same method used to generate tokens for Tyk authHandler 910 type accessTokenGen struct{} 911 912 // GenerateAccessToken generates base64-encoded UUID access and refresh tokens 913 func (accessTokenGen) GenerateAccessToken(data *osin.AccessData, generaterefresh bool) (accesstoken, refreshtoken string, err error) { 914 log.Info("[OAuth] Generating new token") 915 916 var newSession user.SessionState 917 checkPolicy := true 918 if data.UserData != nil { 919 checkPolicy = false 920 err := json.Unmarshal([]byte(data.UserData.(string)), &newSession) 921 if err != nil { 922 log.Info("[GenerateAccessToken] Couldn't decode user.SessionState from UserData, checking policy: ", err) 923 checkPolicy = true 924 } 925 } 926 927 if checkPolicy { 928 // defined in JWT middleware 929 sessionFromPolicy, err := generateSessionFromPolicy(data.Client.GetPolicyID(), "", false) 930 if err != nil { 931 return "", "", errors.New("Couldn't use policy or key rules to create token, failing") 932 } 933 934 newSession = sessionFromPolicy 935 } 936 937 accesstoken = keyGen.GenerateAuthKey(newSession.OrgID) 938 939 if generaterefresh { 940 u6 := uuid.NewV4() 941 refreshtoken = base64.StdEncoding.EncodeToString([]byte(u6.String())) 942 } 943 return 944 } 945 946 // LoadRefresh will load access data from Redis 947 func (r *RedisOsinStorageInterface) GetUser(username string) (*user.SessionState, error) { 948 key := username 949 log.Debug("Loading User key: ", key) 950 accessJSON, err := r.store.GetRawKey(key) 951 952 if err != nil { 953 log.Error("Failure retreiving access token by key: ", err) 954 return nil, err 955 } 956 957 // new interface means having to make this nested... ick. 958 session := user.SessionState{} 959 if err := json.Unmarshal([]byte(accessJSON), &session); err != nil { 960 log.Error("Couldn't unmarshal OAuth auth data object (LoadRefresh): ", err, 961 "; Decoding: ", accessJSON) 962 return nil, err 963 } 964 965 return &session, nil 966 } 967 968 func (r *RedisOsinStorageInterface) SetUser(username string, session *user.SessionState, timeout int64) error { 969 key := username 970 authDataJSON, err := json.Marshal(session) 971 if err != nil { 972 return err 973 } 974 975 if err := r.store.SetRawKey(key, string(authDataJSON), timeout); err != nil { 976 log.Error("Failure setting user token by key: ", err) 977 return err 978 } 979 980 return nil 981 982 }