github.com/futurehomeno/fimpgo@v1.14.0/edgeapp/auth.go (about) 1 package edgeapp 2 3 import ( 4 "bytes" 5 "encoding/json" 6 "errors" 7 "fmt" 8 "github.com/futurehomeno/fimpgo" 9 "github.com/futurehomeno/fimpgo/utils" 10 log "github.com/sirupsen/logrus" 11 "io/ioutil" 12 "net/http" 13 "time" 14 ) 15 16 type OAuth2TokenResponse struct { 17 AccessToken string `json:"access_token"` 18 TokenType string `json:"token_type"` 19 ExpiresIn int64 `json:"expires_in"` 20 RefreshToken string `json:"refresh_token"` 21 Scope interface{} `json:"scope"` 22 } 23 24 type OAuth2RefreshProxyRequest struct { 25 RefreshToken string `json:"refreshToken"` 26 PartnerCode string `json:"partnerCode"` 27 } 28 29 type OAuth2AuthCodeProxyRequest struct { 30 AuthCode string `json:"code"` 31 PartnerCode string `json:"partnerCode"` 32 } 33 34 type OAuth2PasswordProxyRequest struct { 35 PartnerCode string `json:"partnerCode"` 36 Username string `json:"username"` 37 Password string `json:"password"` 38 } 39 40 type FhOAuth2Client struct { 41 hubToken string 42 syncClient *fimpgo.SyncClient 43 appName string 44 partnerName string 45 mqt *fimpgo.MqttTransport 46 mqttServerURI string 47 mqttClientID string 48 refreshTokenApiUrl string 49 authCodeApiUrl string 50 refreshRetry int 51 retryDelay time.Duration // delay in seconds 52 cbRetry int 53 cbRetryDelay time.Duration 54 } 55 56 func (oac *FhOAuth2Client) SetHubToken(hubToken string) { 57 oac.hubToken = hubToken 58 } 59 60 func (oac *FhOAuth2Client) AuthCodeApiUrl() string { 61 return oac.authCodeApiUrl 62 } 63 64 func (oac *FhOAuth2Client) SetAuthCodeApiUrl(authCodeApiUrl string) { 65 oac.authCodeApiUrl = authCodeApiUrl 66 } 67 68 func (oac *FhOAuth2Client) RefreshTokenApiUrl() string { 69 return oac.refreshTokenApiUrl 70 } 71 72 func (oac *FhOAuth2Client) SetRefreshTokenApiUrl(refreshTokenApiUrl string) { 73 oac.refreshTokenApiUrl = refreshTokenApiUrl 74 } 75 76 //NewFhOAuth2Client implements OAuth client which communicates to 3rd party API over FH Auth proxy. 77 func NewFhOAuth2Client(partnerName string, appName string, env string) *FhOAuth2Client { 78 client := &FhOAuth2Client{partnerName: partnerName, mqttServerURI: "tcp://localhost:1883", mqttClientID: "auth_client_" + appName} 79 if env == utils.EnvBeta { 80 client.refreshTokenApiUrl = "https://partners-beta.futurehome.io/api/control/edge/proxy/refresh" 81 client.authCodeApiUrl = "https://partners-beta.futurehome.io/api/control/edge/proxy/auth-code" 82 } else { 83 client.refreshTokenApiUrl = "https://partners.futurehome.io/api/control/edge/proxy/refresh" 84 client.authCodeApiUrl = "https://partners.futurehome.io/api/control/edge/proxy/auth-code" 85 } 86 client.retryDelay = 60 87 client.refreshRetry = 5 88 client.cbRetryDelay = 30 89 client.cbRetry = 7 90 client.appName = appName 91 return client 92 } 93 94 // Init has to be invoked before requesting access token 95 func (oac *FhOAuth2Client) Init() error { 96 return oac.LoadHubTokenFromCB() 97 } 98 99 // SetParameters can be used to change default configuration parameter parameters. Parameters which are set to null values will be ignored 100 func (oac *FhOAuth2Client) SetParameters(mqttServerUri, authCodeApiUrl, refreshTokenApiUrl string, retryDelay time.Duration, refreshRetry int, cbRetry int, cbRetryDelay time.Duration) { 101 if mqttServerUri != "" { 102 oac.mqttServerURI = mqttServerUri 103 } 104 if authCodeApiUrl != "" { 105 oac.authCodeApiUrl = authCodeApiUrl 106 } 107 if refreshTokenApiUrl != "" { 108 oac.refreshTokenApiUrl = refreshTokenApiUrl 109 } 110 if retryDelay != 0 { 111 oac.retryDelay = retryDelay 112 } 113 if refreshRetry != 0 { 114 oac.refreshRetry = refreshRetry 115 } 116 if cbRetry != 0 { 117 oac.cbRetry = cbRetry 118 } 119 if cbRetryDelay != 0 { 120 oac.cbRetryDelay = cbRetryDelay 121 } 122 } 123 124 // ConfigureFimpSyncClient configures fimp sync client , which is used to obtain Hub token from cloud bridge. 125 func (oac *FhOAuth2Client) ConfigureFimpSyncClient() error { 126 if oac.mqt == nil { 127 oac.mqt = fimpgo.NewMqttTransport(oac.mqttServerURI, oac.mqttClientID, "", "", true, 1, 1) 128 err := oac.mqt.Start() 129 if err != nil { 130 log.Error("Error connecting to broker ", err) 131 return err 132 } 133 log.Debug("Auth mqtt client connected") 134 oac.syncClient = fimpgo.NewSyncClient(oac.mqt) 135 } else { 136 log.Error("Mqtt client is not configured") 137 } 138 return nil 139 } 140 141 // LoadHubTokenFromCB - requests hub token from CloudBridge 142 func (oac *FhOAuth2Client) LoadHubTokenFromCB() error { 143 if oac.mqt == nil || oac.syncClient == nil { 144 if err := oac.ConfigureFimpSyncClient(); err != nil { 145 log.Error(err) 146 } 147 } 148 responseTopic := fmt.Sprintf("pt:j1/mt:rsp/rt:app/rn:%s/ad:1", oac.appName) 149 oac.syncClient.AddSubscription(responseTopic) 150 reqMsg := fimpgo.NewStringMessage("cmd.clbridge.get_auth_token", "clbridge", "", nil, nil, nil) 151 reqMsg.ResponseToTopic = responseTopic 152 var err error 153 var response *fimpgo.FimpMessage 154 for i := 0; i < oac.cbRetry; i++ { 155 response, err = oac.syncClient.SendFimp("pt:j1/mt:cmd/rt:app/rn:clbridge/ad:1", reqMsg, 5) 156 if err == nil { 157 break 158 } 159 log.Error("CB is not responding.Retrying") 160 time.Sleep(time.Second * oac.cbRetryDelay) 161 } 162 163 oac.syncClient.Stop() 164 oac.mqt.Stop() 165 if err != nil { 166 return err 167 } 168 if response.Type != "evt.clbridge.auth_token_report" { 169 return errors.New("wrong response msg type") 170 } 171 oac.hubToken, err = response.GetStringValue() 172 return err 173 } 174 175 // ExchangeCodeForTokens - exchanging code for access token 176 func (oac *FhOAuth2Client) ExchangeCodeForTokens(code string) (*OAuth2TokenResponse, error) { 177 req := OAuth2AuthCodeProxyRequest{AuthCode: code, PartnerCode: oac.partnerName} 178 return oac.postMsg(req, oac.refreshTokenApiUrl) 179 } 180 181 // ExchangeRefreshToken - exchange refresh token for new access 182 func (oac *FhOAuth2Client) ExchangeRefreshToken(refreshToken string) (*OAuth2TokenResponse, error) { 183 req := OAuth2RefreshProxyRequest{RefreshToken: refreshToken, PartnerCode: oac.partnerName} 184 return oac.postMsg(req, oac.refreshTokenApiUrl) 185 } 186 187 func (oac *FhOAuth2Client) postMsg(req interface{}, url string) (*OAuth2TokenResponse, error) { 188 if oac.hubToken == "" { 189 log.Info("Empty token.Re-requesting new token") 190 err := oac.LoadHubTokenFromCB() 191 if err != nil { 192 return nil, errors.New("empty hub token.operation aborted") 193 } 194 } 195 reqB, err := json.Marshal(req) 196 if err != nil { 197 return nil, err 198 } 199 client := &http.Client{Timeout: time.Second * 60} 200 r, _ := http.NewRequest("POST", url, bytes.NewBuffer(reqB)) 201 r.Header.Add("Content-Type", "application/json") 202 r.Header.Add("Authorization", "Bearer "+oac.hubToken) 203 //log.Info("Sending using token :",oac.hubToken 204 var resp *http.Response 205 for i := 0; i < oac.refreshRetry; i++ { 206 resp, err = client.Do(r) 207 if err == nil && resp.StatusCode < 400 { 208 break 209 } 210 log.Error("Error response from auth endpoint.Retrying...") 211 time.Sleep(time.Second * oac.retryDelay) 212 } 213 if err != nil { 214 return nil, err 215 } 216 if resp.StatusCode >= 400 { 217 return nil, fmt.Errorf("error %s response from server", resp.Status) 218 } 219 220 bData, err := ioutil.ReadAll(resp.Body) 221 if err != nil { 222 return nil, err 223 } 224 tResp := &OAuth2TokenResponse{} 225 err = json.Unmarshal(bData, tResp) 226 if err != nil { 227 return nil, err 228 } 229 return tResp, nil 230 }