# Setting up the Entra app registration This guide is the **own-registration path**: how to create, in your own tenant, the app registration Elevate signs in with — even if you have never opened the Microsoft Entra admin center before. It is the route to take when you want a registration you control. ## 0. Do you need your own? Three routes get you signed in, and only the first two cover Entra directory roles and PIM for Groups: | Route | What it covers | What it costs you | |---|---|---| | **Your own registration** (this guide) | Everything: Entra roles, Azure roles, groups, access packages | One registration to create, and admin consent per tenant | | **The [shared Elevate app](shared-app-registration.md)** | The same | Nothing to create, but admin consent per tenant — and it is optional, has no SLA, and may change or be withdrawn | | **Microsoft's Azure CLI or Azure PowerShell app** | **Azure resource roles only** | Nothing — no registration and no consent | Microsoft grants the Azure CLI and Azure PowerShell apps no Graph PIM permissions, so an account added that way never reads or activates Entra roles or group memberships, and sees Azure resource approvals only. There is no way around that from the client side; the permissions Entra PIM and PIM for Groups need are admin-consent only and are granted to a registration like this one, not to Microsoft's. Take the shared app if you just want to try Elevate. Follow this guide instead when you want control over the registration's scopes, redirect URIs and lifetime, when you target applications by application id in Conditional Access, when Conditional Access blocks the first-party apps in your tenant, or when you are rolling Elevate out to a fleet. [shared-app-registration.md](shared-app-registration.md) compares the two in detail. ## 1. What the registration is for Elevate needs one app registration: a single, multi-tenant, public-client application. "Public client" means no client secret is ever created, stored, or used — sign-in works with just the application (client) ID. Elevate signs in with this registration through MSAL: - On macOS, MSAL uses the redirect `msauth.://auth` — for the standard build, bundle id `no.reothor.elevate`, so the redirect is `msauth.no.reothor.elevate://auth`. - On Windows, the own-app sign-in goes through the Windows account picker (the WAM broker), which needs the redirect `ms-appx-web://microsoft.aad.brokerplugin/`, with the `http://localhost` loopback redirect as the fallback when the broker is unavailable. Elevate's Windows Settings window shows both, ready to copy. - For the custom-app sign-in method on either platform, sign-in uses the `http://localhost` loopback redirect. You only need to create this registration once per organization; every tenant that wants to use it then grants it admin consent (see step 2 or 3). ## 2. Option A: Azure CLI (recommended) This is the fastest way to create the registration, and it is scripted so you do not have to click through the portal. **Prerequisites:** - The Azure CLI (`az`), version 2.60 or newer. - Signed in with `az login`, as a user who can create app registrations — the built-in Application Developer role or higher. **Steps:** 1. Clone this repository and change into the script's directory: ```bash git clone https://github.com/FrodeHus/elevate.git cd elevate/docs/entra-app ``` 2. Run the script: ```bash ./create-app-registration.sh ``` By default this creates a registration named "Elevate" with the redirect for bundle id `no.reothor.elevate`. Pass a different display name and/or bundle id as arguments if you are registering a differently-named or rebranded build — for example `./create-app-registration.sh "Elevate (Contoso)" no.reothor.elevate`. 3. The script prints the application (client) ID, for example: ``` Application (client) ID: 11111111-2222-3333-4444-555555555555 ``` Copy this value — you will paste it into Elevate's Settings, and use it again for consent. **Granting admin consent:** The registration exists now, but no tenant can use it until an administrator in that tenant grants admin consent for the permissions in step 4 below. For the home tenant (the tenant you created the app in), you can do this from the CLI, as a Privileged Role Administrator or Global Administrator: ```bash az ad app permission admin-consent --id ``` For every other tenant that will use Elevate, an administrator in that tenant needs to open the admin consent URL, substituting that tenant's ID and your client ID: ``` https://login.microsoftonline.com//adminconsent?client_id= ``` You do not have to build this URL by hand for every tenant: once a client ID is configured in Elevate, open the "…" menu next to any signed-in tenant and choose "Open admin consent link…" to get the same link pre-filled for that tenant. ## 3. Option B: Entra admin center, step by step Use this if you would rather click through the portal, or cannot run the Azure CLI. 1. Go to the [Microsoft Entra admin center](https://entra.microsoft.com) → **App registrations** → **New registration**. 2. Give it a name (for example "Elevate"). 3. Under "Supported account types", choose **Accounts in any organizational directory (Any Microsoft Entra ID tenant – Multitenant)**. 4. Leave "Redirect URI" blank here — you add the redirects in the next step. 5. Select **Register**. 6. Open **Authentication** on the new registration, then **Add a platform**: - Choose **iOS/macOS**, enter the bundle ID `no.reothor.elevate`, and select **Configure**. The portal derives the redirect URI `msauth.no.reothor.elevate://auth` for you. - Select **Add a platform** again, choose **Mobile and desktop applications**, and add the custom redirect URIs `http://localhost` and, for Windows, `ms-appx-web://microsoft.aad.brokerplugin/` (the application (client) ID from the registration's Overview page). - Select **Add a platform** again, choose **Web**, and add the redirect URI `https://login.microsoftonline.com/common/oauth2/nativeclient`. This one is not used for sign-in; it only exists so the admin consent link (step 2 above) opens a valid page instead of an error. 7. Open **API permissions** → **Add a permission**: - Choose **Microsoft Graph** → **Delegated permissions**, and add the seven scopes listed in the permission table below. - Select **Add a permission** again, choose **Azure Service Management** → **Delegated permissions**, and add `user_impersonation`. 8. Select **Grant admin consent for \** to consent for your own tenant. Every other tenant that will use Elevate repeats this consent step — either the same way, or by opening the admin consent link described in step 2 above. A note on public client flows: you do **not** need to turn on "Allow public client flows" under Authentication. The iOS/macOS platform and the "Mobile and desktop applications" platform already mark their redirect URIs as public-client. Only turn this setting on if sign-in fails with error AADSTS7000218. ## 4. Permission table All permissions are delegated (the signed-in user's own access, not app-only). "Admin consent" means a tenant administrator must grant it before anyone in that tenant can sign in with this app; a user cannot consent to it themselves. | Permission | Resource | What Elevate uses it for | Admin consent | |---|---|---|---| | `User.Read` | Microsoft Graph | Reads the signed-in user's basic profile after sign-in. | Yes | | `RoleEligibilitySchedule.Read.Directory` | Microsoft Graph | Lists the Entra directory roles the user is eligible to activate. | Yes | | `RoleAssignmentSchedule.ReadWrite.Directory` | Microsoft Graph | Activates and deactivates Entra directory roles. | Yes | | `RoleManagementPolicy.Read.Directory` | Microsoft Graph | Reads each directory role's policy (maximum duration, whether a reason or MFA is required). | Yes | | `PrivilegedEligibilitySchedule.Read.AzureADGroup` | Microsoft Graph | Lists PIM-for-groups eligibility, for the Groups tab. | Yes | | `PrivilegedAssignmentSchedule.ReadWrite.AzureADGroup` | Microsoft Graph | Activates and deactivates PIM-for-groups membership/ownership. | Yes | | `RoleManagementPolicy.Read.AzureADGroup` | Microsoft Graph | Reads each group's PIM policy. | Yes | | `user_impersonation` | Azure Service Management | Discovers tenants/subscriptions and reads, activates, and deactivates Azure resource roles. | User-consentable | | `EntitlementMgmt-SubjectAccess.ReadWrite` | Microsoft Graph | Lists, requests and cancels the user's own entitlement management access packages, for the Access packages window. | No | `EntitlementMgmt-SubjectAccess.ReadWrite` is the one permission a user can consent to themselves. Tenants that consented before it was added see one incremental consent prompt on the next interactive sign-in; an administrator can also grant it for everyone with the consent link in the tenant menu. ## 5. Verify Confirm the registration looks right: ```bash az ad app show --id --query "{name:displayName,audience:signInAudience,public:publicClient.redirectUris}" ``` You should see `audience` as `AzureADMultipleOrgs`, and both `msauth.no.reothor.elevate://auth` and `http://localhost` listed as public-client redirect URIs. Then, in Elevate, open Settings and paste the client id, and add an account to confirm sign-in works end to end. ## 6. Troubleshooting - **AADSTS7000218** ("client assertion or client secret required"): the redirect URI is registered under the Web platform instead of iOS/macOS or "Mobile and desktop applications". Move it to the correct platform (steps above), or, as a fallback, turn on "Allow public client flows". - **AADSTS65001** ("The user or administrator has not consented") or a message about admin consent: consent has not been granted in that tenant. Have an administrator use the admin consent link from the tenant menu, or run the CLI consent command from step 2. - **AADSTS50011** ("The redirect URI ... does not match"): the bundle id in the registration does not match the bundle id of the build you are running. Re-check the iOS/macOS platform's bundle ID against your build, or pass the build's bundle id to the script. - **"PIM for Groups is not permitted" / the Groups tab is empty**: the three `*.AzureADGroup` scopes have not been consented in that tenant. Re-run consent after confirming they are listed under API permissions. ## 7. Optional: read scopes for elevate-audit [elevate-audit](audit.md) does not use this registration; by default it signs in with Microsoft's own Graph PowerShell client. If your tenant blocks that client, you can point `elevate-audit --client-id` at this registration instead. Add these delegated Microsoft Graph permissions and grant admin consent; Elevate itself never asks for them: `User.Read` (already present), `RoleManagement.Read.Directory`, `PrivilegedAssignmentSchedule.Read.AzureADGroup`, `PrivilegedEligibilitySchedule.Read.AzureADGroup`, `GroupMember.Read.All`, `User.ReadBasic.All`, and optionally `AuditLog.Read.All` (for the unused-eligibility rules; without it those rules are skipped). With a custom client the auditor requests `https://graph.microsoft.com/.default`, so it can do only what was consented. ## 8. Using a second registration for some accounts Some accounts may need a different registration than the one in Settings — most often because their tenant has its own copy of the Elevate registration, separate from your organization's. An account can "pin" its own client ID instead of following the one in Settings. The second registration needs the same setup as the Settings one: the redirects **Mobile and desktop applications** asks for on that platform — `msauth.no.reothor.elevate://auth` for signed macOS builds, `http://localhost` for unsigned ones, and `ms-appx-web://microsoft.aad.brokerplugin/` plus `http://localhost` on Windows — the same Microsoft Graph PIM scopes from the [permission table](#4-permission-table), and admin consent in each tenant that account uses. Admin consent links for such an account use its own client ID, not the one in Settings. **Where to choose it:** Add account → Entra app registration offers "Use the registration in Settings (\)" and "Use a different registration", with a field for the Application (client) ID. If the ID you type matches the one in Settings, Elevate says so and still keeps that account pinned to it, so a later change to Settings does not affect it. **Changing it later:** open the account's menu and choose **Change app registration…**. The sheet (a window on Windows) offers "Follow the registration in Settings" or "Use a different registration", and a **Sign in and switch** button. Elevate signs the account in with the chosen registration and keeps its tenants, roles and profiles. Nothing changes if you cancel the sign-in, a different account signs in instead, or something else changes the account meanwhile. When your organization manages the client ID, an account with its own registration can only be moved to the managed one: the sheet then offers just "Follow the registration in Settings", and **Sign in** on that account says so. In Add account, both options are hidden when your organization manages the client ID with the [`ClientId`](enterprise/keys.md#clientid) key. **Platform support:** macOS, the Windows app and the CLI all support this. On Windows the second registration needs `ms-appx-web://microsoft.aad.brokerplugin/` and `http://localhost` as redirect URIs, the same as the Settings one; the CLI needs `http://localhost` only. **From the CLI:** `elevate login --method own --client-id ` adds an account with a registration of its own, and `elevate accounts set-client-id ` moves one there later (`--from-settings` moves it back onto the configured registration, and the same command upgrades an Azure CLI, Azure PowerShell or other-app account). `elevate accounts` names the registration each account uses. The rules above hold: the change is saved only after the same account signs in with the new registration, and under a managed client ID only the managed registration may be chosen. Copy diagnostics never includes any client ID, including one an account pins. **Upgrading an account that is not an Entra app registration yet:** the same account menu offers **Upgrade to Entra app registration…** for an account added with the Azure CLI app, the Azure PowerShell app or **Other app (browser sign-in)**. It works the same way as **Change app registration…** above — choose the Settings registration or a different one, then **Sign in and switch** — and keeps the account's tenants, roles and profiles; nothing changes until the same account signs in with the new registration.