From 63196824a7886f6915c756ff1ddf0121f11a3004 Mon Sep 17 00:00:00 2001 From: zeppi Date: Sun, 26 Sep 2021 23:06:34 -0400 Subject: [PATCH 1/4] wip --- package.json | 3 +- static/silent-check-sso.html | 6 ++ ui/component/app/view.jsx | 2 +- ui/component/header/view.jsx | 12 +++- ui/component/privacyAgreement/index.js | 14 ++-- ui/component/router/view.jsx | 14 +++- ui/index.jsx | 79 ++++++++++++++++------ ui/redux/actions/app.js | 9 +-- ui/redux/actions/user.js | 92 +++++++++++--------------- ui/redux/reducers/user.js | 87 +++++++++++++----------- ui/store.js | 16 +++-- ui/util/keycloak.js | 14 ++++ ui/util/saved-passwords.js | 31 ++++++--- web/src/robots.js | 12 ---- web/src/routes.js | 6 ++ yarn.lock | 21 +++++- 16 files changed, 249 insertions(+), 169 deletions(-) create mode 100644 static/silent-check-sso.html create mode 100644 ui/util/keycloak.js delete mode 100644 web/src/robots.js diff --git a/package.json b/package.json index 03ec42389..3c70ff597 100644 --- a/package.json +++ b/package.json @@ -58,6 +58,7 @@ "if-env": "^1.0.4", "match-sorter": "^6.3.0", "parse-duration": "^1.0.0", + "keycloak-js": "^15.0.2", "react-datetime-picker": "^3.2.1", "react-plastic": "^1.1.1", "react-top-loading-bar": "^2.0.1", @@ -156,7 +157,7 @@ "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-redux": "lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676", - "lbryinc": "lbryio/lbryinc#0b4e41ef90d6347819dd3453f2f9398a5c1b4f36", + "lbryinc": "lbryio/lbryinc#f133a7ed62c1f2202f98e785e923a7d841504d0d", "lint-staged": "^7.0.2", "localforage": "^1.7.1", "lodash-es": "^4.17.14", diff --git a/static/silent-check-sso.html b/static/silent-check-sso.html new file mode 100644 index 000000000..8a7a62bdc --- /dev/null +++ b/static/silent-check-sso.html @@ -0,0 +1,6 @@ + + +

Hi

+ + + diff --git a/ui/component/app/view.jsx b/ui/component/app/view.jsx index 00505e48a..50bcf48b2 100644 --- a/ui/component/app/view.jsx +++ b/ui/component/app/view.jsx @@ -317,7 +317,7 @@ function App(props: Props) { if (previousHasVerifiedEmail === false && hasVerifiedEmail) { analytics.emailVerifiedEvent(); } - }, [previousHasVerifiedEmail, hasVerifiedEmail, signIn]); + }, [previousHasVerifiedEmail, hasVerifiedEmail]); useEffect(() => { if (previousRewardApproved === false && isRewardApproved) { diff --git a/ui/component/header/view.jsx b/ui/component/header/view.jsx index 09a4753c6..daf0f0899 100644 --- a/ui/component/header/view.jsx +++ b/ui/component/header/view.jsx @@ -3,7 +3,7 @@ import { ENABLE_NO_SOURCE_CLAIMS, CHANNEL_STAKED_LEVEL_LIVESTREAM, ENABLE_UI_NOT import * as ICONS from 'constants/icons'; import { SETTINGS } from 'lbry-redux'; import * as PAGES from 'constants/pages'; -import React from 'react'; +import React, { useCallback } from 'react'; import { withRouter } from 'react-router'; import classnames from 'classnames'; import Button from 'component/button'; @@ -16,6 +16,8 @@ import NotificationBubble from 'component/notificationBubble'; import NotificationHeaderButton from 'component/notificationHeaderButton'; import ChannelThumbnail from 'component/channelThumbnail'; import SkipNavigationButton from 'component/skipNavigationButton'; +import keycloak from 'util/keycloak'; + import Logo from 'component/logo'; // @if TARGET='app' import { remote } from 'electron'; @@ -169,17 +171,21 @@ const Header = (props: Props) => { setClientSetting(SETTINGS.THEME, 'dark', true); } } + const login = useCallback(() => { + keycloak && keycloak.login().then((x) => console.log('cb', x)); + }, [keycloak]); const loginButtons = (
); diff --git a/ui/component/privacyAgreement/index.js b/ui/component/privacyAgreement/index.js index 5a1032a09..551ecd208 100644 --- a/ui/component/privacyAgreement/index.js +++ b/ui/component/privacyAgreement/index.js @@ -1,4 +1,3 @@ -import { DOMAIN } from 'config'; import { connect } from 'react-redux'; import { doSetDaemonSetting } from 'redux/actions/settings'; import { doSetWelcomeVersion, doToggle3PAnalytics, doSignOut } from 'redux/actions/app'; @@ -10,17 +9,16 @@ import { version as appVersion } from 'package.json'; import PrivacyAgreement from './view'; -const select = state => ({ +const select = (state) => ({ authenticated: selectUserVerifiedEmail(state), }); -const perform = dispatch => ({ - setWelcomeVersion: version => dispatch(doSetWelcomeVersion(version || WELCOME_VERSION)), - setShareDataInternal: share => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, share)), - setShareDataThirdParty: share => dispatch(doToggle3PAnalytics(share)), +const perform = (dispatch) => ({ + setWelcomeVersion: (version) => dispatch(doSetWelcomeVersion(version || WELCOME_VERSION)), + setShareDataInternal: (share) => dispatch(doSetDaemonSetting(DAEMON_SETTINGS.SHARE_USAGE_DATA, share)), + setShareDataThirdParty: (share) => dispatch(doToggle3PAnalytics(share)), signOut: () => dispatch(doSignOut()), - authenticateIfSharingData: () => - dispatch(doAuthenticate(appVersion, undefined, undefined, true, undefined, undefined, DOMAIN)), + authenticateIfSharingData: () => dispatch(doAuthenticate(appVersion, undefined, undefined, true)), // appVersion, shareData?, shareCallback, callInstall }); export default connect(select, perform)(PrivacyAgreement); diff --git a/ui/component/router/view.jsx b/ui/component/router/view.jsx index 03db2b538..d39d3cfbd 100644 --- a/ui/component/router/view.jsx +++ b/ui/component/router/view.jsx @@ -12,6 +12,7 @@ import LoadingBarOneOff from 'component/loadingBarOneOff'; import { GetLinksData } from 'util/buildHomepage'; import HomePage from 'page/home'; +import keycloak from 'util/keycloak'; // @if TARGET='app' const BackupPage = lazyImport(() => import('page/backup' /* webpackChunkName: "backup" */)); @@ -21,6 +22,8 @@ const BackupPage = lazyImport(() => import('page/backup' /* webpackChunkName: "b const Code2257Page = lazyImport(() => import('web/page/code2257' /* webpackChunkName: "code2257" */)); // @endif +console.log('keycloak', keycloak) + // Chunk: "secondary" const SignInPage = lazyImport(() => import('page/signIn' /* webpackChunkName: "secondary" */)); const SignInWalletPasswordPage = lazyImport(() => @@ -86,7 +89,6 @@ const TopPage = lazyImport(() => import('page/top' /* webpackChunkName: "seconda const UpdatePasswordPage = lazyImport(() => import('page/passwordUpdate' /* webpackChunkName: "passwordUpdate" */)); const Welcome = lazyImport(() => import('page/welcome' /* webpackChunkName: "secondary" */)); const YoutubeSyncPage = lazyImport(() => import('page/youtubeSync' /* webpackChunkName: "secondary" */)); - // Tell the browser we are handling scroll restoration if ('scrollRestoration' in history) { history.scrollRestoration = 'manual'; @@ -128,14 +130,20 @@ function PrivateRoute(props: PrivateRouteProps) { const { component: Component, isAuthenticated, ...rest } = props; const urlSearchParams = new URLSearchParams(props.location.search); const redirectUrl = urlSearchParams.get('redirect'); + // const { keycloak } = useKeycloak(); return ( - isAuthenticated || !IS_WEB ? ( + (isAuthenticated || (keycloak && keycloak.authenticated)) || !IS_WEB ? ( ) : ( - + ) } /> diff --git a/ui/index.jsx b/ui/index.jsx index 9b39b6ed7..1717bd3b9 100644 --- a/ui/index.jsx +++ b/ui/index.jsx @@ -14,7 +14,7 @@ import * as MODALS from 'constants/modal_types'; import React, { Fragment, useState, useEffect } from 'react'; import ReactDOM from 'react-dom'; 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 { setSearchApi } from 'redux/actions/search'; import { doSetLanguage, doFetchLanguage, doUpdateIsNightAsync } from 'redux/actions/settings'; @@ -28,15 +28,11 @@ import { formatLbryUrlForWeb, formatInAppUrl } from 'util/url'; import { PersistGate } from 'redux-persist/integration/react'; import analytics from 'analytics'; import { doToast } from 'redux/actions/notifications'; -import { - getAuthToken, - 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 keycloak from 'util/keycloak'; +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 'scss/third-party.scss'; @@ -98,6 +94,7 @@ Lbry.setOverride( const startTime = Date.now(); analytics.startupEvent(); +const isDev = process.env.NODE_ENV !== 'production'; // @if TARGET='app' const { autoUpdater } = remote.require('electron-updater'); @@ -112,28 +109,33 @@ if (process.env.SEARCH_API_URL) { setSearchApi(process.env.SEARCH_API_URL); } -// Fix to make sure old users' cookies are set to the correct domain -// This can be removed after March 11th, 2021 -// https://github.com/lbryio/lbry-desktop/pull/3830 -doDeprecatedPasswordMigrationMarch2020(); -doAuthTokenRefresh(); +if (getTokens().auth_token) { + doAuthTokenRefresh(); +} // We need to override Lbryio for getting/setting the authToken // 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 // contain a response, so there is no way to know when it's been set -let authToken; Lbryio.setOverride('setAuthToken', (authToken) => { - setAuthToken(authToken); + setAuthToken(authToken); // set the cookie to auth_token= return authToken; }); +Lbryio.setOverride('deleteAuthToken', () => deleteAuthToken()); + +Lbryio.setOverride( + 'getTokens', + () => + new Promise((resolve) => { + resolve(getTokens()); + }) +); Lbryio.setOverride( 'getAuthToken', () => new Promise((resolve) => { - const authTokenToReturn = authToken || getAuthToken(); - resolve(authTokenToReturn); + resolve(getAuthToken()); }) ); @@ -230,7 +232,9 @@ function AppWrapper() { // Splash screen and sdk setup not needed on web const [readyToLaunch, setReadyToLaunch] = useState(IS_WEB); const [persistDone, setPersistDone] = useState(false); + const [keycloakReady, setKeycloakReady] = useState(false); + // init? useEffect(() => { // @if TARGET='app' moment.locale(remote.app.getLocale()); @@ -254,19 +258,48 @@ function AppWrapper() { // @endif }, []); + function initKeycloak() { + console.dir(keycloak) + keycloak.init( + { onLoad: 'check-sso', + silentCheckSsoFallback: false, + didInit: true, + redirectUri: isDev ? 'http://localhost:9090/' : `${SITE_URL}/`} + ).then(function(authenticated) { + setKeycloakReady(true); + console.log('INIT: ', authenticated ? 'Authenticated' : 'Not Authenticated'); + }).catch(function() { + console.log('INIT: FAILED'); + }); + } + + useEffect(() => { + console.log('KCR RENDER', keycloakReady) + if (!keycloakReady) { + initKeycloak(); + } + }, [keycloakReady]); + + // initKeycloak(); + useEffect(() => { if (persistDone) { app.store.dispatch(doToggle3PAnalytics(null, true)); } }, [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(() => { - if (readyToLaunch && persistDone) { + if (readyToLaunch && persistDone && keycloakReady) { // keycloak ready if (DEFAULT_LANGUAGE) { app.store.dispatch(doFetchLanguage(DEFAULT_LANGUAGE)); } app.store.dispatch(doUpdateIsNightAsync()); - app.store.dispatch(doDaemonReady()); + app.store.dispatch(doLbryReady()); app.store.dispatch(doBlackListedOutpointsSubscribe()); app.store.dispatch(doFilteredOutpointsSubscribe()); @@ -274,7 +307,11 @@ function AppWrapper() { const timeToStart = appReadyTime - startTime; analytics.readyEvent(timeToStart); } - }, [readyToLaunch, persistDone]); + }, [readyToLaunch, persistDone, keycloakReady]); + + useEffect(() => { + console.dir(keycloak); + }, [keycloak]); return ( diff --git a/ui/redux/actions/app.js b/ui/redux/actions/app.js index 67a00c4a0..8a4a15446 100644 --- a/ui/redux/actions/app.js +++ b/ui/redux/actions/app.js @@ -6,7 +6,7 @@ import { ipcRenderer, remote } from 'electron'; import path from 'path'; import * as ACTIONS from 'constants/action_types'; import * as MODALS from 'constants/modal_types'; -import { DOMAIN, SIMPLE_SITE } from 'config'; +import { SIMPLE_SITE } from 'config'; import { Lbry, doBalanceSubscribe, @@ -353,7 +353,7 @@ export function doAlertWaitingForSync() { }; } -export function doDaemonReady() { +export function doLbryReady() { return (dispatch, getState) => { const state = getState(); @@ -363,8 +363,6 @@ export function doDaemonReady() { dispatch( doAuthenticate( appVersion, - undefined, - undefined, shareUsageData, (status) => { const trendingAlgorithm = @@ -377,8 +375,7 @@ export function doDaemonReady() { analytics.trendingAlgorithmEvent(trendingAlgorithm); } }, - undefined, - DOMAIN + undefined ) ); dispatch({ type: ACTIONS.DAEMON_READY }); diff --git a/ui/redux/actions/user.js b/ui/redux/actions/user.js index 5738eb3c0..0b32ee28c 100644 --- a/ui/redux/actions/user.js +++ b/ui/redux/actions/user.js @@ -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 }; - if (firebaseToken) { - payload.firebase_token = firebaseToken; - } Lbry.status().then((status) => { payload.app_id = @@ -64,7 +61,7 @@ export function doInstallNew(appVersion, os = null, firebaseToken = null, callba payload.node_id = status.lbry_id; Lbry.version().then((version) => { payload.daemon_version = version.lbrynet_version; - payload.operating_system = os || version.os_system; + payload.operating_system = version.os_system; payload.platform = version.platform; 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() { let time = Date.now(); - return new Promise(function(resolve, reject) { + return new Promise(function (resolve, reject) { (function waitForAuth() { try { 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? +/** + * + * @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( appVersion, - os = null, - firebaseToken = null, shareUsageData = true, callbackForUsersWhoAreSharingData, - callInstall = true, - domain = null + callInstall = true ) { - return (dispatch) => { + return async (dispatch) => { dispatch({ type: ACTIONS.AUTHENTICATION_STARTED, }); - checkAuthBusy() - .then(() => { - return Lbryio.authenticate(DOMAIN, getDefaultLanguage()); - }) - .then((user) => { - if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS); - Lbryio.getAuthToken().then((token) => { + try { + await checkAuthBusy(); + const user = await Lbryio.fetchUser(DOMAIN, getDefaultLanguage()); + console.log('USER', user); + if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS); + // put this back: accessToken: tokens.access_token + if (user.error) { + throw new Error(user.error.message); + } else { + Lbryio.getTokens().then((tokens) => { dispatch({ type: ACTIONS.AUTHENTICATION_SUCCESS, - data: { user, accessToken: token }, + data: { user, accessToken: tokens.auth_token }, // rename 'accessToken' = authToken }); - if (shareUsageData) { dispatch(doRewardList()); dispatch(doFetchInviteStatus(false)); if (callInstall) { - doInstallNew(appVersion, os, firebaseToken, callbackForUsersWhoAreSharingData, domain); + doInstallNew(appVersion, callbackForUsersWhoAreSharingData, DOMAIN); } } }); - }) - .catch((error) => { - if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS); + } + } catch (error) { + if (sessionStorageAvailable) window.sessionStorage.removeItem(AUTH_IN_PROGRESS); - dispatch({ - type: ACTIONS.AUTHENTICATION_FAILURE, - data: { error }, - }); + dispatch({ + type: ACTIONS.AUTHENTICATION_FAILURE, + data: { error }, }); + } }; } @@ -183,7 +167,7 @@ export function doUserFetch() { type: ACTIONS.USER_FETCH_STARTED, }); - Lbryio.getCurrentUser() + Lbryio.fetchCurrentUser() .then((user) => { dispatch({ type: ACTIONS.USER_FETCH_SUCCESS, @@ -204,7 +188,7 @@ export function doUserFetch() { export function doUserCheckEmailVerified() { // This will happen in the background so we don't need loading booleans return (dispatch) => { - Lbryio.getCurrentUser().then((user) => { + Lbryio.fetchCurrentUser().then((user) => { if (user.has_verified_email) { dispatch(doRewardList()); diff --git a/ui/redux/reducers/user.js b/ui/redux/reducers/user.js index d31078f1f..685bed5c8 100644 --- a/ui/redux/reducers/user.js +++ b/ui/redux/reducers/user.js @@ -24,37 +24,44 @@ const defaultState = { invitees: undefined, referralLink: undefined, referralCode: undefined, - user: undefined, - accessToken: undefined, + user: { has_verified_email: false }, + accessToken: undefined, // rename this youtubeChannelImportPending: false, youtubeChannelImportErrorMessage: '', referrerSetIsPending: false, referrerSetError: '', }; -reducers[ACTIONS.AUTHENTICATION_STARTED] = state => +// does this do anything with user? +reducers[ACTIONS.AUTHENTICATION_STARTED] = (state) => Object.assign({}, state, { authenticationIsPending: true, userIsPending: true, + // distinguish accessToken from authToken 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, { authenticationIsPending: false, userIsPending: false, - accessToken: action.data.accessToken, - user: action.data.user, }); -reducers[ACTIONS.AUTHENTICATION_FAILURE] = state => - Object.assign({}, state, { - authenticationIsPending: false, - userIsPending: false, - user: null, - }); - -reducers[ACTIONS.USER_FETCH_STARTED] = state => +reducers[ACTIONS.USER_FETCH_STARTED] = (state) => Object.assign({}, state, { userIsPending: true, }); @@ -66,7 +73,7 @@ reducers[ACTIONS.USER_FETCH_SUCCESS] = (state, action) => 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, { userIsPending: true, user: null, @@ -88,7 +95,7 @@ reducers[ACTIONS.USER_PHONE_NEW_SUCCESS] = (state, action) => phoneNewIsPending: false, }); -reducers[ACTIONS.USER_PHONE_RESET] = state => +reducers[ACTIONS.USER_PHONE_RESET] = (state) => Object.assign({}, state, { phoneToVerify: null, }); @@ -99,7 +106,7 @@ reducers[ACTIONS.USER_PHONE_NEW_FAILURE] = (state, action) => phoneNewErrorMessage: action.data.error, }); -reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = state => +reducers[ACTIONS.USER_PHONE_VERIFY_STARTED] = (state) => Object.assign({}, state, { phoneVerifyIsPending: true, phoneVerifyErrorMessage: '', @@ -118,7 +125,7 @@ reducers[ACTIONS.USER_PHONE_VERIFY_FAILURE] = (state, action) => phoneVerifyErrorMessage: action.data.error, }); -reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = state => +reducers[ACTIONS.USER_EMAIL_NEW_STARTED] = (state) => Object.assign({}, state, { emailNewIsPending: true, 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, { emailAlreadyExists: true, }); -reducers[ACTIONS.USER_EMAIL_NEW_DOES_NOT_EXIST] = state => +reducers[ACTIONS.USER_EMAIL_NEW_DOES_NOT_EXIST] = (state) => Object.assign({}, state, { emailDoesNotExist: true, }); @@ -152,7 +159,7 @@ reducers[ACTIONS.USER_EMAIL_NEW_FAILURE] = (state, action) => emailNewErrorMessage: action.data.error, }); -reducers[ACTIONS.USER_EMAIL_NEW_CLEAR_ENTRY] = state => { +reducers[ACTIONS.USER_EMAIL_NEW_CLEAR_ENTRY] = (state) => { const newUser = { ...state.user }; 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, { passwordResetSuccess: false, passwordResetPending: false, @@ -175,7 +182,7 @@ reducers[ACTIONS.USER_PASSWORD_SET_CLEAR] = state => passwordSetSuccess: false, }); -reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = state => +reducers[ACTIONS.USER_EMAIL_VERIFY_STARTED] = (state) => Object.assign({}, state, { emailVerifyIsPending: true, emailVerifyErrorMessage: '', @@ -202,7 +209,7 @@ reducers[ACTIONS.USER_EMAIL_VERIFY_SET] = (state, action) => emailToVerify: action.data.email, }); -reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = state => +reducers[ACTIONS.USER_IDENTITY_VERIFY_STARTED] = (state) => Object.assign({}, state, { identityVerifyIsPending: true, 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, { inviteStatusIsPending: true, }); @@ -243,13 +250,13 @@ reducers[ACTIONS.USER_INVITE_STATUS_FETCH_SUCCESS] = (state, action) => referralCode: action.data.referralCode, }); -reducers[ACTIONS.USER_INVITE_NEW_STARTED] = state => +reducers[ACTIONS.USER_INVITE_NEW_STARTED] = (state) => Object.assign({}, state, { inviteNewIsPending: true, inviteNewErrorMessage: '', }); -reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = state => +reducers[ACTIONS.USER_INVITE_NEW_SUCCESS] = (state) => Object.assign({}, state, { inviteNewIsPending: false, inviteNewErrorMessage: '', @@ -261,14 +268,14 @@ reducers[ACTIONS.USER_INVITE_NEW_FAILURE] = (state, action) => inviteNewErrorMessage: action.data.error.message, }); -reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = state => +reducers[ACTIONS.USER_INVITE_STATUS_FETCH_FAILURE] = (state) => Object.assign({}, state, { inviteStatusIsPending: false, invitesRemaining: null, invitees: null, }); -reducers[ACTIONS.USER_YOUTUBE_IMPORT_STARTED] = state => +reducers[ACTIONS.USER_YOUTUBE_IMPORT_STARTED] = (state) => Object.assign({}, state, { youtubeChannelImportPending: true, youtubeChannelImportErrorMessage: '', @@ -293,28 +300,28 @@ reducers[ACTIONS.USER_YOUTUBE_IMPORT_FAILURE] = (state, action) => youtubeChannelImportErrorMessage: action.data, }); -reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_STARTED] = state => +reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_STARTED] = (state) => Object.assign({}, state, { resendingVerificationEmail: true, }); -reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_SUCCESS] = state => +reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_SUCCESS] = (state) => Object.assign({}, state, { resendingVerificationEmail: false, }); -reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_FAILURE] = state => +reducers[ACTIONS.USER_EMAIL_VERIFY_RETRY_FAILURE] = (state) => Object.assign({}, state, { resendingVerificationEmail: false, }); -reducers[ACTIONS.USER_SET_REFERRER_STARTED] = state => +reducers[ACTIONS.USER_SET_REFERRER_STARTED] = (state) => Object.assign({}, state, { referrerSetIsPending: true, referrerSetError: defaultState.referrerSetError, }); -reducers[ACTIONS.USER_SET_REFERRER_SUCCESS] = state => +reducers[ACTIONS.USER_SET_REFERRER_SUCCESS] = (state) => Object.assign({}, state, { referrerSetIsPending: false, referrerSetError: defaultState.referrerSetError, @@ -326,25 +333,25 @@ reducers[ACTIONS.USER_SET_REFERRER_FAILURE] = (state, action) => referrerSetError: action.data.error.message, }); -reducers[ACTIONS.USER_SET_REFERRER_RESET] = state => +reducers[ACTIONS.USER_SET_REFERRER_RESET] = (state) => Object.assign({}, state, { referrerSetIsPending: false, referrerSetError: defaultState.referrerSetError, }); -reducers[ACTIONS.USER_PASSWORD_EXISTS] = state => +reducers[ACTIONS.USER_PASSWORD_EXISTS] = (state) => Object.assign({}, state, { passwordExistsForUser: true, }); -reducers[ACTIONS.USER_PASSWORD_RESET_STARTED] = state => +reducers[ACTIONS.USER_PASSWORD_RESET_STARTED] = (state) => Object.assign({}, state, { passwordResetPending: true, passwordResetSuccess: defaultState.passwordResetSuccess, passwordResetError: null, }); -reducers[ACTIONS.USER_PASSWORD_RESET_SUCCESS] = state => +reducers[ACTIONS.USER_PASSWORD_RESET_SUCCESS] = (state) => Object.assign({}, state, { passwordResetPending: false, passwordResetSuccess: true, @@ -356,13 +363,13 @@ reducers[ACTIONS.USER_PASSWORD_RESET_FAILURE] = (state, action) => passwordResetError: action.data.error, }); -reducers[ACTIONS.USER_PASSWORD_SET_STARTED] = state => +reducers[ACTIONS.USER_PASSWORD_SET_STARTED] = (state) => Object.assign({}, state, { passwordSetPending: true, passwordSetSuccess: defaultState.passwordSetSuccess, }); -reducers[ACTIONS.USER_PASSWORD_SET_SUCCESS] = state => +reducers[ACTIONS.USER_PASSWORD_SET_SUCCESS] = (state) => Object.assign({}, state, { passwordSetPending: false, passwordSetSuccess: true, diff --git a/ui/store.js b/ui/store.js index c7f415253..d728cfd12 100644 --- a/ui/store.js +++ b/ui/store.js @@ -11,7 +11,7 @@ import { routerMiddleware } from 'connected-react-router'; import createRootReducer from './reducers'; import { Lbry, buildSharedStateMiddleware, ACTIONS as LBRY_REDUX_ACTIONS } from 'lbry-redux'; import { doSyncLoop } from 'redux/actions/sync'; -import { getAuthToken } from 'util/saved-passwords'; +import { getTokens } from 'util/saved-passwords'; import { generateInitialUrl } from 'util/url'; import { X_LBRY_AUTH_TOKEN } from 'constants/token'; @@ -189,12 +189,14 @@ const sharedStateCb = ({ dispatch, getState }) => { const populateAuthTokenHeader = () => { return (next) => (action) => { - if ( - (action.type === ACTIONS.USER_FETCH_SUCCESS || action.type === ACTIONS.AUTHENTICATION_SUCCESS) && - action.data.user.has_verified_email === true - ) { - const authToken = getAuthToken(); - Lbry.setApiHeader(X_LBRY_AUTH_TOKEN, authToken); + if (action.type === ACTIONS.USER_FETCH_SUCCESS || action.type === ACTIONS.AUTHENTICATION_SUCCESS) { + if (action.data) { + } + const tokens = getTokens(); + // if (tokens.access_token) { + // Lbry.setApiHeader('Authorization', 'Bearer ' + tokens.access_token); + // } + Lbry.setApiHeader(X_LBRY_AUTH_TOKEN, tokens.auth_token); } return next(action); diff --git a/ui/util/keycloak.js b/ui/util/keycloak.js new file mode 100644 index 000000000..a3388c5b3 --- /dev/null +++ b/ui/util/keycloak.js @@ -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; diff --git a/ui/util/saved-passwords.js b/ui/util/saved-passwords.js index db71bdd8d..70e6e54e7 100644 --- a/ui/util/saved-passwords.js +++ b/ui/util/saved-passwords.js @@ -1,4 +1,4 @@ -const { DOMAIN } = require('../../config.js'); +const { DOMAIN } = require('config.js'); const AUTH_TOKEN = 'auth_token'; const 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; const isProduction = process.env.NODE_ENV === 'production'; const maxExpiration = 2147483647; +const { default: keycloak } = require('util/keycloak'); let sessionPassword; function setCookie(name, value, expirationDaysOnWeb) { @@ -59,7 +60,7 @@ function deleteCookie(name) { } function setSavedPassword(value, saveToDisk) { - return new Promise(resolve => { + return new Promise((resolve) => { const password = value === undefined || value === null ? '' : value; sessionPassword = password; @@ -74,17 +75,17 @@ function setSavedPassword(value, saveToDisk) { } function getSavedPassword() { - return new Promise(resolve => { + return new Promise((resolve) => { if (sessionPassword) { resolve(sessionPassword); } - return getPasswordFromCookie().then(p => resolve(p)); + return getPasswordFromCookie().then((p) => resolve(p)); }); } function getPasswordFromCookie() { - return new Promise(resolve => { + return new Promise((resolve) => { let password; password = getCookie(SAVED_PASSWORD); resolve(password); @@ -92,7 +93,7 @@ function getPasswordFromCookie() { } function deleteSavedPassword() { - return new Promise(resolve => { + return new Promise((resolve) => { deleteCookie(SAVED_PASSWORD); resolve(); }); @@ -102,19 +103,28 @@ function getAuthToken() { 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) { return setCookie(AUTH_TOKEN, value, 365); } function deleteAuthToken() { - return new Promise(resolve => { + return new Promise((resolve) => { deleteCookie(AUTH_TOKEN); resolve(); }); } function doSignOutCleanup() { - return new Promise(resolve => { + return new Promise((resolve) => { deleteAuthToken(); deleteSavedPassword(); resolve(); @@ -122,10 +132,10 @@ function doSignOutCleanup() { } function doAuthTokenRefresh() { - const authToken = getAuthToken(); + const { auth_token: authToken } = getAuthToken(); if (authToken) { deleteAuthToken(); - setAuthToken(authToken); + setCookie(AUTH_TOKEN, authToken, 365); } } @@ -147,6 +157,7 @@ module.exports = { deleteSavedPassword, getAuthToken, setAuthToken, + getTokens, deleteAuthToken, doSignOutCleanup, doAuthTokenRefresh, diff --git a/web/src/robots.js b/web/src/robots.js deleted file mode 100644 index c76140386..000000000 --- a/web/src/robots.js +++ /dev/null @@ -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 }; diff --git a/web/src/routes.js b/web/src/routes.js index 38f1ceb38..98102ff1c 100644 --- a/web/src/routes.js +++ b/web/src/routes.js @@ -2,6 +2,7 @@ const { getHtml } = require('./html'); const { getRss } = require('./rss'); const { getHomepageJSON } = require('./getHomepageJSON'); const { generateStreamUrl } = require('../../ui/util/web'); +const { createReadStream } = require('fs'); const fetch = require('node-fetch'); const Router = require('@koa/router'); 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) => { const streamUrl = getStreamUrl(ctx); const downloadUrl = `${streamUrl}?download=1`; diff --git a/yarn.lock b/yarn.lock index 96aee291f..84c5d715e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3495,9 +3495,10 @@ balanced-match@^1.0.0: resolved "https://registry.yarnpkg.com/balanced-match/-/balanced-match-1.0.0.tgz#89b4d199ab2bee49de164ea02b89ce462d71b767" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base64-js@^1.0.2: +base64-js@1.3.1, base64-js@^1.0.2: version "1.3.1" 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: version "0.11.2" @@ -8297,6 +8298,7 @@ hoek@4.x.x: hoist-non-react-statics@^3.1.0, hoist-non-react-statics@^3.3.0: version "3.3.2" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45" + integrity sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw== dependencies: react-is "^16.7.0" @@ -9822,6 +9824,11 @@ js-base64@^2.1.9: resolved "https://registry.yarnpkg.com/js-base64/-/js-base64-2.6.4.tgz#f4e686c5de1ea1f867dbcad3d46d969428df98c4" 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: version "3.0.2" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-3.0.2.tgz#9866df395102130e38f7f996bceb65443209c25b" @@ -10062,6 +10069,14 @@ jszip@~2.5.0: dependencies: 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: version "2.2.0" resolved "https://registry.yarnpkg.com/keycode/-/keycode-2.2.0.tgz#3d0af56dc7b8b8e5cba8d0a97f107204eec22b04" @@ -10148,9 +10163,9 @@ lbry-redux@lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676: reselect "^3.0.0" uuid "^8.3.1" -lbryinc@lbryio/lbryinc#0b4e41ef90d6347819dd3453f2f9398a5c1b4f36: +lbryinc@lbryio/lbryinc#f133a7ed62c1f2202f98e785e923a7d841504d0d: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/0b4e41ef90d6347819dd3453f2f9398a5c1b4f36" + resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/f133a7ed62c1f2202f98e785e923a7d841504d0d" dependencies: reselect "^3.0.0" -- 2.45.2 From 19bf47cc3f0c3efe9f8efad5bfaab5684bd655d3 Mon Sep 17 00:00:00 2001 From: zeppi Date: Mon, 27 Sep 2021 00:52:08 -0400 Subject: [PATCH 2/4] wip --- package.json | 2 +- ui/index.jsx | 30 +++++++++++++++++------------- ui/store.js | 9 +++++---- yarn.lock | 4 ++-- 4 files changed, 25 insertions(+), 20 deletions(-) diff --git a/package.json b/package.json index 3c70ff597..15aeb7c66 100644 --- a/package.json +++ b/package.json @@ -157,7 +157,7 @@ "json-loader": "^0.5.4", "lbry-format": "https://github.com/lbryio/lbry-format.git", "lbry-redux": "lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676", - "lbryinc": "lbryio/lbryinc#f133a7ed62c1f2202f98e785e923a7d841504d0d", + "lbryinc": "lbryio/lbryinc#6714d125602da6d383d8d8d9d6ebffb30b46869b", "lint-staged": "^7.0.2", "localforage": "^1.7.1", "lodash-es": "^4.17.14", diff --git a/ui/index.jsx b/ui/index.jsx index 1717bd3b9..59d37072d 100644 --- a/ui/index.jsx +++ b/ui/index.jsx @@ -259,22 +259,25 @@ function AppWrapper() { }, []); function initKeycloak() { - console.dir(keycloak) - keycloak.init( - { onLoad: 'check-sso', + console.dir(keycloak); + keycloak + .init({ + onLoad: 'check-sso', silentCheckSsoFallback: false, - didInit: true, - redirectUri: isDev ? 'http://localhost:9090/' : `${SITE_URL}/`} - ).then(function(authenticated) { - setKeycloakReady(true); - console.log('INIT: ', authenticated ? 'Authenticated' : 'Not Authenticated'); - }).catch(function() { - console.log('INIT: FAILED'); - }); + silentCheckSsoRedirectUri: window.location.origin + '/silent-check-sso.html', + redirectUri: isDev ? 'http://localhost:9090/' : `${SITE_URL}/`, + }) + .then(function (authenticated) { + setKeycloakReady(true); + console.log('INIT: ', authenticated ? 'Authenticated' : 'Not Authenticated'); + }) + .catch(function () { + console.log('INIT: FAILED'); + }); } useEffect(() => { - console.log('KCR RENDER', keycloakReady) + console.log('KCR RENDER', keycloakReady); if (!keycloakReady) { initKeycloak(); } @@ -294,7 +297,8 @@ function AppWrapper() { * and we have checked with keycloak for a token */ useEffect(() => { - if (readyToLaunch && persistDone && keycloakReady) { // keycloak ready + if (readyToLaunch && persistDone && keycloakReady) { + // keycloak ready if (DEFAULT_LANGUAGE) { app.store.dispatch(doFetchLanguage(DEFAULT_LANGUAGE)); } diff --git a/ui/store.js b/ui/store.js index d728cfd12..8c7fade8d 100644 --- a/ui/store.js +++ b/ui/store.js @@ -193,10 +193,11 @@ const populateAuthTokenHeader = () => { if (action.data) { } const tokens = getTokens(); - // if (tokens.access_token) { - // Lbry.setApiHeader('Authorization', 'Bearer ' + tokens.access_token); - // } - Lbry.setApiHeader(X_LBRY_AUTH_TOKEN, tokens.auth_token); + if (tokens.access_token) { + Lbry.setApiHeader('Authorization', 'Bearer ' + tokens.access_token); + } else { + Lbry.setApiHeader(X_LBRY_AUTH_TOKEN, tokens.auth_token); + } } return next(action); diff --git a/yarn.lock b/yarn.lock index 84c5d715e..d2a4ca6af 100644 --- a/yarn.lock +++ b/yarn.lock @@ -10163,9 +10163,9 @@ lbry-redux@lbryio/lbry-redux#32b578707116d45f5b51b7ab523d200e75668676: reselect "^3.0.0" uuid "^8.3.1" -lbryinc@lbryio/lbryinc#f133a7ed62c1f2202f98e785e923a7d841504d0d: +lbryinc@lbryio/lbryinc#6714d125602da6d383d8d8d9d6ebffb30b46869b: version "0.0.1" - resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/f133a7ed62c1f2202f98e785e923a7d841504d0d" + resolved "https://codeload.github.com/lbryio/lbryinc/tar.gz/6714d125602da6d383d8d8d9d6ebffb30b46869b" dependencies: reselect "^3.0.0" -- 2.45.2 From 5e4ea29e1b2d514dd92d3bdc41f41468e2fbc83e Mon Sep 17 00:00:00 2001 From: zeppi Date: Thu, 30 Sep 2021 22:57:07 -0400 Subject: [PATCH 3/4] cleaning --- ui/component/router/view.jsx | 3 --- ui/index.jsx | 3 --- ui/redux/reducers/user.js | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/ui/component/router/view.jsx b/ui/component/router/view.jsx index d39d3cfbd..184f9e143 100644 --- a/ui/component/router/view.jsx +++ b/ui/component/router/view.jsx @@ -22,8 +22,6 @@ const BackupPage = lazyImport(() => import('page/backup' /* webpackChunkName: "b const Code2257Page = lazyImport(() => import('web/page/code2257' /* webpackChunkName: "code2257" */)); // @endif -console.log('keycloak', keycloak) - // Chunk: "secondary" const SignInPage = lazyImport(() => import('page/signIn' /* webpackChunkName: "secondary" */)); const SignInWalletPasswordPage = lazyImport(() => @@ -130,7 +128,6 @@ function PrivateRoute(props: PrivateRouteProps) { const { component: Component, isAuthenticated, ...rest } = props; const urlSearchParams = new URLSearchParams(props.location.search); const redirectUrl = urlSearchParams.get('redirect'); - // const { keycloak } = useKeycloak(); return ( { // @if TARGET='app' moment.locale(remote.app.getLocale()); @@ -283,8 +282,6 @@ function AppWrapper() { } }, [keycloakReady]); - // initKeycloak(); - useEffect(() => { if (persistDone) { app.store.dispatch(doToggle3PAnalytics(null, true)); diff --git a/ui/redux/reducers/user.js b/ui/redux/reducers/user.js index 685bed5c8..ca95dfc61 100644 --- a/ui/redux/reducers/user.js +++ b/ui/redux/reducers/user.js @@ -25,7 +25,7 @@ const defaultState = { referralLink: undefined, referralCode: undefined, user: { has_verified_email: false }, - accessToken: undefined, // rename this + accessToken: undefined, // rename this to authToken if keeping. youtubeChannelImportPending: false, youtubeChannelImportErrorMessage: '', referrerSetIsPending: false, -- 2.45.2 From 2f4a6d5f47dc51582b391ead4ef1a45e105d897b Mon Sep 17 00:00:00 2001 From: zeppi Date: Thu, 30 Sep 2021 23:19:14 -0400 Subject: [PATCH 4/4] lint --- ui/store.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ui/store.js b/ui/store.js index 8c7fade8d..d86d6c57f 100644 --- a/ui/store.js +++ b/ui/store.js @@ -194,7 +194,7 @@ const populateAuthTokenHeader = () => { } const tokens = getTokens(); if (tokens.access_token) { - Lbry.setApiHeader('Authorization', 'Bearer ' + tokens.access_token); + Lbry.setApiHeader('Authorization', `Bearer ${tokens.access_token}`); } else { Lbry.setApiHeader(X_LBRY_AUTH_TOKEN, tokens.auth_token); } -- 2.45.2