github.com/mundipagg/boleto-api@v0.0.0-20230620145841-3f9ec742599f/stone/authentication.go (about)

     1  package stone
     2  
     3  import (
     4  	"encoding/json"
     5  	"strings"
     6  	"sync"
     7  
     8  	"github.com/mundipagg/boleto-api/config"
     9  	"github.com/mundipagg/boleto-api/db"
    10  	"github.com/mundipagg/boleto-api/log"
    11  	"github.com/mundipagg/boleto-api/models"
    12  	"github.com/mundipagg/boleto-api/util"
    13  )
    14  
    15  var (
    16  	HttpClient         = &util.HTTPClient{}
    17  	mu                 sync.Mutex
    18  	AccessTokenPayload = map[string]string{
    19  		"client_id":             "",
    20  		"grant_type":            "client_credentials",
    21  		"client_assertion_type": "urn:ietf:params:oauth:client-assertion-type:jwt-bearer",
    22  		"client_assertion":      "",
    23  	}
    24  )
    25  
    26  const (
    27  	issuerBank              = "stone"
    28  	BadRequestError         = "status code 400"
    29  	DocumentNotFound        = "mongo: no documents in result"
    30  	recoverTokenFailMessage = "token query into mongo has failed"
    31  	saveTokenFailMessage    = "token save into mongo has failed"
    32  )
    33  
    34  type AuthResponse struct {
    35  	AccessToken           string `json:"access_token"`
    36  	AccessTokenExpiresAt  int    `json:"expires_in"`
    37  	RefreshToken          string `json:"refresh_token"`
    38  	RefreshTokenCreatedAt int    `json:"refresh_expires_in"`
    39  	TokenType             string `json:"token_type"`
    40  	NotBeforePolicy       int    `json:"not-before-policy"`
    41  	SessionState          string `json:"session_state"`
    42  	Scope                 string `json:"scope"`
    43  }
    44  
    45  func authenticate(clientID string, log *log.Log) (string, error) {
    46  	tk, err := fetchTokenFromStorage(clientID)
    47  	if err != nil {
    48  		log.Error(err, recoverTokenFailMessage)
    49  	}
    50  
    51  	if tk != "" {
    52  		log.InfoWithParams("Token recovered from mongo", "Information", map[string]interface{}{"Content": tk})
    53  		return tk, nil
    54  	}
    55  
    56  	return authenticateAndSaveToken(clientID, log)
    57  }
    58  
    59  func authenticateAndSaveToken(clientID string, log *log.Log) (string, error) {
    60  	mu.Lock()
    61  	defer mu.Unlock()
    62  
    63  	tk, err := AuthenticationWithRetryOnBadRequest(log)
    64  	if err != nil {
    65  		return "", err
    66  	}
    67  
    68  	token := models.NewToken(clientID, issuerBank, tk)
    69  	if err := db.SaveToken(token); err != nil {
    70  		log.Error(err, saveTokenFailMessage)
    71  	}
    72  
    73  	return tk, nil
    74  }
    75  
    76  func fetchTokenFromStorage(clientID string) (string, error) {
    77  	token, err := db.GetTokenByClientIDAndIssuerBank(clientID, issuerBank)
    78  	if err != nil {
    79  		if err.Error() == DocumentNotFound {
    80  			return "", nil
    81  		}
    82  		return "", err
    83  	}
    84  
    85  	return token.AccessToken, nil
    86  }
    87  
    88  // AuthenticationWithRetryOnBadRequest encapsulates logic for retry access token request once again
    89  // in bad request status code. That's because duplicated jti returns this mencioned status code
    90  func AuthenticationWithRetryOnBadRequest(log *log.Log) (string, error) {
    91  	var tk string
    92  	var err error
    93  
    94  	if tk, err = doAuthentication(log); err != nil {
    95  		if !strings.Contains(err.Error(), BadRequestError) {
    96  			return "", err
    97  		}
    98  		return doAuthentication(log)
    99  	}
   100  
   101  	return tk, nil
   102  }
   103  
   104  func doAuthentication(log *log.Log) (string, error) {
   105  	jwt, err := generateJWT()
   106  	if err != nil {
   107  		return "", err
   108  	}
   109  
   110  	AccessTokenPayload["client_assertion"] = jwt
   111  	AccessTokenPayload["client_id"] = config.Get().StoneClientID
   112  
   113  	resp, err := HttpClient.PostFormURLEncoded(config.Get().URLStoneToken, AccessTokenPayload, log)
   114  
   115  	if err != nil {
   116  		return "", err
   117  	}
   118  
   119  	var r AuthResponse
   120  	err = json.Unmarshal(resp, &r)
   121  	if err != nil {
   122  		return "", err
   123  	}
   124  
   125  	return r.AccessToken, nil
   126  }