Auth: Implement Token URL JWT Auth (#52662)

* Auth: check of auth_token in url and resolve user if present

* check if auth_token is passed in url

* Auth: Pass auth_token for request if present in path

* no need to decode token in index

* temp

* use loadURLToken and set authorization header

* cache token in memory and strip it from url

* Use loadURLToken

* Keep token in url

* strip sensitive query strings from url used by context logger

* adapt login by url to jwt token

* add jwt iframe devenv

* add jwt iframe devenv instructions

* add access note

* add test for cleaning request

* ensure jwt token is not carried into handlers

* do not reshuffle queries, might be important

* add correct db dump location

* prefer set token instead of cached token

Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>

Co-authored-by: Karl Persson <kalle.persson@grafana.com>
Co-authored-by: Ieva <ieva.vasiljeva@grafana.com>
This commit is contained in:
Jo
2022-07-27 14:10:47 +00:00
committed by GitHub
parent 7ba076de10
commit c2d3c90bc8
12 changed files with 138 additions and 5 deletions

View File

@@ -17,6 +17,7 @@ import { AppEvents, DataQueryErrorType } from '@grafana/data';
import { BackendSrv as BackendService, BackendSrvRequest, config, FetchError, FetchResponse } from '@grafana/runtime';
import appEvents from 'app/core/app_events';
import { getConfig } from 'app/core/config';
import { loadUrlToken } from 'app/core/utils/urlToken';
import { DashboardSearchHit } from 'app/features/search/types';
import { getGrafanaStorage } from 'app/features/storage/storage';
import { TokenRevokedModal } from 'app/features/users/TokenRevokedModal';
@@ -128,6 +129,17 @@ export class BackendSrv implements BackendService {
options = this.parseRequestOptions(options);
const token = loadUrlToken();
if (token !== null && token !== '') {
if (!options.headers) {
options.headers = {};
}
if (config.jwtUrlLogin && config.jwtHeaderName) {
options.headers[config.jwtHeaderName] = `${token}`;
}
}
const fromFetchStream = this.getFromFetchStream<T>(options);
const failureStream = fromFetchStream.pipe(this.toFailureStream<T>(options));
const successStream = fromFetchStream.pipe(

View File

@@ -0,0 +1,13 @@
let cachedToken = '';
export const loadUrlToken = (): string | null => {
const params = new URLSearchParams(window.location.search);
const token = params.get('auth_token');
if (token !== null && token !== '') {
cachedToken = token;
return token;
}
return cachedToken;
};

View File

@@ -18,6 +18,7 @@ import {
StreamingFrameOptions,
} from '@grafana/runtime/src/services/live';
import { BackendDataSourceResponse } from '@grafana/runtime/src/utils/queryResponse';
import { loadUrlToken } from 'app/core/utils/urlToken';
import { StreamingResponseData } from '../data/utils';
@@ -66,7 +67,14 @@ export class CentrifugeService implements CentrifugeSrv {
constructor(private deps: CentrifugeSrvDeps) {
this.dataStreamSubscriberReadiness = deps.dataStreamSubscriberReadiness.pipe(share(), startWith(true));
const liveUrl = `${deps.appUrl.replace(/^http/, 'ws')}/api/live/ws`;
let liveUrl = `${deps.appUrl.replace(/^http/, 'ws')}/api/live/ws`;
const token = loadUrlToken();
if (token !== null && token !== '') {
liveUrl += '?auth_token=' + token;
}
this.centrifuge = new Centrifuge(liveUrl, {
timeout: 30000,
});