fix(front): iframe URL runtime fallback + IdNot CORS via same-origin API + scope encoding\n\nfeat(front): inject build/env metadata comment, favicon links\n\nci: docker_tag=ext
Some checks failed
build-and-push-ext / build_push (push) Failing after 5s
Some checks failed
build-and-push-ext / build_push (push) Failing after 5s
This commit is contained in:
parent
f659362682
commit
c2d64fce15
@ -33,13 +33,13 @@ export default class Auth extends BaseApiService {
|
|||||||
? `${window.location.origin}/authorized-client`
|
? `${window.location.origin}/authorized-client`
|
||||||
: `${variables.FRONT_APP_HOST}/authorized-client`;
|
: `${variables.FRONT_APP_HOST}/authorized-client`;
|
||||||
|
|
||||||
// Resolve backend base for calling the state endpoint (prefer explicit BACK_BASE)
|
// Use same-origin API (proxied by dev4) to avoid CORS
|
||||||
const backBase = variables.BACK_BASE || `${variables.BACK_API_PROTOCOL}://${variables.BACK_API_HOST}${variables.BACK_API_PORT ? `:${variables.BACK_API_PORT}` : ''}`;
|
const apiOrigin = `${variables.BACK_API_PROTOCOL}://${variables.BACK_API_HOST}${variables.BACK_API_PORT ? `:${variables.BACK_API_PORT}` : ''}`;
|
||||||
const stateEndpoint = new URL(`/api/v1/idnot/state`, backBase);
|
const stateEndpoint = new URL(`${variables.BACK_API_ROOT_URL || '/api'}/v1/idnot/state`, apiOrigin);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 1) Ask backend for a signed state that embeds next_url
|
// 1) Ask backend for a signed state that embeds next_url
|
||||||
const resp = await fetch(stateEndpoint.toString(), {
|
const resp = await fetch(stateEndpoint.toString(), {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
body: JSON.stringify({ next_url: nextUrl })
|
body: JSON.stringify({ next_url: nextUrl })
|
||||||
@ -56,7 +56,8 @@ export default class Auth extends BaseApiService {
|
|||||||
// 2) Build the IdNot authorization URL with fixed redirect_uri and the signed state
|
// 2) Build the IdNot authorization URL with fixed redirect_uri and the signed state
|
||||||
const fixedRedirect = variables.IDNOT_REDIRECT_URI_FIXED || 'http://local.4nkweb.com:3000/authorized-client';
|
const fixedRedirect = variables.IDNOT_REDIRECT_URI_FIXED || 'http://local.4nkweb.com:3000/authorized-client';
|
||||||
const authorizeBase = `${variables.IDNOT_BASE_URL}${variables.IDNOT_AUTHORIZE_ENDPOINT}`;
|
const authorizeBase = `${variables.IDNOT_BASE_URL}${variables.IDNOT_AUTHORIZE_ENDPOINT}`;
|
||||||
const authorizeUrl = `${authorizeBase}?client_id=${encodeURIComponent(variables.IDNOT_CLIENT_ID)}&redirect_uri=${encodeURIComponent(fixedRedirect)}&scope=openid,profile&response_type=code&state=${encodeURIComponent(state)}`;
|
const scopeParam = encodeURIComponent('openid profile');
|
||||||
|
const authorizeUrl = `${authorizeBase}?client_id=${encodeURIComponent(variables.IDNOT_CLIENT_ID)}&redirect_uri=${encodeURIComponent(fixedRedirect)}&scope=${scopeParam}&response_type=code&state=${encodeURIComponent(state)}`;
|
||||||
|
|
||||||
console.log('[IDNOT] authorizeUrl', authorizeUrl);
|
console.log('[IDNOT] authorizeUrl', authorizeUrl);
|
||||||
console.log('[IDNOT] state', state);
|
console.log('[IDNOT] state', state);
|
||||||
|
@ -53,12 +53,9 @@ export default function StepEmail(props: IProps) {
|
|||||||
const variables = FrontendVariables.getInstance();
|
const variables = FrontendVariables.getInstance();
|
||||||
try {
|
try {
|
||||||
const nextUrl = typeof window !== 'undefined' ? `${window.location.origin}/authorized-client` : `${variables.FRONT_APP_HOST}/authorized-client`;
|
const nextUrl = typeof window !== 'undefined' ? `${window.location.origin}/authorized-client` : `${variables.FRONT_APP_HOST}/authorized-client`;
|
||||||
let backBase = variables.BACK_BASE || (process.env.NEXT_PUBLIC_BACK_BASE as string) || `${variables.BACK_API_PROTOCOL}://${variables.BACK_API_HOST}${variables.BACK_API_PORT ? `:${variables.BACK_API_PORT}` : ''}`;
|
// Always prefer same-origin API to avoid CORS: https://dev4.4nkweb.com/api
|
||||||
if (!backBase || !/^https?:\/\//i.test(backBase)) {
|
const apiBase = `${variables.BACK_API_PROTOCOL}://${variables.BACK_API_HOST}${variables.BACK_API_PORT ? `:${variables.BACK_API_PORT}` : ''}${variables.BACK_API_ROOT_URL || ''}`;
|
||||||
console.warn('[IDNOT] BACK_BASE invalid or missing, falling back to https://dev3.4nkweb.com');
|
const stateEndpoint = `${apiBase.replace(/\/$/, '')}/v1/idnot/state`;
|
||||||
backBase = 'https://dev3.4nkweb.com';
|
|
||||||
}
|
|
||||||
const stateEndpoint = `${backBase.replace(/\/$/, '')}/api/v1/idnot/state`;
|
|
||||||
const resp = await fetch(stateEndpoint, {
|
const resp = await fetch(stateEndpoint, {
|
||||||
method: 'POST',
|
method: 'POST',
|
||||||
headers: { 'Content-Type': 'application/json' },
|
headers: { 'Content-Type': 'application/json' },
|
||||||
@ -75,7 +72,8 @@ export default function StepEmail(props: IProps) {
|
|||||||
}
|
}
|
||||||
const fixedRedirect = variables.IDNOT_REDIRECT_URI_FIXED || 'http://local.4nkweb.com:3000/authorized-client';
|
const fixedRedirect = variables.IDNOT_REDIRECT_URI_FIXED || 'http://local.4nkweb.com:3000/authorized-client';
|
||||||
const authorizeBase = `${variables.IDNOT_BASE_URL}${variables.IDNOT_AUTHORIZE_ENDPOINT}`;
|
const authorizeBase = `${variables.IDNOT_BASE_URL}${variables.IDNOT_AUTHORIZE_ENDPOINT}`;
|
||||||
const authorizeUrl = `${authorizeBase}?client_id=${encodeURIComponent(variables.IDNOT_CLIENT_ID)}&redirect_uri=${encodeURIComponent(fixedRedirect)}&scope=openid,profile&response_type=code&state=${encodeURIComponent(state)}`;
|
const scopeParam = encodeURIComponent('openid profile');
|
||||||
|
const authorizeUrl = `${authorizeBase}?client_id=${encodeURIComponent(variables.IDNOT_CLIENT_ID)}&redirect_uri=${encodeURIComponent(fixedRedirect)}&scope=${scopeParam}&response_type=code&state=${encodeURIComponent(state)}`;
|
||||||
console.log('[IDNOT] authorizeUrl', authorizeUrl);
|
console.log('[IDNOT] authorizeUrl', authorizeUrl);
|
||||||
console.log('[IDNOT] state', state);
|
console.log('[IDNOT] state', state);
|
||||||
router.push(authorizeUrl);
|
router.push(authorizeUrl);
|
||||||
|
@ -89,14 +89,18 @@ const MyApp = (({
|
|||||||
// Configure iframe target and URL on client-side only
|
// Configure iframe target and URL on client-side only
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setMounted(true);
|
setMounted(true);
|
||||||
try {
|
try {
|
||||||
if (_4nkUrl && typeof _4nkUrl === 'string' && _4nkUrl.trim().length > 0) {
|
const runtime4nkUrl = (_4nkUrl && _4nkUrl.trim().length > 0)
|
||||||
|
? _4nkUrl
|
||||||
|
: ((process as any)?.env?.NEXT_PUBLIC_4NK_URL || (publicRuntimeConfig as any)?.NEXT_PUBLIC_4NK_URL || '');
|
||||||
|
|
||||||
|
if (runtime4nkUrl && typeof runtime4nkUrl === 'string' && runtime4nkUrl.trim().length > 0) {
|
||||||
const origin = (() => {
|
const origin = (() => {
|
||||||
try { return new URL(_4nkUrl).origin; } catch { return _4nkUrl; }
|
try { return new URL(runtime4nkUrl).origin; } catch { return runtime4nkUrl; }
|
||||||
})();
|
})();
|
||||||
IframeReference.setTargetOrigin(origin);
|
IframeReference.setTargetOrigin(origin);
|
||||||
|
|
||||||
const candidate = (publicRuntimeConfig as any).NEXT_PUBLIC_4NK_IFRAME_URL ?? _4nkUrl;
|
const candidate = (publicRuntimeConfig as any).NEXT_PUBLIC_4NK_IFRAME_URL ?? runtime4nkUrl;
|
||||||
const iframe = (() => {
|
const iframe = (() => {
|
||||||
try { return new URL(candidate).toString(); } catch { return origin; }
|
try { return new URL(candidate).toString(); } catch { return origin; }
|
||||||
})();
|
})();
|
||||||
@ -147,25 +151,35 @@ const MyApp = (({
|
|||||||
}) as AppType;
|
}) as AppType;
|
||||||
|
|
||||||
MyApp.getInitialProps = async () => {
|
MyApp.getInitialProps = async () => {
|
||||||
return {
|
// Fallback runtime: use process.env when runtimeConfig is empty (e.g., image built without build args)
|
||||||
backApiProtocol: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PROTOCOL,
|
const getEnv = (key: string): string => {
|
||||||
backApiHost: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_HOST,
|
const fromRuntime = (publicRuntimeConfig as any)?.[key];
|
||||||
backApiPort: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_PORT,
|
if (typeof fromRuntime === 'string' && fromRuntime.trim().length > 0) {
|
||||||
backApiRootUrl: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_ROOT_URL,
|
return fromRuntime;
|
||||||
backApiVersion: publicRuntimeConfig.NEXT_PUBLIC_BACK_API_VERSION,
|
}
|
||||||
frontAppHost: publicRuntimeConfig.NEXT_PUBLIC_FRONT_APP_HOST,
|
const fromProcess = (process as any)?.env?.[key];
|
||||||
frontAppPort: publicRuntimeConfig.NEXT_PUBLIC_FRONT_APP_PORT,
|
return typeof fromProcess === 'string' ? fromProcess : '';
|
||||||
idNotBaseUrl: publicRuntimeConfig.NEXT_PUBLIC_IDNOT_BASE_URL,
|
};
|
||||||
idNotAuthorizeEndpoint: publicRuntimeConfig.NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT,
|
|
||||||
idNotClientId: publicRuntimeConfig.NEXT_PUBLIC_IDNOT_CLIENT_ID,
|
return {
|
||||||
idNotRedirectUri: publicRuntimeConfig.NEXT_PUBLIC_IDNOT_REDIRECT_URI,
|
backApiProtocol: getEnv('NEXT_PUBLIC_BACK_API_PROTOCOL'),
|
||||||
fcAuthorizeEndpoint: publicRuntimeConfig.NEXT_PUBLIC_FC_AUTHORIZE_ENDPOINT,
|
backApiHost: getEnv('NEXT_PUBLIC_BACK_API_HOST'),
|
||||||
fcClientId: publicRuntimeConfig.NEXT_PUBLIC_FC_CLIENT_ID,
|
backApiPort: getEnv('NEXT_PUBLIC_BACK_API_PORT'),
|
||||||
docaposteApiUrl: publicRuntimeConfig.NEXT_PUBLIC_DOCAPOSTE_API_URL,
|
backApiRootUrl: getEnv('NEXT_PUBLIC_BACK_API_ROOT_URL'),
|
||||||
_4nkUrl: publicRuntimeConfig.NEXT_PUBLIC_4NK_URL,
|
backApiVersion: getEnv('NEXT_PUBLIC_BACK_API_VERSION'),
|
||||||
_4nkIframeUrl: publicRuntimeConfig.NEXT_PUBLIC_4NK_IFRAME_URL,
|
frontAppHost: getEnv('NEXT_PUBLIC_FRONT_APP_HOST'),
|
||||||
apiUrl: publicRuntimeConfig.NEXT_PUBLIC_API_URL,
|
frontAppPort: getEnv('NEXT_PUBLIC_FRONT_APP_PORT'),
|
||||||
};
|
idNotBaseUrl: getEnv('NEXT_PUBLIC_IDNOT_BASE_URL'),
|
||||||
|
idNotAuthorizeEndpoint: getEnv('NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT'),
|
||||||
|
idNotClientId: getEnv('NEXT_PUBLIC_IDNOT_CLIENT_ID'),
|
||||||
|
idNotRedirectUri: getEnv('NEXT_PUBLIC_IDNOT_REDIRECT_URI'),
|
||||||
|
fcAuthorizeEndpoint: getEnv('NEXT_PUBLIC_FC_AUTHORIZE_ENDPOINT'),
|
||||||
|
fcClientId: getEnv('NEXT_PUBLIC_FC_CLIENT_ID'),
|
||||||
|
docaposteApiUrl: getEnv('NEXT_PUBLIC_DOCAPOSTE_API_URL'),
|
||||||
|
_4nkUrl: getEnv('NEXT_PUBLIC_4NK_URL'),
|
||||||
|
_4nkIframeUrl: getEnv('NEXT_PUBLIC_4NK_IFRAME_URL'),
|
||||||
|
apiUrl: getEnv('NEXT_PUBLIC_API_URL'),
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
export default MyApp;
|
export default MyApp;
|
||||||
|
63
src/pages/_document.tsx
Normal file
63
src/pages/_document.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
import Document, { Html, Head, Main, NextScript, DocumentContext } from 'next/document';
|
||||||
|
|
||||||
|
class MyDocument extends Document {
|
||||||
|
static async getInitialProps(ctx: DocumentContext) {
|
||||||
|
const initialProps = await Document.getInitialProps(ctx);
|
||||||
|
|
||||||
|
// Collect public env vars
|
||||||
|
const entries = Object.entries(process.env || {})
|
||||||
|
.filter(([k]) => k.startsWith('NEXT_PUBLIC_'))
|
||||||
|
.map(([k, v]) => `${k}=${String(v ?? '')}`)
|
||||||
|
.sort();
|
||||||
|
|
||||||
|
// App version from package.json
|
||||||
|
let appVersion = '';
|
||||||
|
try {
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-var-requires
|
||||||
|
const pkg = require('../../package.json');
|
||||||
|
appVersion = pkg?.version || '';
|
||||||
|
} catch {
|
||||||
|
appVersion = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const buildDate = new Date().toISOString();
|
||||||
|
const dockerTag = process.env.DOCKER_TAG || process.env.IMAGE_TAG || 'ext';
|
||||||
|
|
||||||
|
const metaComment = [
|
||||||
|
'Build Meta:',
|
||||||
|
`version=${appVersion}`,
|
||||||
|
`build_date=${buildDate}`,
|
||||||
|
`docker_tag=${dockerTag}`,
|
||||||
|
'env:',
|
||||||
|
...entries,
|
||||||
|
].join('\n');
|
||||||
|
|
||||||
|
return { ...initialProps, metaComment } as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
render() {
|
||||||
|
// @ts-expect-error injected by getInitialProps
|
||||||
|
const metaComment: string = (this.props as any).metaComment || '';
|
||||||
|
return (
|
||||||
|
<Html>
|
||||||
|
<Head>
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<link rel="alternate icon" href="/favicon.ico" />
|
||||||
|
<link rel="apple-touch-icon" href="/apple-touch-icon.png" />
|
||||||
|
</Head>
|
||||||
|
<body>
|
||||||
|
{metaComment && (
|
||||||
|
<div
|
||||||
|
aria-hidden
|
||||||
|
dangerouslySetInnerHTML={{ __html: `<!--\n${metaComment}\n-->` }}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<Main />
|
||||||
|
<NextScript />
|
||||||
|
</body>
|
||||||
|
</Html>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyDocument;
|
33
src/pages/api/env.ts
Normal file
33
src/pages/api/env.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from 'next';
|
||||||
|
|
||||||
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
||||||
|
const pick = (keys: string[]) => keys.reduce<Record<string, string | undefined>>((acc, k) => {
|
||||||
|
acc[k] = process.env[k];
|
||||||
|
return acc;
|
||||||
|
}, {});
|
||||||
|
|
||||||
|
const clientKeys = [
|
||||||
|
'NEXT_PUBLIC_4NK_URL',
|
||||||
|
'NEXT_PUBLIC_4NK_IFRAME_URL',
|
||||||
|
'NEXT_PUBLIC_BACK_BASE',
|
||||||
|
'NEXT_PUBLIC_IDNOT_BASE_URL',
|
||||||
|
'NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT',
|
||||||
|
'NEXT_PUBLIC_IDNOT_CLIENT_ID',
|
||||||
|
'NEXT_PUBLIC_IDNOT_REDIRECT_URI',
|
||||||
|
'NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED',
|
||||||
|
'NEXT_PUBLIC_BACK_API_PROTOCOL',
|
||||||
|
'NEXT_PUBLIC_BACK_API_HOST',
|
||||||
|
'NEXT_PUBLIC_BACK_API_PORT',
|
||||||
|
'NEXT_PUBLIC_BACK_API_ROOT_URL',
|
||||||
|
'NEXT_PUBLIC_BACK_API_VERSION',
|
||||||
|
'NEXT_PUBLIC_API_URL',
|
||||||
|
'NEXT_PUBLIC_DEFAULT_VALIDATOR_ID',
|
||||||
|
'NEXT_PUBLIC_DEFAULT_STORAGE_URLS',
|
||||||
|
];
|
||||||
|
|
||||||
|
res.status(200).json({
|
||||||
|
time: new Date().toISOString(),
|
||||||
|
client: pick(clientKeys),
|
||||||
|
});
|
||||||
|
}
|
47
src/pages/env.tsx
Normal file
47
src/pages/env.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { GetServerSideProps } from 'next';
|
||||||
|
import getConfig from 'next/config';
|
||||||
|
|
||||||
|
type Props = {
|
||||||
|
now: string;
|
||||||
|
env: Record<string, string | undefined>;
|
||||||
|
runtime: Record<string, any>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default function EnvPage({ now, env, runtime }: Props) {
|
||||||
|
return (
|
||||||
|
<div style={{ fontFamily: 'monospace', padding: 16 }}>
|
||||||
|
<h2>Runtime environment</h2>
|
||||||
|
<div>Time: {now}</div>
|
||||||
|
<h3>process.env (client-safe)</h3>
|
||||||
|
<pre>{JSON.stringify(env, null, 2)}</pre>
|
||||||
|
<h3>next/config publicRuntimeConfig</h3>
|
||||||
|
<pre>{JSON.stringify(runtime.publicRuntimeConfig ?? {}, null, 2)}</pre>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export const getServerSideProps: GetServerSideProps<Props> = async ({ res }) => {
|
||||||
|
res.setHeader('Cache-Control', 'no-store, no-cache, must-revalidate, proxy-revalidate');
|
||||||
|
const keys = [
|
||||||
|
'NEXT_PUBLIC_4NK_URL',
|
||||||
|
'NEXT_PUBLIC_4NK_IFRAME_URL',
|
||||||
|
'NEXT_PUBLIC_BACK_BASE',
|
||||||
|
'NEXT_PUBLIC_IDNOT_BASE_URL',
|
||||||
|
'NEXT_PUBLIC_IDNOT_AUTHORIZE_ENDPOINT',
|
||||||
|
'NEXT_PUBLIC_IDNOT_CLIENT_ID',
|
||||||
|
'NEXT_PUBLIC_IDNOT_REDIRECT_URI',
|
||||||
|
'NEXT_PUBLIC_IDNOT_REDIRECT_URI_FIXED',
|
||||||
|
'NEXT_PUBLIC_BACK_API_PROTOCOL',
|
||||||
|
'NEXT_PUBLIC_BACK_API_HOST',
|
||||||
|
'NEXT_PUBLIC_BACK_API_PORT',
|
||||||
|
'NEXT_PUBLIC_BACK_API_ROOT_URL',
|
||||||
|
'NEXT_PUBLIC_BACK_API_VERSION',
|
||||||
|
'NEXT_PUBLIC_API_URL',
|
||||||
|
'NEXT_PUBLIC_DEFAULT_VALIDATOR_ID',
|
||||||
|
'NEXT_PUBLIC_DEFAULT_STORAGE_URLS',
|
||||||
|
];
|
||||||
|
const env: Record<string, string | undefined> = {};
|
||||||
|
for (const k of keys) env[k] = process.env[k];
|
||||||
|
const runtime = getConfig();
|
||||||
|
return { props: { now: new Date().toISOString(), env, runtime } };
|
||||||
|
};
|
Loading…
x
Reference in New Issue
Block a user