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

[SDK-2692] Remember organization ID for silent authentication #788

Merged
merged 17 commits into from
Sep 13, 2021

Conversation

stevehobbsdev
Copy link
Contributor

@stevehobbsdev stevehobbsdev commented Sep 3, 2021

Key changes:

  • If an org_id claim is returned in the ID token, the SDK stores it in a new cookie to act as a "hint" for silent authentication
  • When doing getTokenSilently with iframe + prompt=none, if the hint cookie is available and no value for organization was given in the options, the value from the cookie is read and sent to the authorization request
  • If the hint cookie is available and organization is present in the options, the options value takes precedence
  • The cookie is removed on logout
  • The cookie is removed if there is no org_id claim in the ID token

I also evolved the playground a bit so that you can toggle the organization ID being passed through constructor options or directly to loginWithRedirect. I also added JSON output at the bottom so that the options being used to initialize the SDK are visible.

@stevehobbsdev stevehobbsdev added CH: Fixed PR is fixing a bug review:medium Medium review labels Sep 3, 2021
@stevehobbsdev stevehobbsdev modified the milestone: vNext Sep 3, 2021
@stevehobbsdev stevehobbsdev marked this pull request as ready for review September 3, 2021 21:25
@stevehobbsdev stevehobbsdev requested a review from a team as a code owner September 3, 2021 21:25
this.orgHintCookieName,
decodedToken.claims.org_id
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do we need to remove the cookie if there is no org_id available?

else {
      this.cookieStorage.remove(this.orgHintCookieName);
}

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there's no org_id claim it means that the user did not sign in in the context of an organization, so IMO it makes sense to remove the cookie so that we don't try to use it the next time.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I was asking because in the above case, we are not removing it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You're right, I've added this now in 98dda60

this.cookieStorage.remove(
buildOrganizationHintCookieName(this.options.client_id)
);
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not setting the cookie here? I assume it is because we know it is already set at this point? But it's not entirely clear for me if this is actually 100% guaranteed.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Any reason we are not using this.orgHintCookieName here instead of buildOrganizationHintCookieName(this.options.client_id) ?

However, curious to know if it would make sense to drop this.orgHintCookieName and use buildOrganizationHintCookieName(this.options.client_id) everywhere. It would allow to work if client_id is ever changed after constructing an Auth0Client (Not sure we should explicitly support that, but we could).

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why are we not setting the cookie here?

We could set it here as well, I think that's fine.

Any reason we are not using this.orgHintCookieName here

I think that's just an oversight, I'd like to use it here as well. I did start down the road of just calling the function but I think if we wanted to support changing the client ID (can't think of a use-case right now) there'd be more work to do than just this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will also need to review getTokenWithPopup as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 0211ca7. getTokenWithPopup looks to be ok as it just calls loginWithPopup anyway.

@stevehobbsdev stevehobbsdev marked this pull request as draft September 7, 2021 10:19
@stevehobbsdev
Copy link
Contributor Author

Converting back to a draft for the meantime. I've identified a couple E2E tests I want to put in, as well as fix the existing broken one.

For the broken one, I'm still trying to figure out why its needed. The name of the test is "retrieves an access token for another audience using a refresh token" which is not something you can actually do. What it's actually testing is that the fallback to an iframe works but there's nothing about the test that should cause that to happen. Looking into it today.

adamjmcgrath
adamjmcgrath previously approved these changes Sep 8, 2021
@stevehobbsdev
Copy link
Contributor Author

I'm ready for a review now. I had intentions to get an E2E test set up for this but it specifically means intercepting the call to /authorize, which happens in an iframe. It looks to be challenging, but extra challenging here because the iframe is created just-in-time. For now I've tested the behaviour manually, and also set up the playground so that node-oidc-provider can echo the org_id claim back to us. I could verify the result in a test the org_id claim is written out to the screen, but that's kind of testing what the test itself is setting up. It would be an OK test if we were still hitting real Auth0.

@stevehobbsdev stevehobbsdev marked this pull request as ready for review September 8, 2021 15:55
Cypress.Commands.add('toggleSwitch', name =>
cy.get(`[data-cy=switch-${name}]`).click()
);
Cypress.Commands.add('setSwitch', (name, value) => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I refactored toggleSwitch into setSwitch where you explicitly set the value of the switch so you can see exactly what it's set to in a test. toggleSwitch requires you to understand what the default for that switch is, which is poor.

Comment on lines +28 to +30
claims: {
org_id: null
},
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I couldn't see a better way to do this, but this config for node-oidc-provider allows it to return an org_id claim, by specifying the claims parameter to the authentication request. Ideally, I'd like the server to just echo back the organization value in the org_id claim but couldn't find a good way to do it or if that scenario is supported by the server. Makes sense if it's strictly OIDC compliant.

https://openid.net/specs/openid-connect-core-1_0.html#ClaimsParameter

Comment on lines +229 to +238
<input
type="checkbox"
class="form-check-input"
id="org-login-check"
v-model="useOrgAtLogin"
data-cy="useOrgAtLogin"
/>
<label for="org-login-check" class="form-check-label"
>Apply only at loginWithRedirect</label
>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This allows us to toggle whether the organization ID is given to the constructor options, or only to loginWithRedirect for easy testing.

Comment on lines +372 to +376
<div class="col-md-12">
<h3 class="mb-3">Client Options</h3>

<pre><code>{{ clientOptions }}</code></pre>
</div>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Writing these options out just makes it easier to debug.

Comment on lines +526 to +534
if (_self.organization) {
const claims = {
id_token: {
org_id: { values: [_self.organization] }
}
};

clientOptions.claims = JSON.stringify(claims);
}
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is setting the claims parameter for the authentication call. Used when using node-oidc-provider as the OP, ignored by Auth0 completely (looks like it doesn't support it).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
CH: Fixed PR is fixing a bug review:medium Medium review
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants