github.com/greenpau/go-authcrunch@v1.1.4/pkg/idp/oauth/user_info.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package oauth 16 17 import ( 18 "encoding/json" 19 "fmt" 20 cfgutil "github.com/greenpau/go-authcrunch/pkg/util/cfg" 21 "go.uber.org/zap" 22 "io/ioutil" 23 "net/http" 24 "strings" 25 ) 26 27 func (b *IdentityProvider) fetchUserInfo(tokenData, userData map[string]interface{}) error { 28 // The fetching of user info happens only if the below conditions are 29 // are met. 30 if b.config.Driver != "generic" || !b.ScopeExists("openid") || b.userInfoURL == "" { 31 return nil 32 } 33 if len(b.userInfoFields) == 0 { 34 return nil 35 } 36 if tokenData == nil || userData == nil { 37 return nil 38 } 39 if _, exists := tokenData["access_token"]; !exists { 40 return fmt.Errorf("access_token not found") 41 } 42 43 // Initialize HTTP client. 44 cli, err := b.newBrowser() 45 if err != nil { 46 return err 47 } 48 49 req, err := http.NewRequest("GET", b.userInfoURL, nil) 50 if err != nil { 51 return err 52 } 53 54 req.Header.Set("Accept", "application/json") 55 req.Header.Add("Authorization", "Bearer "+tokenData["access_token"].(string)) 56 57 // Fetch data from the URL. 58 resp, err := cli.Do(req) 59 if err != nil { 60 return err 61 } 62 63 respBody, err := ioutil.ReadAll(resp.Body) 64 resp.Body.Close() 65 if err != nil { 66 return err 67 } 68 69 b.logger.Debug( 70 "User info received", 71 zap.Any("body", respBody), 72 zap.String("url", b.userInfoURL), 73 ) 74 75 userinfo := make(map[string]interface{}) 76 if err := json.Unmarshal(respBody, &userinfo); err != nil { 77 return err 78 } 79 80 b.logger.Debug( 81 "User info received", 82 zap.Any("userinfo", userinfo), 83 zap.String("url", b.userInfoURL), 84 ) 85 86 var roles []string 87 if _, exists := b.userInfoFields["all"]; exists { 88 roles = extractUserInfoRoles(userinfo, b.userInfoRolesFieldName) 89 if len(userinfo) > 0 { 90 userData["userinfo"] = userinfo 91 } 92 } else { 93 for k := range userinfo { 94 if _, exists := b.userInfoFields[k]; !exists { 95 delete(userinfo, k) 96 } 97 } 98 if len(userinfo) > 0 { 99 roles = extractUserInfoRoles(userinfo, b.userInfoRolesFieldName) 100 if len(userinfo) > 0 { 101 userData["userinfo"] = userinfo 102 } 103 } 104 } 105 106 if len(roles) > 0 { 107 userData["roles"] = roles 108 } 109 return nil 110 } 111 112 func extractUserInfoRoles(m map[string]interface{}, rolesFieldName string) []string { 113 entries := make(map[string]interface{}) 114 var roles []string 115 for k, v := range m { 116 if !strings.HasSuffix(k, rolesFieldName) && !strings.HasSuffix(k, "groups") { 117 continue 118 } 119 switch values := v.(type) { 120 case string: 121 roles = append(roles, values) 122 entries[k] = true 123 case []interface{}: 124 for _, value := range values { 125 switch roleName := value.(type) { 126 case string: 127 roles = append(roles, roleName) 128 entries[k] = true 129 } 130 } 131 } 132 } 133 134 for k := range entries { 135 delete(m, k) 136 } 137 138 return cfgutil.DedupStrArr(roles) 139 }