diff --git a/mcp/integration-prompt.md b/mcp/integration-prompt.md
index 6dfbe64..ec8f08b 100644
--- a/mcp/integration-prompt.md
+++ b/mcp/integration-prompt.md
@@ -2,18 +2,7 @@
You are integrating The Bridge into an Angular application. This adds authentication, tenant context, role and privilege access control, feature flags and billing.
-## Decide first — which login surface?
-
-This decision shapes everything else. Make it before writing code; getting it wrong means rewriting the auth pages.
-
-| You want | Use | What you build |
-|---|---|---|
-| The fastest path; Bridge owns the login UI | **Hosted auth** (default) | Nothing — no login page |
-| Login inside your app | **SDK auth** | Nothing either — `...bridgeAuthRoutes()` serves every sign-in page |
-
-**Setting `loginRoute` in `BridgeConfig` is the entire switch.** Without it you get hosted; with it you get in-app. If you are being redirected to a route you never built, that field is why.
-
-If the user has not said which they want, ask.
+Sign-in happens inside the app: the login, signup and password pages render in the app's own layout, and `...bridgeAuthRoutes()` serves every one of them. For the sign-in pages in detail, matching them to the app's design and customising them, read `sdk-auth-prompt.md` (`get_integration_guide` topic `auth`, or `bridge guide angular sdk-auth`).
## Prerequisites
@@ -44,8 +33,9 @@ import { routes } from './app.routes';
const bridgeConfig: BridgeConfig = {
appId: environment.bridgeAppId,
+ // Where a signed-out visitor is sent: the app's own login page.
+ loginRoute: '/auth/login',
// Defaults to production. Required for a stage / local / self-hosted app.
- // The hosted sign-in pages follow it on Bridge's domains.
apiBaseUrl: environment.bridgeApiBaseUrl,
};
@@ -59,6 +49,8 @@ export const appConfig: ApplicationConfig = {
};
```
+(If the developer explicitly asks for Bridge's hosted sign-in page, or the app already uses it, respect that: leave out `loginRoute`, sign in with `AuthService.login()` instead of a link to `/auth/login`, and log out with `AuthService.logout()`; a local or self-hosted Bridge also sets `hostedUrl`. Everything else stays the same.)
+
Configuration is code (Angular has no env-var convention): each option resolves as *explicit option > default*, and an empty string is unset. **An empty `appId` throws at startup, naming the option.** If `provideBridge()` itself is missing, nothing errors loudly: flags return their defaults and auth never resolves — check that first when something seems inert.
## Step 2b — Let Bridge serve its pages
@@ -80,7 +72,7 @@ export const routes: Routes = [
];
```
-**Do not write an OAuth callback, a set-password page, or subscription pages.** These spreads own them, including the address in every signup verification email. The sign-in pages are public by construction; to replace one, `bridgeAuthRoutes({ overrides: { login: { component: MyLogin } } })`.
+**Do not write a login page, a signup page, an OAuth callback, a set-password page, or subscription pages.** These spreads own them, including the address in every signup verification email. The sign-in pages are public by construction; to replace one, `bridgeAuthRoutes({ overrides: { login: { component: MyLogin } } })`.
## Step 3 — Guard routes declaratively
@@ -123,34 +115,64 @@ export const routes: Routes = [
> **Gating a whole page? Use a rule, not a component-level `if`.** A rule redirects before the component ever renders; an `if` inside the component means the page mounts, fetches, and only then hides itself — which leaks both the route's existence and whatever the page loaded on the way.
-## Step 4 — Read the user
+## Step 4 — Read the user, sign in and out
```ts
import { Component, inject } from '@angular/core';
+import { RouterLink } from '@angular/router';
import { AuthService } from '@nebulr-group/bridge-angular';
-@Component({ selector: 'app-header', standalone: true, template: `…` })
+@Component({
+ selector: 'app-header',
+ standalone: true,
+ imports: [RouterLink],
+ template: `
+ @if (auth.isAuthenticated()) {
+ {{ auth.profile()?.fullName ?? auth.profile()?.email }}
+
+ } @else {
+ Log in
+ }
+ `,
+})
export class HeaderComponent {
- private auth = inject(AuthService);
+ protected auth = inject(AuthService);
+
+ logout(): Promise {
+ return this.auth.getBridgeAuth().logout({ redirectTo: '/auth/login' });
+ }
}
```
+Always pass `redirectTo` to `logout()` so the user lands on the app's own login page. A signed-out visitor on a protected route is sent to `/auth/login` and brought back to that route afterwards; the link is only for pages that are public.
+
`ProfileService` exposes profile fields; `realtimeStatus` reports the live-channel state.
+## Checklist
+
+- [ ] `@nebulr-group/bridge-angular` installed
+- [ ] `provideBridge(bridgeConfig)` in `appConfig.providers`, with a non-empty `appId`, `loginRoute: '/auth/login'`, and `apiBaseUrl` for a stage or local app
+- [ ] `...bridgeAuthRoutes()` in the routes; no hand-written login, signup, callback or set-password page
+- [ ] The sign-in pages match the app's design (`sdk-auth-prompt.md`)
+- [ ] Protected routes guarded with `bridgeAuthGuard()` and a `RouteGuardConfig`
+- [ ] `logout()` passes `redirectTo: '/auth/login'`
+
## Verify it works
Do not stop at a clean build. Run it and confirm:
-1. A protected route while signed out starts the login flow.
-2. After login you land **inside the app**.
+1. A protected route while signed out redirects to `/auth/login` **inside the app**.
+2. After login you land back on that route, inside the app.
3. A reload keeps you signed in.
-4. A flag-gated route redirects when the flag is off, and is reachable when on.
+4. Log out: you land on the app's `/auth/login`.
+5. A flag-gated route redirects when the flag is off, and is reachable when on.
+6. The developer does steps 1, 2 and 4 themselves in a browser, and signs up once at `/auth/signup`; do not create test users. If a step fails, `bridge event auth-attempts --user ` names the reason.
## Where to go next
| Goal | Guide |
|---|---|
-| Login/signup inside your app | `sdk-auth-prompt.md` |
+| The sign-in pages, matching the app's design, customising them | `sdk-auth-prompt.md` |
| Gate features behind a flag | `feature-flags-prompt.md` |
| Plans, checkout, quotas | `billing-prompt.md` |
| Team / workspace management UI | `team-prompt.md` |
diff --git a/mcp/sdk-auth-prompt.md b/mcp/sdk-auth-prompt.md
index 4dd0ae7..ee144b3 100644
--- a/mcp/sdk-auth-prompt.md
+++ b/mcp/sdk-auth-prompt.md
@@ -1,19 +1,6 @@
# Bridge Angular — Authentication
-You are wiring authentication into an Angular application that uses The Bridge.
-
-## Decide first — hosted or in-app?
-
-| You want | Mode | What you build | Config |
-|---|---|---|---|
-| Bridge owns the login UI | **Hosted** (default) | Nothing — no login page | No `loginRoute` |
-| Login inside your app | **SDK auth** | Nothing — `...bridgeAuthRoutes()` serves the pages | Set `loginRoute: '/auth/login'` |
-
-**One config field is the whole switch.** Adding `loginRoute` to `BridgeConfig` turns hosted mode off. If you are being redirected to a route you never built, that is why.
-
-If the user has not said which they want, ask. A wrong guess here means rewriting the auth pages.
-
-The rest of this guide covers **SDK auth**.
+You are wiring authentication into an Angular application that uses The Bridge. Sign-in happens inside the app: the login, signup and password pages render in the app's own layout, and users never leave it. `...bridgeAuthRoutes()` serves every one of those pages; you write no login, signup, callback or set-password page.
## The components — reach for these, do not hand-roll
@@ -28,7 +15,6 @@ The rest of this guide covers **SDK auth**.
| MFA challenge / setup | ``, `` |
| Workspace ("tenant") selection | ``, `` |
| SSO button | `` |
-| Hosted-login entry point | `` |
> **`` is not just an email and password box.** It drives forgot-password, magic link, passkeys, MFA and workspace selection as inline steps, and it decides which methods to show from the app's own configuration — which the client cannot see. Rebuilding any of it means reimplementing a flow that exists and then keeping it in sync with settings you have no visibility of.
>
@@ -53,6 +39,8 @@ const bridgeConfig: BridgeConfig = {
};
```
+(If the developer explicitly asks for Bridge's hosted sign-in page, or the app already uses it, respect that: leave out `loginRoute`, sign in with `AuthService.login()` instead of a link to `/auth/login`, and log out with `AuthService.logout()`; a local or self-hosted Bridge also sets `hostedUrl`. Everything else stays the same.)
+
## Step 2 — Spread the auth routes
```ts
@@ -67,7 +55,34 @@ export const routes: Routes = [
That serves login, signup, the OAuth callback, set password (the address in every signup verification and password-reset email), forgot password, magic link, passkey setup and workspace selection. They are public by construction — no route rule needed, and no redirect loop.
-Customise only as far as needed: `--bridge-*` CSS tokens; `bridgeAuthRoutes({ frame: AuthLayoutComponent, heading: (page) => … })`; replace one page with `overrides: { login: { component: MyLoginComponent } }` (a page you own renders `` and navigates itself).
+## Match the app's design — always
+
+The sign-in pages render inside the app, so they must look like the rest of it. Bridge's built-in look (indigo buttons, grey borders) is a fallback for an app with no design, never the finished result. Do this without asking:
+
+1. **Read the app's design.** The global stylesheet (`src/styles.css` / `styles.scss`, as listed under `styles` in `angular.json`) and its CSS variables, `tailwind.config.*` or the Tailwind `@theme` block, a component library's theme (Angular Material, PrimeNG, Taiga, …), the font the app loads, and one existing primary button and text input.
+2. **Set the `--bridge-*` tokens from it** in that global stylesheet, pointing at the app's own variables rather than copying values, so dark mode and later theme changes carry over:
+
+ ```css
+ :root {
+ --bridge-primary: var(--color-primary); /* the app's primary button background */
+ --bridge-primary-hover: var(--color-primary-hover);
+ --bridge-primary-fg: var(--color-primary-foreground);
+ --bridge-border: var(--color-border);
+ --bridge-input-focus: var(--color-ring);
+ --bridge-border-radius: var(--radius);
+ --bridge-foreground: var(--color-foreground);
+ --bridge-muted: var(--color-muted-foreground);
+ --bridge-bg: var(--color-background);
+ --bridge-muted-bg: var(--color-muted);
+ }
+ ```
+
+ The names on the right are examples: use the variables the app actually defines, or its literal values when it has none. Map its error and success colours to the `--bridge-alert-*` tokens. The components inherit the page font; set nothing for it unless the app puts its font on something other than `body`. The full token list: `bridge guide mechanisms`.
+3. **Give the pages the app's frame** when the app's own pages sit in a card, a panel or a split layout: pass `bridgeAuthRoutes({ frame: AuthLayoutComponent })` with a component that wraps `` in the app's own card or shell, built from the app's own classes or components, so sign-in is not the one page that looks different.
+
+Ask the developer for colours only when the project has no styles of its own.
+
+Customise further only as far as needed: `--bridge-*` CSS tokens (above); `bridgeAuthRoutes({ frame: AuthLayoutComponent, heading: (page) => … })`; replace one page with `overrides: { login: { component: MyLoginComponent } }` (a page you own renders `` and navigates itself).
## Step 3 — Guard the rest
@@ -75,7 +90,7 @@ Use the declarative `RouteGuardConfig` + `bridgeAuthGuard` from `integration-pro
## Reading the user
-Inject `AuthService` for auth state and `ProfileService` for profile fields. For the raw token — to call your own backend — take it from the auth service, send it as `Authorization: Bearer `, and verify it server-side with `@nebulr-group/bridge-nestjs` or `@nebulr-group/bridge-express`.
+Inject `AuthService` for auth state and `ProfileService` for profile fields. To log out, call `getBridgeAuth().logout({ redirectTo: '/auth/login' })` on `AuthService` so the user lands on the app's own login page. For the raw token — to call your own backend — take it from the auth service, send it as `Authorization: Bearer `, and verify it server-side with `@nebulr-group/bridge-nestjs` or `@nebulr-group/bridge-express`.
> **Never make an authorization decision on the client alone.** Hiding a control is UX. The check that matters happens on the server against the verified token.
@@ -83,7 +98,8 @@ Inject `AuthService` for auth state and `ProfileService` for profile fields. For
- **Hand-rolling a password form** instead of `` — loses magic link, passkeys, MFA and workspace selection, all configured server-side.
- **Writing login/signup/callback pages by hand** — `bridgeAuthRoutes()` already serves them; a hand-written set usually forgets `set-password/:token`, which every signup email links to.
-- **Setting `loginRoute` while expecting hosted login**, or the reverse.
+- **Leaving out `loginRoute`.** The guard then sends signed-out visitors off the app instead of to `/auth/login`.
+- **Logging out without `redirectTo`.** The user then leaves the app instead of landing on `/auth/login`.
- **Trusting auth state for authorization.** It says a session exists, not what it may do.
- **Expecting an NgModule.** These are standalone components; import them into `imports: []`.