diff --git a/README.md b/README.md index 439815f3c..e28f83c29 100644 --- a/README.md +++ b/README.md @@ -1477,6 +1477,41 @@ err = accessManager.UpdateGroupInProject("tstprj", "tstgroup", projectGroup) err = accessManager.DeleteExistingProjectGroup("tstprj", "tstgroup") ``` +#### Create an access token for the JFrog Platform + +```go +import "github.com/jfrog/jfrog-client-go/access/services" + +False := false // required to be passed by reference below +True := true // required to be passed by reference below +createParams := services.CreateTokenParams{ + CommonTokenParams: auth.CommonTokenParams{ + Scope: "applied-permissions/groups:grp", + ExpiresIn: 3600, + Refreshable: &True, + Audience: "jfrt@*", + }, + Description: "my best token", + IncludeReferenceToken: &False, + Username: "username", +} + +accessToken, err := accessManager.CreateAccessToken(createParams) +``` + +#### Refresh an existing access token + +```go +import "github.com/jfrog/jfrog-client-go/access/services" + +refreshParams := services.CreateTokenParams{ + CommonTokenParams: auth.CommonTokenParams{ + RefreshToken: accessToken.RefreshToken, + }, +} +refreshedToken, err := accessManager.RefreshAccessToken(refreshParams) +``` + ## Distribution APIs ### Creating Distribution Service Manager diff --git a/access/services/accesstoken.go b/access/services/accesstoken.go index 82678d8f1..c8a85e443 100644 --- a/access/services/accesstoken.go +++ b/access/services/accesstoken.go @@ -3,12 +3,13 @@ package services import ( "encoding/json" "fmt" + "net/http" + "github.com/jfrog/jfrog-client-go/artifactory/services/utils" "github.com/jfrog/jfrog-client-go/auth" "github.com/jfrog/jfrog-client-go/http/jfroghttpclient" "github.com/jfrog/jfrog-client-go/utils/errorutils" "github.com/jfrog/jfrog-client-go/utils/io/httputils" - "net/http" ) // #nosec G101 -- False positive - no hardcoded credentials. @@ -21,39 +22,59 @@ type TokenService struct { type CreateTokenParams struct { auth.CommonTokenParams - IncludeReferenceToken *bool `json:"include_reference_token,omitempty"` + Description string `json:"description,omitempty"` + IncludeReferenceToken *bool `json:"include_reference_token,omitempty"` + Username string `json:"username,omitempty"` } func NewCreateTokenParams(params CreateTokenParams) CreateTokenParams { - return CreateTokenParams{CommonTokenParams: params.CommonTokenParams, IncludeReferenceToken: params.IncludeReferenceToken} + return CreateTokenParams{ + CommonTokenParams: params.CommonTokenParams, + Description: params.Description, + IncludeReferenceToken: params.IncludeReferenceToken, + Username: params.Username, + } } func NewTokenService(client *jfroghttpclient.JfrogHttpClient) *TokenService { return &TokenService{client: client} } +// Create an access token for the JFrog Platform func (ps *TokenService) CreateAccessToken(params CreateTokenParams) (auth.CreateTokenResponseData, error) { return ps.createAccessToken(params) } +// Refresh an existing access token without having to provide the old token. +// The Refresh Token is the same API endpoint as Create Token, with a specific grant type: refresh_token func (ps *TokenService) RefreshAccessToken(token CreateTokenParams) (auth.CreateTokenResponseData, error) { - param, err := createRefreshTokenRequestParams(token) - if err != nil { - return auth.CreateTokenResponseData{}, err + // Validate provided parameters + if token.RefreshToken == "" { + return auth.CreateTokenResponseData{}, errorutils.CheckErrorf("error: trying to refresh token, but 'refresh_token' field wasn't provided. ") } - return ps.createAccessToken(*param) + // Set refresh required parameters + var trueValue = true + params := NewCreateTokenParams(token) + params.GrantType = "refresh_token" + params.Refreshable = &trueValue + + return ps.createAccessToken(params) } // createAccessToken is used to create & refresh access tokens. func (ps *TokenService) createAccessToken(params CreateTokenParams) (auth.CreateTokenResponseData, error) { - // Set the request headers + // Create output response variable tokenInfo := auth.CreateTokenResponseData{} + + // Set the request headers httpDetails := ps.ServiceDetails.CreateHttpClientDetails() utils.SetContentType("application/json", &httpDetails.Headers) err := ps.addAccessTokenAuthorizationHeader(params, &httpDetails) if err != nil { return tokenInfo, err } + + // Marshall the request body requestContent, err := json.Marshal(params) if errorutils.CheckError(err) != nil { return tokenInfo, err @@ -66,10 +87,14 @@ func (ps *TokenService) createAccessToken(params CreateTokenParams) (auth.Create if err = errorutils.CheckResponseStatusWithBody(resp, body, http.StatusOK); err != nil { return tokenInfo, err } + + // Unmarshall the response body and return err = json.Unmarshal(body, &tokenInfo) return tokenInfo, errorutils.CheckError(err) } +// Use AccessToken from ServiceDetails (which is the default behaviour) +// If that is not present then we can use the token we are refreshing as the token func (ps *TokenService) addAccessTokenAuthorizationHeader(params CreateTokenParams, httpDetails *httputils.HttpClientDetails) error { access := ps.ServiceDetails.GetAccessToken() if access == "" { @@ -81,16 +106,3 @@ func (ps *TokenService) addAccessTokenAuthorizationHeader(params CreateTokenPara utils.AddHeader("Authorization", fmt.Sprintf("Bearer %s", access), &httpDetails.Headers) return nil } - -func createRefreshTokenRequestParams(p CreateTokenParams) (*CreateTokenParams, error) { - var trueValue = true - // Validate provided parameters - if p.RefreshToken == "" { - return nil, errorutils.CheckErrorf("error: trying to refresh token, but 'refresh_token' field wasn't provided. ") - } - params := NewCreateTokenParams(p) - // Set refresh required parameters - params.GrantType = "refresh_token" - params.Refreshable = &trueValue - return ¶ms, nil -}