Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider implementing OpenID Connect ID Token-based credentials for service accounts #99

Closed
lesv opened this issue Apr 21, 2017 · 7 comments · Fixed by #303
Closed

Consider implementing OpenID Connect ID Token-based credentials for service accounts #99

lesv opened this issue Apr 21, 2017 · 7 comments · Fixed by #303
Assignees
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Milestone

Comments

@lesv
Copy link
Contributor

lesv commented Apr 21, 2017

This would satisfy the Identity-aware Proxy use case as well as a few other niche cases that have come up.

Context:
https://gist.github.com/jonparrott/cffca2fa7881e03fbe6ff7c25773c9cf
https://github.com/GoogleCloudPlatform/python-docs-samples/blob/master/iap/make_iap_request.py#L121

See googleapis/google-auth-library-python#130 for additional context.

@JustinBeckwith JustinBeckwith added 🚨 This issue needs some love. triage me I really want to be triaged. labels Jun 8, 2018
@chingor13 chingor13 added the type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design. label Jun 19, 2018
@JustinBeckwith JustinBeckwith removed triage me I really want to be triaged. enhancement 🚨 This issue needs some love. labels Jun 19, 2018
@salrashid123
Copy link
Contributor

@broady @JustinBeckwith @ptone

I can implement it in ImpersonatedCredentials.java pretty easily (its just an API call to iamcredentials.generateIdToken()

however, i'd like confirmation the interface is ok below.

Note, the verifyIdToken may involve a 3rd party library. Her'es an example of getting and verifying ID tokens on service accounts, compute engine

public interface IdTokenProvider {

  class IdTokenProviderException extends RuntimeException {
  String getIdToken(String target_audience);
  String getIdToken(String target_audience, boolean include_email);

  /* TODO: java 1.8+ supports statics in interface defintions.
   * boolean verifyIdToken(String idToken, String audience);
  */
}

  • google-auth-library-java/oauth2_http/java/com/google/auth/oauth2/ImpersonatedCredentials.java
public String getIdToken(String target_audience) {
   // issue idToken with given audience
   return "theIdToken"
}

public String getIdToken(String target_audience, boolean include_email) {
   // issue idToken with given audience and include the email claim
   return "theIdToken"
}

public static boolean verifyIdToken(String idToken, String audience) {
  // verify id token for signature, exp and audience
  return true;
}

@JustinBeckwith
Copy link
Contributor

I think @chingor13 is actively working on something in this space.

@salrashid123
Copy link
Contributor

ok, let me know if there's any part i can assit with, the impersonatedCredential's implementation of idtokens is here if you need https://gist.github.com/salrashid123/2cd2fb924fa9e4435273abae86b35597

@chingor13
Copy link
Contributor

To clarify, I have been actively working on the JWTCredentials implementation, but not the IdToken implementation.

For the interface, this library does not generally like to return the primitive tokens as the primary return types. Instead, I'd prefer to return a Credentials implementation that can be used with our downstream client libraries. So instead of returning the id token as a String, we'd prefer to return an IdTokenCredentials instance that implements Credentials. The user could use these credentials with a google-cloud-java library. We could provide an accessor on IdTokenCredentials to return the raw token similar to how OAuth2Credentials#getAccessToken() returns an oauth2 token for inspection.

So perhaps the interface should be something like:

public interface IdTokenProvider {
  IdTokenCredentials idTokenWithAudience(String audience);
}

A few other things to consider:

  • Does the interface need to be public?
  • Can you clarify why the version with include_email is necessary?

Note that we do have an IdToken implementation in google-oauth-client that does include verification.

@salrashid123
Copy link
Contributor

  • re: returning a Credential. SGTM.

  • re: include_email. Well, thats just to make it parity with the underlying API for iamcredentials.generateIdToken()
    delegates isnt' included since its inherited automatically from the sourceCredentials

re: public interface IdTokenProvider { ...ah...i don't think you can make the interface private outside of hte package...AFAIK

re: verify step being done outside library
thats fine by me.


note, i wrote up the flows here but so far just used a 3rd party library for java for serviceAccounts. Once impersonatedCredentials in any language is available, i'll add it in there


Are you ok w/ a PR for starters that implements idtokens at the moment?

@chingor13
Copy link
Contributor

Yep, let's go ahead and start on this.

@salrashid123
Copy link
Contributor

i've got the code ready but wanted confirmation
I've basically created a new IdTokenCredential which accepts a source GoogleCredential that implements an IdTokenProvider interface

  • IdTokenCredentials:
public static IdTokenCredentials create(GoogleCredentials sourceCredentials, String targetAudience,
      List<String> options) {
  • IdTokenProvider interface
IdToken idTokenWithAudience(String targetAudience, List<String> options);

I wanted to confirm this is ok (i've got it working but before i file the PR and testcase...


sample usage

 String credPath = "/svc.json";
 String targetAudience = "https://myapp-6w42z6vi3q-uc.a.run.app";
  • ADC
// ADC (ServiceAccount)
// export GOOGLE_APPLICATION_CREDENTIALS=svc.json
GoogleCredentials adcCreds = GoogleCredentials.getApplicationDefault();
//IdTokenCredentials tokenCredential = IdTokenCredentials.create(adcCreds, targetAudience);
IdTokenCredentials tokenCredential = IdTokenCredentials.newBuilder()
  .setSourceCredentials(adcCreds)
  .setTargetAudience(targetAudience).build();
  • ServiceAccountCredentials:
// ServiceAccountCredentials
ServiceAccountCredentials saCreds = ServiceAccountCredentials
       .fromStream(new FileInputStream(credPath));
saCreds = (ServiceAccountCredentials) saCreds.createScoped(Arrays.asList("https://www.googleapis.com/auth/iam"));

IdTokenCredentials tokenCredential = IdTokenCredentials.create(saCreds, targetAudience);
  • ImpersonatedCredentials
// ImpersonatedCredentials
ImpersonatedCredentials imCreds = ImpersonatedCredentials.create(saCreds,
   "impersonated-account@project.iam.gserviceaccount.com", null,
   Arrays.asList("https://www.googleapis.com/auth/userinfo.email"), 300);

IdTokenCredentials tokenCredential = IdTokenCredentials.create(imCreds, 
   targetAudience, Arrays.asList(ImpersonatedCredentials.INCLUDE_EMAIL));
  • ComputeCredentials:
// ComputeEngineCredentials
ComputeEngineCredentials caCreds = ComputeEngineCredentials.create();

IdTokenCredentials tokenCredential = IdTokenCredentials.create(caCreds, 
  targetAudience, 
  Arrays.asList(ComputeEngineCredentials.ID_TOKEN_FORMAT_FULL, 
                        ComputeEngineCredentials.ID_TOKEN_LICENSES_TRUE));               
  • then with any of the above, invoke the target endpoint
// Invoke the API
GenericUrl genericUrl = new GenericUrl("https://myapp-6w42z6vi3q-uc.a.run.app");
HttpCredentialsAdapter adapter = new HttpCredentialsAdapter(tokenCredential);
HttpTransport transport = new NetHttpTransport();
HttpRequest request = transport.createRequestFactory(adapter).buildGetRequest(genericUrl);
request.setThrowExceptionOnExecuteError(false);
HttpResponse response = request.execute();
String r = response.parseAsString();
System.out.println(r);

System.out.println(tokenCredential.getIdToken().getTokenValue());
System.out.println(tokenCredential.getIdToken().getExpirationTime());
System.out.println(tokenCredential.getIdToken().getAudience());

If this looks ok (vs something like deriving an idtokencredential directly from another credential idtokencredential = computecredential.getIDToken(aud,etc) ...let me know; i think the latter is a bit complecated since i have to figure out how to do automatic refresh by chaining both credentials....I think its cleaner with what i've got here

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
type: feature request ‘Nice-to-have’ improvement, new feature or different behavior or design.
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants