github.com/dbernstein1/tyk@v2.9.0-beta9-dl-apic+incompatible/gateway/mw_openid.go (about) 1 package gateway 2 3 import ( 4 "crypto/md5" 5 "encoding/base64" 6 "errors" 7 "fmt" 8 "net/http" 9 "sync" 10 11 "github.com/sirupsen/logrus" 12 jwt "github.com/dgrijalva/jwt-go" 13 14 "github.com/TykTechnologies/openid2go/openid" 15 "github.com/TykTechnologies/tyk/apidef" 16 "github.com/TykTechnologies/tyk/user" 17 ) 18 19 const OIDPREFIX = "openid" 20 21 type OpenIDMW struct { 22 BaseMiddleware 23 providerConfiguration *openid.Configuration 24 provider_client_policymap map[string]map[string]string 25 lock sync.RWMutex 26 } 27 28 func (k *OpenIDMW) Name() string { 29 return "OpenIDMW" 30 } 31 32 func (k *OpenIDMW) EnabledForSpec() bool { 33 return k.Spec.UseOpenID 34 } 35 36 func (k *OpenIDMW) Init() { 37 k.provider_client_policymap = make(map[string]map[string]string) 38 // Create an OpenID Configuration and store 39 var err error 40 k.providerConfiguration, err = openid.NewConfiguration(openid.ProvidersGetter(k.getProviders), 41 openid.ErrorHandler(k.dummyErrorHandler)) 42 43 if err != nil { 44 k.Logger().WithError(err).Error("OpenID configuration error") 45 } 46 } 47 48 func (k *OpenIDMW) getProviders() ([]openid.Provider, error) { 49 providers := []openid.Provider{} 50 k.Logger().Debug("Setting up providers: ", k.Spec.OpenIDOptions.Providers) 51 for _, provider := range k.Spec.OpenIDOptions.Providers { 52 iss := provider.Issuer 53 k.Logger().Debug("Setting up Issuer: ", iss) 54 providerClientArray := make([]string, len(provider.ClientIDs)) 55 56 i := 0 57 for clientID, policyID := range provider.ClientIDs { 58 clID, _ := base64.StdEncoding.DecodeString(clientID) 59 clientID := string(clID) 60 61 k.lock.Lock() 62 if k.provider_client_policymap[iss] == nil { 63 k.provider_client_policymap[iss] = map[string]string{clientID: policyID} 64 } else { 65 k.provider_client_policymap[iss][clientID] = policyID 66 } 67 k.lock.Unlock() 68 69 k.Logger().Debug("--> Setting up client: ", clientID, " with policy: ", policyID) 70 providerClientArray[i] = clientID 71 i++ 72 } 73 74 p, err := openid.NewProvider(iss, providerClientArray) 75 76 if err != nil { 77 k.Logger().WithError(err).WithFields(logrus.Fields{ 78 "provider": iss, 79 }).Error("Failed to create provider") 80 } else { 81 providers = append(providers, p) 82 } 83 } 84 85 return providers, nil 86 } 87 88 // We don't want any of the error handling, we use our own 89 func (k *OpenIDMW) dummyErrorHandler(e error, w http.ResponseWriter, r *http.Request) bool { 90 k.Logger().WithError(e).Warning("JWT Invalid") 91 return true 92 } 93 94 func (k *OpenIDMW) ProcessRequest(w http.ResponseWriter, r *http.Request, _ interface{}) (error, int) { 95 logger := k.Logger() 96 // 1. Validate the JWT 97 ouser, token, halt := openid.AuthenticateOIDWithUser(k.providerConfiguration, w, r) 98 99 // 2. Generate the internal representation for the key 100 if halt { 101 // Fire Authfailed Event 102 k.reportLoginFailure("[JWT]", r) 103 return errors.New("Key not authorised"), http.StatusUnauthorized 104 } 105 106 // 3. Create or set the session to match 107 iss, found := token.Claims.(jwt.MapClaims)["iss"] 108 clients, cfound := token.Claims.(jwt.MapClaims)["aud"] 109 110 if !found && !cfound { 111 logger.Error("No issuer or audiences found!") 112 k.reportLoginFailure("[NOT GENERATED]", r) 113 return errors.New("Key not authorised"), http.StatusUnauthorized 114 } 115 116 // decide if we use policy ID from provider client settings or list of policies from scope-policy mapping 117 useScope := len(k.Spec.JWTScopeToPolicyMapping) != 0 118 119 k.lock.RLock() 120 clientSet, foundIssuer := k.provider_client_policymap[iss.(string)] 121 k.lock.RUnlock() 122 if !foundIssuer { 123 logger.Error("No issuer or audiences found!") 124 k.reportLoginFailure("[NOT GENERATED]", r) 125 return errors.New("Key not authorised"), http.StatusUnauthorized 126 } 127 128 policyID := "" 129 clientID := "" 130 switch v := clients.(type) { 131 case string: 132 k.lock.RLock() 133 policyID = clientSet[v] 134 k.lock.RUnlock() 135 clientID = v 136 case []interface{}: 137 for _, audVal := range v { 138 k.lock.RLock() 139 policy, foundPolicy := clientSet[audVal.(string)] 140 k.lock.RUnlock() 141 if foundPolicy { 142 clientID = audVal.(string) 143 policyID = policy 144 break 145 } 146 } 147 } 148 149 if !useScope && policyID == "" { 150 logger.Error("No matching policy found!") 151 k.reportLoginFailure("[NOT GENERATED]", r) 152 return errors.New("Key not authorised"), http.StatusUnauthorized 153 } 154 155 data := []byte(ouser.ID) 156 keyID := fmt.Sprintf("%x", md5.Sum(data)) 157 sessionID := generateToken(k.Spec.OrgID, keyID) 158 159 if k.Spec.OpenIDOptions.SegregateByClient { 160 // We are segregating by client, so use it as part of the internal token 161 logger.Debug("Client ID:", clientID) 162 sessionID = generateToken(k.Spec.OrgID, fmt.Sprintf("%x", md5.Sum([]byte(clientID)))+keyID) 163 } 164 165 logger.Debug("Generated Session ID: ", sessionID) 166 167 var policiesToApply []string 168 if !useScope { 169 policiesToApply = append(policiesToApply, policyID) 170 } else { 171 scopeClaimName := k.Spec.JWTScopeClaimName 172 if scopeClaimName == "" { 173 scopeClaimName = "scope" 174 } 175 176 if scope := getScopeFromClaim(token.Claims.(jwt.MapClaims), scopeClaimName); scope != nil { 177 // add all policies matched from scope-policy mapping 178 policiesToApply = mapScopeToPolicies(k.Spec.JWTScopeToPolicyMapping, scope) 179 } 180 } 181 182 session, exists := k.CheckSessionAndIdentityForValidKey(sessionID, r) 183 if !exists { 184 // Create it 185 logger.Debug("Key does not exist, creating") 186 session = user.SessionState{} 187 188 if !useScope { 189 // We need a base policy as a template, either get it from the token itself OR a proxy client ID within Tyk 190 newSession, err := generateSessionFromPolicy(policyID, 191 k.Spec.OrgID, 192 true) 193 194 if err != nil { 195 k.reportLoginFailure(sessionID, r) 196 logger.Error("Could not find a valid policy to apply to this token!") 197 return errors.New("Key not authorized: no matching policy"), http.StatusForbidden 198 } 199 200 session = newSession 201 } 202 203 session.OrgID = k.Spec.OrgID 204 session.MetaData = map[string]interface{}{"TykJWTSessionID": sessionID, "ClientID": clientID} 205 session.Alias = clientID + ":" + ouser.ID 206 207 // Update the session in the session manager in case it gets called again 208 logger.Debug("Policy applied to key") 209 } 210 // apply new policy to session if any and update session 211 session.SetPolicies(policiesToApply...) 212 if err := k.ApplyPolicies(&session); err != nil { 213 k.Logger().WithError(err).Error("Could not apply new policy from OIDC client to session") 214 return errors.New("Key not authorized: could not apply new policy"), http.StatusForbidden 215 } 216 217 // 4. Set session state on context, we will need it later 218 switch k.Spec.BaseIdentityProvidedBy { 219 case apidef.OIDCUser, apidef.UnsetAuth: 220 ctxSetSession(r, &session, sessionID, true) 221 } 222 ctxSetJWTContextVars(k.Spec, r, token) 223 224 return nil, http.StatusOK 225 } 226 227 func (k *OpenIDMW) reportLoginFailure(tykId string, r *http.Request) { 228 k.Logger().WithFields(logrus.Fields{ 229 "key": obfuscateKey(tykId), 230 }).Warning("Attempted access with invalid key.") 231 232 // Fire Authfailed Event 233 AuthFailed(k, r, tykId) 234 235 // Report in health check 236 reportHealthValue(k.Spec, KeyFailure, "1") 237 }