1717package com .google .cloud .spanner .jdbc ;
1818
1919import com .google .auth .Credentials ;
20+ import com .google .auth .oauth2 .AccessToken ;
2021import com .google .auth .oauth2 .GoogleCredentials ;
2122import com .google .auth .oauth2 .ServiceAccountCredentials ;
2223import com .google .cloud .NoCredentials ;
@@ -140,6 +141,7 @@ public String[] getValidValues() {
140141 static final boolean DEFAULT_READONLY = false ;
141142 static final boolean DEFAULT_RETRY_ABORTS_INTERNALLY = true ;
142143 private static final String DEFAULT_CREDENTIALS = null ;
144+ private static final String DEFAULT_OAUTH_TOKEN = null ;
143145 private static final String DEFAULT_NUM_CHANNELS = null ;
144146 private static final String DEFAULT_USER_AGENT = null ;
145147
@@ -156,6 +158,10 @@ public String[] getValidValues() {
156158 public static final String RETRY_ABORTS_INTERNALLY_PROPERTY_NAME = "retryAbortsInternally" ;
157159 /** Name of the 'credentials' connection property. */
158160 public static final String CREDENTIALS_PROPERTY_NAME = "credentials" ;
161+ /**
162+ * OAuth token to use for authentication. Cannot be used in combination with a credentials file.
163+ */
164+ public static final String OAUTH_TOKEN_PROPERTY_NAME = "oauthToken" ;
159165 /** Name of the 'numChannels' connection property. */
160166 public static final String NUM_CHANNELS_PROPERTY_NAME = "numChannels" ;
161167 /** Custom user agent string is only for other Google libraries. */
@@ -173,6 +179,7 @@ public String[] getValidValues() {
173179 ConnectionProperty .createBooleanProperty (
174180 RETRY_ABORTS_INTERNALLY_PROPERTY_NAME , "" , DEFAULT_RETRY_ABORTS_INTERNALLY ),
175181 ConnectionProperty .createStringProperty (CREDENTIALS_PROPERTY_NAME , "" ),
182+ ConnectionProperty .createStringProperty (OAUTH_TOKEN_PROPERTY_NAME , "" ),
176183 ConnectionProperty .createStringProperty (NUM_CHANNELS_PROPERTY_NAME , "" ),
177184 ConnectionProperty .createBooleanProperty (
178185 USE_PLAIN_TEXT_PROPERTY_NAME , "" , DEFAULT_USE_PLAIN_TEXT ),
@@ -223,6 +230,7 @@ public static void closeSpanner() {
223230 public static class Builder {
224231 private String uri ;
225232 private String credentialsUrl ;
233+ private String oauthToken ;
226234 private Credentials credentials ;
227235 private List <StatementExecutionInterceptor > statementExecutionInterceptors =
228236 Collections .emptyList ();
@@ -308,6 +316,22 @@ public Builder setCredentialsUrl(String credentialsUrl) {
308316 return this ;
309317 }
310318
319+ /**
320+ * Sets the OAuth token to use with this connection. The token must be a valid token with access
321+ * to the resources (project/instance/database) that the connection will be accessing. This
322+ * authentication method cannot be used in combination with a credentials file. If both an OAuth
323+ * token and a credentials file is specified, the {@link #build()} method will throw an
324+ * exception.
325+ *
326+ * @param oauthToken A valid OAuth token for the Google Cloud project that is used by this
327+ * connection.
328+ * @return this builder
329+ */
330+ public Builder setOAuthToken (String oauthToken ) {
331+ this .oauthToken = oauthToken ;
332+ return this ;
333+ }
334+
311335 @ VisibleForTesting
312336 Builder setStatementExecutionInterceptors (List <StatementExecutionInterceptor > interceptors ) {
313337 this .statementExecutionInterceptors = interceptors ;
@@ -339,6 +363,7 @@ public static Builder newBuilder() {
339363
340364 private final String uri ;
341365 private final String credentialsUrl ;
366+ private final String oauthToken ;
342367
343368 private final boolean usePlainText ;
344369 private final String host ;
@@ -363,6 +388,13 @@ private ConnectionOptions(Builder builder) {
363388 this .uri = builder .uri ;
364389 this .credentialsUrl =
365390 builder .credentialsUrl != null ? builder .credentialsUrl : parseCredentials (builder .uri );
391+ this .oauthToken =
392+ builder .oauthToken != null ? builder .oauthToken : parseOAuthToken (builder .uri );
393+ // Check that not both credentials and an OAuth token have been specified.
394+ Preconditions .checkArgument (
395+ (builder .credentials == null && this .credentialsUrl == null ) || this .oauthToken == null ,
396+ "Cannot specify both credentials and an OAuth token." );
397+
366398 this .usePlainText = parseUsePlainText (this .uri );
367399 this .userAgent = parseUserAgent (this .uri );
368400
@@ -376,8 +408,13 @@ private ConnectionOptions(Builder builder) {
376408 // Using credentials on a plain text connection is not allowed, so if the user has not specified
377409 // any credentials and is using a plain text connection, we should not try to get the
378410 // credentials from the environment, but default to NoCredentials.
379- if (builder .credentials == null && this .credentialsUrl == null && this .usePlainText ) {
411+ if (builder .credentials == null
412+ && this .credentialsUrl == null
413+ && this .oauthToken == null
414+ && this .usePlainText ) {
380415 this .credentials = NoCredentials .getInstance ();
416+ } else if (this .oauthToken != null ) {
417+ this .credentials = new GoogleCredentials (new AccessToken (oauthToken , null ));
381418 } else {
382419 this .credentials =
383420 builder .credentials == null
@@ -446,6 +483,12 @@ static String parseCredentials(String uri) {
446483 return value != null ? value : DEFAULT_CREDENTIALS ;
447484 }
448485
486+ @ VisibleForTesting
487+ static String parseOAuthToken (String uri ) {
488+ String value = parseUriProperty (uri , OAUTH_TOKEN_PROPERTY_NAME );
489+ return value != null ? value : DEFAULT_OAUTH_TOKEN ;
490+ }
491+
449492 @ VisibleForTesting
450493 static String parseNumChannels (String uri ) {
451494 String value = parseUriProperty (uri , NUM_CHANNELS_PROPERTY_NAME );
0 commit comments