mirror of
https://github.com/elastic/kibana.git
synced 2025-04-24 01:38:56 -04:00
[Actions] Implement generic API support for OAuth JWT authentication flow for the action connectors. (#117902)
* [Actions] Implement generic support for OAuth JWT authentication flow for the action connectors. * fixed test * fixed test * fixed test * added jsonwebtoken lib * fixed test * fixed test * added docs link * fixed due to comments * fixed test * fixed test * fixed typecheck * fixed due to comments * fixed test Co-authored-by: Kibana Machine <42973632+kibanamachine@users.noreply.github.com>
This commit is contained in:
parent
3d819705e1
commit
113688274c
10 changed files with 513 additions and 48 deletions
|
@ -184,6 +184,7 @@
|
|||
"@turf/distance": "6.0.1",
|
||||
"@turf/helpers": "6.0.1",
|
||||
"@turf/length": "^6.0.2",
|
||||
"@types/jsonwebtoken": "^8.5.6",
|
||||
"JSONStream": "1.3.5",
|
||||
"abort-controller": "^3.0.0",
|
||||
"abortcontroller-polyfill": "^1.7.3",
|
||||
|
|
|
@ -0,0 +1,51 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
jest.mock('jsonwebtoken', () => ({
|
||||
sign: jest.fn(),
|
||||
}));
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import jwt from 'jsonwebtoken';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { createJWTAssertion } from './create_jwt_assertion';
|
||||
|
||||
const jwtSign = jwt.sign as jest.Mock;
|
||||
const mockLogger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
describe('createJWTAssertion', () => {
|
||||
test('creating a JWT token from provided claims with default values', () => {
|
||||
jwtSign.mockReturnValueOnce('123456qwertyjwttoken');
|
||||
|
||||
const assertion = createJWTAssertion(mockLogger, 'test', '123456', {
|
||||
audience: '1',
|
||||
issuer: 'someappid',
|
||||
subject: 'test@gmail.com',
|
||||
});
|
||||
|
||||
expect(assertion).toMatchInlineSnapshot('"123456qwertyjwttoken"');
|
||||
});
|
||||
|
||||
test('throw the exception and log the proper error if token was not get successfuly', () => {
|
||||
jwtSign.mockImplementationOnce(() => {
|
||||
throw new Error('{"message": "jwt wrong header", "name": "JsonWebTokenError"}');
|
||||
});
|
||||
const fn = () =>
|
||||
createJWTAssertion(mockLogger, 'test', '123456', {
|
||||
audience: '1',
|
||||
issuer: 'someappid',
|
||||
subject: 'test@gmail.com',
|
||||
});
|
||||
expect(fn).toThrowError();
|
||||
|
||||
expect(mockLogger.warn.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"Unable to generate JWT token. Error: Error: {\\"message\\": \\"jwt wrong header\\", \\"name\\": \\"JsonWebTokenError\\"}",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,55 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
// eslint-disable-next-line import/no-extraneous-dependencies
|
||||
import jwt, { Algorithm } from 'jsonwebtoken';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
|
||||
export interface JWTClaims {
|
||||
audience: string;
|
||||
subject: string;
|
||||
issuer: string;
|
||||
expireInMilisecons?: number;
|
||||
keyId?: string;
|
||||
}
|
||||
|
||||
export function createJWTAssertion(
|
||||
logger: Logger,
|
||||
privateKey: string,
|
||||
privateKeyPassword: string,
|
||||
reservedClaims: JWTClaims,
|
||||
customClaims?: Record<string, string>
|
||||
): string {
|
||||
const { subject, audience, issuer, expireInMilisecons, keyId } = reservedClaims;
|
||||
const iat = Math.floor(Date.now() / 1000);
|
||||
|
||||
const headerObj = { algorithm: 'RS256' as Algorithm, ...(keyId ? { keyid: keyId } : {}) };
|
||||
|
||||
const payloadObj = {
|
||||
sub: subject, // subject claim identifies the principal that is the subject of the JWT
|
||||
aud: audience, // audience claim identifies the recipients that the JWT is intended for
|
||||
iss: issuer, // issuer claim identifies the principal that issued the JWT
|
||||
iat, // issued at claim identifies the time at which the JWT was issued
|
||||
exp: iat + (expireInMilisecons ?? 3600), // expiration time claim identifies the expiration time on or after which the JWT MUST NOT be accepted for processing
|
||||
...(customClaims ?? {}),
|
||||
};
|
||||
|
||||
try {
|
||||
const jwtToken = jwt.sign(
|
||||
JSON.stringify(payloadObj),
|
||||
{
|
||||
key: privateKey,
|
||||
passphrase: privateKeyPassword,
|
||||
},
|
||||
headerObj
|
||||
);
|
||||
return jwtToken;
|
||||
} catch (error) {
|
||||
const errorMessage = `Unable to generate JWT token. Error: ${error}`;
|
||||
logger.warn(errorMessage);
|
||||
throw new Error(errorMessage);
|
||||
}
|
||||
}
|
|
@ -11,8 +11,8 @@ jest.mock('axios', () => ({
|
|||
import axios from 'axios';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
import { requestOAuthClientCredentialsToken } from './request_oauth_client_credentials_token';
|
||||
|
||||
const createAxiosInstanceMock = axios.create as jest.Mock;
|
||||
const axiosInstanceMock = jest.fn();
|
||||
|
@ -122,7 +122,7 @@ describe('requestOAuthClientCredentialsToken', () => {
|
|||
|
||||
expect(mockLogger.warn.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"error thrown getting the access token from https://test for clientID: 123456: {\\"error\\":\\"invalid_scope\\",\\"error_description\\":\\"AADSTS70011: The provided value for the input parameter \'scope\' is not valid.\\"}",
|
||||
"error thrown getting the access token from https://test for params: {\\"scope\\":\\"test\\",\\"client_id\\":\\"123456\\",\\"client_secret\\":\\"secrert123\\"}: {\\"error\\":\\"invalid_scope\\",\\"error_description\\":\\"AADSTS70011: The provided value for the input parameter 'scope' is not valid.\\"}",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
|
|
@ -4,66 +4,40 @@
|
|||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import qs from 'query-string';
|
||||
import axios from 'axios';
|
||||
import stringify from 'json-stable-stringify';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { request } from './axios_utils';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
import { OAuthTokenResponse, requestOAuthToken } from './request_oauth_token';
|
||||
import { RewriteResponseCase } from '../../../../actions/common';
|
||||
|
||||
export const OAUTH_CLIENT_CREDENTIALS_GRANT_TYPE = 'client_credentials';
|
||||
|
||||
interface ClientCredentialsRequestParams {
|
||||
export interface ClientCredentialsOAuthRequestParams {
|
||||
scope?: string;
|
||||
clientId?: string;
|
||||
clientSecret?: string;
|
||||
}
|
||||
|
||||
export interface ClientCredentialsResponse {
|
||||
tokenType: string;
|
||||
accessToken: string;
|
||||
expiresIn: number;
|
||||
}
|
||||
const rewriteBodyRequest: RewriteResponseCase<ClientCredentialsOAuthRequestParams> = ({
|
||||
clientId,
|
||||
clientSecret,
|
||||
...res
|
||||
}) => ({
|
||||
...res,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
});
|
||||
|
||||
export async function requestOAuthClientCredentialsToken(
|
||||
tokenUrl: string,
|
||||
logger: Logger,
|
||||
params: ClientCredentialsRequestParams,
|
||||
params: ClientCredentialsOAuthRequestParams,
|
||||
configurationUtilities: ActionsConfigurationUtilities
|
||||
): Promise<ClientCredentialsResponse> {
|
||||
const axiosInstance = axios.create();
|
||||
const { clientId, clientSecret, scope } = params;
|
||||
|
||||
const res = await request({
|
||||
axios: axiosInstance,
|
||||
url: tokenUrl,
|
||||
method: 'post',
|
||||
logger,
|
||||
data: qs.stringify({
|
||||
scope,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
grant_type: OAUTH_CLIENT_CREDENTIALS_GRANT_TYPE,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
},
|
||||
): Promise<OAuthTokenResponse> {
|
||||
return await requestOAuthToken<ClientCredentialsOAuthRequestParams>(
|
||||
tokenUrl,
|
||||
OAUTH_CLIENT_CREDENTIALS_GRANT_TYPE,
|
||||
configurationUtilities,
|
||||
validateStatus: () => true,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
return {
|
||||
tokenType: res.data.token_type,
|
||||
accessToken: res.data.access_token,
|
||||
expiresIn: res.data.expires_in,
|
||||
};
|
||||
} else {
|
||||
const errString = stringify(res.data);
|
||||
logger.warn(
|
||||
`error thrown getting the access token from ${tokenUrl} for clientID: ${clientId}: ${errString}`
|
||||
);
|
||||
throw new Error(errString);
|
||||
}
|
||||
logger,
|
||||
rewriteBodyRequest(params)
|
||||
);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,131 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
jest.mock('axios', () => ({
|
||||
create: jest.fn(),
|
||||
}));
|
||||
import axios from 'axios';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
import { requestOAuthJWTToken } from './request_oauth_jwt_token';
|
||||
|
||||
const createAxiosInstanceMock = axios.create as jest.Mock;
|
||||
const axiosInstanceMock = jest.fn();
|
||||
|
||||
const mockLogger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
describe('requestOAuthJWTToken', () => {
|
||||
beforeEach(() => {
|
||||
createAxiosInstanceMock.mockReturnValue(axiosInstanceMock);
|
||||
});
|
||||
|
||||
test('making a token request with the required options', async () => {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
axiosInstanceMock.mockReturnValueOnce({
|
||||
status: 200,
|
||||
data: {
|
||||
tokenType: 'Bearer',
|
||||
accessToken: 'dfjsdfgdjhfgsjdf',
|
||||
expiresIn: 123,
|
||||
},
|
||||
});
|
||||
await requestOAuthJWTToken(
|
||||
'https://test',
|
||||
{
|
||||
assertion: 'someJWTvalueishere',
|
||||
clientId: 'client-id-1',
|
||||
clientSecret: 'some-client-secret',
|
||||
scope: 'test',
|
||||
},
|
||||
mockLogger,
|
||||
configurationUtilities
|
||||
);
|
||||
|
||||
expect(axiosInstanceMock.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"https://test",
|
||||
Object {
|
||||
"data": "assertion=someJWTvalueishere&client_id=client-id-1&client_secret=some-client-secret&grant_type=urn%3Aietf%3Aparams%3Aoauth%3Agrant-type%3Ajwt-bearer&scope=test",
|
||||
"headers": Object {
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
"httpAgent": undefined,
|
||||
"httpsAgent": Agent {
|
||||
"_events": Object {
|
||||
"free": [Function],
|
||||
"newListener": [Function],
|
||||
},
|
||||
"_eventsCount": 2,
|
||||
"_maxListeners": undefined,
|
||||
"_sessionCache": Object {
|
||||
"list": Array [],
|
||||
"map": Object {},
|
||||
},
|
||||
"defaultPort": 443,
|
||||
"freeSockets": Object {},
|
||||
"keepAlive": false,
|
||||
"keepAliveMsecs": 1000,
|
||||
"maxCachedSessions": 100,
|
||||
"maxFreeSockets": 256,
|
||||
"maxSockets": Infinity,
|
||||
"maxTotalSockets": Infinity,
|
||||
"options": Object {
|
||||
"path": null,
|
||||
"rejectUnauthorized": true,
|
||||
},
|
||||
"protocol": "https:",
|
||||
"requests": Object {},
|
||||
"scheduling": "lifo",
|
||||
"sockets": Object {},
|
||||
"totalSocketCount": 0,
|
||||
Symbol(kCapture): false,
|
||||
},
|
||||
"maxContentLength": 1000000,
|
||||
"method": "post",
|
||||
"proxy": false,
|
||||
"timeout": 360000,
|
||||
"validateStatus": [Function],
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('throw the exception and log the proper error if token was not get successfuly', async () => {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
axiosInstanceMock.mockReturnValueOnce({
|
||||
status: 400,
|
||||
data: {
|
||||
error: 'invalid_scope',
|
||||
error_description:
|
||||
"AADSTS70011: The provided value for the input parameter 'scope' is not valid.",
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
requestOAuthJWTToken(
|
||||
'https://test',
|
||||
{
|
||||
assertion: 'someJWTvalueishere',
|
||||
clientId: 'client-id-1',
|
||||
clientSecret: 'some-client-secret',
|
||||
scope: 'test',
|
||||
},
|
||||
mockLogger,
|
||||
configurationUtilities
|
||||
)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
'"{\\"error\\":\\"invalid_scope\\",\\"error_description\\":\\"AADSTS70011: The provided value for the input parameter \'scope\' is not valid.\\"}"'
|
||||
);
|
||||
|
||||
expect(mockLogger.warn.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"error thrown getting the access token from https://test for params: {\\"assertion\\":\\"someJWTvalueishere\\",\\"scope\\":\\"test\\",\\"client_id\\":\\"client-id-1\\",\\"client_secret\\":\\"some-client-secret\\"}: {\\"error\\":\\"invalid_scope\\",\\"error_description\\":\\"AADSTS70011: The provided value for the input parameter 'scope' is not valid.\\"}",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
import { OAuthTokenResponse, requestOAuthToken } from './request_oauth_token';
|
||||
import { RewriteResponseCase } from '../../../../actions/common';
|
||||
|
||||
// This is a standard for JSON Web Token (JWT) Profile
|
||||
// for OAuth 2.0 Client Authentication and Authorization Grants https://datatracker.ietf.org/doc/html/rfc7523#section-8.1
|
||||
export const OAUTH_JWT_BEARER_GRANT_TYPE = 'urn:ietf:params:oauth:grant-type:jwt-bearer';
|
||||
|
||||
interface JWTOAuthRequestParams {
|
||||
assertion: string;
|
||||
clientId?: string;
|
||||
clientSecret?: string;
|
||||
scope?: string;
|
||||
}
|
||||
|
||||
const rewriteBodyRequest: RewriteResponseCase<JWTOAuthRequestParams> = ({
|
||||
clientId,
|
||||
clientSecret,
|
||||
...res
|
||||
}) => ({
|
||||
...res,
|
||||
client_id: clientId,
|
||||
client_secret: clientSecret,
|
||||
});
|
||||
|
||||
export async function requestOAuthJWTToken(
|
||||
tokenUrl: string,
|
||||
params: JWTOAuthRequestParams,
|
||||
logger: Logger,
|
||||
configurationUtilities: ActionsConfigurationUtilities
|
||||
): Promise<OAuthTokenResponse> {
|
||||
return await requestOAuthToken<JWTOAuthRequestParams>(
|
||||
tokenUrl,
|
||||
OAUTH_JWT_BEARER_GRANT_TYPE,
|
||||
configurationUtilities,
|
||||
logger,
|
||||
rewriteBodyRequest(params)
|
||||
);
|
||||
}
|
|
@ -0,0 +1,138 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
jest.mock('axios', () => ({
|
||||
create: jest.fn(),
|
||||
}));
|
||||
import axios from 'axios';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { loggingSystemMock } from '../../../../../../src/core/server/mocks';
|
||||
import { requestOAuthToken } from './request_oauth_token';
|
||||
import { actionsConfigMock } from '../../actions_config.mock';
|
||||
|
||||
const createAxiosInstanceMock = axios.create as jest.Mock;
|
||||
const axiosInstanceMock = jest.fn();
|
||||
|
||||
const mockLogger = loggingSystemMock.create().get() as jest.Mocked<Logger>;
|
||||
|
||||
interface TestOAuthRequestParams {
|
||||
someAdditionalParam?: string;
|
||||
clientId?: string;
|
||||
clientSecret?: string;
|
||||
}
|
||||
|
||||
describe('requestOAuthToken', () => {
|
||||
beforeEach(() => {
|
||||
createAxiosInstanceMock.mockReturnValue(axiosInstanceMock);
|
||||
});
|
||||
|
||||
test('making a token request with the required options', async () => {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
axiosInstanceMock.mockReturnValueOnce({
|
||||
status: 200,
|
||||
data: {
|
||||
tokenType: 'Bearer',
|
||||
accessToken: 'dfjsdfgdjhfgsjdf',
|
||||
expiresIn: 123,
|
||||
},
|
||||
});
|
||||
|
||||
await requestOAuthToken<TestOAuthRequestParams>(
|
||||
'https://test',
|
||||
'test',
|
||||
configurationUtilities,
|
||||
mockLogger,
|
||||
{
|
||||
client_id: '123456',
|
||||
client_secret: 'secrert123',
|
||||
some_additional_param: 'test',
|
||||
}
|
||||
);
|
||||
|
||||
expect(axiosInstanceMock.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"https://test",
|
||||
Object {
|
||||
"data": "client_id=123456&client_secret=secrert123&grant_type=test&some_additional_param=test",
|
||||
"headers": Object {
|
||||
"Content-Type": "application/x-www-form-urlencoded; charset=UTF-8",
|
||||
},
|
||||
"httpAgent": undefined,
|
||||
"httpsAgent": Agent {
|
||||
"_events": Object {
|
||||
"free": [Function],
|
||||
"newListener": [Function],
|
||||
},
|
||||
"_eventsCount": 2,
|
||||
"_maxListeners": undefined,
|
||||
"_sessionCache": Object {
|
||||
"list": Array [],
|
||||
"map": Object {},
|
||||
},
|
||||
"defaultPort": 443,
|
||||
"freeSockets": Object {},
|
||||
"keepAlive": false,
|
||||
"keepAliveMsecs": 1000,
|
||||
"maxCachedSessions": 100,
|
||||
"maxFreeSockets": 256,
|
||||
"maxSockets": Infinity,
|
||||
"maxTotalSockets": Infinity,
|
||||
"options": Object {
|
||||
"path": null,
|
||||
"rejectUnauthorized": true,
|
||||
},
|
||||
"protocol": "https:",
|
||||
"requests": Object {},
|
||||
"scheduling": "lifo",
|
||||
"sockets": Object {},
|
||||
"totalSocketCount": 0,
|
||||
Symbol(kCapture): false,
|
||||
},
|
||||
"maxContentLength": 1000000,
|
||||
"method": "post",
|
||||
"proxy": false,
|
||||
"timeout": 360000,
|
||||
"validateStatus": [Function],
|
||||
},
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test('throw the exception and log the proper error if token was not get successfuly', async () => {
|
||||
const configurationUtilities = actionsConfigMock.create();
|
||||
axiosInstanceMock.mockReturnValueOnce({
|
||||
status: 400,
|
||||
data: {
|
||||
error: 'invalid_scope',
|
||||
error_description:
|
||||
"AADSTS70011: The provided value for the input parameter 'scope' is not valid.",
|
||||
},
|
||||
});
|
||||
|
||||
await expect(
|
||||
requestOAuthToken<TestOAuthRequestParams>(
|
||||
'https://test',
|
||||
'test',
|
||||
configurationUtilities,
|
||||
mockLogger,
|
||||
{
|
||||
client_id: '123456',
|
||||
client_secret: 'secrert123',
|
||||
some_additional_param: 'test',
|
||||
}
|
||||
)
|
||||
).rejects.toThrowErrorMatchingInlineSnapshot(
|
||||
'"{\\"error\\":\\"invalid_scope\\",\\"error_description\\":\\"AADSTS70011: The provided value for the input parameter \'scope\' is not valid.\\"}"'
|
||||
);
|
||||
|
||||
expect(mockLogger.warn.mock.calls[0]).toMatchInlineSnapshot(`
|
||||
Array [
|
||||
"error thrown getting the access token from https://test for params: {\\"client_id\\":\\"123456\\",\\"client_secret\\":\\"secrert123\\",\\"some_additional_param\\":\\"test\\"}: {\\"error\\":\\"invalid_scope\\",\\"error_description\\":\\"AADSTS70011: The provided value for the input parameter 'scope' is not valid.\\"}",
|
||||
]
|
||||
`);
|
||||
});
|
||||
});
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License
|
||||
* 2.0; you may not use this file except in compliance with the Elastic License
|
||||
* 2.0.
|
||||
*/
|
||||
|
||||
import qs from 'query-string';
|
||||
import axios from 'axios';
|
||||
import stringify from 'json-stable-stringify';
|
||||
import { Logger } from '../../../../../../src/core/server';
|
||||
import { request } from './axios_utils';
|
||||
import { ActionsConfigurationUtilities } from '../../actions_config';
|
||||
import { AsApiContract } from '../../../../actions/common';
|
||||
|
||||
export interface OAuthTokenResponse {
|
||||
tokenType: string;
|
||||
accessToken: string;
|
||||
expiresIn: number;
|
||||
}
|
||||
|
||||
export async function requestOAuthToken<T>(
|
||||
tokenUrl: string,
|
||||
grantType: string,
|
||||
configurationUtilities: ActionsConfigurationUtilities,
|
||||
logger: Logger,
|
||||
bodyRequest: AsApiContract<T>
|
||||
): Promise<OAuthTokenResponse> {
|
||||
const axiosInstance = axios.create();
|
||||
|
||||
const res = await request({
|
||||
axios: axiosInstance,
|
||||
url: tokenUrl,
|
||||
method: 'post',
|
||||
logger,
|
||||
data: qs.stringify({
|
||||
...bodyRequest,
|
||||
grant_type: grantType,
|
||||
}),
|
||||
headers: {
|
||||
'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8',
|
||||
},
|
||||
configurationUtilities,
|
||||
validateStatus: () => true,
|
||||
});
|
||||
|
||||
if (res.status === 200) {
|
||||
return {
|
||||
tokenType: res.data.token_type,
|
||||
accessToken: res.data.access_token,
|
||||
expiresIn: res.data.expires_in,
|
||||
};
|
||||
} else {
|
||||
const errString = stringify(res.data);
|
||||
logger.warn(
|
||||
`error thrown getting the access token from ${tokenUrl} for params: ${JSON.stringify(
|
||||
bodyRequest
|
||||
)}: ${errString}`
|
||||
);
|
||||
throw new Error(errString);
|
||||
}
|
||||
}
|
|
@ -5783,6 +5783,13 @@
|
|||
resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.30.tgz#44cb52f32a809734ca562e685c6473b5754a7818"
|
||||
integrity sha512-sqm9g7mHlPY/43fcSNrCYfOeX9zkTTK+euO5E6+CVijSMm5tTjkVdwdqRkY3ljjIAf8679vps5jKUoJBCLsMDA==
|
||||
|
||||
"@types/jsonwebtoken@^8.5.6":
|
||||
version "8.5.6"
|
||||
resolved "https://registry.yarnpkg.com/@types/jsonwebtoken/-/jsonwebtoken-8.5.6.tgz#1913e5a61e70a192c5a444623da4901a7b1a9d42"
|
||||
integrity sha512-+P3O/xC7nzVizIi5VbF34YtqSonFsdnbXBnWUCYRiKOi1f9gA4sEFvXkrGr/QVV23IbMYvcoerI7nnhDUiWXRQ==
|
||||
dependencies:
|
||||
"@types/node" "*"
|
||||
|
||||
"@types/kbn__ace@link:bazel-bin/packages/kbn-ace/npm_module_types":
|
||||
version "0.0.0"
|
||||
uid ""
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue