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

Add an optional authentication mode for HTTP resources #58589

Merged
merged 33 commits into from
Mar 7, 2020

Conversation

mshustov
Copy link
Contributor

@mshustov mshustov commented Feb 26, 2020

Summary

closes #41959
The current PR adds optional authentication mode for HTTP resources that acts similarly to optional mode in hapi https://hapi.dev/api/?v=17.9.2#-routeoptionsauthmode

'optional' - authentication is optional - the request must include valid credentials or no credentials at all.

For this, I added the notHandled outcome for the auth interceptor. Getting it from auth hook, Kibana server behaves differently:

  • for optional authentication mode, allows an user to access a resource
  • for required authentication mode (default behavior), the Kibana server responds with the unauthorized response.

All other cases, such as handling redirection to 3rd party IdP is up to Security plugin internal logic. Should we create followup issues in the Security team backlog?
@elastic/kibana-security let me know if the current logic makes sense for you.

Checklist

Delete any items that are not applicable to this PR.

For maintainers

Dev docs

A route config accepts authRequired: 'optional'. A user can access a resource if has valid credentials or no credentials at all. Can be useful when we grant access to a resource but want to identify a user if possible.

router.get( { path: '/',  options: { authRequired: 'optional' } }, handler);

private getAuthOption(
authRequired: RouteConfigOptions<any>['authRequired'] = true
): false | { mode: 'required' | 'optional' } {
if (this.authRegistered) {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It sets mode explicitly. I had to change the logic to inline it with mode: optional declaration since it cannot be used without auth strategy registration. the previous implementation relies on the internal hapi logic that true === undefined.

@@ -684,6 +701,27 @@ describe('Auth', () => {
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
});

it('attach security header to not handled auth response', async () => {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

duplicates tests in src/core/server/http/integration_tests/router.test.ts should I remove them?

Copy link
Contributor

Choose a reason for hiding this comment

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

One seems enough. Not sure in which file it makes more sense to keep it though. There seems to be overlapping 'coverage' between the two test files.

const authOptions = request.route.settings.auth;
if (typeof authOptions === 'object') {
// 'try' is used in the legacy platform
if (authOptions.mode === 'optional' || authOptions.mode === 'try') {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

It will throw en exception in case of try after the migration is done. I decided to add an exhaustive check to catch the options introduced in the new hapi versions if any.

@mshustov mshustov added Feature:New Platform release_note:plugin_api_changes Contains a Plugin API changes section for the breaking plugin API changes section. Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v7.7.0 v8.0.0 labels Feb 26, 2020
@elasticmachine
Copy link
Contributor

Pinging @elastic/kibana-platform (Team:Platform)

@mshustov mshustov changed the title Issue 41959 optional auth Add an optional authentication mode for HTTP resources Feb 26, 2020
@mshustov
Copy link
Contributor Author

mshustov commented Feb 26, 2020

@elasticmachine merge upstream

@mshustov mshustov marked this pull request as ready for review February 26, 2020 17:05
@mshustov mshustov requested review from a team as code owners February 26, 2020 17:05
Copy link
Contributor

@pgayvallet pgayvallet left a comment

Choose a reason for hiding this comment

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

Implementation looks good to me.

const route = http.anonymousPaths.isAnonymous(window.location.pathname) ? '/defaults' : '';
const capabilities = await http.post<Capabilities>(`/api/core/capabilities${route}`, {
const capabilities = await http.post<Capabilities>('/api/core/capabilities', {
Copy link
Contributor

Choose a reason for hiding this comment

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

👍

const router = http.createRouter('/api/core/capabilities');
const router = http.createRouter('');
Copy link
Contributor

Choose a reason for hiding this comment

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

NIT: why remove the prefix here?

@@ -684,6 +701,27 @@ describe('Auth', () => {
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
});

it('attach security header to not handled auth response', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

One seems enough. Not sure in which file it makes more sense to keep it though. There seems to be overlapping 'coverage' between the two test files.

Comment on lines 64 to 69
isAuthenticated(result: AuthResult): result is Authenticated {
return result && result.type === AuthResultType.authenticated;
},
isNotHandled(result: AuthResult): result is AuthNotHandled {
return result && result.type === AuthResultType.notHandled;
},
Copy link
Contributor

Choose a reason for hiding this comment

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

Are the result && really needed? It seems result should not be false-ish with current typing?

Copy link
Member

Choose a reason for hiding this comment

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

I guess that's because we're receiving result from auth hook handler that can return whatever it wants? Then probably result?.type would be better since isNotHandled would always return boolean and not falsy value of result.

Copy link
Contributor Author

@mshustov mshustov Mar 4, 2020

Choose a reason for hiding this comment

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

right. this API can be called from js code

@azasypkin
Copy link
Member

ACK: will review tomorrow morning, sorry for the delay

@azasypkin azasypkin self-requested a review March 3, 2020 14:43
type: AuthResultType.notHandled,
};
},
redirected(headers: { location: string } & ResponseHeaders): AuthResult {
Copy link
Contributor

Choose a reason for hiding this comment

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

Has our convention been to use lowercase header names? Really just asking for my own education.

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 don't remember we ever discussed it

src/core/server/http/router/route.ts Show resolved Hide resolved
src/core/server/http/router/route.ts Outdated Show resolved Hide resolved
describe('Options', () => {
describe('authRequired', () => {
describe('optional', () => {
it('User has access to a route if auth mechanism not registered', async () => {
Copy link
Contributor

Choose a reason for hiding this comment

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

Should we verify the value of context.auth.isAuthenticated in these tests?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

we have separate tests for them, but it doesn't hurt since here we can verify:

  • a route was hit
  • auth API returns a correct result

src/core/server/index.ts Outdated Show resolved Hide resolved
@mshustov mshustov requested review from joshdover and azasypkin March 6, 2020 10:29
@azasypkin
Copy link
Member

ACK: reviewing...

Copy link
Member

@azasypkin azasypkin left a comment

Choose a reason for hiding this comment

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

LGTM. Tested locally with login route - seems to work as expected, thanks!

src/core/server/http/lifecycle/auth.ts Outdated Show resolved Hide resolved
@mshustov mshustov merged commit 5c43653 into elastic:master Mar 7, 2020
@mshustov mshustov deleted the issue-41959-optional-auth branch March 7, 2020 10:56
mshustov added a commit to mshustov/kibana that referenced this pull request Mar 7, 2020
* add authRequred: 'optional'

* expose auth status via request context

* update security plugin to use notHandled auth outcome

* capabilities service uses optional auth

* update tests

* attach security headers only to unauthorised response

* add isAuthenticated tests for 'optional' auth mode

* security plugin relies on http.auth.isAuthenticated to calc capabilities

* generate docs

* reword test suit names

* update tests

* update test checking isAuth on optional auth path

* address Oleg comments

* add test for auth: try

* fix

* pass isAuthenticted as boolean via context

* remove response header from notHandled

* update docs

* add redirected for auth interceptor

* security plugin uses t.redirected to be compat with auth: optional

* update docs

* require location header in the interface

* address comments #1

* declare isAuthenticated on KibanaRequest

* remove auth.isAuthenticated from scope

* update docs

* remove unnecessary comment

* do not fail on FakrRequest

* small improvements
@kibanamachine
Copy link
Contributor

💚 Build Succeeded

History

To update your PR or re-run it, just comment with:
@elasticmachine merge upstream

mshustov added a commit that referenced this pull request Mar 7, 2020
* add authRequred: 'optional'

* expose auth status via request context

* update security plugin to use notHandled auth outcome

* capabilities service uses optional auth

* update tests

* attach security headers only to unauthorised response

* add isAuthenticated tests for 'optional' auth mode

* security plugin relies on http.auth.isAuthenticated to calc capabilities

* generate docs

* reword test suit names

* update tests

* update test checking isAuth on optional auth path

* address Oleg comments

* add test for auth: try

* fix

* pass isAuthenticted as boolean via context

* remove response header from notHandled

* update docs

* add redirected for auth interceptor

* security plugin uses t.redirected to be compat with auth: optional

* update docs

* require location header in the interface

* address comments #1

* declare isAuthenticated on KibanaRequest

* remove auth.isAuthenticated from scope

* update docs

* remove unnecessary comment

* do not fail on FakrRequest

* small improvements
gmmorris added a commit to gmmorris/kibana that referenced this pull request Mar 9, 2020
* master: (154 commits)
  Add an optional authentication mode for HTTP resources (elastic#58589)
  Implement embeddable drilldown menu options (elastic#59232)
  [Alerting] "Create alert" graph visualization design improvements (elastic#59399)
  Alerting update route throttle property is missing (elastic#59580)
  [SIEM] Adds 'Load prebuilt rules' Cypress test (elastic#59529)
  Show error if field is not found during filter rendering (elastic#59298)
  Navigate back to discover app during test, because the saved search from the preceding test has major performance problems when used with this test (elastic#59571)
  Check for alert dialog when doing a force logout (elastic#59329)
  ensure fs deletes are not cwd dependent (elastic#59570)
  Empty message for APM service map (elastic#59518)
  [Drilldowns] <ActionWizard/> Component (elastic#59032)
  [Reporting] Improve the page exit error messages (elastic#59351)
  Ensure logged out starting state for tests that need it (elastic#59322)
  Hide input value from kbn-config-schema error messages (elastic#58843)
  [ML] Transforms: Migrate client plugin to NP. (elastic#59443)
  [ML] Disable failing functional tests
  [SIEM] Update Timeline to use the latest euiFlyoutBody style (elastic#59524)
  Temporarily remove the project mappings for PR labels (elastic#59493)
  [Alerting] replace index threshold graph usage of watcher APIs with new API (elastic#59385)
  [ML] Show view series link in anomalies table for machine_learning_user role (elastic#59549)
  ...
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Feature:New Platform release_note:plugin_api_changes Contains a Plugin API changes section for the breaking plugin API changes section. Team:Core Core services & architecture: plugins, logging, config, saved objects, http, ES client, i18n, etc v7.7.0 v8.0.0
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Add an optional authentication mode for HTTP resources
7 participants