package auth import ( "context" "encoding/json" "errors" "net/http" "strings" "github.com/fiskerinc/cloud-services/pkg/common" "github.com/fiskerinc/cloud-services/pkg/db/queries" "github.com/fiskerinc/cloud-services/pkg/httpclient" "github.com/fiskerinc/cloud-services/pkg/jwt" "github.com/fiskerinc/cloud-services/pkg/logger" "github.com/fiskerinc/cloud-services/pkg/utils/envtool" "github.com/fiskerinc/cloud-services/pkg/utils" ) var getUserURL string = envtool.GetEnv("AUTH_GET_USER", "https://dev-auth.fiskerdps.com/auth/me") func AppendUserMiddleware(next http.HandlerFunc, apiCalls queries.APICallsInterface) http.HandlerFunc { wrapper := func(w http.ResponseWriter, r *http.Request) { _, err := jwt.GetAuthorizationHeader(r) if err != nil { logger.Warn().Err(err).Msgf("token invalid %s %s", r.Method, r.RequestURI) utils.RespError(w, http.StatusUnauthorized, err.Error()) return } // go to auth to get user information req, _ := http.NewRequest("GET", getUserURL, nil) req.Header.Set("Authorization", r.Header.Get("Authorization")) resp, err := httpclient.Do(req) if err != nil { logger.Warn().Err(err).Msgf("Unable to fetch user %s %s", r.Method, r.RequestURI) utils.RespError(w, http.StatusUnauthorized, err.Error()) return } defer resp.Body.Close() if resp != nil && resp.StatusCode != 200 { logger.Warn().Err(err).Msgf("Unable to fetch user %s %s", r.Method, r.RequestURI) if err != nil { utils.RespError(w, http.StatusUnauthorized, err.Error()) } else { utils.RespError(w, http.StatusUnauthorized, resp.Status) } return } user, err := extractUserAttributes(resp) if err != nil { logger.Warn().Err(err).Msgf("Unable to parse user response %s %s", r.Method, r.RequestURI) utils.RespError(w, http.StatusUnauthorized, err.Error()) return } err = logAPICall(user, r.RequestURI, r.Method, apiCalls) if err != nil { logger.Warn().Msgf("Call log %s %s '%v'", r.Method, r.RequestURI, err) } ctx := r.Context() ctx = context.WithValue(ctx, "identity", user) next.ServeHTTP(w, r.WithContext(ctx)) } return wrapper } func AppendUserTokenMiddleware(next http.HandlerFunc, apiCalls queries.APICallsInterface) http.HandlerFunc { wrapper := func(w http.ResponseWriter, r *http.Request) { token, err := jwt.GetAuthorizationHeader(r) if err != nil { logger.Warn().Err(err).Msgf("token invalid %s %s", r.Method, r.RequestURI) utils.RespError(w, http.StatusUnauthorized, err.Error()) return } valid := jwt.NewJWTValidator("") _, err = valid.ValidateToken(token.Token) if err != nil { logger.Warn().Err(err).Msgf("token invalid %s %s", r.Method, r.RequestURI) utils.RespError(w, http.StatusUnauthorized, err.Error()) return } payload, err := jwt.GetPayload(token.Token) if err != nil { logger.Warn().Err(err).Msgf("token invalid %s %s", r.Method, r.RequestURI) utils.RespError(w, http.StatusUnauthorized, err.Error()) return } err = logAPICall(payload, r.RequestURI, r.Method, apiCalls) if err != nil { logger.Warn().Msgf("Call log %s %s '%v'", r.Method, r.RequestURI, err) } ctx := r.Context() ctx = context.WithValue(ctx, "identity", payload) next.ServeHTTP(w, r.WithContext(ctx)) } return wrapper } func extractUserAttributes(resp *http.Response) (map[string]interface{}, error) { user := make(map[string]interface{}) err := json.NewDecoder(resp.Body).Decode(&user) if err != nil { return nil, err } username, ok := user["id"] if !ok { return nil, errors.New("invalid user token") } user["username"] = username return user, nil } func logAPICall(payload map[string]interface{}, uri string, method string, apiCalls queries.APICallsInterface) error { var ( username string ok bool ) if username, ok = payload["username"].(string); !ok || username == "" { return nil } endpoint, _, _ := strings.Cut(uri, "?") _, err := apiCalls.Insert(common.APICall{ ClientID: username, AccessType: common.AccessTypeJWT, Endpoint: endpoint, Method: method, }) return err }