[feature] add the possibility to enter oculus token manually

This commit is contained in:
MathieuG-P
2023-11-12 02:24:40 +01:00
parent a5bb3a1c3d
commit 4cd2fb8da0
12 changed files with 208 additions and 46 deletions
@@ -7,10 +7,11 @@ import { OculusDownloader } from "../../models/oculus-downloader.class";
import { Cookie, session } from "electron";
import { Progression, ensurePathNotAlreadyExist } from "../../helpers/fs.helpers";
import { BSLocalVersionService } from "../bs-local-version.service";
import { Observable, finalize, from, map, switchMap } from "rxjs";
import { Observable, finalize, from, map, of, switchMap } from "rxjs";
import path from "path";
import { DownloadInfo } from "./bs-steam-downloader.service";
import { BsStore } from "../../../shared/models/bs-store.enum";
import { isOculusTokenValid } from "../../../shared/helpers/oculus.helpers";
export class BsOculusDownloaderService {
@@ -37,34 +38,7 @@ export class BsOculusDownloaderService {
}
private isUserTokenValid(token: string): boolean{
// Code taken from "https://github.com/ComputerElite/QuestAppVersionSwitcher" (TokenTools.cs)
log.info("Checking if Oculus user token is valid");
if(!token){
log.info("Oculus user token is empty");
return false;
}
if(token.includes("%")){
log.info("Token contains %. Token most likely comes from an uri and won't work");
return false;
}
if(!token.startsWith("OC")){
log.info("Token does not start with OC.");
return false;
}
if(token.includes("|")){
log.info("Token contains | which usually indicates an application token which is not valid for user tokens");
return false;
}
if(token.match(/OC\d{15}/)){
log.info("Token matches /OC[0-9}{15}/ which usually indicates a changed oculus store token");
return false;
}
return true;
return isOculusTokenValid(token, log.info);
}
private isCookieValid(cookie: Cookie): boolean {
@@ -147,7 +121,9 @@ export class BsOculusDownloaderService {
let downloadVersion: BSVersion
return from(this.getUserTokenFromMetaAuth(downloadInfo.stay)).pipe(
const tokenObs$ = downloadInfo.token ? of(downloadInfo.token) : from(this.getUserTokenFromMetaAuth(downloadInfo.stay));
return tokenObs$.pipe(
switchMap(token => {
if(!downloadInfo.isVerification){
return this.createDownloadVersion(downloadInfo.bsVersion).then(({version, dest}) => ({token, version, dest}))
@@ -169,7 +145,9 @@ export class BsOculusDownloaderService {
let downloadVersion: BSVersion
return from(this.getAuthToken()).pipe(
const tokenObs$ = downloadInfo.token ? of(downloadInfo.token) : from(this.getAuthToken());
return tokenObs$.pipe(
map(token => {
if(!token){
throw new CustomError("No Meta auth token was found in cookies for auto download", "NO_META_AUTH_TOKEN");
@@ -159,6 +159,7 @@ export interface DownloadInfo {
bsVersion: BSVersion;
isVerification?: boolean;
stay?: boolean;
token?: string;
}
export interface DownloadSteamInfo extends DownloadInfo {
@@ -0,0 +1,59 @@
import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { ModalComponent, ModalExitCode } from "../../../../services/modale.service";
import { BsmImage } from "renderer/components/shared/bsm-image.component";
import BeatWaiting from "../../../../../../assets/images/apngs/beat-impatient.png";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { useState } from "react";
import { isOculusTokenValid } from "shared/helpers/oculus.helpers";
import { logRenderError } from "renderer";
export const EnterMetaTokenModal: ModalComponent<string> = ({resolver}) => {
const t = useTranslation();
const [token, setToken] = useState("");
const [showToken, setShowToken] = useState(false);
const submit = () => {
resolver({exitCode: ModalExitCode.COMPLETED, data: token});
}
const cancel = () => {
resolver({exitCode: ModalExitCode.CANCELED});
}
const isTokenValid = (() => {
return isOculusTokenValid(token, logRenderError);
})();
return (
<form className="flex flex-col w-80 gap-4">
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("modals.enter-meta-token.title")}</h1>
<BsmImage className="h-20 w-20 mx-auto" image={BeatWaiting}/>
<p>{t("modals.enter-meta-token.body.need-token")}</p>
<a href="https://github.com/Zagrios/bs-manager/wiki/How-to-obtain-your-Oculus-Token" target="_blank" className="underline">{t("modals.enter-meta-token.body.how-obtain-token")}</a>
<div>
<div className="flex flex-row items-center justify-between">
<label className="font-bold cursor-pointer tracking-wide inline-block" htmlFor="meta_token">{t("modals.enter-meta-token.body.input-label")}</label>
{!isTokenValid && token.length > 0 && (
<span className="text-orange-700 dark:text-orange-400 text-xs whitespace-normal min-w-0">{t("modals.enter-meta-token.body.token-is-invalid")}</span>
)}
</div>
<div className="bg-light-main-color-1 dark:bg-main-color-1 rounded-md flex box-border h-9">
<input className="grow px-1 py-[2px] outline-none bg-transparent" onChange={e => setToken(e.target.value)} value={token} type={showToken ? "text" : "password"} name="meta_token" id="meta_token" placeholder="OCAStr43sdx2..." />
<BsmButton className="shrink-0 m-1 rounded-md p-0.5 !bg-light-main-color-3 dark:!bg-main-color-3" icon={showToken ? "eye-cross" : "eye"} withBar={false} onClick={() => setShowToken(prev => !prev)} />
</div>
</div>
<div className="flex flex-row gap-2">
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="cancel" text="misc.cancel" withBar={false} onClick={cancel}/>
<BsmButton className="rounded-md flex justify-center items-center transition-all h-9 w-full" typeColor="primary" text="modals.enter-meta-token.valid-btn" withBar={false} onClick={submit} disabled={!isTokenValid}/>
</div>
</form>
);
}
@@ -4,17 +4,24 @@ import { BsmButton } from "renderer/components/shared/bsm-button.component";
import { useTranslation } from "renderer/hooks/use-translation.hook";
import { MetaIcon } from "renderer/components/svgs/icons/meta-icon.component";
import { BsmCheckbox } from "renderer/components/shared/bsm-checkbox.component";
import Tippy from "@tippyjs/react";
export const LoginToMetaModal: ModalComponent<boolean> = ({ resolver }) => {
type ReturnType = { method: MetaAuthMethod.META, stay: boolean } | { method: MetaAuthMethod.MANUAL };
export const LoginToMetaModal: ModalComponent<ReturnType> = ({ resolver }) => {
const t = useTranslation();
const [stay, setStay] = useState(false);
const submit = () => {
resolver({ exitCode: ModalExitCode.COMPLETED, data: stay });
resolver({ exitCode: ModalExitCode.COMPLETED, data: { method: MetaAuthMethod.META, stay } });
};
const enterTokenManually = () => {
resolver({ exitCode: ModalExitCode.COMPLETED, data: { method: MetaAuthMethod.MANUAL } });
}
return (
<form className="flex flex-col justify-center items-center w-96 gap-4">
<h1 className="text-3xl uppercase tracking-wide w-full text-center">{t("modals.connect-to-meta.title")}</h1>
@@ -32,6 +39,16 @@ export const LoginToMetaModal: ModalComponent<boolean> = ({ resolver }) => {
</div>
<BsmButton className="rounded-md flex justify-center items-center transition-all h-10 w-full" typeColor="primary" text="modals.connect-to-meta.connect-to-meta" withBar={false} onClick={submit}/>
<Tippy className="!bg-neutral-900" arrow={false} content={t("modals.connect-to-meta.body.enter-token-manually-tooltip")} >
<p className="text-sm italic underline text-center -translate-y-1 leading-3 cursor-pointer" onClick={enterTokenManually}>{t("modals.connect-to-meta.body.enter-token-manually")}</p>
</Tippy>
</form>
);
};
export enum MetaAuthMethod {
MANUAL = "manual",
META = "meta"
}
+5 -1
View File
@@ -15,7 +15,7 @@ const ipc = IpcService.getInstance();
const themeService = ThemeService.getInstance();
window.onerror = (...data) => {
ipc.sendLazy("log-error", { args: data });
logRenderError(data);
};
document.addEventListener("DOMContentLoaded", () => {
@@ -58,4 +58,8 @@ if (launcherContainer) {
});
}
export function logRenderError(...params: unknown[]){
ipc.sendLazy("log-error", { args: params });
}
@@ -6,11 +6,12 @@ import { ProgressBarService } from "../progress-bar.service";
import { CustomError } from "shared/models/exceptions/custom-error.class";
import { NotificationService } from "../notification.service";
import { ModalExitCode, ModalService } from "../modale.service";
import { LoginToMetaModal } from "renderer/components/modal/modal-types/bs-downgrade/login-to-meta-modal.component";
import { LoginToMetaModal, MetaAuthMethod } from "renderer/components/modal/modal-types/bs-downgrade/login-to-meta-modal.component";
import { DownloaderServiceInterface } from "./bs-store-downloader.interface";
import { AbstractBsDownloaderService } from "./abstract-bs-downloader.service";
import { DownloadInfo } from "main/services/bs-version-download/bs-steam-downloader.service";
import { MetaAuthErrorCodes, OculusDownloaderErrorCodes } from "shared/models/bs-version-download/oculus-download.model";
import { EnterMetaTokenModal } from "renderer/components/modal/modal-types/bs-downgrade/enter-meta-token-modal.component";
export class OculusDownloaderService extends AbstractBsDownloaderService implements DownloaderServiceInterface{
@@ -103,16 +104,26 @@ export class OculusDownloaderService extends AbstractBsDownloaderService impleme
if(autoDownload){ return autoDownload; }
const [completed, stay] = await this.modals.openModal(LoginToMetaModal).then(res => [res.exitCode === ModalExitCode.COMPLETED, res.data]);
const {exitCode, data} = await this.modals.openModal(LoginToMetaModal)
if(!completed){
return completed;
if(exitCode !== ModalExitCode.COMPLETED){
return false
}
return lastValueFrom(this.startDownloadBsVersion({ bsVersion, isVerification, stay })).then(() => true);
if(data.method === MetaAuthMethod.META){
return lastValueFrom(this.startDownloadBsVersion({ bsVersion, isVerification, stay: data.stay })).then(() => true);
}
})().then(() => {
if(autoDownloadFailed){
const tokenRes = await this.modals.openModal(EnterMetaTokenModal);
if(tokenRes.exitCode !== ModalExitCode.COMPLETED){
return false;
}
return lastValueFrom(this.startDownloadBsVersion({ bsVersion, isVerification, token: tokenRes.data })).then(() => true);
})().then(res => {
if(autoDownloadFailed || !res){
return bsVersion;
}
+32
View File
@@ -0,0 +1,32 @@
export function isOculusTokenValid(token: string, logger?: (...args: unknown[]) => void): boolean{
// Code taken from "https://github.com/ComputerElite/QuestAppVersionSwitcher" (TokenTools.cs)
logger?.("Checking if Oculus user token is valid");
if(!token){
logger?.("Oculus user token is empty");
return false;
}
if(token.includes("%")){
logger?.("Token contains %. Token most likely comes from an uri and won't work");
return false;
}
if(!token.startsWith("OC")){
logger?.("Token does not start with OC.");
return false;
}
if(token.includes("|")){
logger?.("Token contains | which usually indicates an application token which is not valid for user tokens");
return false;
}
if(token.match(/OC\d{15}/)){
logger?.("Token matches /OC[0-9}{15}/ which usually indicates a changed oculus store token");
return false;
}
logger?.("Oculus user token is valid");
return true;
}