.. _azure_proxy_ios_settings: ====================================== Peek Field App — OAuth2 Settings ====================================== The Peek field app reads the Application Proxy host and OAuth2 parameters from a built-in defaults asset. For enterprise MDM deployments the same keys can be overridden per device at runtime. Defaults Asset ============== File: ``peek_field_app/src/assets/peek_core_device/server-info-tuple-defaults.json`` Shape (matches ``ServerInfoTuple`` in ``peek_core_device/plugin-module/_private/tuples/server-info-tuple.ts``):: { "host": "peekfieldapp-synerty.msappproxy.net", "useSsl": true, "httpPort": 443, "websocketPort": 443, "hasConnected": true, "oauth2AppId": "", "oauth2TenantId": "", "oauth2WebRedirectUrl": "https://peekfieldapp-synerty.msappproxy.net/.auth/login/aad/callback", "oauth2BundleId": "com.synerty.peek" } The ``oauth2WebRedirectUrl`` field controls which OAuth2 scope format the field app requests: - **Omit / leave blank** → scope is ``api:///user_impersonation`` (matches an ``api://`` identifier URI on the Entra app registration). - **Set to the proxy callback URL** → scope is ``https:///user_impersonation`` (matches an ``https://`` identifier URI, which is the recommended format for Application Proxy deployments). The recommended setup uses the Application Proxy external URL as the identifier URI (``https://peekfieldapp-synerty.msappproxy.net``). In that case set ``oauth2WebRedirectUrl`` to the proxy's OIDC callback path:: "oauth2WebRedirectUrl": "https://peekfieldapp-synerty.msappproxy.net/.auth/login/aad/callback" If you registered ``api://`` as the identifier URI instead, omit ``oauth2WebRedirectUrl`` from the JSON. Field reference: +-------------------------+------------------------------------------------+ | Key | Meaning | +=========================+================================================+ | ``host`` | External Application Proxy hostname | +-------------------------+------------------------------------------------+ | ``useSsl`` | Must be ``true`` for Application Proxy | +-------------------------+------------------------------------------------+ | ``httpPort`` | ``443`` (TLS) | +-------------------------+------------------------------------------------+ | ``websocketPort`` | ``443`` (shares the HTTPS port through proxy) | +-------------------------+------------------------------------------------+ | ``hasConnected`` | Set ``true`` for baked-in proxy deployments so | | | the app skips the Connect screen and goes | | | straight to OAuth2 on launch | +-------------------------+------------------------------------------------+ | ``oauth2AppId`` | ``appId`` of the Entra app registration | +-------------------------+------------------------------------------------+ | ``oauth2TenantId`` | Entra directory (tenant) ID | +-------------------------+------------------------------------------------+ | ``oauth2WebRedirectUrl``| **Omit for Capacitor builds.** When present | | | and non-empty, shifts the OAuth2 scope from | | | ``api:///…`` to | | | ``https:///…``. Only needed if the PWA | | | path uses an Application Proxy URL-based | | | identifier URI. See "Browser / PWA Build" | | | below. | +-------------------------+------------------------------------------------+ | ``oauth2BundleId`` | iOS bundle ID — used to build | | | ``msauth.://auth`` | +-------------------------+------------------------------------------------+ The asset is loaded once, on first launch, by ``ServerInfoTuple.loadDefaultValues()``. Once ``hasConnected`` flips to ``true`` the values persist in the device's offline tuple storage; changes to this JSON will not be picked up until the app is reinstalled (or offline storage is cleared). MDM Overrides (iOS) =================== For MDM-managed fleets, push the same values as AppConfig keys. The keys are defined in ``MdmAppConfigKeyEnum`` (``peek_core_device/plugin-module/_private/hardware-info/hardware-info.ts``): +------------------------------------+---------------------------+ | AppConfig key | Maps to | +====================================+===========================+ | ``peek.serverConnection.host`` | ``host`` | +------------------------------------+---------------------------+ | ``peek.serverConnection.useSsl`` | ``useSsl`` | +------------------------------------+---------------------------+ | ``peek.serverConnection.httpPort`` | ``httpPort`` | +------------------------------------+---------------------------+ | ``peek.serverConnection.`` | | | ``websocketPort`` | ``websocketPort`` | +------------------------------------+---------------------------+ | ``peek.serverConnection.`` | | | ``hasConnected`` | ``hasConnected`` | +------------------------------------+---------------------------+ | ``peek.oauth2.appId`` | ``oauth2AppId`` | +------------------------------------+---------------------------+ | ``peek.oauth2.tenantId`` | ``oauth2TenantId`` | +------------------------------------+---------------------------+ | ``peek.oauth2.webRedirectUrl`` | ``oauth2WebRedirectUrl`` | +------------------------------------+---------------------------+ | ``peek.oauth2.bundleId`` | ``oauth2BundleId`` | +------------------------------------+---------------------------+ MDM values are read via ``@capacitor-community/mdm-appconfig`` and take precedence over the JSON defaults on ``FIELD_IOS`` builds only. iOS Bundle ID and URL Scheme ============================ The iOS bundle identifier in ``ios/App/App.xcodeproj/project.pbxproj`` (``PRODUCT_BUNDLE_IDENTIFIER``) **must** match ``oauth2BundleId``. The Capacitor OAuth2 plugin builds the redirect URI as ``msauth.://auth`` at runtime and that exact value must be registered as the iOS redirect URI on the Entra app registration (see :ref:`azure_proxy_setup`). If you change the bundle ID: 1. Update ``PRODUCT_BUNDLE_IDENTIFIER`` in Xcode. 2. Update ``oauth2BundleId`` in the JSON above. 3. Update the iOS platform redirect URI on the Entra app. 4. Re-register the MSAL redirect handler in ``Info.plist`` (``CFBundleURLSchemes``) if it is hardcoded. Angular Build Configuration =========================== The Peek field app uses a dedicated Angular build configuration, ``capacitor``, which sets ``serviceWorkerEnabled: false`` via ``environment.capacitor.ts``. The Angular service worker **must** be disabled at build time for Capacitor (iOS/Android) builds — leaving it enabled causes the service worker to detect build-hash changes and trigger continuous WebView reloads via ``clients.navigate()``, which bypasses any JavaScript-level interception. The ``capacitor`` configuration is the default for ``npm run build``. For web/PWA builds use ``ng build --configuration=production``. Browser / PWA Build =================== When the PWA is served behind Application Proxy, authentication is handled by the proxy's own pre-auth flow (the user signs in once at the proxy, the proxy sets a session cookie, and the backend receives the ``X-MS-CLIENT-PRINCIPAL`` header). The PWA does **not** run its own MSAL flow, so ``oauth2WebRedirectUrl`` is not needed on the native side. The proxy's pre-auth does, however, use the PeekFieldApp app registration and sends the external URL as its ``redirect_uri``. That external URL must be registered under the **Web** platform of the app registration (see :ref:`azure_proxy_setup`). Registering it under **Single-page application** instead forces PKCE on the proxy's server-side token exchange and the proxy login fails with ``AADSTS9002325``. Xcode Cloud Build ================= For Xcode Cloud deployments the defaults asset is not edited by hand. Fill in ``app_spec.json`` at the repo root (including the ``oauth2`` section) and run ``patch_and_tag_for_xcode_build.py`` — the script writes ``server-info-tuple-defaults.json`` automatically before tagging the build. See :ref:`xcode_cloud_app_spec` for the full field reference, the ``serverConnection`` / ``oauth2`` mapping table, and a worked example. Troubleshooting =============== **App shows "Connect To Server" instead of redirecting to Entra.** The defaults JSON is blank or ``host`` is ``null``. Confirm the asset is present in the built ``App.app`` bundle at ``public/assets/peek_core_device/server-info-tuple-defaults.json``. **Proxy login returns ``AADSTS9002325`` (PKCE required).** The Application Proxy external URL has been registered under the *Single-page application* platform on the Entra app registration. Move it to **Web** — Application Proxy pre-auth uses a server-side (confidential-client) token exchange without PKCE, and SPA registration forces PKCE. The iOS flow uses ``msauth.://auth`` under *iOS / macOS* only. **Proxy login returns ``AADSTS50011`` (redirect URI mismatch).** The Application Proxy external URL is not registered on the app registration at all. Add it under the **Web** platform — see :ref:`azure_proxy_setup`. **OAuth2 browser opens but returns a different redirect-URI error.** The iOS platform redirect URI is missing or mismatched. Confirm Entra has ``msauth.://auth`` under *iOS / macOS* with ```` equal to ``oauth2BundleId``. **OAuth2 succeeds but the WebSocket drops after ~85 seconds.** Application Proxy ``applicationServerTimeout`` is still ``Default``. Set it to ``Long`` on the Application Proxy app settings. **App reloads continuously (~450 ms cycle) immediately after launch.** The Angular service worker was enabled in the Capacitor build. Ensure the build was produced with ``npm run build`` (which uses the ``capacitor`` configuration) rather than a web/production configuration. The service worker detects build-hash changes on every reinstall and triggers ``clients.navigate()`` from the worker context, which cannot be suppressed from JavaScript. See "Angular Build Configuration" above. **App crashes or reloads once ~400 ms after launch, then stabilises.** ``VortexService.setVortexUrl(null)`` was not called for the native platform. The ``VortexService`` static default URL is ``"/vortex"``; on Capacitor it must be explicitly set to ``null`` so the service does not attempt an HTTP connection to the Capacitor asset server at startup. This is handled automatically by ``main.ts`` as of the current version.