[web] Get passkeys working on localhost too (#2031)
This commit is contained in:
9
web/apps/accounts/.env.development
Normal file
9
web/apps/accounts/.env.development
Normal file
@@ -0,0 +1,9 @@
|
||||
# Copy this file into `.env.local` and uncomment these to develop against apps
|
||||
# and server running on localhost.
|
||||
#
|
||||
# For details, please see `apps/photos/.env.development`
|
||||
|
||||
#NEXT_PUBLIC_ENTE_ENDPOINT = http://localhost:8080
|
||||
#NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT = http://localhost:3002
|
||||
#NEXT_PUBLIC_ENTE_ACCOUNTS_URL = http://localhost:3001
|
||||
#NEXT_PUBLIC_ENTE_PAYMENTS_URL = http://localhost:3001
|
||||
@@ -2,7 +2,27 @@
|
||||
|
||||
Code that runs on `accounts.ente.io`.
|
||||
|
||||
Passkeys are tied to domains, so this app serves as a common sharing point to
|
||||
allow the passkey to be tied to the user's Ente account and be used by all our
|
||||
other apps. For more details, see
|
||||
Primarily, this serves a common domain where our clients (mobile and web / auth
|
||||
and photos) can create and authenticate using shared passkeys tied to the user's
|
||||
Ente account. Passkeys can be shared by multiple domains, so we didn't strictly
|
||||
need a separate web origin for sharing passkeys across our web clients, but we
|
||||
do need a web origin to handle the passkey flow for the mobile clients.
|
||||
|
||||
For more details about the Passkey flows,
|
||||
[docs/webauthn-passkeys.md](../../docs/webauthn-passkeys.md).
|
||||
|
||||
## Development
|
||||
|
||||
To set this up to work with a locally running museum, modify your local
|
||||
`museum.yaml` to set the relaying party's ID to "localhost" (without any port
|
||||
number).
|
||||
|
||||
```yaml
|
||||
webauthn:
|
||||
rpid: "localhost"
|
||||
rporigins:
|
||||
- "http://localhost:3001"
|
||||
```
|
||||
|
||||
Note that browsers already treat `localhost` as a secure domain, so Passkey APIs
|
||||
will work even if our local dev server is using `http`.
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import Page from "@ente/accounts/pages/passkeys/finish";
|
||||
|
||||
// See: [Note: Finish passkey flow in the requesting app]
|
||||
export default Page;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import log from "@/next/log";
|
||||
import { clientPackageName } from "@/next/types/app";
|
||||
import { nullToUndefined } from "@/utils/transform";
|
||||
import {
|
||||
CenteredFlex,
|
||||
VerticallyCentered,
|
||||
@@ -18,6 +19,7 @@ import { useEffect, useState } from "react";
|
||||
import {
|
||||
beginPasskeyAuthentication,
|
||||
finishPasskeyAuthentication,
|
||||
isWhitelistedRedirect,
|
||||
type BeginPasskeyAuthenticationResponse,
|
||||
} from "services/passkey";
|
||||
|
||||
@@ -31,25 +33,16 @@ const PasskeysFlow = () => {
|
||||
const init = async () => {
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
|
||||
// get redirect from the query params
|
||||
const redirect = searchParams.get("redirect") as string;
|
||||
// Extract redirect from the query params.
|
||||
const redirect = nullToUndefined(searchParams.get("redirect"));
|
||||
const redirectURL = redirect ? new URL(redirect) : undefined;
|
||||
|
||||
const redirectURL = new URL(redirect);
|
||||
if (process.env.NEXT_PUBLIC_DISABLE_REDIRECT_CHECK !== "true") {
|
||||
if (
|
||||
redirect !== "" &&
|
||||
!(
|
||||
redirectURL.host.endsWith(".ente.io") ||
|
||||
redirectURL.host.endsWith(".ente.sh") ||
|
||||
redirectURL.host.endsWith("bada-frame.pages.dev")
|
||||
) &&
|
||||
redirectURL.protocol !== "ente:" &&
|
||||
redirectURL.protocol !== "enteauth:"
|
||||
) {
|
||||
setInvalidInfo(true);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
// Ensure that redirectURL is whitelisted, otherwise show an invalid
|
||||
// "login" URL error to the user.
|
||||
if (!redirectURL || !isWhitelistedRedirect(redirectURL)) {
|
||||
setInvalidInfo(true);
|
||||
setLoading(false);
|
||||
return;
|
||||
}
|
||||
|
||||
let pkg = clientPackageName["photos"];
|
||||
@@ -60,6 +53,7 @@ const PasskeysFlow = () => {
|
||||
}
|
||||
|
||||
setData(LS_KEYS.CLIENT_PACKAGE, { name: pkg });
|
||||
// The server needs to know the app on whose behalf we're trying to log in
|
||||
HTTPService.setHeaders({
|
||||
"X-Client-Package": pkg,
|
||||
});
|
||||
|
||||
@@ -140,46 +140,44 @@ const Passkeys = () => {
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<PasskeysContext.Provider
|
||||
value={{
|
||||
selectedPasskey,
|
||||
setSelectedPasskey,
|
||||
setShowPasskeyDrawer,
|
||||
refreshPasskeys: init,
|
||||
}}
|
||||
>
|
||||
<CenteredFlex>
|
||||
<Box maxWidth="20rem">
|
||||
<Box marginBottom="1rem">
|
||||
<Typography>{t("PASSKEYS_DESCRIPTION")}</Typography>
|
||||
</Box>
|
||||
<FormPaper
|
||||
style={{
|
||||
padding: "1rem",
|
||||
}}
|
||||
>
|
||||
<SingleInputForm
|
||||
fieldType="text"
|
||||
placeholder={t("ENTER_PASSKEY_NAME")}
|
||||
buttonText={t("ADD_PASSKEY")}
|
||||
initialValue={""}
|
||||
callback={handleSubmit}
|
||||
submitButtonProps={{
|
||||
sx: {
|
||||
marginBottom: 1,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormPaper>
|
||||
<Box marginTop="1rem">
|
||||
<PasskeysList passkeys={passkeys} />
|
||||
</Box>
|
||||
<PasskeysContext.Provider
|
||||
value={{
|
||||
selectedPasskey,
|
||||
setSelectedPasskey,
|
||||
setShowPasskeyDrawer,
|
||||
refreshPasskeys: init,
|
||||
}}
|
||||
>
|
||||
<CenteredFlex>
|
||||
<Box maxWidth="20rem">
|
||||
<Box marginBottom="1rem">
|
||||
<Typography>{t("PASSKEYS_DESCRIPTION")}</Typography>
|
||||
</Box>
|
||||
</CenteredFlex>
|
||||
<ManagePasskeyDrawer open={showPasskeyDrawer} />
|
||||
</PasskeysContext.Provider>
|
||||
</>
|
||||
<FormPaper
|
||||
style={{
|
||||
padding: "1rem",
|
||||
}}
|
||||
>
|
||||
<SingleInputForm
|
||||
fieldType="text"
|
||||
placeholder={t("ENTER_PASSKEY_NAME")}
|
||||
buttonText={t("ADD_PASSKEY")}
|
||||
initialValue={""}
|
||||
callback={handleSubmit}
|
||||
submitButtonProps={{
|
||||
sx: {
|
||||
marginBottom: 1,
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</FormPaper>
|
||||
<Box marginTop="1rem">
|
||||
<PasskeysList passkeys={passkeys} />
|
||||
</Box>
|
||||
</Box>
|
||||
</CenteredFlex>
|
||||
<ManagePasskeyDrawer open={showPasskeyDrawer} />
|
||||
</PasskeysContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
@@ -191,16 +189,14 @@ interface PasskeysListProps {
|
||||
|
||||
const PasskeysList: React.FC<PasskeysListProps> = ({ passkeys }) => {
|
||||
return (
|
||||
<>
|
||||
<MenuItemGroup>
|
||||
{passkeys.map((passkey, i) => (
|
||||
<Fragment key={passkey.id}>
|
||||
<PasskeyListItem passkey={passkey} />
|
||||
{i < passkeys.length - 1 && <MenuItemDivider />}
|
||||
</Fragment>
|
||||
))}
|
||||
</MenuItemGroup>
|
||||
</>
|
||||
<MenuItemGroup>
|
||||
{passkeys.map((passkey, i) => (
|
||||
<Fragment key={passkey.id}>
|
||||
<PasskeyListItem passkey={passkey} />
|
||||
{i < passkeys.length - 1 && <MenuItemDivider />}
|
||||
</Fragment>
|
||||
))}
|
||||
</MenuItemGroup>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import { isDevBuild } from "@/next/env";
|
||||
import log from "@/next/log";
|
||||
import { toB64URLSafeNoPadding } from "@ente/shared/crypto/internal/libsodium";
|
||||
import HTTPService from "@ente/shared/network/HTTPService";
|
||||
@@ -81,6 +82,18 @@ export const getPasskeyRegistrationOptions = async () => {
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Return `true` if the given {@link redirectURL} (obtained from the redirect
|
||||
* query parameter passed around during the passkey verification flow) is one of
|
||||
* the whitelisted URLs that we allow redirecting to on success.
|
||||
*/
|
||||
export const isWhitelistedRedirect = (redirectURL: URL) =>
|
||||
(isDevBuild && redirectURL.host.endsWith("localhost")) ||
|
||||
redirectURL.host.endsWith(".ente.io") ||
|
||||
redirectURL.host.endsWith(".ente.sh") ||
|
||||
redirectURL.protocol == "ente:" ||
|
||||
redirectURL.protocol == "enteauth:";
|
||||
|
||||
export const finishPasskeyRegistration = async (
|
||||
friendlyName: string,
|
||||
credential: Credential,
|
||||
|
||||
@@ -54,102 +54,6 @@ body {
|
||||
height: 100vh;
|
||||
}
|
||||
|
||||
.pswp__button--custom {
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
background: none !important;
|
||||
background-image: none !important;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.pswp__item video {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.pswp-item-container {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.pswp-item-container > * {
|
||||
position: absolute;
|
||||
transition: opacity 1s ease;
|
||||
max-width: 100%;
|
||||
max-height: 100%;
|
||||
}
|
||||
|
||||
.pswp-item-container > img {
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.pswp-item-container > video {
|
||||
opacity: 0;
|
||||
}
|
||||
|
||||
.pswp-item-container > div.download-banner {
|
||||
width: 100%;
|
||||
height: 16vh;
|
||||
padding: 2vh 0;
|
||||
background-color: #151414;
|
||||
color: #ddd;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: space-around;
|
||||
opacity: 0.8;
|
||||
font-size: 20px;
|
||||
}
|
||||
|
||||
.download-banner > a {
|
||||
width: 130px;
|
||||
}
|
||||
|
||||
.pswp__img {
|
||||
object-fit: contain;
|
||||
}
|
||||
|
||||
.pswp__button--arrow--left,
|
||||
.pswp__button--arrow--right {
|
||||
color: #fff;
|
||||
background-color: #333333 !important;
|
||||
border-radius: 50%;
|
||||
width: 56px;
|
||||
height: 56px;
|
||||
}
|
||||
.pswp__button--arrow--left::before,
|
||||
.pswp__button--arrow--right::before {
|
||||
background: none !important;
|
||||
}
|
||||
|
||||
.pswp__button--arrow--left {
|
||||
margin-left: 20px;
|
||||
}
|
||||
|
||||
.pswp__button--arrow--right {
|
||||
margin-right: 20px;
|
||||
}
|
||||
|
||||
.pswp-custom-caption-container {
|
||||
width: 100%;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
bottom: 56px;
|
||||
background-color: transparent !important;
|
||||
}
|
||||
|
||||
.pswp__caption--empty {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.bg-upload-progress-bar {
|
||||
background-color: #51cd7c;
|
||||
}
|
||||
|
||||
div.otp-input input {
|
||||
width: 36px !important;
|
||||
height: 36px;
|
||||
@@ -168,18 +72,3 @@ div.otp-input input:focus {
|
||||
transition: 0.5s;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.flash-message {
|
||||
padding: 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
@-webkit-keyframes rotation {
|
||||
from {
|
||||
-webkit-transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
-webkit-transform: rotate(359deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@ Add the following to `web/apps/photos/.env.local`:
|
||||
|
||||
```env
|
||||
NEXT_PUBLIC_ENTE_ENDPOINT = http://localhost:8080
|
||||
NEXT_PUBLIC_ENTE_PAYMENTS_ENDPOINT = http://localhost:3001
|
||||
NEXT_PUBLIC_ENTE_PAYMENTS_URL = http://localhost:3001
|
||||
```
|
||||
|
||||
Then start it locally
|
||||
|
||||
@@ -41,13 +41,13 @@
|
||||
#
|
||||
# NEXT_PUBLIC_ENTE_ENDPOINT = http://localhost:3000
|
||||
|
||||
# The Ente API endpoint for accounts related functionality
|
||||
# The URL of the accounts app
|
||||
#
|
||||
# NEXT_PUBLIC_ENTE_ACCOUNTS_ENDPOINT = http://localhost:3001
|
||||
# NEXT_PUBLIC_ENTE_ACCOUNTS_URL = http://localhost:3001
|
||||
|
||||
# The Ente API endpoint for payments related functionality
|
||||
# The URL of the payments app
|
||||
#
|
||||
# NEXT_PUBLIC_ENTE_PAYMENTS_ENDPOINT = http://localhost:3001
|
||||
# NEXT_PUBLIC_ENTE_PAYMENTS_URL = http://localhost:3001
|
||||
|
||||
# The URL for the shared albums deployment
|
||||
#
|
||||
@@ -69,7 +69,7 @@
|
||||
#
|
||||
# NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT = http://localhost:3002
|
||||
|
||||
# The URL of the family plans web app deployment
|
||||
# The URL of the family plans web app
|
||||
#
|
||||
# Currently the source code for the family plan related pages is in a separate
|
||||
# repository (https://github.com/ente-io/families). The mobile app also uses
|
||||
@@ -77,7 +77,7 @@
|
||||
#
|
||||
# Enhancement: Consider moving that into the app/ folder in this repository.
|
||||
#
|
||||
# NEXT_PUBLIC_ENTE_FAMILY_ENDPOINT = http://localhost:3001
|
||||
# NEXT_PUBLIC_ENTE_FAMILY_URL = http://localhost:3001
|
||||
|
||||
# The JSON which describes the expected results of our integration tests. See
|
||||
# `upload.test.ts` for more details of the expected format.
|
||||
|
||||
@@ -11,15 +11,21 @@
|
||||
|
||||
#NEXT_PUBLIC_ENTE_ENDPOINT = http://localhost:8080
|
||||
|
||||
# If you wish to preview how the shared albums work, you can use `yarn
|
||||
# dev:albums`. You'll need to run two instances.
|
||||
|
||||
# The equivalent CLI commands using env vars would be:
|
||||
# If you wish to preview how the shared albums work, you can also uncomment
|
||||
# this, and run two apps:
|
||||
#
|
||||
# # For the normal web app
|
||||
# NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 yarn dev:photos
|
||||
#
|
||||
# # For the albums app
|
||||
# NEXT_PUBLIC_ENTE_ENDPOINT=http://localhost:8080 NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT=http://localhost:3002 yarn dev:albums
|
||||
# - `yarn dev:photos` (the main app)
|
||||
# - `yarn dev:albums` (the sidecar app, in this case, albums)
|
||||
|
||||
#NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT = http://localhost:3002
|
||||
|
||||
# We also have various sidecar apps. These all run on a separate port, 3001,
|
||||
# since usually when developing we usually need to run only one of them in
|
||||
# addition to the main photos app. So you can uncomment this entire set.
|
||||
#
|
||||
# You'll also need to create a similar `.env.local` or `.env.development.local`
|
||||
# in the app you're running (e.g. in apps/accounts), and put an
|
||||
# `NEXT_PUBLIC_ENTE_ENDPOINT` in there.
|
||||
|
||||
#NEXT_PUBLIC_ENTE_ACCOUNTS_URL = http://localhost:3001
|
||||
#NEXT_PUBLIC_ENTE_PAYMENTS_URL = http://localhost:3001
|
||||
|
||||
@@ -22,7 +22,7 @@ import {
|
||||
generateEncryptionKey,
|
||||
} from "@ente/shared/crypto/internal/libsodium";
|
||||
import { useLocalState } from "@ente/shared/hooks/useLocalState";
|
||||
import { getAccountsURL } from "@ente/shared/network/api";
|
||||
import { accountsAppURL } from "@ente/shared/network/api";
|
||||
import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage";
|
||||
import { THEME_COLOR } from "@ente/shared/themes/constants";
|
||||
import { downloadAsFile } from "@ente/shared/utils";
|
||||
@@ -507,10 +507,11 @@ const UtilitySection: React.FC<UtilitySectionProps> = ({ closeSidebar }) => {
|
||||
);
|
||||
}
|
||||
|
||||
// Ente Accounts specific JWT token.
|
||||
const accountsToken = await getAccountsToken();
|
||||
|
||||
window.open(
|
||||
`${getAccountsURL()}${
|
||||
`${accountsAppURL()}${
|
||||
ACCOUNTS_PAGES.ACCOUNT_HANDOFF
|
||||
}?package=${clientPackageName["photos"]}&token=${accountsToken}`,
|
||||
);
|
||||
|
||||
@@ -342,7 +342,7 @@ credential authentication. We use Accounts as the central WebAuthn hub because
|
||||
credentials are locked to an FQDN.
|
||||
|
||||
```tsx
|
||||
window.location.href = `${getAccountsURL()}/passkeys/flow?passkeySessionID=${passkeySessionID}&redirect=${
|
||||
window.location.href = `${accountsAppURL()}/passkeys/flow?passkeySessionID=${passkeySessionID}&redirect=${
|
||||
window.location.origin
|
||||
}/passkeys/finish`;
|
||||
```
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
} from "@ente/shared/crypto/helpers";
|
||||
import type { B64EncryptionResult } from "@ente/shared/crypto/types";
|
||||
import { CustomError } from "@ente/shared/error";
|
||||
import { getAccountsURL, getEndpoint } from "@ente/shared/network/api";
|
||||
import { accountsAppURL, apiOrigin } from "@ente/shared/network/api";
|
||||
import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
|
||||
import {
|
||||
LS_KEYS,
|
||||
@@ -166,7 +166,7 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
|
||||
isTwoFactorPasskeysEnabled: true,
|
||||
});
|
||||
InMemoryStore.set(MS_KEYS.REDIRECT_URL, PAGES.ROOT);
|
||||
window.location.href = `${getAccountsURL()}/passkeys/flow?passkeySessionID=${passkeySessionID}&redirect=${
|
||||
window.location.href = `${accountsAppURL()}/passkeys/flow?passkeySessionID=${passkeySessionID}&redirect=${
|
||||
window.location.origin
|
||||
}/passkeys/finish`;
|
||||
return undefined;
|
||||
@@ -313,12 +313,12 @@ const Header_ = styled("div")`
|
||||
`;
|
||||
|
||||
const ConnectionDetails: React.FC = () => {
|
||||
const apiOrigin = new URL(getEndpoint());
|
||||
const host = new URL(apiOrigin()).host;
|
||||
|
||||
return (
|
||||
<ConnectionDetails_>
|
||||
<Typography variant="small" color="text.faint">
|
||||
{apiOrigin.host}
|
||||
{host}
|
||||
</Typography>
|
||||
</ConnectionDetails_>
|
||||
);
|
||||
|
||||
@@ -6,34 +6,27 @@ import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage";
|
||||
import { useRouter } from "next/router";
|
||||
import React, { useEffect } from "react";
|
||||
|
||||
/**
|
||||
* [Note: Finish passkey flow in the requesting app]
|
||||
*
|
||||
* The passkey finish step needs to happen in the context of the client which
|
||||
* invoked the passkey flow since it needs to save the obtained credentials
|
||||
* in local storage (which is tied to the current origin).
|
||||
*/
|
||||
const Page: React.FC = () => {
|
||||
const router = useRouter();
|
||||
|
||||
const init = async () => {
|
||||
// get response from query params
|
||||
useEffect(() => {
|
||||
// Extract response from query params
|
||||
const searchParams = new URLSearchParams(window.location.search);
|
||||
const response = searchParams.get("response");
|
||||
|
||||
if (!response) return;
|
||||
|
||||
// decode response
|
||||
const decodedResponse = JSON.parse(atob(response));
|
||||
saveCredentials(response);
|
||||
|
||||
const { keyAttributes, encryptedToken, token, id } = decodedResponse;
|
||||
setData(LS_KEYS.USER, {
|
||||
...getData(LS_KEYS.USER),
|
||||
token,
|
||||
encryptedToken,
|
||||
id,
|
||||
});
|
||||
setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
|
||||
const redirectURL = InMemoryStore.get(MS_KEYS.REDIRECT_URL);
|
||||
InMemoryStore.delete(MS_KEYS.REDIRECT_URL);
|
||||
router.push(redirectURL ?? PAGES.ROOT);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
init();
|
||||
}, []);
|
||||
|
||||
return (
|
||||
@@ -44,3 +37,34 @@ const Page: React.FC = () => {
|
||||
};
|
||||
|
||||
export default Page;
|
||||
|
||||
/**
|
||||
* Extract credentials from a successful passkey flow "response" query parameter
|
||||
* and save them to local storage for use by subsequent steps (or normal
|
||||
* functioning) of the app.
|
||||
*
|
||||
* @param response The string that is passed as the response query parameter to
|
||||
* us (we're the final "finish" page in the passkey flow).
|
||||
*/
|
||||
const saveCredentials = (response: string) => {
|
||||
// Decode response string.
|
||||
const decodedResponse = JSON.parse(atob(response));
|
||||
|
||||
// Only one of `encryptedToken` or `token` will be present depending on the
|
||||
// account's lifetime:
|
||||
//
|
||||
// - The plaintext "token" will be passed during fresh signups, where we
|
||||
// don't yet have keys to encrypt it, the account itself is being created
|
||||
// as we go through this flow.
|
||||
//
|
||||
// - The encrypted `encryptedToken` will be present otherwise (i.e. if the
|
||||
// user is signing into an existing account).
|
||||
const { keyAttributes, encryptedToken, token, id } = decodedResponse;
|
||||
setData(LS_KEYS.USER, {
|
||||
...getData(LS_KEYS.USER),
|
||||
token,
|
||||
encryptedToken,
|
||||
id,
|
||||
});
|
||||
setData(LS_KEYS.KEY_ATTRIBUTES, keyAttributes);
|
||||
};
|
||||
|
||||
@@ -10,7 +10,7 @@ import SingleInputForm, {
|
||||
type SingleInputFormProps,
|
||||
} from "@ente/shared/components/SingleInputForm";
|
||||
import { ApiError } from "@ente/shared/error";
|
||||
import { getAccountsURL } from "@ente/shared/network/api";
|
||||
import { accountsAppURL } from "@ente/shared/network/api";
|
||||
import InMemoryStore, { MS_KEYS } from "@ente/shared/storage/InMemoryStore";
|
||||
import localForage from "@ente/shared/storage/localForage";
|
||||
import { LS_KEYS, getData, setData } from "@ente/shared/storage/localStorage";
|
||||
@@ -85,7 +85,7 @@ const Page: React.FC<PageProps> = ({ appContext }) => {
|
||||
isTwoFactorPasskeysEnabled: true,
|
||||
});
|
||||
setIsFirstLogin(true);
|
||||
window.location.href = `${getAccountsURL()}/passkeys/flow?passkeySessionID=${passkeySessionID}&redirect=${
|
||||
window.location.href = `${accountsAppURL()}/passkeys/flow?passkeySessionID=${passkeySessionID}&redirect=${
|
||||
window.location.origin
|
||||
}/passkeys/finish`;
|
||||
router.push(PAGES.CREDENTIALS);
|
||||
|
||||
@@ -34,13 +34,15 @@ export const getUploadEndpoint = () => {
|
||||
return `https://uploader.ente.io`;
|
||||
};
|
||||
|
||||
export const getAccountsURL = () => {
|
||||
const accountsURL = process.env.NEXT_PUBLIC_ENTE_ACCOUNTS_ENDPOINT;
|
||||
if (accountsURL) {
|
||||
return accountsURL;
|
||||
}
|
||||
return `https://accounts.ente.io`;
|
||||
};
|
||||
/**
|
||||
* Return the URL of the Ente Accounts app.
|
||||
*
|
||||
* Defaults to our production instance, "https://accounts.ente.io", but can be
|
||||
* overridden by setting the `NEXT_PUBLIC_ENTE_ACCOUNTS_URL` environment
|
||||
* variable to a non-empty value.
|
||||
*/
|
||||
export const accountsAppURL = () =>
|
||||
process.env.NEXT_PUBLIC_ENTE_ACCOUNTS_URL || `https://accounts.ente.io`;
|
||||
|
||||
export const getAlbumsURL = () => {
|
||||
const albumsURL = process.env.NEXT_PUBLIC_ENTE_ALBUMS_ENDPOINT;
|
||||
@@ -55,7 +57,7 @@ export const getAlbumsURL = () => {
|
||||
* family plans.
|
||||
*/
|
||||
export const getFamilyPortalURL = () => {
|
||||
const familyURL = process.env.NEXT_PUBLIC_ENTE_FAMILY_ENDPOINT;
|
||||
const familyURL = process.env.NEXT_PUBLIC_ENTE_FAMILY_URL;
|
||||
if (familyURL) {
|
||||
return familyURL;
|
||||
}
|
||||
@@ -66,7 +68,7 @@ export const getFamilyPortalURL = () => {
|
||||
* Return the URL for the host that handles payment related functionality.
|
||||
*/
|
||||
export const getPaymentsURL = () => {
|
||||
const paymentsURL = process.env.NEXT_PUBLIC_ENTE_PAYMENTS_ENDPOINT;
|
||||
const paymentsURL = process.env.NEXT_PUBLIC_ENTE_PAYMENTS_URL;
|
||||
if (paymentsURL) {
|
||||
return paymentsURL;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user