github.com/IBM-Cloud/bluemix-go@v0.0.0-20240314082800-4e02a69b84b2/authentication/iam.go (about)

     1  package authentication
     2  
     3  import (
     4  	"encoding/base64"
     5  	"fmt"
     6  
     7  	bluemix "github.com/IBM-Cloud/bluemix-go"
     8  	"github.com/IBM-Cloud/bluemix-go/bmxerror"
     9  	"github.com/IBM-Cloud/bluemix-go/rest"
    10  )
    11  
    12  //IAMError ...
    13  type IAMError struct {
    14  	ErrorCode    string `json:"errorCode"`
    15  	ErrorMessage string `json:"errorMessage"`
    16  	ErrorDetails string `json:"errorDetails"`
    17  }
    18  
    19  //Description ...
    20  func (e IAMError) Description() string {
    21  	if e.ErrorDetails != "" {
    22  		return e.ErrorDetails
    23  	}
    24  	return e.ErrorMessage
    25  }
    26  
    27  //IAMTokenResponse ...
    28  type IAMTokenResponse struct {
    29  	AccessToken     string `json:"access_token"`
    30  	RefreshToken    string `json:"refresh_token"`
    31  	UAAAccessToken  string `json:"uaa_token"`
    32  	UAARefreshToken string `json:"uaa_refresh_token"`
    33  	TokenType       string `json:"token_type"`
    34  }
    35  
    36  //IAMAuthRepository ...
    37  type IAMAuthRepository struct {
    38  	config   *bluemix.Config
    39  	client   *rest.Client
    40  	endpoint string
    41  }
    42  
    43  //NewIAMAuthRepository ...
    44  func NewIAMAuthRepository(config *bluemix.Config, client *rest.Client) (*IAMAuthRepository, error) {
    45  	var endpoint string
    46  
    47  	if config.TokenProviderEndpoint != nil {
    48  		endpoint = *config.TokenProviderEndpoint
    49  	} else {
    50  		var err error
    51  		endpoint, err = config.EndpointLocator.IAMEndpoint()
    52  		if err != nil {
    53  			return nil, err
    54  		}
    55  	}
    56  
    57  	return &IAMAuthRepository{
    58  		config:   config,
    59  		client:   client,
    60  		endpoint: endpoint,
    61  	}, nil
    62  }
    63  
    64  //AuthenticatePassword ...
    65  func (auth *IAMAuthRepository) AuthenticatePassword(username string, password string) error {
    66  	return auth.getToken(map[string]string{
    67  		"grant_type": "password",
    68  		"username":   username,
    69  		"password":   password,
    70  	})
    71  }
    72  
    73  //AuthenticateAPIKey ...
    74  func (auth *IAMAuthRepository) AuthenticateAPIKey(apiKey string) error {
    75  	return auth.getToken(map[string]string{
    76  		"grant_type": "urn:ibm:params:oauth:grant-type:apikey",
    77  		"apikey":     apiKey,
    78  	})
    79  }
    80  
    81  //AuthenticateSSO ...
    82  func (auth *IAMAuthRepository) AuthenticateSSO(passcode string) error {
    83  	return auth.getToken(map[string]string{
    84  		"grant_type": "urn:ibm:params:oauth:grant-type:passcode",
    85  		"passcode":   passcode,
    86  	})
    87  }
    88  
    89  //RefreshToken ...
    90  func (auth *IAMAuthRepository) RefreshToken() (string, error) {
    91  	data := map[string]string{
    92  		"grant_type":    "refresh_token",
    93  		"refresh_token": auth.config.IAMRefreshToken,
    94  	}
    95  
    96  	err := auth.getToken(data)
    97  	if err != nil {
    98  		return "", err
    99  	}
   100  
   101  	return auth.config.IAMAccessToken, nil
   102  }
   103  
   104  //GetPasscode ...
   105  func (auth *IAMAuthRepository) GetPasscode() (string, error) {
   106  	request := rest.PostRequest(auth.endpoint+"/identity/passcode").
   107  		Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("bx:bx"))).
   108  		Field("grant_type", "refresh_token").
   109  		Field("refresh_token", auth.config.IAMRefreshToken).
   110  		Field("response_type", "cloud_iam")
   111  
   112  	res := make(map[string]string, 0)
   113  	var apiErr IAMError
   114  
   115  	resp, err := auth.client.Do(request, &res, &apiErr)
   116  	if err != nil {
   117  		return "", err
   118  	}
   119  
   120  	if apiErr.ErrorCode != "" {
   121  		if apiErr.ErrorCode == "BXNIM0407E" {
   122  			return "", bmxerror.New(ErrCodeInvalidToken, apiErr.Description())
   123  		}
   124  		return "", bmxerror.NewRequestFailure(apiErr.ErrorCode, apiErr.Description(), resp.StatusCode)
   125  	}
   126  
   127  	return res["passcode"], nil
   128  }
   129  
   130  func (auth *IAMAuthRepository) getToken(data map[string]string) error {
   131  	request := rest.PostRequest(auth.endpoint+"/identity/token").
   132  		Set("Authorization", "Basic "+base64.StdEncoding.EncodeToString([]byte("bx:bx"))).
   133  		Field("response_type", "cloud_iam")
   134  
   135  	for k, v := range data {
   136  		request.Field(k, v)
   137  	}
   138  
   139  	var tokens IAMTokenResponse
   140  	var apiErr IAMError
   141  
   142  	resp, err := auth.client.Do(request, &tokens, &apiErr)
   143  	if err != nil {
   144  		return err
   145  	}
   146  
   147  	if apiErr.ErrorCode != "" {
   148  		if apiErr.ErrorCode == "BXNIM0407E" {
   149  			if resp != nil && resp.Header != nil {
   150  				return bmxerror.New(ErrCodeInvalidToken, fmt.Sprintf("Transaction-Id:%s %s", resp.Header["Transaction-Id"], apiErr.Description()))
   151  			}
   152  			return bmxerror.New(ErrCodeInvalidToken, apiErr.Description())
   153  		}
   154  		if resp != nil && resp.Header != nil {
   155  			return bmxerror.NewRequestFailure(apiErr.ErrorCode, fmt.Sprintf("Transaction-Id:%s %s", resp.Header["Transaction-Id"], apiErr.Description()), resp.StatusCode)
   156  		}
   157  		return bmxerror.NewRequestFailure(apiErr.ErrorCode, apiErr.Description(), resp.StatusCode)
   158  	}
   159  
   160  	auth.config.IAMAccessToken = fmt.Sprintf("%s %s", tokens.TokenType, tokens.AccessToken)
   161  	auth.config.IAMRefreshToken = tokens.RefreshToken
   162  
   163  	return nil
   164  }