Angular 21 + Ionic 8 + Capacitor 7 mobile application. Communicates with the mobile BFF (spring-boot-mobile-bff, port 8082) via a session cookie — it never holds or sees OAuth2 tokens.
- Mobile frontend counterpart to
angular-ui - Targets the mobile BFF (
spring-boot-mobile-bffclient) - Packaged as a native app via Capacitor; OAuth2 redirect uses the
myapp://custom URI scheme
- Login button calls
Browser.open()to open the BFF's/oauth2/authorization/{provider}URL in the system browser (Chrome Custom Tab on Android, SFSafariViewController on iOS) - The BFF performs the full OAuth2/PKCE dance with the chosen identity provider
- On success,
MobileAuthenticationSuccessHandlergenerates a short-lived (30 s) one-time code and redirects tomyapp://callback?code=<uuid> - The OS intercepts
myapp://and fires theappUrlOpenevent in the app app.tsextracts the code and callsauthService.exchangeSession(code)POST /session/exchangeatomically validates and deletes the code, setsSet-Cookie: SESSION=..., and returns the user's profile data in the response body- The app stores the profile and navigates to
/profile
Logout opens the BFF's /logout URL in Browser.open(). The system browser carries the session cookie, allowing the BFF to complete the OIDC end-session flow.
On iOS 17+, SFSafariViewController silently closes when it encounters a myapp:// redirect without firing appUrlOpen. This breaks the login callback. iOS login is currently unsupported; Android is fully functional.
| Route | Guard | Description |
|---|---|---|
/ |
None | Home — redirects to /profile if already authenticated |
/profile |
authGuard |
Displays profile data |
/payments |
authGuard |
Payments (Keycloak login required) |
npm install
npm start # http://localhost:4200Requires an AOSP emulator (no Google Play Store) started with -writable-system. Run once per new emulator:
./setup-android.sh # from project rootThis installs the mkcert root CA as a system certificate, adds container hostnames to /etc/hosts, and configures Chrome to ignore certificate errors.
Note:
/etc/hostschanges do not survive a reboot on API 36.run-android.shre-establishes hostname resolution via--host-resolver-rulesandadb reverseon every run, so a manual re-run ofsetup-android.shis only needed after a full emulator wipe.
./run-android.sh # from project rootThis builds the Angular app with the android configuration, syncs Capacitor, deploys to the connected emulator, and sets up adb reverse tunnels and hostname resolution.
environment.android.ts uses two separate URLs:
| Variable | Value | Reason |
|---|---|---|
bffBaseUrl |
https://10.0.2.2:8082 |
In-app WebView API calls — 10.0.2.2 is the emulator's alias for the host machine; the cert includes this IP as a SAN |
bffBrowserUrl |
https://spring-boot-mobile-bff:8082 |
Browser.open() OAuth2 URL — must use the registered hostname; resolved in the external browser via --host-resolver-rules + adb reverse |
iOS uses the standard Capacitor Xcode workflow. From the project root, sync web assets:
cd ionic-mobile && npm run cap:ios # builds Angular, syncs Capacitor, opens XcodeThen build and run from Xcode targeting the simulator or a device.
Note: Login is broken on iOS 17+ (see Known limitation above). The simulator can be used for UI and navigation testing but the OAuth2 flow will not complete.
environment.ios.ts uses https://spring-boot-mobile-bff:8082 for both bffBaseUrl and bffBrowserUrl. The iOS Simulator shares macOS's network stack and /etc/hosts, so the BFF hostname resolves directly.
Add the mkcert root CA to the simulator's keychain for TLS trust:
xcrun simctl keychain booted add-root-cert certs/rootCA.pem| Configuration | Environment file | Used for |
|---|---|---|
development (default) |
environment.ts |
npm start |
android |
environment.android.ts |
run-android.sh |
ios |
environment.ios.ts |
iOS builds |
docker |
environment.docker.ts |
Docker / CI builds |
| Package | Purpose |
|---|---|
@ionic/angular |
Ionic UI components |
@capacitor/core |
Capacitor runtime |
@capacitor/ios |
iOS native platform |
@capacitor/android |
Android native platform |
@capacitor/app |
appUrlOpen event (OAuth2 callback) |
@capacitor/browser |
Browser.open() for OAuth2 and logout |
- Same coding patterns as
angular-ui: standalone components, signals, functional providers, strict templates, Prettier (100-char, single quotes) - The
myapp://redirect URI is registered in both identity providers and the Capacitor native projects — it will not resolve in a plain browser context