@@ -31,6 +31,7 @@ import (
3131 "io"
3232 "net/http"
3333 "net/url"
34+ "os"
3435 "regexp"
3536 "strconv"
3637 "strings"
@@ -266,10 +267,13 @@ func (c *connectionImpl) GetTablesForDBSchema(ctx context.Context, catalog strin
266267}
267268
268269type bigQueryTokenResponse struct {
269- AccessToken string `json:"access_token"`
270- ExpiresIn int `json:"expires_in"`
271- Scope string `json:"scope"`
272- TokenType string `json:"token_type"`
270+ AccessToken string `json:"access_token"`
271+ ExpiresIn int `json:"expires_in"`
272+ Scope string `json:"scope"`
273+ TokenType string `json:"token_type"`
274+ Error string `json:"error"`
275+ ErrorDescription string `json:"error_description"`
276+ ErrorURI string `json:"error_uri"`
273277}
274278
275279// GetCurrentCatalog implements driverbase.CurrentNamespacer.
@@ -712,9 +716,36 @@ func (c *connectionImpl) newClient(ctx context.Context) error {
712716 // First, establish base authentication
713717 switch c .authType {
714718 case OptionValueAuthTypeJSONCredentialFile :
715- authOptions = append (authOptions , option .WithAuthCredentialsFile (c .credentialsType , c .credentials ))
719+ credType := c .credentialsType
720+ if credType == "" {
721+ // Auto-detect credential type from the JSON file
722+ detected , err := detectCredentialTypeFromFile (c .credentials )
723+ if err != nil {
724+ return adbc.Error {
725+ Code : adbc .StatusInvalidArgument ,
726+ Msg : fmt .Sprintf ("[bq] failed to detect credential type from file %q: %s" , c .credentials , err .Error ()),
727+ }
728+ }
729+ credType = detected
730+ }
731+ c .credentialsType = credType
732+ c .Logger .Debug ("Using JSON credential file" , "file" , c .credentials , "credentialType" , string (credType ))
733+ authOptions = append (authOptions , option .WithAuthCredentialsFile (credType , c .credentials ))
716734 case OptionValueAuthTypeJSONCredentialString :
717- authOptions = append (authOptions , option .WithAuthCredentialsJSON (c .credentialsType , []byte (c .credentials )))
735+ credType := c .credentialsType
736+ if credType == "" {
737+ detected , err := detectCredentialTypeFromJSON ([]byte (c .credentials ))
738+ if err != nil {
739+ return adbc.Error {
740+ Code : adbc .StatusInvalidArgument ,
741+ Msg : fmt .Sprintf ("[bq] failed to detect credential type from JSON: %s" , err .Error ()),
742+ }
743+ }
744+ credType = detected
745+ }
746+ c .credentialsType = credType
747+ c .Logger .Debug ("Using JSON credential string" , "credentialType" , string (credType ))
748+ authOptions = append (authOptions , option .WithAuthCredentialsJSON (credType , []byte (c .credentials )))
718749 case OptionValueAuthTypeUserAuthentication :
719750 if c .clientID == "" {
720751 return adbc.Error {
@@ -734,8 +765,10 @@ func (c *connectionImpl) newClient(ctx context.Context) error {
734765 Msg : fmt .Sprintf ("[bq] `%s` parameter is empty" , OptionStringAuthRefreshToken ),
735766 }
736767 }
768+ c .Logger .Debug ("Using user OAuth authentication" )
737769 authOptions = append (authOptions , option .WithTokenSource (c ))
738770 case OptionValueAuthTypeAppDefaultCredentials , OptionValueAuthTypeDefault , "" :
771+ c .Logger .Debug ("Using Application Default Credentials (ADC)" , "authType" , c .authType )
739772 // Use Application Default Credentials (default behavior)
740773 // No additional options needed - ADC is used by default
741774 default :
@@ -1146,5 +1179,44 @@ func (c *connectionImpl) getAccessToken() (*bigQueryTokenResponse, error) {
11461179 if err != nil {
11471180 return nil , errToAdbcErr (adbc .StatusIO , err , "get access token" )
11481181 }
1182+
1183+ if tokenResponse .Error != "" {
1184+ msg := fmt .Sprintf ("[bq] OAuth token error: %s" , tokenResponse .Error )
1185+ if tokenResponse .ErrorDescription != "" {
1186+ msg += ": " + tokenResponse .ErrorDescription
1187+ }
1188+ if isReauthError (tokenResponse .ErrorDescription ) {
1189+ msg += ". " + reauthGuidance
1190+ }
1191+ if tokenResponse .ErrorURI != "" {
1192+ msg += " (see: " + tokenResponse .ErrorURI + ")"
1193+ }
1194+ return nil , adbc.Error {
1195+ Code : adbc .StatusUnauthorized ,
1196+ Msg : msg ,
1197+ }
1198+ }
1199+
11491200 return & tokenResponse , nil
11501201}
1202+
1203+ func detectCredentialTypeFromFile (filename string ) (option.CredentialsType , error ) {
1204+ data , err := os .ReadFile (filename )
1205+ if err != nil {
1206+ return "" , fmt .Errorf ("read credential file: %w" , err )
1207+ }
1208+ return detectCredentialTypeFromJSON (data )
1209+ }
1210+
1211+ func detectCredentialTypeFromJSON (data []byte ) (option.CredentialsType , error ) {
1212+ var f struct {
1213+ Type string `json:"type"`
1214+ }
1215+ if err := json .Unmarshal (data , & f ); err != nil {
1216+ return "" , fmt .Errorf ("parse credential JSON: %w" , err )
1217+ }
1218+ if f .Type == "" {
1219+ return "" , fmt .Errorf ("missing 'type' field in credential JSON" )
1220+ }
1221+ return option .CredentialsType (f .Type ), nil
1222+ }
0 commit comments