-
Notifications
You must be signed in to change notification settings - Fork 2
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
refactor (plus fixes): rewrite auth logic for simplification #7
Conversation
…ect one-config-per-api
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 reordered keys in this file to better associate them with each other.
.env.example
Outdated
# Required for Optimize API tutorials, to acquire auth tokens | ||
OPTIMIZE_CLIENT_ID=fill-this-in | ||
OPTIMIZE_CLIENT_SECRET=fill-this-in | ||
OPTIMIZE_AUDIENCE=optimize.camunda.io |
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.
These keys are now specific to the Optimize API only. There will be no shared use of these keys across other "component" APIs.
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 get hung up here if I put on my super novice user hat. If I download my credentials as environment variables, I get values that look like this:
export ZEEBE_ADDRESS='xx.zeebe.camunda.io:443'
export ZEEBE_CLIENT_ID='xx'
export ZEEBE_CLIENT_SECRET=xx'
export ZEEBE_AUTHORIZATION_SERVER_URL='https://login.cloud.camunda.io/oauth/token'
export ZEEBE_REST_ADDRESS='https://lhr-1.zeebe.camunda.io/xx'
export ZEEBE_GRPC_ADDRESS='grpcs://xx-1.zeebe.camunda.io:443'
export ZEEBE_TOKEN_AUDIENCE='zeebe.camunda.io'
export CAMUNDA_CLUSTER_ID='xx'
export CAMUNDA_CLUSTER_REGION='lhr-1'
export CAMUNDA_CREDENTIALS_SCOPES='Zeebe,Tasklist,Operate,Optimize,Secrets'
export CAMUNDA_TASKLIST_BASE_URL='https://lhr-1.tasklist.camunda.io/xx'
export CAMUNDA_OPTIMIZE_BASE_URL='https://lhr-1.optimize.camunda.io/xx'
export CAMUNDA_OPERATE_BASE_URL='https://lhr-1.operate.camunda.io/xx'
export CAMUNDA_OAUTH_URL='https://login.cloud.camunda.io/oauth/token'
Zeebe is the only component that shows a CLIENT_ID
and CLIENT_SECRET
. This remains true even if the scope is only Optimize.
Do you think this is confusing or misleading?
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 but I'm not sure how to address the fact that client IDs/secrets can be used across multiple APIs, but they don't have to be, and some of them come from the organization level and others come from the cluster level. Supporting the re-use of credentials across multiple APIs seems to provide multiple points of confusion; assigning one secret/key per API in the environment variables still allows for people to reuse credentials, if they want, but also makes it obvious that they need creds that will work for that specific API.
Do you have any suggestions? Can this be addressed in the supporting tutorial?
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.
In the .env.example file, I would note what values you will expect to see in the downloaded credentials. So, in this case, I needed to use the ZEEBE_CLIENT_ID
as the OPTIMIZE_CLIENT_ID
, etc. You can also note this in the docs with an admonition.
I would encourage you or Christina to follow up with the Console team because this looks misleading. I don't think these values are specific to Zeebe but old historical artifacts from a time when there were only Zeebe clients.
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.
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, I think this is super helpful and something we should keep an eye on in case there is confusion around where to get these values. Thank 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.
@pepopowitz were you going to add these changes to this PR? I'm on the fence about whether this is blocking or not.
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 thought I had! I just pushed these changes.
* @param {string} config.audience - The audience associated with the target API. | ||
* @returns {string} | ||
*/ | ||
export async function getAccessToken(config) { |
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 moved this function to the top of the file, because it is the entry point to this logic, and for users who want to read the code and understand how to use a library to handle auth for their code, this is where they'll want to start.
It also now takes a config
object, which contains three properties: clientId
, clientSecret
, and audience
. Previously we were passing a magic string and doing some lookups within this file to get the correct environment variables, but that was obfuscating important details -- you need the client id, secret, and audience in order to authorize.
const authorizationConfiguration = { | ||
clientId: process.env.ADMINISTRATION_CLIENT_ID, | ||
clientSecret: process.env.ADMINISTRATION_CLIENT_SECRET, | ||
audience: process.env.ADMINISTRATION_AUDIENCE | ||
}; |
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 the idea of assigning this config object once per API client, rather than every single time. Open to feedback on it though.
"administration", | ||
administrationAudience | ||
); | ||
const accessToken = await getAccessToken(authorizationConfiguration); |
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 call gets simplified, and a little more clear IMO.
|
||
const url = `${optimizeApiUrl}/public/dashboard/${dashboardId}`; | ||
const optimizeApiUrl = process.env.OPTIMIZE_BASE_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.
Bug here: the key name didn't match what's in our .env.example, or the other client function (listDashboards
).
|
||
const url = `${optimizeApiUrl}/public/dashboard/${dashboardId}`; | ||
const optimizeApiUrl = process.env.OPTIMIZE_BASE_URL; | ||
const url = `${optimizeApiUrl}/api/public/dashboard/${dashboardId}`; |
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.
Bug here: the /api
was missing at the beginning, resulting in a failed API call.
@@ -55,7 +59,7 @@ async function deleteDashboard([dashboardId]) { | |||
|
|||
// Process the results from the API call. | |||
if (response.status === 204) { | |||
console.log(`Dashboard ${clientId} was deleted!`); | |||
console.log(`Dashboard ${dashboardId} was deleted!`); |
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.
Bug here: clientId
was copy-pasta, and this line was throwing an undefined variable error.
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.
Went through this for Optimize and it works!
[BLOCKED]: requires additional changes outside this PR to be merged at roughly the same time.
This PR rewrites the auth logic in our API tutorials, to simplify the logic, and to make it more clear to readers how the client ID, secret, and audience are used for authorization.
It also fixes a couple bugs in the Optimize API client code. I'll call these out with inline comments. I'm open to the idea that these bugs should be fixed in a separate PR, so that they don't have to wait for this and its related PRs (described in "Outside impact") to land.
Outside impact
There are multiple points of impact outside this PR:
Why are we rewriting the auth logic again?
While walking through #5, and reading comments there, I realized that the reusability I've been trying to accomplish in the auth logic contains an abstraction that is confusing, and also adds complexity to the auth.js logic that distracts readers from understanding how their code acquires authorization tokens.
This new approach to auth removes the abstraction ("administration" APIs vs "component" APIs) in favor of requiring the calling client to pass in its own client ID, secret, and audience. This makes it easier to read the auth.js code, and it makes it more obvious that those three bits are required to authorize an API client.