Skip to content

Commit fa5685c

Browse files
committed
internal/iapclient, cmd/gomote: handle authentication failures
When a gomote client attempts oauth authentication via GCP Identity-Aware Proxy and fails it will log an authentication error. It will also ask the user to issue the gomote login command. This should clarify any confusion around when a user is expected to re-authenticate and how to do so. Fixes golang/go#68612 Change-Id: I1afe501829f0bba4d3bf7c369115520243bc892f Reviewed-on: https://go-review.googlesource.com/c/build/+/615435 Reviewed-by: Dmitri Shuralyov <[email protected]> LUCI-TryBot-Result: Go LUCI <[email protected]> Reviewed-by: Dmitri Shuralyov <[email protected]>
1 parent 156c2a7 commit fa5685c

File tree

2 files changed

+22
-2
lines changed

2 files changed

+22
-2
lines changed

cmd/gomote/gomote.go

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -292,6 +292,10 @@ func main() {
292292
func gomoteServerClient(ctx context.Context) protos.GomoteServiceClient {
293293
grpcClient, err := iapclient.GRPCClient(ctx, *serverAddr)
294294
if err != nil {
295+
var authErr iapclient.AuthenticationError
296+
if errors.As(err, &authErr) {
297+
logAndExitf("Authentication error: %s\n\tLogin via: gomote login\n", err)
298+
}
295299
logAndExitf("dialing the server=%s failed with: %s\n", *serverAddr, err)
296300
}
297301
return protos.NewGomoteServiceClient(grpcClient)

internal/iapclient/iapclient.go

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,7 +177,6 @@ func TokenSource(ctx context.Context) (oauth2.TokenSource, error) {
177177
return idtoken.NewTokenSource(ctx, audience)
178178
}
179179
}
180-
181180
refresh, err := cachedToken()
182181
if err != nil {
183182
return nil, err
@@ -243,7 +242,15 @@ func (s *jwtTokenSource) Token() (*oauth2.Token, error) {
243242
defer resp.Body.Close()
244243
if resp.StatusCode != http.StatusOK {
245244
body, _ := io.ReadAll(io.LimitReader(resp.Body, 4<<10))
246-
return nil, fmt.Errorf("IAP token exchange failed: status %v, body %q", resp.Status, body)
245+
tokenRespErr := struct {
246+
Err string `json:"error"`
247+
Description string `json:"error_description"`
248+
}{}
249+
err := fmt.Errorf("IAP token exchange failed: status %v, body %q", resp.Status, body)
250+
if unmarshErr := json.Unmarshal(body, &tokenRespErr); unmarshErr == nil && tokenRespErr.Err == "invalid_grant" {
251+
return nil, AuthenticationError{Err: err, Description: tokenRespErr.Description}
252+
}
253+
return nil, err
247254
}
248255
body, err := io.ReadAll(resp.Body)
249256
if err != nil {
@@ -262,3 +269,12 @@ func (s *jwtTokenSource) Token() (*oauth2.Token, error) {
262269
type jwtTokenJSON struct {
263270
IDToken string `json:"id_token"`
264271
}
272+
273+
// AuthenticationError records an authentication error.
274+
type AuthenticationError struct {
275+
Description string
276+
Err error
277+
}
278+
279+
func (ar AuthenticationError) Error() string { return ar.Description }
280+
func (ar AuthenticationError) Unwrap() error { return ar.Err }

0 commit comments

Comments
 (0)