mirror of
https://github.com/luxfi/explore.git
synced 2026-09-24 00:28:18 +00:00
The explorer shipped three sign-in systems. Two were Blockscout's and both
spoke to its /account/* backend, which this deployment does not run — measured,
every one of those endpoints 404s on api-explore.lux.network:
/account/get_csrf 404 /account/send_otp 404 /account/siwe_message 404
/account/user/info 404 /account/confirm_otp 404 /account/authenticate_via_* 404
So none of it could ever have worked. It is deleted, not re-pointed: the email
+ one-time-code modal, the SIWE wallet login, the CSRF handshake that fired on
every page render, the Dynamic.xyz provider, and the dev page that let you
paste a session cookie by hand. Rewriting any of it against our own backend
would have been building custom auth, which we do not do.
What is left is the standard OIDC client we already had, finished:
* PKCE (S256). This client is public — it runs in a browser and can hold no
secret — so the verifier is the only thing binding a redeemed code to the
page that asked for it. IAM advertises S256 in its discovery document and
we were not sending a challenge at all.
* userinfo went to <issuer>/api/userinfo, which is a 404 and an /api/ path we
do not serve. It is /v1/iam/oauth/userinfo, per IAM's own discovery.
* logout now ends the IdP session (end_session_endpoint) instead of only
dropping our cookie and leaving the user signed in upstream.
All three live in lib/oidc.ts, which already said never to rebuild these paths
at a call site.
The header's provider switch collapses to two states — signed in with IAM, or
account disabled and this is the wallet/settings menu. `rewards` is stated off:
its gate read authProvider === 'auth0', so it was already unreachable.
The ConfigMaps carried NEXT_PUBLIC_OIDC_CLIENT_ID=lux-explore-client-id, a
client that has never existed, so authorize answered 400 unknown client_id.
IAM names clients <org>-explore; all four brands now say so, and hanzo/zoo/pars
get the OIDC keys they never had.
Typecheck: no new error classes, production errors 26 -> 22.
Co-authored-by: Hanzo Dev <[email protected]>
93 lines
3.9 KiB
TypeScript
93 lines
3.9 KiB
TypeScript
import { omit, pickBy } from 'es-toolkit';
|
|
import React from 'react';
|
|
|
|
import type { ExternalChainExtended } from 'types/externalChains';
|
|
|
|
import config from 'configs/app';
|
|
import isNeedProxy from 'lib/api/isNeedProxy';
|
|
import * as cookies from 'lib/cookies';
|
|
import type { Params as FetchParams } from 'lib/hooks/useFetch';
|
|
import useFetch from 'lib/hooks/useFetch';
|
|
|
|
import buildUrl from './buildUrl';
|
|
import getResourceParams from './getResourceParams';
|
|
import type { ResourceName, ResourcePathParams } from './resources';
|
|
|
|
export interface Params<R extends ResourceName> {
|
|
pathParams?: ResourcePathParams<R>;
|
|
queryParams?: Record<string, string | Array<string> | number | boolean | undefined | null>;
|
|
fetchParams?: Pick<FetchParams, 'body' | 'method' | 'signal' | 'headers'>;
|
|
logError?: boolean;
|
|
chain?: ExternalChainExtended;
|
|
}
|
|
|
|
export default function useApiFetch() {
|
|
const fetch = useFetch();
|
|
|
|
return React.useCallback(<R extends ResourceName, SuccessType = unknown, ErrorType = unknown>(
|
|
resourceName: R,
|
|
{ pathParams, queryParams, fetchParams, logError, chain }: Params<R> = {},
|
|
) => {
|
|
const apiToken = cookies.get(cookies.NAMES.API_TOKEN);
|
|
const rewardsApiToken = cookies.get(cookies.NAMES.REWARDS_API_TOKEN);
|
|
const apiTempToken = cookies.get(cookies.NAMES.API_TEMP_TOKEN);
|
|
const showScamTokens = cookies.get(cookies.NAMES.SHOW_SCAM_TOKENS) === 'true';
|
|
|
|
const { api, apiName, resource } = getResourceParams(resourceName, chain);
|
|
const url = buildUrl(resourceName, pathParams, queryParams, undefined, chain);
|
|
const headers = pickBy({
|
|
'x-endpoint': isNeedProxy() ? api.endpoint : undefined,
|
|
Authorization: (() => {
|
|
const feature = config.features.account;
|
|
if (feature.isEnabled && feature.authProvider === 'oidc' && apiToken && apiName === 'general') {
|
|
return `Bearer ${ apiToken }`;
|
|
}
|
|
if ([ 'admin', 'contractInfo' ].includes(apiName)) {
|
|
return apiToken;
|
|
}
|
|
return undefined;
|
|
})(),
|
|
...(apiName === 'general' ? {
|
|
'api-v2-temp-token': apiTempToken,
|
|
'show-scam-tokens': showScamTokens ? 'true' : undefined,
|
|
} : {}),
|
|
...(apiName === 'rewards' && rewardsApiToken ? {
|
|
Authorization: `Bearer ${ rewardsApiToken }`,
|
|
} : {}),
|
|
...resource.headers,
|
|
...fetchParams?.headers,
|
|
}, Boolean) as HeadersInit;
|
|
|
|
return fetch<SuccessType, ErrorType>(
|
|
url,
|
|
{
|
|
// Things to remember:
|
|
//
|
|
// A: Currently, we use only one API-related cookie, "_explorer_key," which is for the account feature.
|
|
// We include credentials only for core API requests.
|
|
// Note that some APIs may share the same origin with the core API, but they don't require credentials (e.g the Stats API).
|
|
//
|
|
// B: We cannot limit the routes for which credentials should be sent exclusively to the "/account/**" routes.
|
|
// This is because the watchlist names and private tags preloading will not function on the API side.
|
|
// Therefore, we include credentials for all core API routes.
|
|
//
|
|
// C: We cannot include credentials in cross-origin requests.
|
|
// In this case, we must explicitly list all the origins allowed to make requests in the "Access-Control-Allow-Origin" header,
|
|
// which is challenging for our devops and backend teams. Thus, we do not use the "include" option here.
|
|
// And because of this, the account feature in cross-origin setup will not work.
|
|
//
|
|
// Considering all of the above, we use:
|
|
// - The "same-origin" option for all core API requests
|
|
// - The "omit" option for all other requests
|
|
credentials: apiName === 'general' ? 'same-origin' : 'omit',
|
|
headers,
|
|
...(fetchParams ? omit(fetchParams, [ 'headers' ]) : {}),
|
|
},
|
|
{
|
|
resource: resource.path,
|
|
logError,
|
|
},
|
|
);
|
|
}, [ fetch ]);
|
|
}
|