Compare commits
9 commits
master
...
auth-refac
Author | SHA1 | Date | |
---|---|---|---|
|
41d05a0dd1 | ||
|
44a3cd160f | ||
|
d0f273825f | ||
|
7b8189b77d | ||
|
b5383a312e | ||
|
6f27525ec2 | ||
|
7f9c51b143 | ||
|
80e9de991b | ||
|
8db9b1a2f0 |
17 changed files with 362 additions and 178 deletions
|
@ -48,6 +48,7 @@
|
||||||
"postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'"
|
"postinstall:warning": "echo '\n\nWARNING\n\nNot all node modules were installed because NODE_ENV is set to \"production\".\nThis should only be set after installing dependencies with \"yarn\". The app will not work.\n\n'"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@react-keycloak/web": "^3.4.0",
|
||||||
"auto-launch": "^5.0.5",
|
"auto-launch": "^5.0.5",
|
||||||
"electron-dl": "^1.11.0",
|
"electron-dl": "^1.11.0",
|
||||||
"electron-log": "^2.2.12",
|
"electron-log": "^2.2.12",
|
||||||
|
@ -58,6 +59,7 @@
|
||||||
"if-env": "^1.0.4",
|
"if-env": "^1.0.4",
|
||||||
"match-sorter": "^6.3.0",
|
"match-sorter": "^6.3.0",
|
||||||
"parse-duration": "^1.0.0",
|
"parse-duration": "^1.0.0",
|
||||||
|
"keycloak-js": "^15.0.2",
|
||||||
"react-datetime-picker": "^3.2.1",
|
"react-datetime-picker": "^3.2.1",
|
||||||
"react-plastic": "^1.1.1",
|
"react-plastic": "^1.1.1",
|
||||||
"react-top-loading-bar": "^2.0.1",
|
"react-top-loading-bar": "^2.0.1",
|
||||||
|
@ -156,7 +158,7 @@
|
||||||
"json-loader": "^0.5.4",
|
"json-loader": "^0.5.4",
|
||||||
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
"lbry-format": "https://github.com/lbryio/lbry-format.git",
|
||||||
"lbry-redux": "lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676",
|
"lbry-redux": "lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676",
|
||||||
"lbryinc": "lbryio/lbryinc#0b4e41ef90d6347819dd3453f2f9398a5c1b4f36",
|
"lbryinc": "lbryio/lbryinc#97fc74315c89580f9fa2408643b0f4feef42750a",
|
||||||
"lint-staged": "^7.0.2",
|
"lint-staged": "^7.0.2",
|
||||||
"localforage": "^1.7.1",
|
"localforage": "^1.7.1",
|
||||||
"lodash-es": "^4.17.14",
|
"lodash-es": "^4.17.14",
|
||||||
|
|
6
static/silent-check-sso.html
Normal file
6
static/silent-check-sso.html
Normal file
|
@ -0,0 +1,6 @@
|
||||||
|
<html>
|
||||||
|
<body>
|
||||||
|
<h1>Hi</h1>
|
||||||
|
<script>parent.postMessage(location.href, location.origin)</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
|
@ -29,6 +29,8 @@ import {
|
||||||
STATUS_FAILING,
|
STATUS_FAILING,
|
||||||
STATUS_DOWN,
|
STATUS_DOWN,
|
||||||
} from 'web/effects/use-degraded-performance';
|
} from 'web/effects/use-degraded-performance';
|
||||||
|
import { useKeycloak } from '@react-keycloak/web';
|
||||||
|
|
||||||
// @endif
|
// @endif
|
||||||
import LANGUAGE_MIGRATIONS from 'constants/language-migrations';
|
import LANGUAGE_MIGRATIONS from 'constants/language-migrations';
|
||||||
|
|
||||||
|
@ -156,6 +158,7 @@ function App(props: Props) {
|
||||||
const isRewardApproved = user && user.is_reward_approved;
|
const isRewardApproved = user && user.is_reward_approved;
|
||||||
const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
|
const previousHasVerifiedEmail = usePrevious(hasVerifiedEmail);
|
||||||
const previousRewardApproved = usePrevious(isRewardApproved);
|
const previousRewardApproved = usePrevious(isRewardApproved);
|
||||||
|
const { authenticated } = useKeycloak();
|
||||||
// @if TARGET='web'
|
// @if TARGET='web'
|
||||||
const [showAnalyticsNag, setShowAnalyticsNag] = usePersistedState('analytics-nag', true);
|
const [showAnalyticsNag, setShowAnalyticsNag] = usePersistedState('analytics-nag', true);
|
||||||
const [lbryTvApiStatus, setLbryTvApiStatus] = useState(STATUS_OK);
|
const [lbryTvApiStatus, setLbryTvApiStatus] = useState(STATUS_OK);
|
||||||
|
@ -174,6 +177,7 @@ function App(props: Props) {
|
||||||
const fromLbrytvParam = urlParams.get('sunset');
|
const fromLbrytvParam = urlParams.get('sunset');
|
||||||
const sanitizedReferrerParam = rawReferrerParam && rawReferrerParam.replace(':', '#');
|
const sanitizedReferrerParam = rawReferrerParam && rawReferrerParam.replace(':', '#');
|
||||||
const shouldHideNag = pathname.startsWith(`/$/${PAGES.EMBED}`) || pathname.startsWith(`/$/${PAGES.AUTH_VERIFY}`);
|
const shouldHideNag = pathname.startsWith(`/$/${PAGES.EMBED}`) || pathname.startsWith(`/$/${PAGES.AUTH_VERIFY}`);
|
||||||
|
// KC
|
||||||
const userId = user && user.id;
|
const userId = user && user.id;
|
||||||
const useCustomScrollbar = !IS_MAC;
|
const useCustomScrollbar = !IS_MAC;
|
||||||
const hasMyChannels = myChannelUrls && myChannelUrls.length > 0;
|
const hasMyChannels = myChannelUrls && myChannelUrls.length > 0;
|
||||||
|
@ -194,7 +198,13 @@ function App(props: Props) {
|
||||||
setShowAnalyticsNag(false);
|
setShowAnalyticsNag(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (authenticated) {
|
||||||
|
console.log('IS KC AUTHED');
|
||||||
|
}
|
||||||
|
}, [authenticated]);
|
||||||
// @endif
|
// @endif
|
||||||
|
// TODO KC HOWTO SETUSER
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (userId) {
|
if (userId) {
|
||||||
analytics.setUser(userId);
|
analytics.setUser(userId);
|
||||||
|
@ -317,7 +327,7 @@ function App(props: Props) {
|
||||||
if (previousHasVerifiedEmail === false && hasVerifiedEmail) {
|
if (previousHasVerifiedEmail === false && hasVerifiedEmail) {
|
||||||
analytics.emailVerifiedEvent();
|
analytics.emailVerifiedEvent();
|
||||||
}
|
}
|
||||||
}, [previousHasVerifiedEmail, hasVerifiedEmail, signIn]);
|
}, [previousHasVerifiedEmail, hasVerifiedEmail]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (previousRewardApproved === false && isRewardApproved) {
|
if (previousRewardApproved === false && isRewardApproved) {
|
||||||
|
@ -375,12 +385,14 @@ function App(props: Props) {
|
||||||
// We know someone is logging in or not when we get their user object
|
// We know someone is logging in or not when we get their user object
|
||||||
// We'll use this to determine when it's time to pull preferences
|
// We'll use this to determine when it's time to pull preferences
|
||||||
// This will no longer work if desktop users no longer get a user object from lbryinc
|
// This will no longer work if desktop users no longer get a user object from lbryinc
|
||||||
|
// KC TODO KEYCLOAK
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user) {
|
if (user) {
|
||||||
setReadyForPrefs(true);
|
setReadyForPrefs(true);
|
||||||
}
|
}
|
||||||
}, [user, setReadyForPrefs]);
|
}, [user, setReadyForPrefs]);
|
||||||
|
|
||||||
|
// TODO KEYCLOAK ISAUTHENTICATED
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (syncError && isAuthenticated && !pathname.includes(PAGES.AUTH_WALLET_PASSWORD) && !currentModal) {
|
if (syncError && isAuthenticated && !pathname.includes(PAGES.AUTH_WALLET_PASSWORD) && !currentModal) {
|
||||||
history.push(`/$/${PAGES.AUTH_WALLET_PASSWORD}?redirect=${pathname}`);
|
history.push(`/$/${PAGES.AUTH_WALLET_PASSWORD}?redirect=${pathname}`);
|
||||||
|
|
32
ui/component/auth/login.jsx
Normal file
32
ui/component/auth/login.jsx
Normal file
|
@ -0,0 +1,32 @@
|
||||||
|
import * as React from 'react';
|
||||||
|
import { useCallback } from 'react';
|
||||||
|
import { Redirect, useLocation } from 'react-router-dom';
|
||||||
|
|
||||||
|
import { useKeycloak } from '@react-keycloak/web';
|
||||||
|
|
||||||
|
const LoginPage = () => {
|
||||||
|
const location = useLocation();
|
||||||
|
const currentLocationState = location.state || {
|
||||||
|
from: { pathname: '/home' },
|
||||||
|
};
|
||||||
|
|
||||||
|
const { keycloak } = useKeycloak();
|
||||||
|
|
||||||
|
const login = useCallback(() => {
|
||||||
|
keycloak && keycloak.login().then((x) => console.log('cb', x));
|
||||||
|
}, [keycloak]);
|
||||||
|
|
||||||
|
if (keycloak && keycloak.authenticated) {
|
||||||
|
return <Redirect to={currentLocationState.from} />;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<button type="button" onClick={login}>
|
||||||
|
Login
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default LoginPage;
|
45
ui/component/auth/provider.jsx
Normal file
45
ui/component/auth/provider.jsx
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
// @flow
|
||||||
|
import * as React from 'react';
|
||||||
|
import { ReactKeycloakProvider, useKeycloak } from '@react-keycloak/web';
|
||||||
|
import keycloak from 'util/keycloak';
|
||||||
|
import type { Node } from 'react';
|
||||||
|
import {
|
||||||
|
getTokens,
|
||||||
|
} from 'util/saved-passwords';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
children: Node,
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AuthProvider = (props: Props) => {
|
||||||
|
const { children } = props;
|
||||||
|
const tokens = getTokens();
|
||||||
|
|
||||||
|
const eventLogger = (event, error) => {
|
||||||
|
console.log('onKeycloakEvent', event, error, keycloak);
|
||||||
|
// if (event === 'onReady') {
|
||||||
|
// setKeycloakReady(true);
|
||||||
|
// }
|
||||||
|
};
|
||||||
|
if (keycloak && tokens.access_token) {
|
||||||
|
return (
|
||||||
|
<ReactKeycloakProvider
|
||||||
|
authClient={keycloak}
|
||||||
|
onEvent={eventLogger}
|
||||||
|
initOptions={
|
||||||
|
{ onLoad: 'check-sso',
|
||||||
|
silentCheckSsoFallback: false,
|
||||||
|
redirectUri: 'http://localhost:9090/'}
|
||||||
|
} // from npmjs docs for @react-keycloak/web
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</ReactKeycloakProvider>
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
return (
|
||||||
|
<>{children}</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AuthProvider;
|
|
@ -1,4 +1,3 @@
|
||||||
import { DOMAIN } from 'config';
|
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { doSetDaemonSetting } from 'redux/actions/settings';
|
import { doSetDaemonSetting } from 'redux/actions/settings';
|
||||||
import { doSetWelcomeVersion, doToggle3PAnalytics, doSignOut } from 'redux/actions/app';
|
import { doSetWelcomeVersion, doToggle3PAnalytics, doSignOut } from 'redux/actions/app';
|
||||||
|
@ -10,17 +9,16 @@ import { version as appVersion } from 'package.json';
|
||||||
|
|
||||||
import PrivacyAgreement from './view';
|
import PrivacyAgreement from './view';
|
||||||
|
|
||||||
const select = state => ({
|
const select = (state) => ({
|
||||||
authenticated: selectUserVerifiedEmail(state),
|
authenticated: selectUserVerifiedEmail(state),
|
||||||
});
|
});
|
||||||
|
|
||||||
const perform = dispatch => ({
|
const perform = (dispatch) => ({
|
||||||
setWelcomeVersion: version => dispatch(doSetWelcomeVersion(version || WELCOME_VERSION)),
|
setWelcomeVersion: (version) => dispatch(doSetWelcomeVersion(version || WELCOME_VERSION)),
|
||||||
setShareDataInternal: share => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, share)),
|
setShareDataInternal: (share) => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, share)),
|
||||||
setShareDataThirdParty: share => dispatch(doToggle3PAnalytics(share)),
|
setShareDataThirdParty: (share) => dispatch(doToggle3PAnalytics(share)),
|
||||||
signOut: () => dispatch(doSignOut()),
|
signOut: () => dispatch(doSignOut()),
|
||||||
authenticateIfSharingData: () =>
|
authenticateIfSharingData: () => dispatch(doAuthenticate(appVersion, undefined, undefined, true)), // appVersion, shareData?, shareCallback, callInstall
|
||||||
dispatch(doAuthenticate(appVersion, undefined, undefined, true, undefined, undefined, DOMAIN)),
|
|
||||||
});
|
});
|
||||||
|
|
||||||
export default connect(select, perform)(PrivacyAgreement);
|
export default connect(select, perform)(PrivacyAgreement);
|
||||||
|
|
|
@ -10,8 +10,9 @@ import { parseURI, isURIValid } from 'lbry-redux';
|
||||||
import { SITE_TITLE, WELCOME_VERSION, SIMPLE_SITE } from 'config';
|
import { SITE_TITLE, WELCOME_VERSION, SIMPLE_SITE } from 'config';
|
||||||
import LoadingBarOneOff from 'component/loadingBarOneOff';
|
import LoadingBarOneOff from 'component/loadingBarOneOff';
|
||||||
import { GetLinksData } from 'util/buildHomepage';
|
import { GetLinksData } from 'util/buildHomepage';
|
||||||
|
import { useKeycloak } from '@react-keycloak/web';
|
||||||
import HomePage from 'page/home';
|
import HomePage from 'page/home';
|
||||||
|
import Login from 'component/auth/login';
|
||||||
|
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
const BackupPage = lazyImport(() => import('page/backup' /* webpackChunkName: "backup" */));
|
const BackupPage = lazyImport(() => import('page/backup' /* webpackChunkName: "backup" */));
|
||||||
|
@ -22,7 +23,7 @@ const Code2257Page = lazyImport(() => import('web/page/code2257' /* webpackChunk
|
||||||
// @endif
|
// @endif
|
||||||
|
|
||||||
// Chunk: "secondary"
|
// Chunk: "secondary"
|
||||||
const SignInPage = lazyImport(() => import('page/signIn' /* webpackChunkName: "secondary" */));
|
// const SignInPage = lazyImport(() => import('page/signIn' /* webpackChunkName: "secondary" */));
|
||||||
const SignInWalletPasswordPage = lazyImport(() =>
|
const SignInWalletPasswordPage = lazyImport(() =>
|
||||||
import('page/signInWalletPassword' /* webpackChunkName: "secondary" */)
|
import('page/signInWalletPassword' /* webpackChunkName: "secondary" */)
|
||||||
);
|
);
|
||||||
|
@ -86,7 +87,6 @@ const TopPage = lazyImport(() => import('page/top' /* webpackChunkName: "seconda
|
||||||
const UpdatePasswordPage = lazyImport(() => import('page/passwordUpdate' /* webpackChunkName: "passwordUpdate" */));
|
const UpdatePasswordPage = lazyImport(() => import('page/passwordUpdate' /* webpackChunkName: "passwordUpdate" */));
|
||||||
const Welcome = lazyImport(() => import('page/welcome' /* webpackChunkName: "secondary" */));
|
const Welcome = lazyImport(() => import('page/welcome' /* webpackChunkName: "secondary" */));
|
||||||
const YoutubeSyncPage = lazyImport(() => import('page/youtubeSync' /* webpackChunkName: "secondary" */));
|
const YoutubeSyncPage = lazyImport(() => import('page/youtubeSync' /* webpackChunkName: "secondary" */));
|
||||||
|
|
||||||
// Tell the browser we are handling scroll restoration
|
// Tell the browser we are handling scroll restoration
|
||||||
if ('scrollRestoration' in history) {
|
if ('scrollRestoration' in history) {
|
||||||
history.scrollRestoration = 'manual';
|
history.scrollRestoration = 'manual';
|
||||||
|
@ -128,14 +128,20 @@ function PrivateRoute(props: PrivateRouteProps) {
|
||||||
const { component: Component, isAuthenticated, ...rest } = props;
|
const { component: Component, isAuthenticated, ...rest } = props;
|
||||||
const urlSearchParams = new URLSearchParams(props.location.search);
|
const urlSearchParams = new URLSearchParams(props.location.search);
|
||||||
const redirectUrl = urlSearchParams.get('redirect');
|
const redirectUrl = urlSearchParams.get('redirect');
|
||||||
|
const { keycloak } = useKeycloak();
|
||||||
return (
|
return (
|
||||||
<Route
|
<Route
|
||||||
{...rest}
|
{...rest}
|
||||||
render={(props) =>
|
render={(props) =>
|
||||||
isAuthenticated || !IS_WEB ? (
|
(isAuthenticated || (keycloak && keycloak.authenticated)) || !IS_WEB ? (
|
||||||
<Component {...props} />
|
<Component {...props} />
|
||||||
) : (
|
) : (
|
||||||
<Redirect to={`/$/${PAGES.AUTH}?redirect=${redirectUrl || props.location.pathname}`} />
|
<Redirect
|
||||||
|
to={{
|
||||||
|
pathname: `/$/${PAGES.AUTH}?redirect=${redirectUrl || props.location.pathname}`,
|
||||||
|
state: { from: props.location },
|
||||||
|
}}
|
||||||
|
/>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
@ -264,10 +270,10 @@ function AppRouter(props: Props) {
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
<Route path={`/$/${PAGES.AUTH_SIGNIN}`} exact component={SignInPage} />
|
<Route path={`/$/${PAGES.AUTH_SIGNIN}`} exact component={Login} />
|
||||||
<Route path={`/$/${PAGES.AUTH_PASSWORD_RESET}`} exact component={PasswordResetPage} />
|
<Route path={`/$/${PAGES.AUTH_PASSWORD_RESET}`} exact component={PasswordResetPage} />
|
||||||
<Route path={`/$/${PAGES.AUTH_PASSWORD_SET}`} exact component={PasswordSetPage} />
|
<Route path={`/$/${PAGES.AUTH_PASSWORD_SET}`} exact component={PasswordSetPage} />
|
||||||
<Route path={`/$/${PAGES.AUTH}`} exact component={SignUpPage} />
|
<Route path={`/$/${PAGES.AUTH}`} exact component={Login} />
|
||||||
<Route path={`/$/${PAGES.AUTH}/*`} exact component={SignUpPage} />
|
<Route path={`/$/${PAGES.AUTH}/*`} exact component={SignUpPage} />
|
||||||
<Route path={`/$/${PAGES.WELCOME}`} exact component={Welcome} />
|
<Route path={`/$/${PAGES.WELCOME}`} exact component={Welcome} />
|
||||||
|
|
||||||
|
|
87
ui/index.jsx
87
ui/index.jsx
|
@ -14,7 +14,7 @@ import * as MODALS from 'constants/modal_types';
|
||||||
import React, { Fragment, useState, useEffect } from 'react';
|
import React, { Fragment, useState, useEffect } from 'react';
|
||||||
import ReactDOM from 'react-dom';
|
import ReactDOM from 'react-dom';
|
||||||
import { Provider } from 'react-redux';
|
import { Provider } from 'react-redux';
|
||||||
import { doDaemonReady, doAutoUpdate, doOpenModal, doHideModal, doToggle3PAnalytics } from 'redux/actions/app';
|
import { doLbryReady, doAutoUpdate, doOpenModal, doHideModal, doToggle3PAnalytics } from 'redux/actions/app';
|
||||||
import { Lbry, isURIValid, apiCall } from 'lbry-redux';
|
import { Lbry, isURIValid, apiCall } from 'lbry-redux';
|
||||||
import { setSearchApi } from 'redux/actions/search';
|
import { setSearchApi } from 'redux/actions/search';
|
||||||
import { doSetLanguage, doFetchLanguage, doUpdateIsNightAsync } from 'redux/actions/settings';
|
import { doSetLanguage, doFetchLanguage, doUpdateIsNightAsync } from 'redux/actions/settings';
|
||||||
|
@ -28,15 +28,12 @@ import { formatLbryUrlForWeb, formatInAppUrl } from 'util/url';
|
||||||
import { PersistGate } from 'redux-persist/integration/react';
|
import { PersistGate } from 'redux-persist/integration/react';
|
||||||
import analytics from 'analytics';
|
import analytics from 'analytics';
|
||||||
import { doToast } from 'redux/actions/notifications';
|
import { doToast } from 'redux/actions/notifications';
|
||||||
import {
|
import { ReactKeycloakProvider } from '@react-keycloak/web';
|
||||||
getAuthToken,
|
import keycloak from 'util/keycloak';
|
||||||
setAuthToken,
|
|
||||||
doDeprecatedPasswordMigrationMarch2020,
|
|
||||||
doAuthTokenRefresh,
|
|
||||||
} from 'util/saved-passwords';
|
|
||||||
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
|
||||||
import { LBRY_WEB_API, DEFAULT_LANGUAGE, LBRY_API_URL, LBRY_WEB_PUBLISH_API } from 'config';
|
|
||||||
|
|
||||||
|
import { getAuthToken, setAuthToken, doAuthTokenRefresh, getTokens, deleteAuthToken } from 'util/saved-passwords';
|
||||||
|
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
||||||
|
import { LBRY_WEB_API, DEFAULT_LANGUAGE, LBRY_API_URL, LBRY_WEB_PUBLISH_API, URL as SITE_URL } from 'config';
|
||||||
// Import 3rd-party styles before ours for the current way we are code-splitting.
|
// Import 3rd-party styles before ours for the current way we are code-splitting.
|
||||||
import 'scss/third-party.scss';
|
import 'scss/third-party.scss';
|
||||||
|
|
||||||
|
@ -112,28 +109,34 @@ if (process.env.SEARCH_API_URL) {
|
||||||
setSearchApi(process.env.SEARCH_API_URL);
|
setSearchApi(process.env.SEARCH_API_URL);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fix to make sure old users' cookies are set to the correct domain
|
// TODO KEYCLOAK
|
||||||
// This can be removed after March 11th, 2021
|
if (getTokens().auth_token) {
|
||||||
// https://github.com/lbryio/lbry-desktop/pull/3830
|
doAuthTokenRefresh();
|
||||||
doDeprecatedPasswordMigrationMarch2020();
|
}
|
||||||
doAuthTokenRefresh();
|
|
||||||
|
|
||||||
// We need to override Lbryio for getting/setting the authToken
|
// We need to override Lbryio for getting/setting the authToken
|
||||||
// We interact with ipcRenderer to get the auth key from a users keyring
|
// We interact with ipcRenderer to get the auth key from a users keyring
|
||||||
// We keep a local variable for authToken because `ipcRenderer.send` does not
|
// We keep a local variable for authToken because `ipcRenderer.send` does not
|
||||||
// contain a response, so there is no way to know when it's been set
|
// contain a response, so there is no way to know when it's been set
|
||||||
let authToken;
|
|
||||||
Lbryio.setOverride('setAuthToken', (authToken) => {
|
Lbryio.setOverride('setAuthToken', (authToken) => {
|
||||||
setAuthToken(authToken);
|
setAuthToken(authToken); // set the cookie to auth_token=
|
||||||
return authToken;
|
return authToken;
|
||||||
});
|
});
|
||||||
|
Lbryio.setOverride('deleteAuthToken', () => deleteAuthToken());
|
||||||
|
|
||||||
|
Lbryio.setOverride(
|
||||||
|
'getTokens',
|
||||||
|
() =>
|
||||||
|
new Promise((resolve) => {
|
||||||
|
resolve(getTokens());
|
||||||
|
})
|
||||||
|
);
|
||||||
|
|
||||||
Lbryio.setOverride(
|
Lbryio.setOverride(
|
||||||
'getAuthToken',
|
'getAuthToken',
|
||||||
() =>
|
() =>
|
||||||
new Promise((resolve) => {
|
new Promise((resolve) => {
|
||||||
const authTokenToReturn = authToken || getAuthToken();
|
resolve(getAuthToken());
|
||||||
resolve(authTokenToReturn);
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -230,6 +233,7 @@ function AppWrapper() {
|
||||||
// Splash screen and sdk setup not needed on web
|
// Splash screen and sdk setup not needed on web
|
||||||
const [readyToLaunch, setReadyToLaunch] = useState(IS_WEB);
|
const [readyToLaunch, setReadyToLaunch] = useState(IS_WEB);
|
||||||
const [persistDone, setPersistDone] = useState(false);
|
const [persistDone, setPersistDone] = useState(false);
|
||||||
|
const [keycloakReady, setKeycloakReady] = useState(false);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// @if TARGET='app'
|
// @if TARGET='app'
|
||||||
|
@ -260,13 +264,18 @@ function AppWrapper() {
|
||||||
}
|
}
|
||||||
}, [persistDone]);
|
}, [persistDone]);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* We have assured we have the latest browser persist,
|
||||||
|
* that daemon has started up,
|
||||||
|
* and we have checked with keycloak for a token
|
||||||
|
*/
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (readyToLaunch && persistDone) {
|
if (readyToLaunch && persistDone && keycloakReady) {
|
||||||
if (DEFAULT_LANGUAGE) {
|
if (DEFAULT_LANGUAGE) {
|
||||||
app.store.dispatch(doFetchLanguage(DEFAULT_LANGUAGE));
|
app.store.dispatch(doFetchLanguage(DEFAULT_LANGUAGE));
|
||||||
}
|
}
|
||||||
app.store.dispatch(doUpdateIsNightAsync());
|
app.store.dispatch(doUpdateIsNightAsync());
|
||||||
app.store.dispatch(doDaemonReady());
|
app.store.dispatch(doLbryReady());
|
||||||
app.store.dispatch(doBlackListedOutpointsSubscribe());
|
app.store.dispatch(doBlackListedOutpointsSubscribe());
|
||||||
app.store.dispatch(doFilteredOutpointsSubscribe());
|
app.store.dispatch(doFilteredOutpointsSubscribe());
|
||||||
|
|
||||||
|
@ -274,7 +283,19 @@ function AppWrapper() {
|
||||||
const timeToStart = appReadyTime - startTime;
|
const timeToStart = appReadyTime - startTime;
|
||||||
analytics.readyEvent(timeToStart);
|
analytics.readyEvent(timeToStart);
|
||||||
}
|
}
|
||||||
}, [readyToLaunch, persistDone]);
|
}, [readyToLaunch, persistDone, keycloakReady]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('keycl', keycloak.token);
|
||||||
|
}, [keycloak]);
|
||||||
|
|
||||||
|
const eventLogger = (event, error) => {
|
||||||
|
console.log('onKeycloakEvent', event, error, keycloak);
|
||||||
|
if (event === 'onReady') {
|
||||||
|
setKeycloakReady(true);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const isDev = process.env.NODE_ENV !== 'production';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Provider store={store}>
|
<Provider store={store}>
|
||||||
|
@ -284,13 +305,23 @@ function AppWrapper() {
|
||||||
loading={<div className="main--launching" />}
|
loading={<div className="main--launching" />}
|
||||||
>
|
>
|
||||||
<Fragment>
|
<Fragment>
|
||||||
{readyToLaunch ? (
|
{readyToLaunch && keycloakReady ? (
|
||||||
<ConnectedRouter history={history}>
|
<ReactKeycloakProvider
|
||||||
<ErrorBoundary>
|
authClient={keycloak}
|
||||||
<App />
|
onEvent={eventLogger}
|
||||||
<SnackBar />
|
initOptions={{
|
||||||
</ErrorBoundary>
|
onLoad: 'check-sso',
|
||||||
</ConnectedRouter>
|
silentCheckSsoFallback: false,
|
||||||
|
redirectUri: isDev ? `http://localhost:9090/` : `${SITE_URL}/`,
|
||||||
|
}} // from npmjs docs for @react-keycloak/web
|
||||||
|
>
|
||||||
|
<ConnectedRouter history={history}>
|
||||||
|
<ErrorBoundary>
|
||||||
|
<App />
|
||||||
|
<SnackBar />
|
||||||
|
</ErrorBoundary>
|
||||||
|
</ConnectedRouter>
|
||||||
|
</ReactKeycloakProvider>
|
||||||
) : (
|
) : (
|
||||||
<Fragment>
|
<Fragment>
|
||||||
<SplashScreen onReadyToLaunch={() => setReadyToLaunch(true)} />
|
<SplashScreen onReadyToLaunch={() => setReadyToLaunch(true)} />
|
||||||
|
|
|
@ -6,7 +6,7 @@ import { ipcRenderer, remote } from 'electron';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
import * as ACTIONS from 'constants/action_types';
|
import * as ACTIONS from 'constants/action_types';
|
||||||
import * as MODALS from 'constants/modal_types';
|
import * as MODALS from 'constants/modal_types';
|
||||||
import { DOMAIN, SIMPLE_SITE } from 'config';
|
import { SIMPLE_SITE } from 'config';
|
||||||
import {
|
import {
|
||||||
Lbry,
|
Lbry,
|
||||||
doBalanceSubscribe,
|
doBalanceSubscribe,
|
||||||
|
@ -353,7 +353,7 @@ export function doAlertWaitingForSync() {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doDaemonReady() {
|
export function doLbryReady() {
|
||||||
return (dispatch, getState) => {
|
return (dispatch, getState) => {
|
||||||
const state = getState();
|
const state = getState();
|
||||||
|
|
||||||
|
@ -363,8 +363,6 @@ export function doDaemonReady() {
|
||||||
dispatch(
|
dispatch(
|
||||||
doAuthenticate(
|
doAuthenticate(
|
||||||
appVersion,
|
appVersion,
|
||||||
undefined,
|
|
||||||
undefined,
|
|
||||||
shareUsageData,
|
shareUsageData,
|
||||||
(status) => {
|
(status) => {
|
||||||
const trendingAlgorithm =
|
const trendingAlgorithm =
|
||||||
|
@ -377,8 +375,7 @@ export function doDaemonReady() {
|
||||||
analytics.trendingAlgorithmEvent(trendingAlgorithm);
|
analytics.trendingAlgorithmEvent(trendingAlgorithm);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
undefined,
|
undefined
|
||||||
DOMAIN
|
|
||||||
)
|
)
|
||||||
);
|
);
|
||||||
dispatch({ type: ACTIONS.DAEMON_READY });
|
dispatch({ type: ACTIONS.DAEMON_READY });
|
||||||
|
|
|
@ -50,11 +50,8 @@ export function doFetchInviteStatus(shouldCallRewardList = true) {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doInstallNew(appVersion, os = null, firebaseToken = null, callbackForUsersWhoAreSharingData, domain) {
|
export function doInstallNew(appVersion, callbackForUsersWhoAreSharingData, domain) {
|
||||||
const payload = { app_version: appVersion, domain };
|
const payload = { app_version: appVersion, domain };
|
||||||
if (firebaseToken) {
|
|
||||||
payload.firebase_token = firebaseToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
Lbry.status().then((status) => {
|
Lbry.status().then((status) => {
|
||||||
payload.app_id =
|
payload.app_id =
|
||||||
|
@ -64,7 +61,7 @@ export function doInstallNew(appVersion, os = null, firebaseToken = null, callba
|
||||||
payload.node_id = status.lbry_id;
|
payload.node_id = status.lbry_id;
|
||||||
Lbry.version().then((version) => {
|
Lbry.version().then((version) => {
|
||||||
payload.daemon_version = version.lbrynet_version;
|
payload.daemon_version = version.lbrynet_version;
|
||||||
payload.operating_system = os || version.os_system;
|
payload.operating_system = version.os_system;
|
||||||
payload.platform = version.platform;
|
payload.platform = version.platform;
|
||||||
Lbryio.call('install', 'new', payload);
|
Lbryio.call('install', 'new', payload);
|
||||||
|
|
||||||
|
@ -75,33 +72,9 @@ export function doInstallNew(appVersion, os = null, firebaseToken = null, callba
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
export function doInstallNewWithParams(
|
|
||||||
appVersion,
|
|
||||||
installationId,
|
|
||||||
nodeId,
|
|
||||||
lbrynetVersion,
|
|
||||||
os,
|
|
||||||
platform,
|
|
||||||
firebaseToken = null
|
|
||||||
) {
|
|
||||||
return () => {
|
|
||||||
const payload = { app_version: appVersion };
|
|
||||||
if (firebaseToken) {
|
|
||||||
payload.firebase_token = firebaseToken;
|
|
||||||
}
|
|
||||||
|
|
||||||
payload.app_id = installationId;
|
|
||||||
payload.node_id = nodeId;
|
|
||||||
payload.daemon_version = lbrynetVersion;
|
|
||||||
payload.operating_system = os;
|
|
||||||
payload.platform = platform;
|
|
||||||
Lbryio.call('install', 'new', payload);
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
function checkAuthBusy() {
|
function checkAuthBusy() {
|
||||||
let time = Date.now();
|
let time = Date.now();
|
||||||
return new Promise(function(resolve, reject) {
|
return new Promise(function (resolve, reject) {
|
||||||
(function waitForAuth() {
|
(function waitForAuth() {
|
||||||
try {
|
try {
|
||||||
sessionStorage.setItem('test', 'available');
|
sessionStorage.setItem('test', 'available');
|
||||||
|
@ -131,48 +104,59 @@ function checkAuthBusy() {
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Call doInstallNew separately so we don't have to pass appVersion and os_system params?
|
// TODO: Call doInstallNew separately so we don't have to pass appVersion and os_system params?
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
* @param appVersion
|
||||||
|
* @param shareUsageData
|
||||||
|
* @param callbackForUsersWhoAreSharingData
|
||||||
|
* @param callInstall
|
||||||
|
* @returns {Function}
|
||||||
|
*
|
||||||
|
* Lbryio.fetchUser:
|
||||||
|
* getTokens then getCurrentUser, and if !user, call userNew and return the user
|
||||||
|
*
|
||||||
|
*/
|
||||||
export function doAuthenticate(
|
export function doAuthenticate(
|
||||||
appVersion,
|
appVersion,
|
||||||
os = null,
|
|
||||||
firebaseToken = null,
|
|
||||||
shareUsageData = true,
|
shareUsageData = true,
|
||||||
callbackForUsersWhoAreSharingData,
|
callbackForUsersWhoAreSharingData,
|
||||||
callInstall = true,
|
callInstall = true
|
||||||
domain = null
|
|
||||||
) {
|
) {
|
||||||
return (dispatch) => {
|
return async (dispatch) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.AUTHENTICATION_STARTED,
|
type: ACTIONS.AUTHENTICATION_STARTED,
|
||||||
});
|
});
|
||||||
checkAuthBusy()
|
try {
|
||||||
.then(() => {
|
await checkAuthBusy();
|
||||||
return Lbryio.authenticate(DOMAIN, getDefaultLanguage());
|
const user = await Lbryio.fetchUser(DOMAIN, getDefaultLanguage());
|
||||||
})
|
console.log('USER', user);
|
||||||
.then((user) => {
|
if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS);
|
||||||
if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS);
|
// put this back: accessToken: tokens.access_token
|
||||||
Lbryio.getAuthToken().then((token) => {
|
if (user.error) {
|
||||||
|
throw new Error(user.error.message);
|
||||||
|
} else {
|
||||||
|
Lbryio.getTokens().then((tokens) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.AUTHENTICATION_SUCCESS,
|
type: ACTIONS.AUTHENTICATION_SUCCESS,
|
||||||
data: { user, accessToken: token },
|
data: { user, accessToken: tokens.auth_token }, // rename 'accessToken' = authToken
|
||||||
});
|
});
|
||||||
|
|
||||||
if (shareUsageData) {
|
if (shareUsageData) {
|
||||||
dispatch(doRewardList());
|
dispatch(doRewardList());
|
||||||
dispatch(doFetchInviteStatus(false));
|
dispatch(doFetchInviteStatus(false));
|
||||||
if (callInstall) {
|
if (callInstall) {
|
||||||
doInstallNew(appVersion, os, firebaseToken, callbackForUsersWhoAreSharingData, domain);
|
doInstallNew(appVersion, callbackForUsersWhoAreSharingData, DOMAIN);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
})
|
}
|
||||||
.catch((error) => {
|
} catch (error) {
|
||||||
if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS);
|
if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS);
|
||||||
|
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.AUTHENTICATION_FAILURE,
|
type: ACTIONS.AUTHENTICATION_FAILURE,
|
||||||
data: { error },
|
data: { error },
|
||||||
});
|
|
||||||
});
|
});
|
||||||
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -183,7 +167,7 @@ export function doUserFetch() {
|
||||||
type: ACTIONS.USER_FETCH_STARTED,
|
type: ACTIONS.USER_FETCH_STARTED,
|
||||||
});
|
});
|
||||||
|
|
||||||
Lbryio.getCurrentUser()
|
Lbryio.fetchCurrentUser()
|
||||||
.then((user) => {
|
.then((user) => {
|
||||||
dispatch({
|
dispatch({
|
||||||
type: ACTIONS.USER_FETCH_SUCCESS,
|
type: ACTIONS.USER_FETCH_SUCCESS,
|
||||||
|
@ -204,7 +188,7 @@ export function doUserFetch() {
|
||||||
export function doUserCheckEmailVerified() {
|
export function doUserCheckEmailVerified() {
|
||||||
// This will happen in the background so we don't need loading booleans
|
// This will happen in the background so we don't need loading booleans
|
||||||
return (dispatch) => {
|
return (dispatch) => {
|
||||||
Lbryio.getCurrentUser().then((user) => {
|
Lbryio.fetchCurrentUser().then((user) => {
|
||||||
if (user.has_verified_email) {
|
if (user.has_verified_email) {
|
||||||
dispatch(doRewardList());
|
dispatch(doRewardList());
|
||||||
|
|
||||||
|
|
|
@ -24,37 +24,44 @@ const defaultState = {
|
||||||
invitees: undefined,
|
invitees: undefined,
|
||||||
referralLink: undefined,
|
referralLink: undefined,
|
||||||
referralCode: undefined,
|
referralCode: undefined,
|
||||||
user: undefined,
|
user: { has_verified_email: false },
|
||||||
accessToken: undefined,
|
accessToken: undefined, // rename this
|
||||||
youtubeChannelImportPending: false,
|
youtubeChannelImportPending: false,
|
||||||
youtubeChannelImportErrorMessage: '',
|
youtubeChannelImportErrorMessage: '',
|
||||||
referrerSetIsPending: false,
|
referrerSetIsPending: false,
|
||||||
referrerSetError: '',
|
referrerSetError: '',
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.AUTHENTICATION_STARTED] = state =>
|
// does this do anything with user?
|
||||||
|
reducers[ACTIONS.AUTHENTICATION_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
authenticationIsPending: true,
|
authenticationIsPending: true,
|
||||||
userIsPending: true,
|
userIsPending: true,
|
||||||
|
// distinguish accessToken from authToken
|
||||||
accessToken: defaultState.accessToken,
|
accessToken: defaultState.accessToken,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.AUTHENTICATION_SUCCESS] = (state, action) =>
|
reducers[ACTIONS.AUTHENTICATION_SUCCESS] = (state, action) => {
|
||||||
|
const newUserState = Object.assign({}, state, {
|
||||||
|
authenticationIsPending: false,
|
||||||
|
userIsPending: false,
|
||||||
|
});
|
||||||
|
if (action.data && action.data.user) {
|
||||||
|
newUserState.user = action.data.user;
|
||||||
|
}
|
||||||
|
if (action.data && action.data.accessToken) {
|
||||||
|
newUserState.accessToken = action.data.accessToken;
|
||||||
|
}
|
||||||
|
return newUserState;
|
||||||
|
};
|
||||||
|
|
||||||
|
reducers[ACTIONS.AUTHENTICATION_FAILURE] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
authenticationIsPending: false,
|
authenticationIsPending: false,
|
||||||
userIsPending: false,
|
userIsPending: false,
|
||||||
accessToken: action.data.accessToken,
|
|
||||||
user: action.data.user,
|
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.AUTHENTICATION_FAILURE] = state =>
|
reducers[ACTIONS.USER_FETCH_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
|
||||||
authenticationIsPending: false,
|
|
||||||
userIsPending: false,
|
|
||||||
user: null,
|
|
||||||
});
|
|
||||||
|
|
||||||
reducers[ACTIONS.USER_FETCH_STARTED] = state =>
|
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
userIsPending: true,
|
userIsPending: true,
|
||||||
});
|
});
|
||||||
|
@ -66,7 +73,7 @@ reducers[ACTIONS.USER_FETCH_SUCCESS] = (state, action) =>
|
||||||
emailToVerify: action.data.user.has_verified_email ? null : state.emailToVerify,
|
emailToVerify: action.data.user.has_verified_email ? null : state.emailToVerify,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_FETCH_FAILURE] = state =>
|
reducers[ACTIONS.USER_FETCH_FAILURE] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
userIsPending: true,
|
userIsPending: true,
|
||||||
user: null,
|
user: null,
|
||||||
|
@ -88,7 +95,7 @@ reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) =>
|
||||||
phoneNewIsPending: false,
|
phoneNewIsPending: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PHONE_RESET] = state =>
|
reducers[ACTIONS.USER_PHONE_RESET] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
phoneToVerify: null,
|
phoneToVerify: null,
|
||||||
});
|
});
|
||||||
|
@ -99,7 +106,7 @@ reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) =>
|
||||||
phoneNewErrorMessage: action.data.error,
|
phoneNewErrorMessage: action.data.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = state =>
|
reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
phoneVerifyIsPending: true,
|
phoneVerifyIsPending: true,
|
||||||
phoneVerifyErrorMessage: '',
|
phoneVerifyErrorMessage: '',
|
||||||
|
@ -118,7 +125,7 @@ reducers[ACTIONS.USER_PHONE_VERIFY_FAILURE] = (state, action) =>
|
||||||
phoneVerifyErrorMessage: action.data.error,
|
phoneVerifyErrorMessage: action.data.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = state =>
|
reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
emailNewIsPending: true,
|
emailNewIsPending: true,
|
||||||
emailNewErrorMessage: '',
|
emailNewErrorMessage: '',
|
||||||
|
@ -136,12 +143,12 @@ reducers[ACTIONS.USER_EMAIL_NEW_SUCCESS] = (state, action) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = state =>
|
reducers[ACTIONS.USER_EMAIL_NEW_EXISTS] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
emailAlreadyExists: true,
|
emailAlreadyExists: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_NEW_DOES_NOT_EXIST] = state =>
|
reducers[ACTIONS.USER_EMAIL_NEW_DOES_NOT_EXIST] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
emailDoesNotExist: true,
|
emailDoesNotExist: true,
|
||||||
});
|
});
|
||||||
|
@ -152,7 +159,7 @@ reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = (state, action) =>
|
||||||
emailNewErrorMessage: action.data.error,
|
emailNewErrorMessage: action.data.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_NEW_CLEAR_ENTRY] = state => {
|
reducers[ACTIONS.USER_EMAIL_NEW_CLEAR_ENTRY] = (state) => {
|
||||||
const newUser = { ...state.user };
|
const newUser = { ...state.user };
|
||||||
delete newUser.primary_email;
|
delete newUser.primary_email;
|
||||||
|
|
||||||
|
@ -166,7 +173,7 @@ reducers[ACTIONS.USER_EMAIL_NEW_CLEAR_ENTRY] = state => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PASSWORD_SET_CLEAR] = state =>
|
reducers[ACTIONS.USER_PASSWORD_SET_CLEAR] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
passwordResetSuccess: false,
|
passwordResetSuccess: false,
|
||||||
passwordResetPending: false,
|
passwordResetPending: false,
|
||||||
|
@ -175,7 +182,7 @@ reducers[ACTIONS.USER_PASSWORD_SET_CLEAR] = state =>
|
||||||
passwordSetSuccess: false,
|
passwordSetSuccess: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = state =>
|
reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
emailVerifyIsPending: true,
|
emailVerifyIsPending: true,
|
||||||
emailVerifyErrorMessage: '',
|
emailVerifyErrorMessage: '',
|
||||||
|
@ -202,7 +209,7 @@ reducers[ACTIONS.USER_EMAIL_VERIFY_SET] = (state, action) =>
|
||||||
emailToVerify: action.data.email,
|
emailToVerify: action.data.email,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = state =>
|
reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
identityVerifyIsPending: true,
|
identityVerifyIsPending: true,
|
||||||
identityVerifyErrorMessage: '',
|
identityVerifyErrorMessage: '',
|
||||||
|
@ -229,7 +236,7 @@ reducers[ACTIONS.FETCH_ACCESS_TOKEN_SUCCESS] = (state, action) => {
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_STARTED] = state =>
|
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
inviteStatusIsPending: true,
|
inviteStatusIsPending: true,
|
||||||
});
|
});
|
||||||
|
@ -243,13 +250,13 @@ reducers[ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS] = (state, action) =>
|
||||||
referralCode: action.data.referralCode,
|
referralCode: action.data.referralCode,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_INVITE_NEW_STARTED] = state =>
|
reducers[ACTIONS.USER_INVITE_NEW_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
inviteNewIsPending: true,
|
inviteNewIsPending: true,
|
||||||
inviteNewErrorMessage: '',
|
inviteNewErrorMessage: '',
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = state =>
|
reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
inviteNewIsPending: false,
|
inviteNewIsPending: false,
|
||||||
inviteNewErrorMessage: '',
|
inviteNewErrorMessage: '',
|
||||||
|
@ -261,14 +268,14 @@ reducers[ACTIONS.USER_INVITE_NEW_FAILURE] = (state, action) =>
|
||||||
inviteNewErrorMessage: action.data.error.message,
|
inviteNewErrorMessage: action.data.error.message,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = state =>
|
reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
inviteStatusIsPending: false,
|
inviteStatusIsPending: false,
|
||||||
invitesRemaining: null,
|
invitesRemaining: null,
|
||||||
invitees: null,
|
invitees: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_YOUTUBE_IMPORT_STARTED] = state =>
|
reducers[ACTIONS.USER_YOUTUBE_IMPORT_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
youtubeChannelImportPending: true,
|
youtubeChannelImportPending: true,
|
||||||
youtubeChannelImportErrorMessage: '',
|
youtubeChannelImportErrorMessage: '',
|
||||||
|
@ -293,28 +300,28 @@ reducers[ACTIONS.USER_YOUTUBE_IMPORT_FAILURE] = (state, action) =>
|
||||||
youtubeChannelImportErrorMessage: action.data,
|
youtubeChannelImportErrorMessage: action.data,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_STARTED] = state =>
|
reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
resendingVerificationEmail: true,
|
resendingVerificationEmail: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_SUCCESS] = state =>
|
reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_SUCCESS] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
resendingVerificationEmail: false,
|
resendingVerificationEmail: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_FAILURE] = state =>
|
reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_FAILURE] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
resendingVerificationEmail: false,
|
resendingVerificationEmail: false,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_SET_REFERRER_STARTED] = state =>
|
reducers[ACTIONS.USER_SET_REFERRER_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
referrerSetIsPending: true,
|
referrerSetIsPending: true,
|
||||||
referrerSetError: defaultState.referrerSetError,
|
referrerSetError: defaultState.referrerSetError,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_SET_REFERRER_SUCCESS] = state =>
|
reducers[ACTIONS.USER_SET_REFERRER_SUCCESS] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
referrerSetIsPending: false,
|
referrerSetIsPending: false,
|
||||||
referrerSetError: defaultState.referrerSetError,
|
referrerSetError: defaultState.referrerSetError,
|
||||||
|
@ -326,25 +333,25 @@ reducers[ACTIONS.USER_SET_REFERRER_FAILURE] = (state, action) =>
|
||||||
referrerSetError: action.data.error.message,
|
referrerSetError: action.data.error.message,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_SET_REFERRER_RESET] = state =>
|
reducers[ACTIONS.USER_SET_REFERRER_RESET] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
referrerSetIsPending: false,
|
referrerSetIsPending: false,
|
||||||
referrerSetError: defaultState.referrerSetError,
|
referrerSetError: defaultState.referrerSetError,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PASSWORD_EXISTS] = state =>
|
reducers[ACTIONS.USER_PASSWORD_EXISTS] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
passwordExistsForUser: true,
|
passwordExistsForUser: true,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PASSWORD_RESET_STARTED] = state =>
|
reducers[ACTIONS.USER_PASSWORD_RESET_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
passwordResetPending: true,
|
passwordResetPending: true,
|
||||||
passwordResetSuccess: defaultState.passwordResetSuccess,
|
passwordResetSuccess: defaultState.passwordResetSuccess,
|
||||||
passwordResetError: null,
|
passwordResetError: null,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PASSWORD_RESET_SUCCESS] = state =>
|
reducers[ACTIONS.USER_PASSWORD_RESET_SUCCESS] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
passwordResetPending: false,
|
passwordResetPending: false,
|
||||||
passwordResetSuccess: true,
|
passwordResetSuccess: true,
|
||||||
|
@ -356,13 +363,13 @@ reducers[ACTIONS.USER_PASSWORD_RESET_FAILURE] = (state, action) =>
|
||||||
passwordResetError: action.data.error,
|
passwordResetError: action.data.error,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PASSWORD_SET_STARTED] = state =>
|
reducers[ACTIONS.USER_PASSWORD_SET_STARTED] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
passwordSetPending: true,
|
passwordSetPending: true,
|
||||||
passwordSetSuccess: defaultState.passwordSetSuccess,
|
passwordSetSuccess: defaultState.passwordSetSuccess,
|
||||||
});
|
});
|
||||||
|
|
||||||
reducers[ACTIONS.USER_PASSWORD_SET_SUCCESS] = state =>
|
reducers[ACTIONS.USER_PASSWORD_SET_SUCCESS] = (state) =>
|
||||||
Object.assign({}, state, {
|
Object.assign({}, state, {
|
||||||
passwordSetPending: false,
|
passwordSetPending: false,
|
||||||
passwordSetSuccess: true,
|
passwordSetSuccess: true,
|
||||||
|
|
16
ui/store.js
16
ui/store.js
|
@ -11,7 +11,7 @@ import { routerMiddleware } from 'connected-react-router';
|
||||||
import createRootReducer from './reducers';
|
import createRootReducer from './reducers';
|
||||||
import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
|
import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux';
|
||||||
import { doSyncLoop } from 'redux/actions/sync';
|
import { doSyncLoop } from 'redux/actions/sync';
|
||||||
import { getAuthToken } from 'util/saved-passwords';
|
import { getTokens } from 'util/saved-passwords';
|
||||||
import { generateInitialUrl } from 'util/url';
|
import { generateInitialUrl } from 'util/url';
|
||||||
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
import { X_LBRY_AUTH_TOKEN } from 'constants/token';
|
||||||
|
|
||||||
|
@ -189,12 +189,14 @@ const sharedStateCb = ({ dispatch, getState }) => {
|
||||||
|
|
||||||
const populateAuthTokenHeader = () => {
|
const populateAuthTokenHeader = () => {
|
||||||
return (next) => (action) => {
|
return (next) => (action) => {
|
||||||
if (
|
if (action.type === ACTIONS.USER_FETCH_SUCCESS || action.type === ACTIONS.AUTHENTICATION_SUCCESS) {
|
||||||
(action.type === ACTIONS.USER_FETCH_SUCCESS || action.type === ACTIONS.AUTHENTICATION_SUCCESS) &&
|
if (action.data) {
|
||||||
action.data.user.has_verified_email === true
|
}
|
||||||
) {
|
const tokens = getTokens();
|
||||||
const authToken = getAuthToken();
|
// if (tokens.access_token) {
|
||||||
Lbry.setApiHeader(X_LBRY_AUTH_TOKEN, authToken);
|
// Lbry.setApiHeader('Authorization', 'Bearer ' + tokens.access_token);
|
||||||
|
// }
|
||||||
|
Lbry.setApiHeader(X_LBRY_AUTH_TOKEN, tokens.auth_token);
|
||||||
}
|
}
|
||||||
|
|
||||||
return next(action);
|
return next(action);
|
||||||
|
|
14
ui/util/keycloak.js
Normal file
14
ui/util/keycloak.js
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
import Keycloak from 'keycloak-js';
|
||||||
|
|
||||||
|
// Setup Keycloak instance as needed
|
||||||
|
// Pass initialization options as required or leave blank to load from 'keycloak.json'
|
||||||
|
const keycloak = Keycloak({
|
||||||
|
url: 'https://sso.odysee.com/auth',
|
||||||
|
realm: 'Users',
|
||||||
|
clientId: 'odysee.com',
|
||||||
|
pkceMethod: 'S256',
|
||||||
|
onLoad: 'check-sso',
|
||||||
|
silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html',
|
||||||
|
});
|
||||||
|
|
||||||
|
export default keycloak;
|
|
@ -1,4 +1,4 @@
|
||||||
const { DOMAIN } = require('../../config.js');
|
const { DOMAIN } = require('config.js');
|
||||||
const AUTH_TOKEN = 'auth_token';
|
const AUTH_TOKEN = 'auth_token';
|
||||||
const SAVED_PASSWORD = 'saved_password';
|
const SAVED_PASSWORD = 'saved_password';
|
||||||
const DEPRECATED_SAVED_PASSWORD = 'saved-password';
|
const DEPRECATED_SAVED_PASSWORD = 'saved-password';
|
||||||
|
@ -6,6 +6,7 @@ const domain =
|
||||||
typeof window === 'object' && window.location.hostname.includes('localhost') ? window.location.hostname : DOMAIN;
|
typeof window === 'object' && window.location.hostname.includes('localhost') ? window.location.hostname : DOMAIN;
|
||||||
const isProduction = process.env.NODE_ENV === 'production';
|
const isProduction = process.env.NODE_ENV === 'production';
|
||||||
const maxExpiration = 2147483647;
|
const maxExpiration = 2147483647;
|
||||||
|
const { default: keycloak } = require('util/keycloak');
|
||||||
let sessionPassword;
|
let sessionPassword;
|
||||||
|
|
||||||
function setCookie(name, value, expirationDaysOnWeb) {
|
function setCookie(name, value, expirationDaysOnWeb) {
|
||||||
|
@ -59,7 +60,7 @@ function deleteCookie(name) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function setSavedPassword(value, saveToDisk) {
|
function setSavedPassword(value, saveToDisk) {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
const password = value === undefined || value === null ? '' : value;
|
const password = value === undefined || value === null ? '' : value;
|
||||||
sessionPassword = password;
|
sessionPassword = password;
|
||||||
|
|
||||||
|
@ -74,17 +75,17 @@ function setSavedPassword(value, saveToDisk) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function getSavedPassword() {
|
function getSavedPassword() {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
if (sessionPassword) {
|
if (sessionPassword) {
|
||||||
resolve(sessionPassword);
|
resolve(sessionPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
return getPasswordFromCookie().then(p => resolve(p));
|
return getPasswordFromCookie().then((p) => resolve(p));
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function getPasswordFromCookie() {
|
function getPasswordFromCookie() {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
let password;
|
let password;
|
||||||
password = getCookie(SAVED_PASSWORD);
|
password = getCookie(SAVED_PASSWORD);
|
||||||
resolve(password);
|
resolve(password);
|
||||||
|
@ -92,7 +93,7 @@ function getPasswordFromCookie() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteSavedPassword() {
|
function deleteSavedPassword() {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
deleteCookie(SAVED_PASSWORD);
|
deleteCookie(SAVED_PASSWORD);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
|
@ -102,19 +103,28 @@ function getAuthToken() {
|
||||||
return getCookie(AUTH_TOKEN);
|
return getCookie(AUTH_TOKEN);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// will take oidc token getter
|
||||||
|
// function getTokens() {
|
||||||
|
// return { auth_token: getAuthToken(), access_token: null };
|
||||||
|
// }
|
||||||
|
|
||||||
|
function getTokens() {
|
||||||
|
return { auth_token: getAuthToken(), access_token: keycloak.token };
|
||||||
|
}
|
||||||
|
|
||||||
function setAuthToken(value) {
|
function setAuthToken(value) {
|
||||||
return setCookie(AUTH_TOKEN, value, 365);
|
return setCookie(AUTH_TOKEN, value, 365);
|
||||||
}
|
}
|
||||||
|
|
||||||
function deleteAuthToken() {
|
function deleteAuthToken() {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
deleteCookie(AUTH_TOKEN);
|
deleteCookie(AUTH_TOKEN);
|
||||||
resolve();
|
resolve();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
function doSignOutCleanup() {
|
function doSignOutCleanup() {
|
||||||
return new Promise(resolve => {
|
return new Promise((resolve) => {
|
||||||
deleteAuthToken();
|
deleteAuthToken();
|
||||||
deleteSavedPassword();
|
deleteSavedPassword();
|
||||||
resolve();
|
resolve();
|
||||||
|
@ -122,10 +132,10 @@ function doSignOutCleanup() {
|
||||||
}
|
}
|
||||||
|
|
||||||
function doAuthTokenRefresh() {
|
function doAuthTokenRefresh() {
|
||||||
const authToken = getAuthToken();
|
const { auth_token: authToken } = getAuthToken();
|
||||||
if (authToken) {
|
if (authToken) {
|
||||||
deleteAuthToken();
|
deleteAuthToken();
|
||||||
setAuthToken(authToken);
|
setCookie(AUTH_TOKEN, authToken, 365);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -147,6 +157,7 @@ module.exports = {
|
||||||
deleteSavedPassword,
|
deleteSavedPassword,
|
||||||
getAuthToken,
|
getAuthToken,
|
||||||
setAuthToken,
|
setAuthToken,
|
||||||
|
getTokens,
|
||||||
deleteAuthToken,
|
deleteAuthToken,
|
||||||
doSignOutCleanup,
|
doSignOutCleanup,
|
||||||
doAuthTokenRefresh,
|
doAuthTokenRefresh,
|
||||||
|
|
|
@ -1,12 +0,0 @@
|
||||||
const fs = require('fs');
|
|
||||||
const path = require('path');
|
|
||||||
|
|
||||||
let robots;
|
|
||||||
async function getRobots(ctx) {
|
|
||||||
if (!robots) {
|
|
||||||
robots = fs.readFileSync(path.join(__dirname, '/../dist/public/robots.txt'), 'utf8');
|
|
||||||
}
|
|
||||||
return robots;
|
|
||||||
}
|
|
||||||
|
|
||||||
module.exports = { getRobots };
|
|
|
@ -2,6 +2,7 @@ const { getHtml } = require('./html');
|
||||||
const { getRss } = require('./rss');
|
const { getRss } = require('./rss');
|
||||||
const { getHomepageJSON } = require('./getHomepageJSON');
|
const { getHomepageJSON } = require('./getHomepageJSON');
|
||||||
const { generateStreamUrl } = require('../../ui/util/web');
|
const { generateStreamUrl } = require('../../ui/util/web');
|
||||||
|
const { createReadStream } = require('fs');
|
||||||
const fetch = require('node-fetch');
|
const fetch = require('node-fetch');
|
||||||
const Router = require('@koa/router');
|
const Router = require('@koa/router');
|
||||||
const { CUSTOM_HOMEPAGE } = require('../../config.js');
|
const { CUSTOM_HOMEPAGE } = require('../../config.js');
|
||||||
|
@ -50,6 +51,11 @@ router.get(`/$/api/content/v1/get`, async (ctx) => {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
router.get(`/$/sso/silent-check-sso.html`, async (ctx) => {
|
||||||
|
ctx.type = 'html';
|
||||||
|
ctx.body = createReadStream('./silent-check-sso.html');
|
||||||
|
})
|
||||||
|
|
||||||
router.get(`/$/download/:claimName/:claimId`, async (ctx) => {
|
router.get(`/$/download/:claimName/:claimId`, async (ctx) => {
|
||||||
const streamUrl = getStreamUrl(ctx);
|
const streamUrl = getStreamUrl(ctx);
|
||||||
const downloadUrl = `${streamUrl}?download=1`;
|
const downloadUrl = `${streamUrl}?download=1`;
|
||||||
|
|
51
yarn.lock
51
yarn.lock
|
@ -1141,6 +1141,13 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
regenerator-runtime "^0.13.4"
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
|
"@babel/runtime@^7.9.0":
|
||||||
|
version "7.15.4"
|
||||||
|
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.15.4.tgz#fd17d16bfdf878e6dd02d19753a39fa8a8d9c84a"
|
||||||
|
integrity sha512-99catp6bHCaxr4sJ/DbTGgHS4+Rs2RVd2g7iOap6SLGPDknRK9ztKNsE/Fg6QhSeh1FGE5f6gHGQmvvn3I3xhw==
|
||||||
|
dependencies:
|
||||||
|
regenerator-runtime "^0.13.4"
|
||||||
|
|
||||||
"@babel/template@^7.10.1", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
"@babel/template@^7.10.1", "@babel/template@^7.8.3", "@babel/template@^7.8.6":
|
||||||
version "7.10.1"
|
version "7.10.1"
|
||||||
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811"
|
resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.10.1.tgz#e167154a94cb5f14b28dc58f5356d2162f539811"
|
||||||
|
@ -1558,6 +1565,22 @@
|
||||||
dependencies:
|
dependencies:
|
||||||
tslib "^2.0.0"
|
tslib "^2.0.0"
|
||||||
|
|
||||||
|
"@react-keycloak/core@^3.2.0":
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-keycloak/core/-/core-3.2.0.tgz#e46f1951b0d7873f7f2fcd73dd0c270cb0b18db8"
|
||||||
|
integrity sha512-1yzU7gQzs+6E1v6hGqxy0Q+kpMHg9sEcke2yxZR29WoU8KNE8E50xS6UbI8N7rWsgyYw8r9W1cUPCOF48MYjzw==
|
||||||
|
dependencies:
|
||||||
|
react-fast-compare "^3.2.0"
|
||||||
|
|
||||||
|
"@react-keycloak/web@^3.4.0":
|
||||||
|
version "3.4.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/@react-keycloak/web/-/web-3.4.0.tgz#725d96fab8e5fa47faff9615cc08574e5dff2222"
|
||||||
|
integrity sha512-yKKSCyqBtn7dt+VckYOW1IM5NW999pPkxDZOXqJ6dfXPXstYhOQCkTZqh8l7UL14PkpsoaHDh7hSJH8whah01g==
|
||||||
|
dependencies:
|
||||||
|
"@babel/runtime" "^7.9.0"
|
||||||
|
"@react-keycloak/core" "^3.2.0"
|
||||||
|
hoist-non-react-statics "^3.3.2"
|
||||||
|
|
||||||
"@samverschueren/stream-to-observable@^0.3.0":
|
"@samverschueren/stream-to-observable@^0.3.0":
|
||||||
version "0.3.0"
|
version "0.3.0"
|
||||||
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
|
resolved "https://registry.yarnpkg.com/@samverschueren/stream-to-observable/-/stream-to-observable-0.3.0.tgz#ecdf48d532c58ea477acfcab80348424f8d0662f"
|
||||||
|
@ -3495,9 +3518,10 @@ balanced-match@^1.0.0:
|
||||||
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767"
|
||||||
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c=
|
||||||
|
|
||||||
base64-js@^1.0.2:
|
base64-js@1.3.1, base64-js@^1.0.2:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
resolved "https://registry.yarnpkg.com/base64-js/-/base64-js-1.3.1.tgz#58ece8cb75dd07e71ed08c736abc5fac4dbf8df1"
|
||||||
|
integrity sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==
|
||||||
|
|
||||||
base@^0.11.1:
|
base@^0.11.1:
|
||||||
version "0.11.2"
|
version "0.11.2"
|
||||||
|
@ -8294,9 +8318,10 @@ hoek@4.x.x:
|
||||||
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
|
resolved "https://registry.yarnpkg.com/hoek/-/hoek-4.2.1.tgz#9634502aa12c445dd5a7c5734b572bb8738aacbb"
|
||||||
integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==
|
integrity sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA==
|
||||||
|
|
||||||
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0:
|
hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0, hoist-non-react-statics@^3.3.2:
|
||||||
version "3.3.2"
|
version "3.3.2"
|
||||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||||
|
integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==
|
||||||
dependencies:
|
dependencies:
|
||||||
react-is "^16.7.0"
|
react-is "^16.7.0"
|
||||||
|
|
||||||
|
@ -9822,6 +9847,11 @@ js-base64@^2.1.9:
|
||||||
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
|
resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4"
|
||||||
integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
|
integrity sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==
|
||||||
|
|
||||||
|
js-sha256@0.9.0:
|
||||||
|
version "0.9.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966"
|
||||||
|
integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA==
|
||||||
|
|
||||||
js-tokens@^3.0.0, js-tokens@^3.0.2:
|
js-tokens@^3.0.0, js-tokens@^3.0.2:
|
||||||
version "3.0.2"
|
version "3.0.2"
|
||||||
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b"
|
||||||
|
@ -10062,6 +10092,14 @@ jszip@~2.5.0:
|
||||||
dependencies:
|
dependencies:
|
||||||
pako "~0.2.5"
|
pako "~0.2.5"
|
||||||
|
|
||||||
|
keycloak-js@^15.0.2:
|
||||||
|
version "15.0.2"
|
||||||
|
resolved "https://registry.yarnpkg.com/keycloak-js/-/keycloak-js-15.0.2.tgz#9d12dd8860953a267b9b18f351ad2e76b8e94a9c"
|
||||||
|
integrity sha512-dv2a4NcPSH3AzGWG3ZtB+VrHpuQLdFBYXtQBj/+oBzm6XNwnVAMdL6LIC0OzCLQpn3rKTQJtNSATAGhbKJgewQ==
|
||||||
|
dependencies:
|
||||||
|
base64-js "1.3.1"
|
||||||
|
js-sha256 "0.9.0"
|
||||||
|
|
||||||
keycode@^2.2.0:
|
keycode@^2.2.0:
|
||||||
version "2.2.0"
|
version "2.2.0"
|
||||||
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
|
resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04"
|
||||||
|
@ -10148,9 +10186,9 @@ lbry-redux@lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676:
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
uuid "^8.3.1"
|
uuid "^8.3.1"
|
||||||
|
|
||||||
lbryinc@lbryio/lbryinc#0b4e41ef90d6347819dd3453f2f9398a5c1b4f36:
|
lbryinc@lbryio/lbryinc#97fc74315c89580f9fa2408643b0f4feef42750a:
|
||||||
version "0.0.1"
|
version "0.0.1"
|
||||||
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/0b4e41ef90d6347819dd3453f2f9398a5c1b4f36"
|
resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/97fc74315c89580f9fa2408643b0f4feef42750a"
|
||||||
dependencies:
|
dependencies:
|
||||||
reselect "^3.0.0"
|
reselect "^3.0.0"
|
||||||
|
|
||||||
|
@ -13380,6 +13418,11 @@ react-error-overlay@^1.0.9:
|
||||||
settle-promise "1.0.0"
|
settle-promise "1.0.0"
|
||||||
source-map "0.5.6"
|
source-map "0.5.6"
|
||||||
|
|
||||||
|
react-fast-compare@^3.2.0:
|
||||||
|
version "3.2.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/react-fast-compare/-/react-fast-compare-3.2.0.tgz#641a9da81b6a6320f270e89724fb45a0b39e43bb"
|
||||||
|
integrity sha512-rtGImPZ0YyLrscKI9xTpV8psd6I8VAtjKCzQDlzyDvqJA8XOW78TXYQwNRNd8g8JZnDu8q9Fu/1v4HPAVwVdHA==
|
||||||
|
|
||||||
react-fit@^1.0.3:
|
react-fit@^1.0.3:
|
||||||
version "1.3.1"
|
version "1.3.1"
|
||||||
resolved "https://registry.yarnpkg.com/react-fit/-/react-fit-1.3.1.tgz#850cb5d554fdfa4b27891f62a9d290d3e7eda57b"
|
resolved "https://registry.yarnpkg.com/react-fit/-/react-fit-1.3.1.tgz#850cb5d554fdfa4b27891f62a9d290d3e7eda57b"
|
||||||
|
|
Loading…
Reference in a new issue