mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-26 12:41:04 +00:00
refactor(freight): move the Fayda callback to /fayda/callback
Namespaces the OAuth landing path in all three places it exists: the API's
ack controller, both web apps' routes, and the redirect_uri env values.
A bare /callback claimed a generic top-level path in every app for one
provider's redirect.
The API side needed care. The ack controller moves to @Controller
('fayda/callback'), and the global-prefix exclusion has to name that exact
path — setGlobalPrefix's exclude is an exact route match, not a subtree, so
excluding "fayda" would have left /fayda/callback served at
/api/fayda/callback and 404ing at the registered redirect_uri, while
reading as though it covered everything under /fayda. Naming the full path
also keeps /api/fayda/verification/* prefixed, which every client calls.
Also drops a stale comment on the portal's callback route describing the
popup that no longer exists, and records why the route is public: behind
RequireAuth the onboarding gate redirects to /portal before the code+state
exchange can run.
NOT verified at runtime — this changes route registration, so boot the API
and confirm GET /fayda/callback answers un-prefixed and
/api/fayda/verification/start still resolves before relying on it.
Deploying this requires registering the new redirect_uri with eSignet
first; FAYDA_WEB_REDIRECT_URI, FAYDA_PORTAL_REDIRECT_URI and any mobile
client must be updated in step or verification breaks with a redirect_uri
mismatch.
This commit is contained in:
@@ -98,10 +98,10 @@ FAYDA_PRIVATE_KEY_BASE64=
|
|||||||
# OAuth redirect_uri for MOBILE clients (must be registered with eSignet)
|
# OAuth redirect_uri for MOBILE clients (must be registered with eSignet)
|
||||||
FAYDA_REDIRECT_URI=http://localhost:3001/api/fayda/verification/complete
|
FAYDA_REDIRECT_URI=http://localhost:3001/api/fayda/verification/complete
|
||||||
# OAuth redirect_uri for WEB clients. Defaults to FAYDA_REDIRECT_URI when unset.
|
# OAuth redirect_uri for WEB clients. Defaults to FAYDA_REDIRECT_URI when unset.
|
||||||
FAYDA_WEB_REDIRECT_URI=http://localhost:3000/callback
|
FAYDA_WEB_REDIRECT_URI=http://localhost:3000/fayda/callback
|
||||||
# OAuth redirect_uri for the customer portal (its own origin — must also be
|
# OAuth redirect_uri for the customer portal (its own origin — must also be
|
||||||
# registered with eSignet). Defaults to FAYDA_WEB_REDIRECT_URI when unset.
|
# registered with eSignet). Defaults to FAYDA_WEB_REDIRECT_URI when unset.
|
||||||
FAYDA_PORTAL_REDIRECT_URI=http://localhost:5173/callback
|
FAYDA_PORTAL_REDIRECT_URI=http://localhost:5173/fayda/callback
|
||||||
CLIENT_ASSERTION_TYPE=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
|
CLIENT_ASSERTION_TYPE=urn:ietf:params:oauth:client-assertion-type:jwt-bearer
|
||||||
FAYDA_SCOPE=openid profile email phone address
|
FAYDA_SCOPE=openid profile email phone address
|
||||||
FAYDA_ACR_VALUES=mosip:idp:acr:generated-code
|
FAYDA_ACR_VALUES=mosip:idp:acr:generated-code
|
||||||
|
|||||||
@@ -21,7 +21,7 @@ flowchart TD
|
|||||||
S0(["Customer visits portal"]):::start
|
S0(["Customer visits portal"]):::start
|
||||||
S0 --> S1["Signup via IAM<br/>GET /auth/check-availability @Public<br/>POST /otp/send + /otp/verify (P)"]:::port
|
S0 --> S1["Signup via IAM<br/>GET /auth/check-availability @Public<br/>POST /otp/send + /otp/verify (P)"]:::port
|
||||||
S1 --> S2{"Identity proofing<br/>(VeriFayda)?"}:::dec
|
S1 --> S2{"Identity proofing<br/>(VeriFayda)?"}:::dec
|
||||||
S2 -->|"Yes"| S3["POST /fayda/verification/start →<br/>/callback → /complete<br/>upsert iam.users (verified_by=fayda) (P)"]:::port
|
S2 -->|"Yes"| S3["POST /fayda/verification/start →<br/>/fayda/callback → /complete<br/>upsert iam.users (verified_by=fayda) (P)"]:::port
|
||||||
S2 -->|"No"| S4
|
S2 -->|"No"| S4
|
||||||
S3 --> S4["POST /companies/onboarding/start<br/>draft company (placeholder TIN, PENDING) (P)"]:::port
|
S3 --> S4["POST /companies/onboarding/start<br/>draft company (placeholder TIN, PENDING) (P)"]:::port
|
||||||
S4 --> S4b["Wizard: PATCH /profile, /onboarding-step,<br/>upload license + docs<br/>GET /onboarding/requirements (P)"]:::port
|
S4 --> S4b["Wizard: PATCH /profile, /onboarding-step,<br/>upload license + docs<br/>GET /onboarding/requirements (P)"]:::port
|
||||||
|
|||||||
@@ -131,7 +131,7 @@ sequenceDiagram
|
|||||||
`HasActiveDelegationGuard` as **global `APP_GUARD`s** — *every* route is JWT-protected unless it
|
`HasActiveDelegationGuard` as **global `APP_GUARD`s** — *every* route is JWT-protected unless it
|
||||||
carries `@Public()`. Fine-grained `FreightPermissionGuard([perm])` decorators add permission checks
|
carries `@Public()`. Fine-grained `FreightPermissionGuard([perm])` decorators add permission checks
|
||||||
on staff routes. Explicitly **public** endpoints: `GET /api/files/:fileId`, `POST /api/otp/{send,verify}`,
|
on staff routes. Explicitly **public** endpoints: `GET /api/files/:fileId`, `POST /api/otp/{send,verify}`,
|
||||||
`GET /api/auth/check-availability`, the `fayda/verification/*` + `/callback` endpoints,
|
`GET /api/auth/check-availability`, the `fayda/verification/*` + `/fayda/callback` endpoints,
|
||||||
`GET /api/payments/{checkout,receipt/:orderId}`, and the service-to-service `POST /api/internal/payments/mark-paid`.
|
`GET /api/payments/{checkout,receipt/:orderId}`, and the service-to-service `POST /api/internal/payments/mark-paid`.
|
||||||
Real login / JWT issuance lives in the **external IAM package**, not this repo. (Note: `@edr/api-common`'s
|
Real login / JWT issuance lives in the **external IAM package**, not this repo. (Note: `@edr/api-common`'s
|
||||||
`@Public` and `@tria-plc/api-common`'s `@IsPublic` both set the same `"isPublic"` metadata key the guard reads.)
|
`@Public` and `@tria-plc/api-common`'s `@IsPublic` both set the same `"isPublic"` metadata key the guard reads.)
|
||||||
@@ -288,7 +288,7 @@ flowchart TD
|
|||||||
chk --> otp["POST /otp/send + /otp/verify (P) @Public"]
|
chk --> otp["POST /otp/send + /otp/verify (P) @Public"]
|
||||||
otp --> fayda{"Identity proofing?"}
|
otp --> fayda{"Identity proofing?"}
|
||||||
fayda -->|"VeriFayda 2.0"| fstart["POST /fayda/verification/start<br/>→ eSignet authorize URL"]
|
fayda -->|"VeriFayda 2.0"| fstart["POST /fayda/verification/start<br/>→ eSignet authorize URL"]
|
||||||
fstart --> fcb["Fayda redirect → GET /callback (ack)<br/>→ GET /fayda/verification/complete<br/>(PKCE code exchange → upsert iam.users)"]
|
fstart --> fcb["Fayda redirect → GET /fayda/callback (ack)<br/>→ GET /fayda/verification/complete<br/>(PKCE code exchange → upsert iam.users)"]
|
||||||
fcb --> onb
|
fcb --> onb
|
||||||
fayda -->|"skip"| onb
|
fayda -->|"skip"| onb
|
||||||
|
|
||||||
@@ -313,7 +313,7 @@ drives the required document set. Booking guards elsewhere `403` if the acting p
|
|||||||
| POST | `/api/fayda/verification/start` | start eSignet session (PKCE) | `@Public` + OptionalJwt | (B) verifayda.service |
|
| POST | `/api/fayda/verification/start` | start eSignet session (PKCE) | `@Public` + OptionalJwt | (B) verifayda.service |
|
||||||
| GET | `/api/fayda/verification/complete` | code→identity, upsert `iam.users` | `@Public` | (B) verifayda.service |
|
| GET | `/api/fayda/verification/complete` | code→identity, upsert `iam.users` | `@Public` | (B) verifayda.service |
|
||||||
| GET | `/api/fayda/verification/status` | current user's Fayda link | JwtGuard | — |
|
| GET | `/api/fayda/verification/status` | current user's Fayda link | JwtGuard | — |
|
||||||
| GET | `/callback` | passive Fayda redirect ack (no `/api`) | `@Public` | popup postMessage |
|
| GET | `/fayda/callback` | passive Fayda redirect ack (no `/api`) | `@Public` | popup postMessage |
|
||||||
| GET·PUT | `/api/me/signature` | reusable signature (MinIO, base64) | JwtGuard | (P)(B) signatures.service |
|
| GET·PUT | `/api/me/signature` | reusable signature (MinIO, base64) | JwtGuard | (P)(B) signatures.service |
|
||||||
| GET | `/api/test_user1` · `/api/test_user2` | permission-guard demo | `PermissionGuard` | (B) demo pages |
|
| GET | `/api/test_user1` · `/api/test_user2` | permission-guard demo | `PermissionGuard` | (B) demo pages |
|
||||||
| GET | `/api/companies/getInfo` · `/profile` · `/dashboard` | company info / KPIs | JwtGuard | (P) companies.service |
|
| GET | `/api/companies/getInfo` · `/profile` · `/dashboard` | company info / KPIs | JwtGuard | (P) companies.service |
|
||||||
|
|||||||
@@ -130,8 +130,11 @@ async function bootstrap() {
|
|||||||
maxAge: 86400, // cache preflight for 24h to cut chatter in dev
|
maxAge: 86400, // cache preflight for 24h to cut chatter in dev
|
||||||
});
|
});
|
||||||
|
|
||||||
// /callback stays un-prefixed: it's the Fayda OAuth redirect_uri ack endpoint.
|
// /fayda/callback stays un-prefixed: it's the Fayda OAuth redirect_uri ack
|
||||||
app.setGlobalPrefix("api", { exclude: ["callback"] });
|
// endpoint. Exact path, not "fayda" — exclusion is an exact route match, so
|
||||||
|
// "fayda" would leave /fayda/callback prefixed (404 at the registered
|
||||||
|
// redirect_uri) while still reading as if it covered the whole subtree.
|
||||||
|
app.setGlobalPrefix("api", { exclude: ["fayda/callback"] });
|
||||||
// enableImplicitConversion is OFF: class-transformer's implicit boolean
|
// enableImplicitConversion is OFF: class-transformer's implicit boolean
|
||||||
// coercion turns any non-empty multipart/form-data string (including the
|
// coercion turns any non-empty multipart/form-data string (including the
|
||||||
// literal "false") into `true`, silently corrupting flags like isHazardous
|
// literal "false") into `true`, silently corrupting flags like isHazardous
|
||||||
|
|||||||
@@ -6,12 +6,14 @@ import { VerifaydaCallbackDto } from './verifayda.dto';
|
|||||||
/**
|
/**
|
||||||
* Plain acknowledgement endpoint for the Fayda redirect_uri when it points at
|
* Plain acknowledgement endpoint for the Fayda redirect_uri when it points at
|
||||||
* the API instead of the web app (e.g. MOBILE clients or connectivity checks).
|
* the API instead of the web app (e.g. MOBILE clients or connectivity checks).
|
||||||
* Registered at /callback (excluded from the global /api prefix in main.ts).
|
* Registered at /fayda/callback (excluded by exact path from the global /api
|
||||||
|
* prefix in main.ts — the exclusion must NOT be widened to "fayda", or
|
||||||
|
* /api/fayda/verification/* loses its prefix too).
|
||||||
* It does NOT consume the verification session — the client must still call
|
* It does NOT consume the verification session — the client must still call
|
||||||
* GET /api/fayda/verification/complete with the echoed code+state.
|
* GET /api/fayda/verification/complete with the echoed code+state.
|
||||||
*/
|
*/
|
||||||
@ApiTags('Fayda Verification')
|
@ApiTags('Fayda Verification')
|
||||||
@Controller('callback')
|
@Controller('fayda/callback')
|
||||||
export class FaydaCallbackController {
|
export class FaydaCallbackController {
|
||||||
@Get()
|
@Get()
|
||||||
@IsPublic()
|
@IsPublic()
|
||||||
|
|||||||
@@ -292,7 +292,10 @@ const buildSidebarSections = (demoItems: SidebarItem[]): SidebarSection[] => [
|
|||||||
label: "Locomotives",
|
label: "Locomotives",
|
||||||
href: "/dashboard/locomotives",
|
href: "/dashboard/locomotives",
|
||||||
icon: <Train />,
|
icon: <Train />,
|
||||||
permission: [FREIGHT_PERMS.locomotives.view, FREIGHT_PERMS.fleet.view],
|
permission: [
|
||||||
|
FREIGHT_PERMS.locomotives.view,
|
||||||
|
FREIGHT_PERMS.fleet.view,
|
||||||
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
label: "Train Builder",
|
label: "Train Builder",
|
||||||
@@ -818,7 +821,7 @@ const App = () => {
|
|||||||
{UserManagementRoutes()}
|
{UserManagementRoutes()}
|
||||||
{/* <Route path="/um/*" element={<UserManagementHostPage />} /> */}
|
{/* <Route path="/um/*" element={<UserManagementHostPage />} /> */}
|
||||||
<Route path="/health" element={<HealthCheck />} />
|
<Route path="/health" element={<HealthCheck />} />
|
||||||
<Route path="/callback" element={<FaydaCallbackPage />} />
|
<Route path="/fayda/callback" element={<FaydaCallbackPage />} />
|
||||||
<Route path="/" element={<Navigate to="/dashboard/overview" replace />} />
|
<Route path="/" element={<Navigate to="/dashboard/overview" replace />} />
|
||||||
<Route
|
<Route
|
||||||
path="/dashboard"
|
path="/dashboard"
|
||||||
@@ -1085,7 +1088,10 @@ const App = () => {
|
|||||||
<Route path="intercity" element={<IntercityPage />} />
|
<Route path="intercity" element={<IntercityPage />} />
|
||||||
<Route path="trucks-on-site" element={<TrucksOnSitePage />} />
|
<Route path="trucks-on-site" element={<TrucksOnSitePage />} />
|
||||||
<Route path="import-trucks" element={<ImportTrucksPage />} />
|
<Route path="import-trucks" element={<ImportTrucksPage />} />
|
||||||
<Route path="edr-last-mile-returns" element={<EDRLastMileReturnsPage />} />
|
<Route
|
||||||
|
path="edr-last-mile-returns"
|
||||||
|
element={<EDRLastMileReturnsPage />}
|
||||||
|
/>
|
||||||
<Route path="container-returns" element={<ContainerReturnsPage />} />
|
<Route path="container-returns" element={<ContainerReturnsPage />} />
|
||||||
<Route path="loaded-inventory" element={<LoadedInventoryPage />} />
|
<Route path="loaded-inventory" element={<LoadedInventoryPage />} />
|
||||||
<Route path="dispatch-queue" element={<DispatchQueuePage />} />
|
<Route path="dispatch-queue" element={<DispatchQueuePage />} />
|
||||||
@@ -1173,7 +1179,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="routes"
|
path="routes"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.routes.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.routes.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<RoutesPage />
|
<RoutesPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1181,7 +1189,12 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="locomotives"
|
path="locomotives"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.locomotives.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[
|
||||||
|
FREIGHT_PERMS.locomotives.view,
|
||||||
|
FREIGHT_PERMS.fleet.view,
|
||||||
|
]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1189,7 +1202,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="trains"
|
path="trains"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1197,7 +1212,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="trains/:id"
|
path="trains/:id"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<TrainDetailPage />
|
<TrainDetailPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1205,7 +1222,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="train-builder"
|
path="train-builder"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<TrainBuilderListPage />
|
<TrainBuilderListPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1213,7 +1232,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="train-builder/:id"
|
path="train-builder/:id"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<TrainBuilderDetailPage />
|
<TrainBuilderDetailPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1221,7 +1242,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="wagons"
|
path="wagons"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1242,7 +1265,12 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="containers"
|
path="containers"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.containers.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[
|
||||||
|
FREIGHT_PERMS.containers.view,
|
||||||
|
FREIGHT_PERMS.fleet.view,
|
||||||
|
]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1250,7 +1278,12 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="cargoes"
|
path="cargoes"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.cargoes.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[
|
||||||
|
FREIGHT_PERMS.cargoes.view,
|
||||||
|
FREIGHT_PERMS.fleet.view,
|
||||||
|
]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1336,7 +1369,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="routes"
|
path="routes"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.routes.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.routes.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<RoutesPage />
|
<RoutesPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1424,7 +1459,12 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="locomotives"
|
path="locomotives"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.locomotives.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[
|
||||||
|
FREIGHT_PERMS.locomotives.view,
|
||||||
|
FREIGHT_PERMS.fleet.view,
|
||||||
|
]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1432,7 +1472,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="trains"
|
path="trains"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1440,7 +1482,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="trains/:id"
|
path="trains/:id"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<TrainDetailPage />
|
<TrainDetailPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1448,7 +1492,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="train-builder"
|
path="train-builder"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<TrainBuilderListPage />
|
<TrainBuilderListPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1456,7 +1502,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="train-builder/:id"
|
path="train-builder/:id"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.trains.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<TrainBuilderDetailPage />
|
<TrainBuilderDetailPage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1464,7 +1512,9 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="wagons"
|
path="wagons"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[FREIGHT_PERMS.wagons.view, FREIGHT_PERMS.fleet.view]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1485,7 +1535,12 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="containers"
|
path="containers"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.containers.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[
|
||||||
|
FREIGHT_PERMS.containers.view,
|
||||||
|
FREIGHT_PERMS.fleet.view,
|
||||||
|
]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
@@ -1493,7 +1548,12 @@ const App = () => {
|
|||||||
<Route
|
<Route
|
||||||
path="cargoes"
|
path="cargoes"
|
||||||
element={
|
element={
|
||||||
<RequirePermission permission={[FREIGHT_PERMS.cargoes.view, FREIGHT_PERMS.fleet.view]}>
|
<RequirePermission
|
||||||
|
permission={[
|
||||||
|
FREIGHT_PERMS.cargoes.view,
|
||||||
|
FREIGHT_PERMS.fleet.view,
|
||||||
|
]}
|
||||||
|
>
|
||||||
<FleetResourcePage />
|
<FleetResourcePage />
|
||||||
</RequirePermission>
|
</RequirePermission>
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ export function isComplaintAuthContext(pathname = ""): boolean {
|
|||||||
pathname.startsWith("/complaints") ||
|
pathname.startsWith("/complaints") ||
|
||||||
pathname === "/complaint-form" ||
|
pathname === "/complaint-form" ||
|
||||||
pathname === "/follow-complaint" ||
|
pathname === "/follow-complaint" ||
|
||||||
pathname === "/callback"
|
pathname === "/fayda/callback"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ let listener: Listener | null = null;
|
|||||||
/** Current-page path patterns where the global modal must stay silent. */
|
/** Current-page path patterns where the global modal must stay silent. */
|
||||||
const EXCLUDED_PATH_PATTERNS = [
|
const EXCLUDED_PATH_PATTERNS = [
|
||||||
/^\/auth/,
|
/^\/auth/,
|
||||||
/^\/callback/,
|
/^\/fayda/,
|
||||||
/warehouse/i,
|
/warehouse/i,
|
||||||
/first-mile/i,
|
/first-mile/i,
|
||||||
/last-mile/i,
|
/last-mile/i,
|
||||||
|
|||||||
@@ -148,7 +148,7 @@ const FleetFormDialog = ({
|
|||||||
});
|
});
|
||||||
}, [open, fields]);
|
}, [open, fields]);
|
||||||
|
|
||||||
// Receive the ?code&state relayed by the /callback popup, exchange it for
|
// Receive the ?code&state relayed by the /fayda/callback popup, exchange it for
|
||||||
// the verified identity, and prefill the matching form fields.
|
// the verified identity, and prefill the matching form fields.
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!open || !verifyWithFayda) return;
|
if (!open || !verifyWithFayda) return;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import type { FaydaCallbackMessage } from "@/services/verifayda.service";
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Landing page for the eSignet redirect_uri (FAYDA_WEB_REDIRECT_URI →
|
* Landing page for the eSignet redirect_uri (FAYDA_WEB_REDIRECT_URI →
|
||||||
* http://localhost:5183/callback). Runs inside the verification popup:
|
* http://localhost:5183/fayda/callback). Runs inside the verification popup:
|
||||||
* relays ?code&state (or ?error) to the window that opened it via
|
* relays ?code&state (or ?error) to the window that opened it via
|
||||||
* postMessage, then closes itself. The opener performs the /complete call
|
* postMessage, then closes itself. The opener performs the /complete call
|
||||||
* so the single-use session is only consumed once, in one place.
|
* so the single-use session is only consumed once, in one place.
|
||||||
|
|||||||
@@ -17,7 +17,7 @@ export interface FaydaCompleteResult {
|
|||||||
userDataSaved?: boolean;
|
userDataSaved?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Message posted from the /callback popup back to the opener window. */
|
/** Message posted from the /fayda/callback popup back to the opener window. */
|
||||||
export interface FaydaCallbackMessage {
|
export interface FaydaCallbackMessage {
|
||||||
type: 'fayda-callback';
|
type: 'fayda-callback';
|
||||||
code?: string;
|
code?: string;
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import ExternalPortalCallback from "@/external-portal/components/Registration/Ex
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Single FAYDA OIDC callback entry point.
|
* Single FAYDA OIDC callback entry point.
|
||||||
* Fayda only allows whitelisted redirect URIs (e.g. /callback) — route
|
* Fayda only allows whitelisted redirect URIs (e.g. /fayda/callback) — route
|
||||||
* internally based on the `state` param sent during authorization.
|
* internally based on the `state` param sent during authorization.
|
||||||
*/
|
*/
|
||||||
export default function FaydaCallbackDispatcher() {
|
export default function FaydaCallbackDispatcher() {
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ const PUBLIC_PATHS = [
|
|||||||
"/set-password",
|
"/set-password",
|
||||||
"/verify-otp",
|
"/verify-otp",
|
||||||
"/verification_page",
|
"/verification_page",
|
||||||
"/callback",
|
"/fayda/callback",
|
||||||
"/complaints",
|
"/complaints",
|
||||||
"/complaint-form",
|
"/complaint-form",
|
||||||
"/follow-complaint",
|
"/follow-complaint",
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ const DEFAULT_CODE_CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM";
|
|||||||
const DEFAULT_NONCE = "g4DEuje5Fx57Vb64dO4oqLHXGT8L8G7g";
|
const DEFAULT_NONCE = "g4DEuje5Fx57Vb64dO4oqLHXGT8L8G7g";
|
||||||
const DEFAULT_STATE = "ptOO76SD";
|
const DEFAULT_STATE = "ptOO76SD";
|
||||||
|
|
||||||
/** OIDC state value that routes the shared /callback to the complaint flow (legacy sign-in). */
|
/** OIDC state value that routes the shared /fayda/callback to the complaint flow (legacy sign-in). */
|
||||||
export const COMPLAINT_FLOW_STATE = "complaint_flow";
|
export const COMPLAINT_FLOW_STATE = "complaint_flow";
|
||||||
|
|
||||||
/** Complaint flow OIDC states — distinguish sign-in vs sign-up endpoints. */
|
/** Complaint flow OIDC states — distinguish sign-in vs sign-up endpoints. */
|
||||||
@@ -66,9 +66,7 @@ export function startExternalPortalFaydaAuth(): void {
|
|||||||
export function generateFaydaAuthorizationUrl(
|
export function generateFaydaAuthorizationUrl(
|
||||||
options: FaydaOidcOptions = {},
|
options: FaydaOidcOptions = {},
|
||||||
): string {
|
): string {
|
||||||
const redirectUri =
|
const redirectUri = options.redirectUri || getDefaultFaydaRedirectUri();
|
||||||
options.redirectUri ||
|
|
||||||
getDefaultFaydaRedirectUri();
|
|
||||||
|
|
||||||
const params = new URLSearchParams({
|
const params = new URLSearchParams({
|
||||||
client_id: import.meta.env.VITE_CLIENT_ID || "",
|
client_id: import.meta.env.VITE_CLIENT_ID || "",
|
||||||
@@ -98,7 +96,7 @@ export function generateFaydaAuthorizationUrl(
|
|||||||
export function getDefaultFaydaRedirectUri(): string {
|
export function getDefaultFaydaRedirectUri(): string {
|
||||||
return (
|
return (
|
||||||
import.meta.env.VITE_REDIRECT_URI ||
|
import.meta.env.VITE_REDIRECT_URI ||
|
||||||
`${window.location.origin}/callback`
|
`${window.location.origin}/fayda/callback`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -106,13 +104,12 @@ export function getDefaultFaydaRedirectUri(): string {
|
|||||||
* Returns the redirect URI registered with FAYDA for the complaint flow.
|
* Returns the redirect URI registered with FAYDA for the complaint flow.
|
||||||
*
|
*
|
||||||
* Must exactly match a URI whitelisted in the FAYDA OIDC client — we reuse
|
* Must exactly match a URI whitelisted in the FAYDA OIDC client — we reuse
|
||||||
* the same /callback path as external-portal registration and distinguish
|
* the same /fayda/callback path as external-portal registration and distinguish
|
||||||
* flows via the `state` parameter (see COMPLAINT_FLOW_STATE).
|
* flows via the `state` parameter (see COMPLAINT_FLOW_STATE).
|
||||||
*/
|
*/
|
||||||
export function getComplaintFaydaRedirectUri(): string {
|
export function getComplaintFaydaRedirectUri(): string {
|
||||||
return (
|
return (
|
||||||
import.meta.env.VITE_COMPLAINT_REDIRECT_URI ||
|
import.meta.env.VITE_COMPLAINT_REDIRECT_URI || getDefaultFaydaRedirectUri()
|
||||||
getDefaultFaydaRedirectUri()
|
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -253,113 +253,115 @@ const App = () => {
|
|||||||
{/* Global API error modal — shows the server's actual error message for
|
{/* Global API error modal — shows the server's actual error message for
|
||||||
every failed request (suppressed on onboarding/auth pages). */}
|
every failed request (suppressed on onboarding/auth pages). */}
|
||||||
<ApiErrorModal />
|
<ApiErrorModal />
|
||||||
<Routes>
|
<Routes>
|
||||||
{/* Public routes */}
|
{/* Public routes */}
|
||||||
<Route index element={<LandingRoute />} />
|
<Route index element={<LandingRoute />} />
|
||||||
<Route path="/logout" element={<LogoutHandler />} />
|
<Route path="/logout" element={<LogoutHandler />} />
|
||||||
<Route
|
<Route
|
||||||
path="/booking/check-status/:orderId"
|
path="/booking/check-status/:orderId"
|
||||||
element={<CheckPaymentPage />}
|
element={<CheckPaymentPage />}
|
||||||
/>
|
/>
|
||||||
{/* Payment provider browser redirects (PAYMENT_RETURN_URL / PAYMENT_FAILURE_URL) */}
|
{/* Payment provider browser redirects (PAYMENT_RETURN_URL / PAYMENT_FAILURE_URL) */}
|
||||||
{/* Fayda (eSignet) redirect_uri — runs in the verification popup and
|
{/* Fayda (eSignet) redirect_uri — the whole tab lands here after
|
||||||
relays the code/state back to the form that opened it. */}
|
verification, completes the code/state exchange and navigates back
|
||||||
<Route path="/callback" element={<FaydaCallbackPage />} />
|
to the page that started it. Public on purpose: behind RequireAuth
|
||||||
<Route path="/payment/success" element={<PaymentSuccessPage />} />
|
the onboarding gate would redirect away before the exchange ran. */}
|
||||||
<Route path="/payment/failure" element={<PaymentFailurePage />} />
|
<Route path="/fayda/callback" element={<FaydaCallbackPage />} />
|
||||||
|
<Route path="/payment/success" element={<PaymentSuccessPage />} />
|
||||||
|
<Route path="/payment/failure" element={<PaymentFailurePage />} />
|
||||||
|
|
||||||
{/* Auth pages — inaccessible once logged in */}
|
{/* Auth pages — inaccessible once logged in */}
|
||||||
<Route element={<RedirectIfAuthed />}>
|
<Route element={<RedirectIfAuthed />}>
|
||||||
<Route path="/login" element={<LoginPage />} />
|
<Route path="/login" element={<LoginPage />} />
|
||||||
<Route path="/signup" element={<SignupPage />} />
|
<Route path="/signup" element={<SignupPage />} />
|
||||||
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
<Route path="/forgot-password" element={<ForgotPasswordPage />} />
|
||||||
</Route>
|
</Route>
|
||||||
|
|
||||||
{/* Staff-issued reset links land here. Deliberately outside
|
{/* Staff-issued reset links land here. Deliberately outside
|
||||||
RedirectIfAuthed: a customer with a stale session still needs the link
|
RedirectIfAuthed: a customer with a stale session still needs the link
|
||||||
to work, and the token — not the session — is what authorises it. */}
|
to work, and the token — not the session — is what authorises it. */}
|
||||||
<Route path="/reset-password" element={<ResetPasswordLinkPage />} />
|
<Route path="/reset-password" element={<ResetPasswordLinkPage />} />
|
||||||
|
|
||||||
{/* Signup-flow pages; reached while a session already exists */}
|
{/* Signup-flow pages; reached while a session already exists */}
|
||||||
<Route path="/otp" element={<VerificationOtpPage />} />
|
<Route path="/otp" element={<VerificationOtpPage />} />
|
||||||
<Route path="/set-password" element={<SetPasswordPage />} />
|
<Route path="/set-password" element={<SetPasswordPage />} />
|
||||||
|
|
||||||
<Route element={<RequireAuth />}>
|
<Route element={<RequireAuth />}>
|
||||||
<Route element={<RequireCompany />}>
|
<Route element={<RequireCompany />}>
|
||||||
<Route
|
<Route
|
||||||
element={
|
element={
|
||||||
<AppLayout
|
<AppLayout
|
||||||
title="EDR Freight"
|
title="EDR Freight"
|
||||||
sidebarItems={sidebarItems}
|
sidebarItems={sidebarItems}
|
||||||
activeHref={location.pathname}
|
activeHref={location.pathname}
|
||||||
onNavigate={navigate}
|
onNavigate={navigate}
|
||||||
enableThemeToggle
|
enableThemeToggle
|
||||||
userName={displayName}
|
userName={displayName}
|
||||||
userEmail={userEmail}
|
userEmail={userEmail}
|
||||||
companyProfiles={companyProfiles}
|
companyProfiles={companyProfiles}
|
||||||
companyType={companyType}
|
companyType={companyType}
|
||||||
onCreateProfile={createProfile}
|
onCreateProfile={createProfile}
|
||||||
onReapplyProfile={reapplyProfile}
|
onReapplyProfile={reapplyProfile}
|
||||||
>
|
>
|
||||||
<OnboardingGate />
|
<OnboardingGate />
|
||||||
</AppLayout>
|
</AppLayout>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Route path="/portal" element={<MyPortalPage />} />
|
<Route path="/portal" element={<MyPortalPage />} />
|
||||||
{/* Bookings are created against a contract, but the full list is
|
{/* Bookings are created against a contract, but the full list is
|
||||||
browsable here. New-booking entry still routes via a contract. */}
|
browsable here. New-booking entry still routes via a contract. */}
|
||||||
<Route path="/bookings" element={<BookingsListPage />} />
|
<Route path="/bookings" element={<BookingsListPage />} />
|
||||||
<Route
|
<Route
|
||||||
path="/bookings/new"
|
path="/bookings/new"
|
||||||
element={<Navigate to="/contracts/new" replace />}
|
element={<Navigate to="/contracts/new" replace />}
|
||||||
/>
|
/>
|
||||||
<Route path="/bookings/:id/edit" element={<EditBookingPage />} />
|
<Route path="/bookings/:id/edit" element={<EditBookingPage />} />
|
||||||
<Route path="/bookings/:id" element={<BookingDetailPage />} />
|
<Route path="/bookings/:id" element={<BookingDetailPage />} />
|
||||||
<Route
|
<Route
|
||||||
path="/bookings/:id/contract"
|
path="/bookings/:id/contract"
|
||||||
element={<BookingContractPage />}
|
element={<BookingContractPage />}
|
||||||
/>
|
/>
|
||||||
<Route path="/contracts" element={<ContractsList />} />
|
<Route path="/contracts" element={<ContractsList />} />
|
||||||
<Route path="/contracts/new" element={<NewContractPage />} />
|
<Route path="/contracts/new" element={<NewContractPage />} />
|
||||||
<Route
|
<Route
|
||||||
path="/contracts/:id/edit"
|
path="/contracts/:id/edit"
|
||||||
element={<NewContractPage mode="edit" />}
|
element={<NewContractPage mode="edit" />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/contracts/:id/shipment-requests/new"
|
path="/contracts/:id/shipment-requests/new"
|
||||||
element={<NewShipmentRequestPage />}
|
element={<NewShipmentRequestPage />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/contracts/:id/bookings/new"
|
path="/contracts/:id/bookings/new"
|
||||||
element={<NewShipmentPage />}
|
element={<NewShipmentPage />}
|
||||||
/>
|
/>
|
||||||
{/* Completion of an initiated (bare) booking after per-booking
|
{/* Completion of an initiated (bare) booking after per-booking
|
||||||
clearance — same form, submits to the complete endpoint. */}
|
clearance — same form, submits to the complete endpoint. */}
|
||||||
<Route
|
<Route
|
||||||
path="/contracts/:id/bookings/:bookingId/complete"
|
path="/contracts/:id/bookings/:bookingId/complete"
|
||||||
element={<NewShipmentPage />}
|
element={<NewShipmentPage />}
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="/contracts/:id/view"
|
path="/contracts/:id/view"
|
||||||
element={<ContractViewPage />}
|
element={<ContractViewPage />}
|
||||||
/>
|
/>
|
||||||
<Route path="/contracts/:id" element={<ContractDetailPage />} />
|
<Route path="/contracts/:id" element={<ContractDetailPage />} />
|
||||||
<Route path="/tracking" element={<TrackingPage />} />
|
<Route path="/tracking" element={<TrackingPage />} />
|
||||||
<Route path="/billing" element={<InvoicesList />} />
|
<Route path="/billing" element={<InvoicesList />} />
|
||||||
<Route path="/billing/:id" element={<InvoiceDetailPage />} />
|
<Route path="/billing/:id" element={<InvoiceDetailPage />} />
|
||||||
{/* Profile was merged into Settings — keep old links working. */}
|
{/* Profile was merged into Settings — keep old links working. */}
|
||||||
<Route
|
<Route
|
||||||
path="/profile"
|
path="/profile"
|
||||||
element={<Navigate to="/settings" replace />}
|
element={<Navigate to="/settings" replace />}
|
||||||
/>
|
/>
|
||||||
<Route path="/signature" element={<MySignaturePage />} />
|
<Route path="/signature" element={<MySignaturePage />} />
|
||||||
<Route path="/settings" element={<SettingsPage />} />
|
<Route path="/settings" element={<SettingsPage />} />
|
||||||
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
</Route>
|
||||||
</Route>
|
|
||||||
|
|
||||||
<Route path="*" element={<Navigate to="/" replace />} />
|
<Route path="*" element={<Navigate to="/" replace />} />
|
||||||
</Routes>
|
</Routes>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ function getInitials(name: string | null): string {
|
|||||||
* The identity is proved on eSignet, which the whole tab navigates to — no
|
* The identity is proved on eSignet, which the whole tab navigates to — no
|
||||||
* popup, because a popup opened after the /start round-trip has lost its user
|
* popup, because a popup opened after the /start round-trip has lost its user
|
||||||
* activation and iOS Safari blocks it outright. eSignet redirects back to
|
* activation and iOS Safari blocks it outright. eSignet redirects back to
|
||||||
* /callback, which completes the exchange and returns the user here; the API
|
* /fayda/callback, which completes the exchange and returns the user here; the API
|
||||||
* writes the person's name, phone, email and address from the verified
|
* writes the person's name, phone, email and address from the verified
|
||||||
* payload. Nothing on this panel is typed.
|
* payload. Nothing on this panel is typed.
|
||||||
*/
|
*/
|
||||||
@@ -82,7 +82,7 @@ export default function FaydaVerifyPanel({
|
|||||||
try {
|
try {
|
||||||
const authorizationUrl = await verifaydaService.start();
|
const authorizationUrl = await verifaydaService.start();
|
||||||
// Record who is being verified and where to come back to before the tab
|
// Record who is being verified and where to come back to before the tab
|
||||||
// leaves — /callback has no other way to know either.
|
// leaves — /fayda/callback has no other way to know either.
|
||||||
stashPendingVerification({
|
stashPendingVerification({
|
||||||
subject,
|
subject,
|
||||||
returnTo:
|
returnTo:
|
||||||
@@ -96,7 +96,7 @@ export default function FaydaVerifyPanel({
|
|||||||
setError(
|
setError(
|
||||||
(err as { response?: { data?: { message?: string } } })?.response?.data
|
(err as { response?: { data?: { message?: string } } })?.response?.data
|
||||||
?.message ??
|
?.message ??
|
||||||
(err instanceof Error ? err.message : "Could not start verification"),
|
(err instanceof Error ? err.message : "Could not start verification"),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -189,7 +189,13 @@ function DataRow({ icon, value }: { icon: ReactNode; value: string | null }) {
|
|||||||
if (!value) return null;
|
if (!value) return null;
|
||||||
return (
|
return (
|
||||||
<Group gap={6} wrap="nowrap">
|
<Group gap={6} wrap="nowrap">
|
||||||
<span style={{ color: "var(--mantine-color-edr-muted-6)", display: "flex", flexShrink: 0 }}>
|
<span
|
||||||
|
style={{
|
||||||
|
color: "var(--mantine-color-edr-muted-6)",
|
||||||
|
display: "flex",
|
||||||
|
flexShrink: 0,
|
||||||
|
}}
|
||||||
|
>
|
||||||
{icon}
|
{icon}
|
||||||
</span>
|
</span>
|
||||||
<Text size="xs" c="edr-muted" truncate>
|
<Text size="xs" c="edr-muted" truncate>
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ import {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Landing page for the portal's eSignet redirect_uri
|
* Landing page for the portal's eSignet redirect_uri
|
||||||
* (FAYDA_PORTAL_REDIRECT_URI → http://localhost:5173/callback).
|
* (FAYDA_PORTAL_REDIRECT_URI → http://localhost:5173/fayda/callback).
|
||||||
*
|
*
|
||||||
* The verification is a full-page redirect, so the page that started it no
|
* The verification is a full-page redirect, so the page that started it no
|
||||||
* longer exists: this page completes the code+state exchange itself against
|
* longer exists: this page completes the code+state exchange itself against
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ import {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The stash is the only thing that survives the full-page handoff to eSignet,
|
* The stash is the only thing that survives the full-page handoff to eSignet,
|
||||||
* so /callback completing against the wrong subject — or throwing on junk left
|
* so /fayda/callback completing against the wrong subject — or throwing on junk left
|
||||||
* behind by an older build — would either misfile a verified identity or dead-
|
* behind by an older build — would either misfile a verified identity or dead-
|
||||||
* end the flow.
|
* end the flow.
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -41,8 +41,8 @@ export interface CompanyIdentityState {
|
|||||||
/**
|
/**
|
||||||
* What the panel was doing when it handed the tab over to eSignet. The
|
* What the panel was doing when it handed the tab over to eSignet. The
|
||||||
* verification is a full-page redirect, so the page that started it is gone by
|
* verification is a full-page redirect, so the page that started it is gone by
|
||||||
* the time /callback runs — this is how /callback knows whose identity the
|
* the time /fayda/callback runs — this is how that page knows whose identity
|
||||||
* code+state belongs to and where to put the user back.
|
* the code+state belongs to and where to put the user back.
|
||||||
*
|
*
|
||||||
* sessionStorage, not localStorage: it is scoped to this tab, so two tabs
|
* sessionStorage, not localStorage: it is scoped to this tab, so two tabs
|
||||||
* verifying different people can't overwrite each other, and it dies with the
|
* verifying different people can't overwrite each other, and it dies with the
|
||||||
|
|||||||
@@ -235,8 +235,8 @@ services:
|
|||||||
PAYMENT_API_URL: http://payment-mock-e2e:4500
|
PAYMENT_API_URL: http://payment-mock-e2e:4500
|
||||||
FAYDA_TOKEN_ENDPOINT: http://fayda-mock-e2e:4400/token
|
FAYDA_TOKEN_ENDPOINT: http://fayda-mock-e2e:4400/token
|
||||||
FAYDA_USERINFO_ENDPOINT: http://fayda-mock-e2e:4400/userinfo
|
FAYDA_USERINFO_ENDPOINT: http://fayda-mock-e2e:4400/userinfo
|
||||||
FAYDA_REDIRECT_URI: http://localhost:${E2E_PORTAL_PORT:-5373}/callback
|
FAYDA_REDIRECT_URI: http://localhost:${E2E_PORTAL_PORT:-5373}/fayda/callback
|
||||||
FAYDA_PORTAL_REDIRECT_URI: http://localhost:${E2E_PORTAL_PORT:-5373}/callback
|
FAYDA_PORTAL_REDIRECT_URI: http://localhost:${E2E_PORTAL_PORT:-5373}/fayda/callback
|
||||||
# Throwaway e2e-only RSA JWK (client_assertion signing) — the mock
|
# Throwaway e2e-only RSA JWK (client_assertion signing) — the mock
|
||||||
# never verifies the signature, this just has to be well-formed.
|
# never verifies the signature, this just has to be well-formed.
|
||||||
# Generated fresh per launch by e2e.mjs (fakeFaydaPrivateKeyBase64),
|
# Generated fresh per launch by e2e.mjs (fakeFaydaPrivateKeyBase64),
|
||||||
|
|||||||
Reference in New Issue
Block a user