mirror of
https://github.com/Tria-plc/edr-platform.git
synced 2026-08-30 09:58:12 +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)
|
||||
FAYDA_REDIRECT_URI=http://localhost:3001/api/fayda/verification/complete
|
||||
# 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
|
||||
# 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
|
||||
FAYDA_SCOPE=openid profile email phone address
|
||||
FAYDA_ACR_VALUES=mosip:idp:acr:generated-code
|
||||
|
||||
@@ -21,7 +21,7 @@ flowchart TD
|
||||
S0(["Customer visits portal"]):::start
|
||||
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
|
||||
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
|
||||
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
|
||||
|
||||
@@ -131,7 +131,7 @@ sequenceDiagram
|
||||
`HasActiveDelegationGuard` as **global `APP_GUARD`s** — *every* route is JWT-protected unless it
|
||||
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}`,
|
||||
`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`.
|
||||
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.)
|
||||
@@ -288,7 +288,7 @@ flowchart TD
|
||||
chk --> otp["POST /otp/send + /otp/verify (P) @Public"]
|
||||
otp --> fayda{"Identity proofing?"}
|
||||
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
|
||||
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 |
|
||||
| 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 | `/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 | `/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 |
|
||||
|
||||
@@ -130,8 +130,11 @@ async function bootstrap() {
|
||||
maxAge: 86400, // cache preflight for 24h to cut chatter in dev
|
||||
});
|
||||
|
||||
// /callback stays un-prefixed: it's the Fayda OAuth redirect_uri ack endpoint.
|
||||
app.setGlobalPrefix("api", { exclude: ["callback"] });
|
||||
// /fayda/callback stays un-prefixed: it's the Fayda OAuth redirect_uri ack
|
||||
// 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
|
||||
// coercion turns any non-empty multipart/form-data string (including the
|
||||
// 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
|
||||
* 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
|
||||
* GET /api/fayda/verification/complete with the echoed code+state.
|
||||
*/
|
||||
@ApiTags('Fayda Verification')
|
||||
@Controller('callback')
|
||||
@Controller('fayda/callback')
|
||||
export class FaydaCallbackController {
|
||||
@Get()
|
||||
@IsPublic()
|
||||
|
||||
Reference in New Issue
Block a user