-
Notifications
You must be signed in to change notification settings - Fork 527
Add IAM support #929
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
Add IAM support #929
Conversation
…rdcoded auth header
…() and refreshToken()
…ken in IamTokenManager
… and add clear warnings
Codecov Report
@@ Coverage Diff @@
## develop #929 +/- ##
=============================================
+ Coverage 38.77% 38.79% +0.01%
- Complexity 1421 1438 +17
=============================================
Files 559 562 +3
Lines 14261 14398 +137
Branches 831 842 +11
=============================================
+ Hits 5530 5586 +56
- Misses 8415 8487 +72
- Partials 316 325 +9
Continue to review full report at Codecov.
|
| Double fractionOfTimeToLive = 0.8; | ||
| Long timeToLive = tokenData.getExpiresIn(); | ||
| Long expirationTime = tokenData.getExpiration(); | ||
| Double refreshTime = expirationTime - (timeToLive * (1.0 - fractionOfTimeToLive)); |
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.
can you review this one more time before merging?
germanattanasio
left a comment
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.
We probably want to update the README with examples of how to use IAM.
| if (iamApiKey != null) { | ||
| tokenManager = new IamTokenManager(new IamOptions.Builder().apiKey(iamApiKey).build()); | ||
| } | ||
| apiKey = CredentialUtils.getAPIKey(name); |
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.
What about the iam_url? When running on stage1 we need to use a different IAM server
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.
We need an CredentialUtils.getIAMUrl(name);
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.
The URL to the IAM endpoint is not present in the VCAP credentials is it? There would be something like "url": "https://gateway-s.watsonplatform.net/assistant/api" but the SDK would have to parse out any info and build the token service URL from there. Is that in the scope of the SDKs? I don't believe I'm doing that in Node.
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.
We need to add the URL to the VCAP services.
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.
My apologies for the confusion, I am doing this in Node. Sounds good 👍
| if (getApiKey() == null) { | ||
| if (tokenManager != null) { | ||
| String accessToken = tokenManager.getToken(); | ||
| builder.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + accessToken); |
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.
"Bearer " should be a constant
| * calls from failing when the service introduces breaking changes. | ||
| * @param iamOptions the options for authenticating through IAM | ||
| */ | ||
| public Assistant(String versionDate, IamOptions iamOptions) { |
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.
They just need to provide iam_api_key. iam_url (optional) and access_token (optional).
|
|
||
| public IamTokenManager(IamOptions options) { | ||
| this.apiKey = options.getApiKey(); | ||
| this.url = (options.getUrl() != null) ? options.getUrl() : "https://iam.ng.bluemix.net/identity/token"; |
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.
use a constant for the URL
| builder.header(HttpHeaders.AUTHORIZATION, DEFAULT_AUTHORIZATION); | ||
|
|
||
| FormBody formBody = new FormBody.Builder() | ||
| .add("grant_type", "urn:ibm:params:oauth:grant-type:apikey") |
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.
All the strings here should be constants
germanattanasio
left a comment
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.
We probably want to update the README with examples of how to use IAM.
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.
@lpatino10 thanks for the shout out 😛
This all looks good to me, as far as the IAM logic goes. Since you're using a builder for the token options, I want to make sure it is clear to the users that they have to manage everything if they use the accessToken() method. It looks like you did a good job of including that in the comments - but I agree with German's suggestion of including examples in the README and maybe reiterating that fact.
| "refresh_token": "99999999", | ||
| "token_type": "Bearer", | ||
| "expires_in": 3600, | ||
| "expiration": 999999999999999999 |
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 someone is still maintaining this thousands of years from now, I hope they remember to change this expiration time!
| this.name = name; | ||
| String iamApiKey = CredentialUtils.getIAMKey(name); | ||
| String iamUrl = CredentialUtils.getIAMUrl(name); | ||
| if (iamApiKey != null && iamUrl != null) { |
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.
Even if the IAM API key is being pulled from VCAP credentials, the IAM URL is still optional, right? I think the tokenManager should still be created if there is an IAM API key but no IAM URL
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.
true
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.
@lpatino10 will you address this? As far as I can see, that is all that's left to be done
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.
Oops, I missed this one. Making the change now.
dpopp07
left a comment
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.
Just had a few suggestions on the documentation
README.md
Outdated
|
|
||
| ### Using IAM | ||
|
|
||
| When authenticating with IAM, you have the option of passing in the IAM API key, the IAM URL, and an IAM access token. **Be aware that passing in an access token means that you're assuming responsibility for maintaining that token's lifecycle.** If you instead pass in an IAM API key, the SDK will manage it for you. |
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 is a bit nit-picky, but I would change this to something like "the IAM API key and optionally the IAM service URL, or an IAM access token. The user would not pass in all three, so "and" may be the wrong word to use. Sorry to sound like grammar 👮
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.
No that's helpful! I was struggling to think of how to convey what was optional or not. I like your idea 👍
README.md
Outdated
| // in the constructor, letting the SDK manage the IAM token | ||
| IamOptions options = new IamOptions.Builder() | ||
| .apiKey("<iam_api_key>") | ||
| .url("<iam_url>") |
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.
Maybe note here that URL is optional and give the default value
README.md
Outdated
| ``` | ||
|
|
||
| ```java | ||
| // in the constructor, taking control of managing IAM token |
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 think "assuming" is more clear here than "taking". Just my opinion
README.md
Outdated
| // in the constructor, taking control of managing IAM token | ||
| IamOptions options = new IamOptions.Builder() | ||
| .accessToken("<access_token>") | ||
| .url("<iam_url>") |
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 the user passes in an access token, there is no need to provide the URL. They will be making the service requests, so the URL would never be used.
README.md
Outdated
| Discovery service = new Discovery("2017-11-07"); | ||
| IamOptions options = new IamOptions.Builder() | ||
| .accessToken("<access_token>") | ||
| .url("<iam_url>") |
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.
See comments above
| service.setIamCredentials(options); | ||
| ``` | ||
|
|
||
| If at any time you would like to let the SDK take over managing your IAM token, simply override your stored IAM credentials with an IAM API key by calling the `setIamCredentials()` method again. |
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 like that you point this out to the user!
dpopp07
left a comment
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.
Nice! Looks good to me
This PR adds in support for IAM in the core of the SDK. With these changes, users have the option of authenticating with Watson services using IAM tokens, which they can manage themselves or let the new
IamTokenManagerclass handle.Shout out to @dpopp07 for leading the way for this in the Node SDK. Definitely leaned on your changes for my implementation 😉