-   Notifications  
You must be signed in to change notification settings  - Fork 298
 
Support for creating custom tokens without service account credentials #183
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
Conversation
|   Resolves #59  |  
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This basically looks good to me. I left some comment nits.
| /** | ||
| * Returns the service account email. | ||
| * | ||
| * @return The service account email set via {@link Builder#setServiceAccount(String)} | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Suggestion: s/service account email/client email address of the service account/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| } | ||
|   |  ||
| /** | ||
| * Sets the service account email that should be associated with an app. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would also suggest to say "client email address of the service account" here.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| * Sets the service account email that should be associated with an app. | ||
| * | ||
| * <p>This is used to <a href="https://firebase.google.com/docs/auth/admin/create-custom-tokens"> | ||
| * create custom auth tokens</a>, when service account credentials are not available. Service | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: "The email address of a service account can..."
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| * with that email to sign tokens remotely. | ||
| * <li>If the code is deployed in the Google App Engine standard environment, uses the | ||
| * <a href="https://cloud.google.com/appengine/docs/standard/java/appidentity/">App Identity | ||
| * service</a> to sign tokens. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems a little long and contains a lot of detail that I would expect to see in the Admin SDK usage docs, but not necessarily in the API documentation. I tried to reduce this down a bit, let me know what you think:
<p>This method generates a token: <li>Using the private key of {@link FirebaseApp}'s service account credentials if provided at initialization. <li>Using the <a href="https://cloud.google.com/iam/reference/rest/v1/projects.serviceAccounts/signBlob">IAM service</a> if a service account email was provided via ({@link com.google.firebase.FirebaseOptions.Builder#setServiceAccount(String)}) <li>Using the <a href="https://cloud.google.com/appengine/docs/standard/java/appidentity/">App Identity service</a> if the code is deployed in the Google App Engine standard environment. <li>Using the <a href="https://cloud.google.com/compute/docs/storing-retrieving-metadata"> local Metadata server</a> If the code is deployed in a different GCP-managed environment (e.g. Google Compute Engine). </ol> <p>This method throws an exception when all the above fails. I don't feel super strongly about this if you want to keep it detailed.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| } catch (IOException e) { | ||
| throw new IllegalStateException( | ||
| "Failed to initialize FirebaseTokenFactory. Make sure to initialize the SDK " | ||
| + "with a service account credential. Alternatively specify a service account " | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
s/. Alternatively specify/or/
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| FirebaseTokenFactory result = this.tokenFactory.get(); | ||
| if (result == null) { | ||
| synchronized (lock) { | ||
| result = this.tokenFactory.get(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If I remember my Java 101 class correctly, then you don't need to use an AtomicReference here since you are grabbing a lock already.
http://www.cs.umd.edu/users/pugh/java/memoryModel/jsr-133-faq.html#synchronization
 "Synchronization ensures that memory writes by a thread before or during a synchronized block are made visible in a predictable manner to other threads which synchronize on the same monitor. "
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Then we'd have to make the variable volatile, to get the double-checked locking semantics to work correctly: https://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html
I don't really have a preference. Drop the AtomicRef and switch to a volatile variable?
| byte[] sign(@NonNull byte[] payload) throws IOException; | ||
|   |  ||
| /** | ||
| * Returns the name of the service account used to sign payloads. | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this the service account email?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yes. Updated comments.
| // with the IAM service to sign bytes. | ||
| HttpRequest request = requestFactory.buildGetRequest(new GenericUrl(METADATA_SERVICE_URL)); | ||
| request.getHeaders().set("Metadata-Flavor", "Google"); | ||
| HttpResponse response = request.execute(); | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we have to to call response.disconnect() here as well?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
| assertEquals(url, interceptor.getResponse().getRequest().getUrl().toString()); | ||
|   |  ||
| // Should fall back to signing-enabled credential | ||
| transport = new MultiRequestMockHttpTransport( | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should this be split into two separate tests?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|   Thanks for the review. Made the suggested changes. Over to @schmidt-sebastian for another look.  |  
| * Returns the client email address of the service account. | ||
| * | ||
| * @return The service account email set via {@link Builder#setServiceAccount(String)} | ||
| * @return The client email of service account set via {@link Builder#setServiceAccount(String)} | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
"of THE service account"
| * <p>This is used to <a href="https://firebase.google.com/docs/auth/admin/create-custom-tokens"> | ||
| * create custom auth tokens</a>, when service account credentials are not available. Service | ||
| * account email can be found in the {@code client_email} field of a service account JSON. | ||
| * create custom auth tokens</a>, when service account credentials are not available. The email | 
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove comma.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done
|   @schmidt-sebastian PTAL at my last commit. This renames   |  
Currently the SDK must be initialized with service account credentials in order to be able to call
FirebaseAuth.createCustomToken(). With this PR, the SDK will attempt to sign custom tokens by calling the IAM service in the cloud when the service account credentials are not provided.go/firebase-admin-sign
go/firebase-admin-iam-sign