grpc/grpc-go · error
unable to transfer oauthAccess PerRPCCredentials
Error message
unable to transfer oauthAccess PerRPCCredentials: %v
What it means
Returned by oauthAccess.GetRequestMetadata when CheckSecurityLevel finds the transport below PrivacyAndIntegrity. oauthAccess (the deprecated NewOauthAccess constructor) attaches a raw OAuth2 token, which gRPC will not send over an insecure channel. The %v is the security-level error.
Solutions
- Dial with credentials.NewTLS(&tls.Config{}).
- Migrate from the deprecated oauth.NewOauthAccess to oauth.TokenSource{ts} (the deprecation note in the source recommends this).
- Use a self-signed cert for local testing rather than insecure.NewCredentials().
Example fix
// before
creds := oauth.NewOauthAccess(token)
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(insecure.NewCredentials()), grpc.WithPerRPCCredentials(creds))
// after
ts := oauth2.StaticTokenSource(token)
conn, _ := grpc.Dial(addr, grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})), grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: ts})) Defensive patterns
Strategy: validation
Validate before calling
// Migrate off the deprecated NewOauthAccess and use TLS:
ts := oauth2.StaticTokenSource(token)
conn, err := grpc.Dial(addr,
grpc.WithTransportCredentials(credentials.NewTLS(&tls.Config{})),
grpc.WithPerRPCCredentials(oauth.TokenSource{TokenSource: ts}),
) Try / catch
if status.Code(err) == codes.Unauthenticated && strings.Contains(err.Error(), "oauthAccess PerRPCCredentials") {
log.Fatal("oauthAccess requires TLS; also consider migrating to oauth.TokenSource")
} Prevention
- Migrate from deprecated oauth.NewOauthAccess to oauth.TokenSource.
- Always use TLS transport with OAuth token credentials.
- Add a CI rule flagging NewOauthAccess usage.
When it happens
Trigger: Using the deprecated oauth.NewOauthAccess(token) and dialing with insecure.NewCredentials(); a bundle downgrade that drops the transport below PrivacyAndIntegrity.
Common situations: Legacy code still on NewOauthAccess (deprecated in favor of oauth.TokenSource) combined with a plaintext dev channel.
Related errors
- unable to transfer jwtAccess PerRPCCredentials
- unable to transfer serviceAccount PerRPCCredentials
- unable to transfer TokenSource PerRPCCredentials
- cannot send secure credentials on an insecure connection
- credentials: cannot send secure credentials on an insecure…
AI-assisted analysis of grpc/grpc-go@0c51461d27 (2026-08-11).
Data as JSON: /api/errors/2065fa2ed1460801.
Report an issue: GitHub.
Appendix: source
Thrown at credentials/oauth/oauth.go:133
return true
}
// oauthAccess supplies PerRPCCredentials from a given token.
type oauthAccess struct {
token oauth2.Token
}
// NewOauthAccess constructs the PerRPCCredentials using a given token.
//
// Deprecated: use oauth.TokenSource instead.
func NewOauthAccess(token *oauth2.Token) credentials.PerRPCCredentials {
return oauthAccess{token: *token}
}
func (oa oauthAccess) GetRequestMetadata(ctx context.Context, _ ...string) (map[string]string, error) {
ri, _ := credentials.RequestInfoFromContext(ctx)
if err := credentials.CheckSecurityLevel(ri.AuthInfo, credentials.PrivacyAndIntegrity); err != nil {
return nil, fmt.Errorf("unable to transfer oauthAccess PerRPCCredentials: %v", err)
}
return map[string]string{
"authorization": oa.token.Type() + " " + oa.token.AccessToken,
}, nil
}
func (oa oauthAccess) RequireTransportSecurity() bool {
return true
}
// NewComputeEngine constructs the PerRPCCredentials that fetches access tokens from
// Google Compute Engine (GCE)'s metadata server. It is only valid to use this
// if your program is running on a GCE instance.
// TODO(dsymonds): Deprecate and remove this.
func NewComputeEngine() credentials.PerRPCCredentials {
return TokenSource{google.ComputeTokenSource("")}
}
View on GitHub (pinned to 0c51461d27)