Authenticate installed apps with user accounts
This guide explains how to authenticate by using user accounts for access to the BigQuery API when your app is installed onto users' machines.
To ensure the app accesses only BigQuery tables that are available to the end user, authenticate by using a user credential. A user credential can run queries against only the end user's Google Cloud project rather than the app's project. As a result, the user is billed for queries instead of the app.
Before you begin
- Create a Google Cloud project that represents your installed app.
- Install the BigQuery client libraries.
-
Install authentication libraries.
Java
If you are using Maven, include the following dependencies in your pom file.
<dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-java6</artifactId> <version>1.31.0</version> </dependency> <dependency> <groupId>com.google.oauth-client</groupId> <artifactId>google-oauth-client-jetty</artifactId> <version>1.31.0</version> </dependency>
Python
Install the oauthlib integration for Google Auth.
pip install --upgrade google-auth-oauthlib
Node.js
Install the oauthlib integration for Google Auth.
npm install google-auth-library
npm install readline-promise
Set up your client credentials
Use the following button to select a project and create the required credentials.Manually create credentials
- Go to the Credentials page in the Google Cloud console.
- Fill out the required fields on the OAuth consent screen.
-
On the Credentials
page, click the Create credentials button.
Choose OAuth client ID.
- Select Desktop as the app type, and then click Create.
-
Download the credentials by clicking the Download JSON button.
Download JSON.
Save the credentials file to
client_secrets.json
. This file must be distributed with your app.
Authenticate and call the API
-
Use the client credentials to perform the
OAuth 2.0
flow.
Java
importcom.google.api.client.auth.oauth2.Credential ; importcom.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp ; importcom.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver ; importcom.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow ; importcom.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets ; importcom.google.api.client.googleapis.javanet.GoogleNetHttpTransport ; importcom.google.api.client.json.JsonFactory ; importcom.google.api.client.json.jackson2.JacksonFactory ; importcom.google.api.client.util.store.FileDataStoreFactory ; importcom.google.api.gax.paging.Page ; importcom.google.auth.oauth2.GoogleCredentials ; importcom.google.auth.oauth2.UserCredentials ; importcom.google.cloud.bigquery.BigQuery ; importcom.google.cloud.bigquery.BigQueryException ; importcom.google.cloud.bigquery.BigQueryOptions ; importcom.google.cloud.bigquery.Dataset ; importcom.google.common.collect.ImmutableList; importjava.io.File; importjava.io.IOException; importjava.io.InputStream; importjava.io.InputStreamReader; importjava.nio.file.Files; importjava.nio.file.Path; importjava.nio.file.Paths; importjava.security.GeneralSecurityException; importjava.util.List; // Sample to authenticate by using a user credential publicclass AuthUserFlow{ privatestaticfinalFileDATA_STORE_DIR= newFile(AuthUserFlow.class.getResource("/").getPath(),"credentials"); privatestaticfinalJsonFactory JSON_FACTORY=JacksonFactory .getDefaultInstance(); // i.e redirect_uri http://localhost:61984/Callback privatestaticfinalintLOCAL_RECEIVER_PORT=61984; publicstaticvoidrunAuthUserFlow(){ // TODO(developer): Replace these variables before running the sample. /** * Download your OAuth2 configuration from the Google Developers Console API Credentials page. * https://console.cloud.google.com/apis/credentials */ PathcredentialsPath=Paths.get("path/to/your/client_secret.json"); List<String>scopes=ImmutableList.of("https://www.googleapis.com/auth/bigquery"); authUserFlow(credentialsPath,scopes); } publicstaticvoidauthUserFlow(PathcredentialsPath,List<String>selectedScopes){ // Reading credentials file try(InputStreaminputStream=Files.newInputStream(credentialsPath)){ // Load client_secret.json file GoogleClientSecrets clientSecrets= GoogleClientSecrets .load(JSON_FACTORY,newInputStreamReader(inputStream)); StringclientId=clientSecrets.getDetails ().getClientId(); StringclientSecret=clientSecrets.getDetails ().getClientSecret(); // Generate the url that will be used for the consent dialog. GoogleAuthorizationCodeFlow flow= newGoogleAuthorizationCodeFlow .Builder( GoogleNetHttpTransport .newTrustedTransport (), JSON_FACTORY, clientSecrets, selectedScopes) .setDataStoreFactory(newFileDataStoreFactory (DATA_STORE_DIR)) .setAccessType("offline") .setApprovalPrompt("auto") .build(); // Exchange an authorization code for refresh token LocalServerReceiver receiver= newLocalServerReceiver .Builder().setPort (LOCAL_RECEIVER_PORT).build(); Credential credential=newAuthorizationCodeInstalledApp (flow,receiver).authorize ("user"); // OAuth2 Credentials representing a user's identity and consent GoogleCredentials credentials= UserCredentials .newBuilder() .setClientId(clientId) .setClientSecret(clientSecret) .setRefreshToken(credential.getRefreshToken ()) .build(); // Initialize client that will be used to send requests. This client only needs to be created // once, and can be reused for multiple requests. BigQuery bigquery= BigQueryOptions .newBuilder().setCredentials(credentials).build().getService(); Page<Dataset>datasets=bigquery.listDatasets (BigQuery.DatasetListOption.pageSize(100)); if(datasets==null){ System.out.println("Dataset does not contain any models"); return; } datasets .iterateAll() .forEach( dataset->System.out.printf("Success! Dataset ID: %s ",dataset.getDatasetId())); }catch(BigQueryException |IOException|GeneralSecurityExceptionex){ System.out.println("Project does not contain any datasets \n"+ex.toString()); } } }
Python
fromgoogle_auth_oauthlibimport flow # A local server is used as the callback URL in the auth flow. appflow = flow.InstalledAppFlow.from_client_secrets_file( "client_secrets.json", scopes=["https://www.googleapis.com/auth/bigquery"] ) # This launches a local server to be used as the callback URL in the desktop # app auth flow. If you are accessing the application remotely, such as over # SSH or a remote Jupyter notebook, this flow will not work. Use the # `gcloud auth application-default login --no-browser` command or workload # identity federation to get authentication tokens, instead. # appflow.run_local_server() credentials = appflow.credentials
Node.js
const{OAuth2Client}=require('google-auth-library'); constreadline=require('readline-promise').default; functionstartRl(){ constrl=readline.createInterface({ input:process.stdin, output:process.stdout, }); returnrl; } /** * Download your OAuth2 configuration from the Google * Developers Console API Credentials page. * https://console.cloud.google.com/apis/credentials */ constkeys=require('./oauth2.keys.json'); /** * Create a new OAuth2Client, and go through the OAuth2 content * workflow. Return the full client to the callback. */ asyncfunctiongetRedirectUrl(){ constrl=main.startRl(); // Create an oAuth client to authorize the API call. Secrets are kept in a `keys.json` file, // which should be downloaded from the Google Developers Console. constoAuth2Client=newOAuth2Client ( keys.installed.client_id, keys.installed.client_secret, keys.installed.redirect_uris[0] ); // Generate the url that will be used for the consent dialog. constauthorizeUrl=oAuth2Client.generateAuthUrl ({ access_type:'offline', scope:'https://www.googleapis.com/auth/bigquery', prompt:'consent', }); console.info( `Please visit this URL to authorize this application: ${authorizeUrl}` ); constcode=awaitrl.questionAsync('Enter the authorization code: '); consttokens=awaitmain.exchangeCode(code); rl.close(); returntokens; } // Exchange an authorization code for an access token asyncfunctionexchangeCode(code){ constoAuth2Client=newOAuth2Client ( keys.installed.client_id, keys.installed.client_secret, keys.installed.redirect_uris[0] ); constr=awaitoAuth2Client.getToken (code); console.info(r.tokens); returnr.tokens; } asyncfunctionauthFlow(projectId='project_id'){ /** * TODO(developer): * Save Project ID as environment variable PROJECT_ID="project_id" * Uncomment the following line before running the sample. */ // projectId = process.env.PROJECT_ID; consttokens=awaitmain.getRedirectUrl(); constcredentials={ type:'authorized_user', client_id:keys.installed.client_id, client_secret:keys.installed.client_secret, refresh_token:tokens.refresh_token, }; return{ projectId, credentials, }; }
-
Use the authenticated credentials to connect to the BigQuery API.
Java
importcom.google.api.client.auth.oauth2.Credential ; importcom.google.api.client.extensions.java6.auth.oauth2.AuthorizationCodeInstalledApp ; importcom.google.api.client.extensions.jetty.auth.oauth2.LocalServerReceiver ; importcom.google.api.client.googleapis.auth.oauth2.GoogleAuthorizationCodeFlow ; importcom.google.api.client.googleapis.auth.oauth2.GoogleClientSecrets ; importcom.google.api.client.googleapis.javanet.GoogleNetHttpTransport ; importcom.google.api.client.json.JsonFactory ; importcom.google.api.client.json.jackson2.JacksonFactory ; importcom.google.api.client.util.store.FileDataStoreFactory ; importcom.google.auth.oauth2.GoogleCredentials ; importcom.google.auth.oauth2.UserCredentials ; importcom.google.cloud.bigquery.BigQuery ; importcom.google.cloud.bigquery.BigQueryException ; importcom.google.cloud.bigquery.BigQueryOptions ; importcom.google.cloud.bigquery.QueryJobConfiguration ; importcom.google.cloud.bigquery.TableResult ; importcom.google.common.collect.ImmutableList; importjava.io.File; importjava.io.IOException; importjava.io.InputStream; importjava.io.InputStreamReader; importjava.nio.file.Files; importjava.nio.file.Path; importjava.nio.file.Paths; importjava.security.GeneralSecurityException; importjava.util.List; // Sample to query by using a user credential publicclass AuthUserQuery{ privatestaticfinalFileDATA_STORE_DIR= newFile(AuthUserQuery.class.getResource("/").getPath(),"credentials"); privatestaticfinalJsonFactory JSON_FACTORY=JacksonFactory .getDefaultInstance(); // i.e redirect_uri http://localhost:61984/Callback privatestaticfinalintLOCAL_RECEIVER_PORT=61984; publicstaticvoidrunAuthUserQuery(){ // TODO(developer): Replace these variables before running the sample. /** * Download your OAuth2 configuration from the Google Developers Console API Credentials page. * https://console.cloud.google.com/apis/credentials */ PathcredentialsPath=Paths.get("path/to/your/client_secret.json"); List<String>scopes=ImmutableList.of("https://www.googleapis.com/auth/bigquery"); Stringquery= "SELECT name, SUM(number) as total" +" FROM `bigquery-public-data.usa_names.usa_1910_current`" +" WHERE name = 'William'" +" GROUP BY name;"; authUserQuery(credentialsPath,scopes,query); } publicstaticvoidauthUserQuery( PathcredentialsPath,List<String>selectedScopes,Stringquery){ // Reading credentials file try(InputStreaminputStream=Files.newInputStream(credentialsPath)){ // Load client_secret.json file GoogleClientSecrets clientSecrets= GoogleClientSecrets .load(JSON_FACTORY,newInputStreamReader(inputStream)); StringclientId=clientSecrets.getDetails ().getClientId(); StringclientSecret=clientSecrets.getDetails ().getClientSecret(); // Generate the url that will be used for the consent dialog. GoogleAuthorizationCodeFlow flow= newGoogleAuthorizationCodeFlow .Builder( GoogleNetHttpTransport .newTrustedTransport (), JSON_FACTORY, clientSecrets, selectedScopes) .setDataStoreFactory(newFileDataStoreFactory (DATA_STORE_DIR)) .setAccessType("offline") .setApprovalPrompt("auto") .build(); // Exchange an authorization code for refresh token LocalServerReceiver receiver= newLocalServerReceiver .Builder().setPort (LOCAL_RECEIVER_PORT).build(); Credential credential=newAuthorizationCodeInstalledApp (flow,receiver).authorize ("user"); // OAuth2 Credentials representing a user's identity and consent GoogleCredentials credentials= UserCredentials .newBuilder() .setClientId(clientId) .setClientSecret(clientSecret) .setRefreshToken(credential.getRefreshToken ()) .build(); // Initialize client that will be used to send requests. This client only needs to be created // once, and can be reused for multiple requests. BigQuery bigquery= BigQueryOptions .newBuilder().setCredentials(credentials).build().getService(); QueryJobConfiguration queryConfig=QueryJobConfiguration .newBuilder(query).build(); TableResult results=bigquery.query (queryConfig); results .iterateAll () .forEach(row->row.forEach(val->System.out.printf("%s,",val.toString()))); System.out.println("Query performed successfully."); }catch(BigQueryException |IOException|GeneralSecurityException|InterruptedExceptionex){ System.out.println("Query not performed \n"+ex.toString()); } } }
Python
fromgoogle.cloudimport bigquery # TODO: Uncomment the line below to set the `project` variable. # project = 'user-project-id' # # The `project` variable defines the project to be billed for query # processing. The user must have the bigquery.jobs.create permission on # this project to run a query. See: # https://cloud.google.com/bigquery/docs/access-control#permissions client = bigquery .Client (project=project, credentials=credentials) query_string = """SELECT name, SUM(number) as total FROM `bigquery-public-data.usa_names.usa_1910_current` WHERE name = 'William' GROUP BY name; """ results = client.query_and_wait (query_string) # Print the results. for row in results: # Wait for the job to complete. print("{}: {}".format(row["name"], row["total"]))
Node.js
asyncfunctionquery(){ const{BigQuery}=require('@google-cloud/bigquery'); constcredentials=awaitmain.authFlow(); constbigquery=newBigQuery (credentials); // Queries the U.S. given names dataset for the state of Texas. constquery=`SELECT name, SUM(number) as total FROM \`bigquery-public-data.usa_names.usa_1910_current\` WHERE name = 'William' GROUP BY name;`; // For all options, see https://cloud.google.com/bigquery/docs/reference/rest/v2/jobs/query constoptions={ query:query, }; // Run the query as a job const[job]=awaitbigquery.createQueryJob(options); console.log(`Job ${job .id} started.`); // Wait for the query to finish const[rows]=awaitjob .getQueryResults (); // Print the results console.log('Rows:'); rows.forEach(row=>console.log(row)); returnrows; } constmain={ query, authFlow, exchangeCode, getRedirectUrl, startRl, }; module.exports={ main, }; if(module===require.main){ query().catch(console.error); }
When you run the sample code, it launches a browser that requests access to the project that is associated with the client secrets. You can use the resulting credentials to access the user's BigQuery resources because the sample requested the BigQuery scope.
What's next
- Learn about other ways to authenticate your app to access the BigQuery API.
- Learn about authentication with end user credentials for all Cloud APIs.