Skip to content
This repository was archived by the owner on May 25, 2023. It is now read-only.

Commit af836e1

Browse files
author
Sam McGeown
committed
Tweak authentication process for vRA 8.4 (#1)
1 parent 207fa28 commit af836e1

2 files changed

Lines changed: 80 additions & 33 deletions

File tree

cmd/api-func-shared.go

Lines changed: 62 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,52 +18,85 @@ import (
1818
)
1919

2020
func ensureTargetConnection() error {
21-
// If the targetConfig.accesstoken is not set or testAccesToken returns false
22-
if !testAccessToken() {
23-
var authError error
24-
// Authenticate
25-
if targetConfig.apitoken != "" {
26-
targetConfig.accesstoken, authError = authenticateCloud(targetConfig)
27-
} else {
28-
targetConfig.accesstoken, authError = authenticateOnPrem(targetConfig)
29-
}
30-
if authError != nil {
31-
return authError
21+
if testAccessToken() { // If the Access Token is OK
22+
log.Debugln("Access Token is valid")
23+
} else {
24+
var refreshTokenError, credentialError error
25+
targetConfig.accesstoken, refreshTokenError = authenticateApiToken(targetConfig.server, targetConfig.apitoken) // Test the API Token (refresh_token)
26+
if refreshTokenError != nil { // We could not get an access token from the API Token
27+
log.Debugln("Refresh Token is invalid")
28+
if targetConfig.server == "api.mgmt.cloud.vmware.com" { // If it's vRA Cloud we have no credentials to authenticate
29+
return refreshTokenError // Return the token error
30+
}
31+
targetConfig.apitoken, credentialError = authenticateCredentials(targetConfig.server, targetConfig.username, targetConfig.password, targetConfig.domain)
32+
if credentialError != nil {
33+
return credentialError // Return the credential error
34+
}
35+
// Try again, now we have a new access token
36+
targetConfig.accesstoken, refreshTokenError = authenticateApiToken(targetConfig.server, targetConfig.apitoken) // Test the API Token (refresh_token)
37+
if refreshTokenError != nil {
38+
return refreshTokenError
39+
}
3240
}
33-
if viper.ConfigFileUsed() != "" {
41+
42+
if viper.ConfigFileUsed() != "" { // If we're using a Config file
3443
viper.Set("target."+currentTargetName+".accesstoken", targetConfig.accesstoken)
44+
viper.Set("target."+currentTargetName+".apitoken", targetConfig.apitoken)
3545
viper.WriteConfig()
3646
}
47+
3748
}
3849
return nil
3950
}
4051

41-
func authenticateOnPrem(target config) (string, error) {
42-
log.Debugln("Authenticating vRA")
52+
// authenticateCredentials - returns the API Refresh Token for vRA On-premesis (8.0.1+)
53+
func authenticateCredentials(server string, username string, password string, domain string) (string, error) {
54+
log.Debugln("Authenticating vRA with Credentials")
55+
var authPath string
56+
var authBody AuthenticationRequest
57+
authBody.Username = username
58+
authBody.Password = password
4359
client := resty.New()
44-
queryResponse, err := client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: ignoreCert}).R().
45-
SetBody(AuthenticationRequest{target.username, target.password, target.domain}).
60+
61+
if domain == "" {
62+
log.Debugln("Basic Auth")
63+
// Use Basic Authentication
64+
authPath = "/csp/gateway/am/api/login?access_token"
65+
} else {
66+
log.Debugln("Enhanced Auth")
67+
// Use Enhanced Login (e.g. domain users)
68+
authPath = "/csp/gateway/am/idp/auth/login?access_token"
69+
authBody.Domain = domain
70+
}
71+
72+
loginResponse, err := client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: ignoreCert}).R().
73+
SetBody(authBody).
4674
SetResult(&AuthenticationResponse{}).
4775
SetError(&AuthenticationError{}).
48-
Post("https://" + target.server + "/csp/gateway/am/idp/auth/login?access_token")
49-
if queryResponse.IsError() {
50-
return "", errors.New(queryResponse.Error().(*AuthenticationError).ServerMessage)
76+
Post("https://" + server + authPath)
77+
if loginResponse.IsError() {
78+
log.Debugln("Authentication failed")
79+
return "", errors.New(loginResponse.Error().(*AuthenticationError).ServerMessage)
5180
}
52-
return queryResponse.Result().(*AuthenticationResponse).AccessToken, err
81+
log.Debugln("Authentication succeeded")
82+
return loginResponse.Result().(*AuthenticationResponse).RefreshToken, err
5383
}
54-
func authenticateCloud(target config) (string, error) {
55-
log.Debugln("Authenticating vRA Cloud")
84+
85+
// authenticateApiToken - get vRA Access token (valid for 8h)
86+
func authenticateApiToken(server string, token string) (string, error) {
87+
log.Debug("Attempting to authenticate the API Refresh Token")
5688
client := resty.New()
5789
queryResponse, err := client.SetTLSClientConfig(&tls.Config{InsecureSkipVerify: ignoreCert}).R().
58-
SetBody(AuthenticationRequestCloud{target.apitoken}).
59-
SetResult(&AuthenticationResponseCloud{}).
60-
SetError(&AuthenticationError{}).
61-
Post("https://" + target.server + "/iaas/api/login")
90+
SetBody(ApiAuthentication{token}).
91+
SetResult(&ApiAuthenticationResponse{}).
92+
SetError(&ApiAuthenticationError{}).
93+
Post("https://" + server + "/iaas/api/login")
6294
if queryResponse.IsError() {
63-
log.Debugln("Authentication failed!", queryResponse.RawResponse)
64-
return "", errors.New(queryResponse.Error().(*AuthenticationError).ServerMessage)
95+
log.Debug("Refresh Token failed")
96+
return "", errors.New(queryResponse.Error().(*ApiAuthenticationError).Message)
6597
}
66-
return queryResponse.Result().(*AuthenticationResponseCloud).Token, err
98+
log.Debug("Refresh Token succeeded")
99+
return queryResponse.Result().(*ApiAuthenticationResponse).Token, err
67100
}
68101

69102
func testAccessToken() bool {

cmd/api-struct.go

Lines changed: 18 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@ type AuthenticationRequest struct {
2020
Domain string `json:"domain"`
2121
}
2222

23+
// TokenRequest - vRA Authentication request structure
24+
type TokenRequest struct {
25+
RefreshToken string `json:"refresh_token"`
26+
}
27+
2328
// AuthenticationResponse - Authentication response structure
2429
type AuthenticationResponse struct {
2530
Scope string `json:"scope"`
@@ -30,17 +35,26 @@ type AuthenticationResponse struct {
3035
ExpiresIn int `json:"expires_in"`
3136
}
3237

33-
// AuthenticationRequestCloud - vRA Authentication request structure for Cloud
34-
type AuthenticationRequestCloud struct {
38+
// ApiAuthentication - vRA Authentication request structure for API login with a refresh token
39+
type ApiAuthentication struct {
3540
RefreshToken string `json:"refreshToken"`
3641
}
3742

38-
// AuthenticationResponseCloud - Authentication response structure for Cloud
39-
type AuthenticationResponseCloud struct {
43+
// ApiAuthenticationResponse - Authentication response structure for API login with a refresh token
44+
type ApiAuthenticationResponse struct {
4045
TokenType string `json:"tokenType"`
4146
Token string `json:"token"`
4247
}
4348

49+
// ApiAuthenticationError - API Authentication error structure
50+
type ApiAuthenticationError struct {
51+
Message string `json:"message"`
52+
StatusCode int64 `json:"statusCode"`
53+
ErrorCode int64 `json:"errorCode"`
54+
ServerErrorId string `json:"serverErrorId"`
55+
DocumentKind string `json:"documentKind"`
56+
}
57+
4458
// AuthenticationError - Authentication error structure
4559
type AuthenticationError struct {
4660
Timestamp int64 `json:"timestamp"`

0 commit comments

Comments
 (0)