* extend AuthResultData with response headers * add tests * update docs * rename headers --> requestHeaders to clarify intention * update docs * address comments
This commit is contained in:
parent
c6345ac995
commit
2dcc69c4fe
22 changed files with 295 additions and 76 deletions
|
@ -9,5 +9,5 @@ Auth Headers map
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export declare type AuthHeaders = Record<string, string>;
|
||||
export declare type AuthHeaders = Record<string, string | string[]>;
|
||||
```
|
||||
|
|
|
@ -1,13 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthResultData](./kibana-plugin-server.authresultdata.md) > [headers](./kibana-plugin-server.authresultdata.headers.md)
|
||||
|
||||
## AuthResultData.headers property
|
||||
|
||||
Auth specific headers to authenticate a user against Elasticsearch.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
headers: AuthHeaders;
|
||||
```
|
|
@ -1,21 +0,0 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthResultData](./kibana-plugin-server.authresultdata.md)
|
||||
|
||||
## AuthResultData interface
|
||||
|
||||
Result of an incoming request authentication.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface AuthResultData
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [headers](./kibana-plugin-server.authresultdata.headers.md) | <code>AuthHeaders</code> | Auth specific headers to authenticate a user against Elasticsearch. |
|
||||
| [state](./kibana-plugin-server.authresultdata.state.md) | <code>Record<string, any></code> | Data to associate with an incoming request. Any downstream plugin may get access to the data. |
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthResultParams](./kibana-plugin-server.authresultparams.md)
|
||||
|
||||
## AuthResultParams interface
|
||||
|
||||
Result of an incoming request authentication.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
export interface AuthResultParams
|
||||
```
|
||||
|
||||
## Properties
|
||||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [requestHeaders](./kibana-plugin-server.authresultparams.requestheaders.md) | <code>AuthHeaders</code> | Auth specific headers to attach to a request object. Used to perform a request to Elasticsearch on behalf of an authenticated user. |
|
||||
| [responseHeaders](./kibana-plugin-server.authresultparams.responseheaders.md) | <code>AuthHeaders</code> | Auth specific headers to attach to a response object. Used to send back authentication mechanism related headers to a client when needed. |
|
||||
| [state](./kibana-plugin-server.authresultparams.state.md) | <code>Record<string, any></code> | Data to associate with an incoming request. Any downstream plugin may get access to the data. |
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthResultParams](./kibana-plugin-server.authresultparams.md) > [requestHeaders](./kibana-plugin-server.authresultparams.requestheaders.md)
|
||||
|
||||
## AuthResultParams.requestHeaders property
|
||||
|
||||
Auth specific headers to attach to a request object. Used to perform a request to Elasticsearch on behalf of an authenticated user.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
requestHeaders?: AuthHeaders;
|
||||
```
|
|
@ -0,0 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthResultParams](./kibana-plugin-server.authresultparams.md) > [responseHeaders](./kibana-plugin-server.authresultparams.responseheaders.md)
|
||||
|
||||
## AuthResultParams.responseHeaders property
|
||||
|
||||
Auth specific headers to attach to a response object. Used to send back authentication mechanism related headers to a client when needed.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
responseHeaders?: AuthHeaders;
|
||||
```
|
|
@ -1,13 +1,13 @@
|
|||
<!-- Do not edit this file. It is automatically generated by API Documenter. -->
|
||||
|
||||
[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthResultData](./kibana-plugin-server.authresultdata.md) > [state](./kibana-plugin-server.authresultdata.state.md)
|
||||
[Home](./index.md) > [kibana-plugin-server](./kibana-plugin-server.md) > [AuthResultParams](./kibana-plugin-server.authresultparams.md) > [state](./kibana-plugin-server.authresultparams.state.md)
|
||||
|
||||
## AuthResultData.state property
|
||||
## AuthResultParams.state property
|
||||
|
||||
Data to associate with an incoming request. Any downstream plugin may get access to the data.
|
||||
|
||||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
state: Record<string, any>;
|
||||
state?: Record<string, any>;
|
||||
```
|
|
@ -9,5 +9,5 @@ Authentication is successful with given credentials, allow request to pass throu
|
|||
<b>Signature:</b>
|
||||
|
||||
```typescript
|
||||
authenticated: (data?: Partial<AuthResultData>) => AuthResult;
|
||||
authenticated: (data?: AuthResultParams) => AuthResult;
|
||||
```
|
||||
|
|
|
@ -16,7 +16,7 @@ export interface AuthToolkit
|
|||
|
||||
| Property | Type | Description |
|
||||
| --- | --- | --- |
|
||||
| [authenticated](./kibana-plugin-server.authtoolkit.authenticated.md) | <code>(data?: Partial<AuthResultData>) => AuthResult</code> | Authentication is successful with given credentials, allow request to pass through |
|
||||
| [authenticated](./kibana-plugin-server.authtoolkit.authenticated.md) | <code>(data?: AuthResultParams) => AuthResult</code> | Authentication is successful with given credentials, allow request to pass through |
|
||||
| [redirected](./kibana-plugin-server.authtoolkit.redirected.md) | <code>(url: string) => AuthResult</code> | Authentication requires to interrupt request handling and redirect to a configured url |
|
||||
| [rejected](./kibana-plugin-server.authtoolkit.rejected.md) | <code>(error: Error, options?: {</code><br/><code> statusCode?: number;</code><br/><code> }) => AuthResult</code> | Authentication is unsuccessful, fail the request with specified error. |
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ The plugin integrates with the core system via lifecycle events: `setup`<!-- -->
|
|||
|
||||
| Interface | Description |
|
||||
| --- | --- |
|
||||
| [AuthResultData](./kibana-plugin-server.authresultdata.md) | Result of an incoming request authentication. |
|
||||
| [AuthResultParams](./kibana-plugin-server.authresultparams.md) | Result of an incoming request authentication. |
|
||||
| [AuthToolkit](./kibana-plugin-server.authtoolkit.md) | A tool set defining an outcome of Auth interceptor for incoming request. |
|
||||
| [CallAPIOptions](./kibana-plugin-server.callapioptions.md) | The set of options that defines how API call should be made and result be processed. |
|
||||
| [CoreSetup](./kibana-plugin-server.coresetup.md) | Context passed to the plugins <code>setup</code> method. |
|
||||
|
|
|
@ -916,6 +916,99 @@ describe('setup contract', () => {
|
|||
expect(fromRegisterOnPostAuth).toEqual({});
|
||||
expect(fromRouteHandler).toEqual({});
|
||||
});
|
||||
|
||||
it('attach security header to a successful response', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { registerAuth, registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
await registerAuth((req, t) => {
|
||||
return t.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
const router = new Router('/');
|
||||
router.get({ path: '/', validate: false }, (req, res) => res.ok({ header: 'ok' }));
|
||||
registerRouter(router);
|
||||
|
||||
await server.start();
|
||||
|
||||
const response = await supertest(innerServer.listener)
|
||||
.get('/')
|
||||
.expect(200);
|
||||
|
||||
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
|
||||
});
|
||||
|
||||
it('attach security header to an error response', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { registerAuth, registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
await registerAuth((req, t) => {
|
||||
return t.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
const router = new Router('/');
|
||||
router.get({ path: '/', validate: false }, (req, res) => res.badRequest(new Error('reason')));
|
||||
registerRouter(router);
|
||||
|
||||
await server.start();
|
||||
|
||||
const response = await supertest(innerServer.listener)
|
||||
.get('/')
|
||||
.expect(400);
|
||||
|
||||
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
|
||||
});
|
||||
|
||||
// TODO un-skip when NP ResponseFactory supports configuring custom headers
|
||||
it.skip('logs warning if Auth Security Header rewrites response header for success response', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { registerAuth, registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
await registerAuth((req, t) => {
|
||||
return t.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
const router = new Router('/');
|
||||
router.get({ path: '/', validate: false }, (req, res) => res.ok({}));
|
||||
registerRouter(router);
|
||||
|
||||
await server.start();
|
||||
|
||||
await supertest(innerServer.listener)
|
||||
.get('/')
|
||||
.expect(200);
|
||||
|
||||
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot();
|
||||
});
|
||||
|
||||
it.skip('logs warning if Auth Security Header rewrites response header for error response', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { registerAuth, registerRouter, server: innerServer } = await server.setup(config);
|
||||
|
||||
await registerAuth((req, t) => {
|
||||
return t.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
const router = new Router('/');
|
||||
router.get({ path: '/', validate: false }, (req, res) => res.badRequest(new Error('reason')));
|
||||
registerRouter(router);
|
||||
|
||||
await server.start();
|
||||
|
||||
await supertest(innerServer.listener)
|
||||
.get('/')
|
||||
.expect(400);
|
||||
|
||||
expect(loggingServiceMock.collect(logger).warn).toMatchInlineSnapshot();
|
||||
});
|
||||
});
|
||||
|
||||
describe('#auth.isAuthenticated()', () => {
|
||||
|
|
|
@ -25,7 +25,7 @@ import { createServer, getListenerOptions, getServerOptions } from './http_tools
|
|||
import { adoptToHapiAuthFormat, AuthenticationHandler } from './lifecycle/auth';
|
||||
import { adoptToHapiOnPostAuthFormat, OnPostAuthHandler } from './lifecycle/on_post_auth';
|
||||
import { adoptToHapiOnPreAuthFormat, OnPreAuthHandler } from './lifecycle/on_pre_auth';
|
||||
import { Router, KibanaRequest } from './router';
|
||||
import { Router, KibanaRequest, ResponseHeaders } from './router';
|
||||
import {
|
||||
SessionStorageCookieOptions,
|
||||
createCookieSessionStorageFactory,
|
||||
|
@ -90,11 +90,13 @@ export class HttpServer {
|
|||
|
||||
private readonly log: Logger;
|
||||
private readonly authState: AuthStateStorage;
|
||||
private readonly authHeaders: AuthHeadersStorage;
|
||||
private readonly authRequestHeaders: AuthHeadersStorage;
|
||||
private readonly authResponseHeaders: AuthHeadersStorage;
|
||||
|
||||
constructor(private readonly logger: LoggerFactory, private readonly name: string) {
|
||||
this.authState = new AuthStateStorage(() => this.authRegistered);
|
||||
this.authHeaders = new AuthHeadersStorage();
|
||||
this.authRequestHeaders = new AuthHeadersStorage();
|
||||
this.authResponseHeaders = new AuthHeadersStorage();
|
||||
this.log = logger.get('http', 'server', name);
|
||||
}
|
||||
|
||||
|
@ -131,7 +133,7 @@ export class HttpServer {
|
|||
auth: {
|
||||
get: this.authState.get,
|
||||
isAuthenticated: this.authState.isAuthenticated,
|
||||
getAuthHeaders: this.authHeaders.get,
|
||||
getAuthHeaders: this.authRequestHeaders.get,
|
||||
},
|
||||
isTlsEnabled: config.ssl.enabled,
|
||||
// Return server instance with the connection options so that we can properly
|
||||
|
@ -247,12 +249,19 @@ export class HttpServer {
|
|||
this.authRegistered = true;
|
||||
|
||||
this.server.auth.scheme('login', () => ({
|
||||
authenticate: adoptToHapiAuthFormat(fn, (req, { state, headers }) => {
|
||||
authenticate: adoptToHapiAuthFormat(fn, (req, { state, requestHeaders, responseHeaders }) => {
|
||||
this.authState.set(req, state);
|
||||
this.authHeaders.set(req, headers);
|
||||
// we mutate headers only for the backward compatibility with the legacy platform.
|
||||
// where some plugin read directly from headers to identify whether a user is authenticated.
|
||||
Object.assign(req.headers, headers);
|
||||
|
||||
if (responseHeaders) {
|
||||
this.authResponseHeaders.set(req, responseHeaders);
|
||||
}
|
||||
|
||||
if (requestHeaders) {
|
||||
this.authRequestHeaders.set(req, requestHeaders);
|
||||
// we mutate headers only for the backward compatibility with the legacy platform.
|
||||
// where some plugin read directly from headers to identify whether a user is authenticated.
|
||||
Object.assign(req.headers, requestHeaders);
|
||||
}
|
||||
}),
|
||||
}));
|
||||
this.server.auth.strategy('session', 'login');
|
||||
|
@ -262,5 +271,40 @@ export class HttpServer {
|
|||
// should be applied for all routes if they don't specify auth strategy in route declaration
|
||||
// https://github.com/hapijs/hapi/blob/master/API.md#-serverauthdefaultoptions
|
||||
this.server.auth.default('session');
|
||||
|
||||
this.server.ext('onPreResponse', (request, t) => {
|
||||
const authResponseHeaders = this.authResponseHeaders.get(request);
|
||||
this.extendResponseWithHeaders(request, authResponseHeaders);
|
||||
return t.continue;
|
||||
});
|
||||
}
|
||||
|
||||
private extendResponseWithHeaders(request: Request, headers?: ResponseHeaders) {
|
||||
const response = request.response;
|
||||
if (!headers || !response) return;
|
||||
|
||||
if (response instanceof Error) {
|
||||
this.findHeadersIntersection(response.output.headers, headers);
|
||||
// hapi wraps all error response in Boom object internally
|
||||
response.output.headers = {
|
||||
...response.output.headers,
|
||||
...(headers as any), // hapi types don't specify string[] as valid value
|
||||
};
|
||||
} else {
|
||||
for (const [headerName, headerValue] of Object.entries(headers)) {
|
||||
this.findHeadersIntersection(response.headers, headers);
|
||||
response.header(headerName, headerValue as any); // hapi types don't specify string[] as valid value
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// NOTE: responseHeaders contains not a full list of response headers, but only explicitly set on a response object.
|
||||
// any headers added by hapi internally, like `content-type`, `content-length`, etc. do not present here.
|
||||
private findHeadersIntersection(responseHeaders: ResponseHeaders, headers: ResponseHeaders) {
|
||||
Object.keys(headers).forEach(headerName => {
|
||||
if (responseHeaders[headerName] !== undefined) {
|
||||
this.log.warn(`Server rewrites a response header [${headerName}].`);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,6 +30,11 @@ export {
|
|||
} from './router';
|
||||
export { BasePathProxyServer } from './base_path_proxy_server';
|
||||
export { OnPreAuthHandler, OnPreAuthToolkit } from './lifecycle/on_pre_auth';
|
||||
export { AuthenticationHandler, AuthHeaders, AuthResultData, AuthToolkit } from './lifecycle/auth';
|
||||
export {
|
||||
AuthenticationHandler,
|
||||
AuthHeaders,
|
||||
AuthResultParams,
|
||||
AuthToolkit,
|
||||
} from './lifecycle/auth';
|
||||
export { OnPostAuthHandler, OnPostAuthToolkit } from './lifecycle/on_post_auth';
|
||||
export { SessionStorageFactory, SessionStorage } from './session_storage';
|
||||
|
|
|
@ -101,7 +101,7 @@ describe('http service', () => {
|
|||
sessionStorage.set({ value: user, expires: Date.now() + sessionDurationMs });
|
||||
return t.authenticated({
|
||||
state: user,
|
||||
headers: {
|
||||
requestHeaders: {
|
||||
authorization: token,
|
||||
},
|
||||
});
|
||||
|
@ -167,7 +167,7 @@ describe('http service', () => {
|
|||
const { registerAuth, registerRouter } = http;
|
||||
|
||||
await registerAuth((req, t) => {
|
||||
return t.authenticated({ headers: authHeaders });
|
||||
return t.authenticated({ requestHeaders: authHeaders });
|
||||
});
|
||||
|
||||
const router = new Router('/new-platform');
|
||||
|
@ -187,7 +187,7 @@ describe('http service', () => {
|
|||
expect(headers).toEqual(authHeaders);
|
||||
});
|
||||
|
||||
it('pass request authorization header to Elasticsearch if registerAuth was not set', async () => {
|
||||
it('passes request authorization header to Elasticsearch if registerAuth was not set', async () => {
|
||||
const authorizationHeader = 'Basic: username:password';
|
||||
const { http, elasticsearch } = await root.setup();
|
||||
const { registerRouter } = http;
|
||||
|
@ -214,6 +214,56 @@ describe('http service', () => {
|
|||
authorization: authorizationHeader,
|
||||
});
|
||||
});
|
||||
|
||||
it('attach security header to a successful response handled by Legacy platform', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { http } = await root.setup();
|
||||
const { registerAuth } = http;
|
||||
|
||||
await registerAuth((req, t) => {
|
||||
return t.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
await root.start();
|
||||
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: '/legacy',
|
||||
handler: () => 'ok',
|
||||
});
|
||||
|
||||
const response = await kbnTestServer.request.get(root, '/legacy').expect(200);
|
||||
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
|
||||
});
|
||||
|
||||
it('attach security header to an error response handled by Legacy platform', async () => {
|
||||
const authResponseHeader = {
|
||||
'www-authenticate': 'Negotiate ade0234568a4209af8bc0280289eca',
|
||||
};
|
||||
const { http } = await root.setup();
|
||||
const { registerAuth } = http;
|
||||
|
||||
await registerAuth((req, t) => {
|
||||
return t.authenticated({ responseHeaders: authResponseHeader });
|
||||
});
|
||||
|
||||
await root.start();
|
||||
|
||||
const kbnServer = kbnTestServer.getKbnServer(root);
|
||||
kbnServer.server.route({
|
||||
method: 'GET',
|
||||
path: '/legacy',
|
||||
handler: () => {
|
||||
throw Boom.badRequest();
|
||||
},
|
||||
});
|
||||
|
||||
const response = await kbnTestServer.request.get(root, '/legacy').expect(400);
|
||||
expect(response.header['www-authenticate']).toBe(authResponseHeader['www-authenticate']);
|
||||
});
|
||||
});
|
||||
|
||||
describe('#registerOnPostAuth()', () => {
|
||||
|
|
|
@ -25,7 +25,7 @@ describe('adoptToHapiAuthFormat', () => {
|
|||
it('allows to associate arbitrary data with an incoming request', async () => {
|
||||
const authData = {
|
||||
state: { foo: 'bar' },
|
||||
headers: { authorization: 'baz' },
|
||||
requestHeaders: { authorization: 'baz' },
|
||||
};
|
||||
const authenticatedMock = jest.fn();
|
||||
const onSuccessMock = jest.fn();
|
||||
|
|
|
@ -27,7 +27,7 @@ enum ResultType {
|
|||
rejected = 'rejected',
|
||||
}
|
||||
|
||||
interface Authenticated extends AuthResultData {
|
||||
interface Authenticated extends AuthResultParams {
|
||||
type: ResultType.authenticated;
|
||||
}
|
||||
|
||||
|
@ -45,11 +45,12 @@ interface Rejected {
|
|||
type AuthResult = Authenticated | Rejected | Redirected;
|
||||
|
||||
const authResult = {
|
||||
authenticated(data: Partial<AuthResultData> = {}): AuthResult {
|
||||
authenticated(data: Partial<AuthResultParams> = {}): AuthResult {
|
||||
return {
|
||||
type: ResultType.authenticated,
|
||||
state: data.state || {},
|
||||
headers: data.headers || {},
|
||||
state: data.state,
|
||||
requestHeaders: data.requestHeaders,
|
||||
responseHeaders: data.responseHeaders,
|
||||
};
|
||||
},
|
||||
redirected(url: string): AuthResult {
|
||||
|
@ -82,21 +83,27 @@ const authResult = {
|
|||
* @public
|
||||
* */
|
||||
|
||||
export type AuthHeaders = Record<string, string>;
|
||||
export type AuthHeaders = Record<string, string | string[]>;
|
||||
|
||||
/**
|
||||
* Result of an incoming request authentication.
|
||||
* @public
|
||||
* */
|
||||
export interface AuthResultData {
|
||||
export interface AuthResultParams {
|
||||
/**
|
||||
* Data to associate with an incoming request. Any downstream plugin may get access to the data.
|
||||
*/
|
||||
state: Record<string, any>;
|
||||
state?: Record<string, any>;
|
||||
/**
|
||||
* Auth specific headers to authenticate a user against Elasticsearch.
|
||||
* Auth specific headers to attach to a request object.
|
||||
* Used to perform a request to Elasticsearch on behalf of an authenticated user.
|
||||
*/
|
||||
headers: AuthHeaders;
|
||||
requestHeaders?: AuthHeaders;
|
||||
/**
|
||||
* Auth specific headers to attach to a response object.
|
||||
* Used to send back authentication mechanism related headers to a client when needed.
|
||||
*/
|
||||
responseHeaders?: AuthHeaders;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -105,7 +112,7 @@ export interface AuthResultData {
|
|||
*/
|
||||
export interface AuthToolkit {
|
||||
/** Authentication is successful with given credentials, allow request to pass through */
|
||||
authenticated: (data?: Partial<AuthResultData>) => AuthResult;
|
||||
authenticated: (data?: AuthResultParams) => AuthResult;
|
||||
/** Authentication requires to interrupt request handling and redirect to a configured url */
|
||||
redirected: (url: string) => AuthResult;
|
||||
/** Authentication is unsuccessful, fail the request with specified error. */
|
||||
|
@ -127,7 +134,7 @@ export type AuthenticationHandler = (
|
|||
/** @public */
|
||||
export function adoptToHapiAuthFormat(
|
||||
fn: AuthenticationHandler,
|
||||
onSuccess: (req: Request, data: AuthResultData) => void = noop
|
||||
onSuccess: (req: Request, data: AuthResultParams) => void = noop
|
||||
) {
|
||||
return async function interceptAuth(
|
||||
req: Request,
|
||||
|
@ -141,7 +148,11 @@ export function adoptToHapiAuthFormat(
|
|||
);
|
||||
}
|
||||
if (authResult.isAuthenticated(result)) {
|
||||
onSuccess(req, { state: result.state, headers: result.headers });
|
||||
onSuccess(req, {
|
||||
state: result.state,
|
||||
requestHeaders: result.requestHeaders,
|
||||
responseHeaders: result.responseHeaders,
|
||||
});
|
||||
return h.authenticated({ credentials: result.state || {} });
|
||||
}
|
||||
if (authResult.isRedirected(result)) {
|
||||
|
|
|
@ -21,6 +21,7 @@ import { pick } from '../../../utils';
|
|||
|
||||
/** @public */
|
||||
export type Headers = Record<string, string | string[] | undefined>;
|
||||
export type ResponseHeaders = Record<string, string | string[]>;
|
||||
|
||||
const normalizeHeaderField = (field: string) => field.trim().toLowerCase();
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
* under the License.
|
||||
*/
|
||||
|
||||
export { Headers, filterHeaders } from './headers';
|
||||
export { Headers, filterHeaders, ResponseHeaders } from './headers';
|
||||
export { Router } from './router';
|
||||
export { KibanaRequest, KibanaRequestRoute, ensureRawRequest, isRealRequest } from './request';
|
||||
export { RouteMethod, RouteConfigOptions } from './route';
|
||||
|
|
|
@ -61,7 +61,7 @@ export {
|
|||
export {
|
||||
AuthenticationHandler,
|
||||
AuthHeaders,
|
||||
AuthResultData,
|
||||
AuthResultParams,
|
||||
AuthToolkit,
|
||||
GetAuthHeaders,
|
||||
KibanaRequest,
|
||||
|
|
|
@ -28,17 +28,18 @@ export type APICaller = (endpoint: string, clientParams: Record<string, any>, op
|
|||
export type AuthenticationHandler = (request: KibanaRequest, t: AuthToolkit) => AuthResult | Promise<AuthResult>;
|
||||
|
||||
// @public
|
||||
export type AuthHeaders = Record<string, string>;
|
||||
export type AuthHeaders = Record<string, string | string[]>;
|
||||
|
||||
// @public
|
||||
export interface AuthResultData {
|
||||
headers: AuthHeaders;
|
||||
state: Record<string, any>;
|
||||
export interface AuthResultParams {
|
||||
requestHeaders?: AuthHeaders;
|
||||
responseHeaders?: AuthHeaders;
|
||||
state?: Record<string, any>;
|
||||
}
|
||||
|
||||
// @public
|
||||
export interface AuthToolkit {
|
||||
authenticated: (data?: Partial<AuthResultData>) => AuthResult;
|
||||
authenticated: (data?: AuthResultParams) => AuthResult;
|
||||
redirected: (url: string) => AuthResult;
|
||||
rejected: (error: Error, options?: {
|
||||
statusCode?: number;
|
||||
|
|
|
@ -158,7 +158,7 @@ describe('setupAuthentication()', () => {
|
|||
expect(mockAuthToolkit.authenticated).toHaveBeenCalledTimes(1);
|
||||
expect(mockAuthToolkit.authenticated).toHaveBeenCalledWith({
|
||||
state: mockUser,
|
||||
headers: mockAuthHeaders,
|
||||
requestHeaders: mockAuthHeaders,
|
||||
});
|
||||
expect(mockAuthToolkit.redirected).not.toHaveBeenCalled();
|
||||
expect(mockAuthToolkit.rejected).not.toHaveBeenCalled();
|
||||
|
|
|
@ -107,7 +107,7 @@ export async function setupAuthentication({
|
|||
if (authenticationResult.succeeded()) {
|
||||
return t.authenticated({
|
||||
state: authenticationResult.user,
|
||||
headers: authenticationResult.authHeaders,
|
||||
requestHeaders: authenticationResult.authHeaders,
|
||||
});
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue