update expired access token automatically (using refreshtoken). if updatedSource is returned from client, it must be stored in the DB.

This commit is contained in:
Jason Kulatunga 2022-09-08 23:51:46 -07:00
parent 187d72b085
commit 68eb18e64b
7 changed files with 87 additions and 26 deletions

View File

@ -11,18 +11,19 @@ import (
"net/http"
)
func NewClient(providerId string, appConfig config.Interface, globalLogger logrus.FieldLogger, credentials models.Source, testHttpClient ...*http.Client) (base.Client, error) {
func NewClient(providerId string, appConfig config.Interface, globalLogger logrus.FieldLogger, credentials models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
var providerClient base.Client
var updatedSource *models.Source
var err error
switch providerId {
case "anthem":
providerClient, err = cigna.NewClient(appConfig, globalLogger, credentials, testHttpClient...)
providerClient, updatedSource, err = cigna.NewClient(appConfig, globalLogger, credentials, testHttpClient...)
case "cigna":
providerClient, err = cigna.NewClient(appConfig, globalLogger, credentials, testHttpClient...)
providerClient, updatedSource, err = cigna.NewClient(appConfig, globalLogger, credentials, testHttpClient...)
default:
return nil, errors.New(fmt.Sprintf("Unknown Provider Type: %s", providerId))
return nil, updatedSource, errors.New(fmt.Sprintf("Unknown Provider Type: %s", providerId))
}
return providerClient, err
return providerClient, updatedSource, err
}

View File

@ -21,15 +21,55 @@ type BaseClient struct {
Source models.Source
}
func NewBaseClient(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) *BaseClient {
func NewBaseClient(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*BaseClient, *models.Source, error) {
var httpClient *http.Client
var updatedSource *models.Source
if len(testHttpClient) == 0 {
httpClient = oauth2.NewClient(context.Background(), oauth2.StaticTokenSource(
&oauth2.Token{
TokenType: "Bearer",
RefreshToken: source.RefreshToken,
AccessToken: source.AccessToken,
}))
//check if we need to refresh the access token
//https://github.com/golang/oauth2/issues/84#issuecomment-520099526
// https://chromium.googlesource.com/external/github.com/golang/oauth2/+/8f816d62a2652f705144857bbbcc26f2c166af9e/oauth2.go#239
ctx := context.Background()
conf := &oauth2.Config{
ClientID: source.ClientId,
ClientSecret: "",
Endpoint: oauth2.Endpoint{
AuthURL: fmt.Sprintf("%s/authorize", source.OauthEndpointBaseUrl),
TokenURL: fmt.Sprintf("%s/token", source.OauthEndpointBaseUrl),
},
//RedirectURL: "",
//Scopes: nil,
}
token := &oauth2.Token{
TokenType: "Bearer",
RefreshToken: source.RefreshToken,
AccessToken: source.AccessToken,
Expiry: time.Unix(source.ExpiresAt, 0),
}
if token.Expiry.Before(time.Now()) { // expired so let's update it
src := conf.TokenSource(ctx, token)
newToken, err := src.Token() // this actually goes and renews the tokens
if err != nil {
return nil, nil, err
}
if newToken.AccessToken != token.AccessToken {
token = newToken
// update the "source" credential with new data (which will need to be sent
updatedSource = &source
updatedSource.AccessToken = newToken.AccessToken
updatedSource.ExpiresAt = newToken.Expiry.Unix()
// Don't overwrite `RefreshToken` with an empty value
// if this was a token refreshing request.
if newToken.RefreshToken != "" {
updatedSource.RefreshToken = newToken.RefreshToken
}
}
}
// OLD CODE
httpClient = oauth2.NewClient(ctx, oauth2.StaticTokenSource(token))
} else {
//Testing mode.
httpClient = testHttpClient[0]
@ -41,7 +81,7 @@ func NewBaseClient(appConfig config.Interface, globalLogger logrus.FieldLogger,
Logger: globalLogger,
OauthClient: httpClient,
Source: source,
}
}, updatedSource, nil
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -16,10 +16,11 @@ type FHIR401Client struct {
*BaseClient
}
func NewFHIR401Client(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*FHIR401Client, error) {
func NewFHIR401Client(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*FHIR401Client, *models.Source, error) {
baseClient, updatedSource, err := NewBaseClient(appConfig, globalLogger, source, testHttpClient...)
return &FHIR401Client{
NewBaseClient(appConfig, globalLogger, source, testHttpClient...),
}, nil
baseClient,
}, updatedSource, err
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -35,7 +35,7 @@ func TestNewFHIR401Client(t *testing.T) {
})
//test
client, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
client, _, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
RefreshToken: "test-refresh-token",
AccessToken: "test-access-token",
})
@ -55,7 +55,7 @@ func TestFHIR401Client_ProcessBundle(t *testing.T) {
testLogger := logrus.WithFields(logrus.Fields{
"type": "test",
})
client, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
client, _, err := NewFHIR401Client(fakeConfig, testLogger, models.Source{
RefreshToken: "test-refresh-token",
AccessToken: "test-access-token",
})

View File

@ -13,10 +13,11 @@ type FHIR430Client struct {
*BaseClient
}
func NewFHIR430Client(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*FHIR430Client, error) {
func NewFHIR430Client(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (*FHIR430Client, *models.Source, error) {
baseClient, updatedSource, err := NewBaseClient(appConfig, globalLogger, source, testHttpClient...)
return &FHIR430Client{
NewBaseClient(appConfig, globalLogger, source, testHttpClient...),
}, nil
baseClient,
}, updatedSource, err
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

View File

@ -14,11 +14,11 @@ type CignaClient struct {
*base.FHIR401Client
}
func NewClient(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, error) {
baseClient, err := base.NewFHIR401Client(appConfig, globalLogger, source, testHttpClient...)
func NewClient(appConfig config.Interface, globalLogger logrus.FieldLogger, source models.Source, testHttpClient ...*http.Client) (base.Client, *models.Source, error) {
baseClient, updatedSource, err := base.NewFHIR401Client(appConfig, globalLogger, source, testHttpClient...)
return CignaClient{
baseClient,
}, err
}, updatedSource, err
}
func (c CignaClient) SyncAll(db database.DatabaseRepository) error {

View File

@ -30,12 +30,21 @@ func CreateSource(c *gin.Context) {
}
// after creating the source, we should do a bulk import
sourceClient, err := hub.NewClient(providerCred.ProviderId, nil, logger, providerCred)
sourceClient, updatedSource, err := hub.NewClient(providerCred.ProviderId, nil, logger, providerCred)
if err != nil {
logger.Errorln("An error occurred while initializing hub client using source credential", err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
return
}
if updatedSource != nil {
err := databaseRepo.CreateSource(c, updatedSource)
if err != nil {
logger.Errorln("An error occurred while updating provider credential", err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
return
}
}
err = sourceClient.SyncAll(databaseRepo)
if err != nil {
logger.Errorln("An error occurred while bulk import of resources from source", err)
@ -83,12 +92,21 @@ func RawRequestSource(c *gin.Context) {
return
}
client, err := hub.NewClient(c.Param("sourceType"), nil, logger, *foundSource)
client, updatedSource, err := hub.NewClient(c.Param("sourceType"), nil, logger, *foundSource)
if err != nil {
logger.Errorf("Could not initialize source client", err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
return
}
if updatedSource != nil {
err := databaseRepo.CreateSource(c, updatedSource)
if err != nil {
logger.Errorln("An error occurred while updating provider credential", err)
c.JSON(http.StatusInternalServerError, gin.H{"success": false})
return
}
}
var resp map[string]interface{}
err = client.GetRequest(c.Param("path"), &resp)
if err != nil {