Entra ID Sign-In Setup
Power Portals Pro signs users in with Microsoft through ASP.NET Core's standard AddMicrosoftAccount provider, which needs an app registration in Microsoft Entra ID. The registration is what tells Entra that your portal is allowed to ask for sign-ins, where it may send users back to, and which credentials it will prove itself with. This page walks through every property the portal actually depends on.
This is not the Dataverse connection app
Two separate registrations are involved in a typical portal, and mixing them up is a common first-run mistake. The Dataverse connection app is a service principal that reads and writes data as the portal itself, and its credentials go under
D365:ClientId/D365:ClientSecret. The registration described on this page is only for signing users in, and its credentials go underAuthentication:Microsoft:ClientId/ClientSecret. Use two registrations — they need different properties, and they have very different blast radius if leaked.
1. Register the Application
Create the registration in the Microsoft Entra admin center. You need at least the Application Developer role in the tenant.
- Sign in to the Entra admin center, and if you belong to more than one tenant, use the Settings icon to switch to the tenant that should own the registration. An app registration cannot be moved between tenants afterwards.
- Browse to Entra ID > App registrations and select New registration.
- Enter a Name, for example
Contoso Portal Sign-In. Users see this name on the consent screen, so make it something they will recognize as your portal. It can be changed later. - Under Supported account types, pick the audience that matches your portal — see the table below. This is the property that decides who is allowed to sign in.
- Select Register.
- On the Overview page, copy the Application (client) ID. That value becomes
Authentication:Microsoft:ClientId.
Choosing Supported Account Types
This setting is the portal's front door. Pick the narrowest option that still covers everyone who must sign in:
| Supported account types | Who can sign in |
|---|---|
| Single tenant only — <your tenant> | Only users and guests in your own tenant. The right choice for an employee-facing portal — it matches the Internal users audience in the project template. |
| Multiple Entra ID tenants | Users in any Entra tenant, but not personal Microsoft accounts. Use this for partner or B2B portals where every user signs in with a work or school account from their own organization. |
| Any Entra ID Tenant + Personal Microsoft accounts | Work, school, and personal accounts (Outlook.com, Hotmail, Xbox). The widest option, and the usual choice for a customer-facing portal where visitors may not belong to any organization. |
| Personal accounts only | Consumer Microsoft accounts only — no work or school accounts. |
Note
The ASP.NET Core Microsoft provider always authorizes against the multi-audience
/common/endpoint, so the registration's Supported account types setting — not your application code — is what actually accepts or rejects a given account. If an account is turned away withAADSTS50020, the registration is narrower than the audience you intended.
2. Add the Redirect URI
After a successful sign-in, Entra sends the user back to your portal at the provider's callback path, which is /signin-microsoft. Entra only redirects to addresses registered in advance, so every host your portal runs on needs its own entry.
- Open Authentication under Manage, then select Add a platform.
- Choose the Web platform — not Single-page application. The portal server performs the token exchange using the client secret, and that stays true for Blazor WebAssembly and React hosts, where sign-in is still handled server-side.
- Enter your portal's address followed by
/signin-microsoft. - Repeat for every environment. The development URL from
launchSettings.jsonand each deployed host all need their own redirect URI. They can share one registration, or you can keep a separate registration per environment.
https://localhost:7228/signin-microsoft
https://your-portal.example.com/signin-microsoft
Important
The redirect URI must match what the portal sends exactly — scheme, host, port and path.
https://localhost:7228/signin-microsoftandhttps://localhost:7228/signin-microsoft/are not the same address, and neither are two different ports. Entra requireshttpsfor everything exceptlocalhost.
If the portal runs behind a reverse proxy, load balancer or container ingress that terminates TLS, configure ASP.NET Core's forwarded headers middleware. Without it the app believes the request arrived over plain HTTP and builds an http:// redirect URI, which will not match the registered https:// one.
3. Create a Client Secret
The secret is how your portal proves it is really the registered application when it exchanges the authorization code for a token.
- Open Certificates & secrets under Manage and select the Client secrets tab.
- Select New client secret, give it a description that names the environment (for example
portal-production), and choose an expiry. - Copy the Value column immediately — not the Secret ID. The value is shown only once, and it becomes
Authentication:Microsoft:ClientSecret.
Important
Client secrets expire, and 24 months is the maximum. When one expires, every Microsoft sign-in fails with
AADSTS7000215until it is replaced, so track the expiry date and rotate ahead of it. For production, certificate credentials or a federated identity credential avoid the expiring-secret problem entirely.
4. Verify API Permissions
A new registration is granted Microsoft Graph User.Read (delegated) automatically, and that is the only permission the sign-in flow needs. The provider requests the https://graph.microsoft.com/user.read scope and reads the signed-in user's profile from https://graph.microsoft.com/v1.0/me. If someone has removed that permission, add it back under API permissions. Granting admin consent is optional in most workforce tenants but avoids showing each user a consent prompt on first sign-in; in external tenants it is required.
Power Portals Pro reads the following from that profile when it links or creates a portal user:
| Graph property | Claim | How the portal uses it |
|---|---|---|
id |
NameIdentifier |
The stable key for the external login. Stored against the portal user so the same Microsoft account resolves to the same user on every later sign-in. |
mail / userPrincipalName |
Email |
Matched against existing contacts (and system users) to find the portal user this sign-in belongs to, and used as the email address when a new account is created. Which contact columns are searched is configurable — see Matching an external sign-in to a contact. |
givenName, surname |
GivenName, Surname |
Prefills the first and last name on the registration form when a new portal user is created. |
Note
The
userPrincipalName. A UPN is not always a routable email address — guest accounts in particular carry UPNs shaped likeuser_contoso.com#EXT#@yourtenant.onmicrosoft.com— so if your portal matches users by email, expect those accounts not to line up with an existing contact.
5. Store the Client ID and Secret
The portal reads both values from configuration under the Authentication:Microsoft section. In development, keep them in user secrets rather than appsettings.json so they never reach source control:
{
"Authentication": {
"Microsoft": {
"ClientId": "<application (client) id>",
"ClientSecret": "<client secret value>"
}
}
}
Or from the command line, in the server project's directory:
dotnet user-secrets set "Authentication:Microsoft:ClientId" "<application (client) id>"
dotnet user-secrets set "Authentication:Microsoft:ClientSecret" "<client secret value>"
Tip
In hosted environments, supply the same keys as environment variables or from a secret store. Environment variables use a double underscore instead of the colon —
Authentication__Microsoft__ClientIdandAuthentication__Microsoft__ClientSecret— because the colon separator is not portable across platforms.
6. Enable the Provider in Program.cs
Register the provider alongside the rest of your authentication setup in the server project's Program.cs:
builder.Services.AddAuthentication().AddMicrosoftAccount(microsoftOptions =>
{
microsoftOptions.ClientId = builder.Configuration.GetRequiredValue("Authentication:Microsoft:ClientId");
microsoftOptions.ClientSecret = builder.Configuration.GetRequiredValue("Authentication:Microsoft:ClientSecret");
});
The project template writes this call for you when you select the Internal users or Both audience, or when you opt into Microsoft sign-in for External users. If you generated the project without it, the same call ships commented out in Program.cs — uncomment it and supply the two configuration values.
7. Sign In and Verify
Run the portal and open the login page. A Microsoft button now appears among the sign-in options. The first time an account signs in, the portal links the Microsoft identity to a portal user — matching an existing contact by email where it can, otherwise walking the visitor through registration with the name and email from their profile prefilled.
If the account also exists as a Dataverse systemuser, the session can run as that internal user instead of as a contact. That behavior needs no extra configuration in the app registration:
See SystemUser Sign-In
Troubleshooting
AADSTS50011— redirect URI mismatch. The address the portal sent is not registered. Compare the URI in the error with the Web platform entries character for character, including the port and the absence of a trailing slash.AADSTS7000215— invalid client secret. Usually the Secret ID was copied instead of the secret's Value, or the secret has expired. Create a new secret and update configuration.AADSTS50020— user account from identity provider does not exist in tenant. The account signing in is outside the registration's Supported account types. Widen the setting to cover that audience.- The sign-in round-trip lands on
http://. The portal is behind a TLS-terminating proxy without forwarded headers middleware configured, so it is generating insecure redirect URIs. - No email on the new account. The Microsoft account has no mailbox and its
userPrincipalNamewas used instead. See the note in step 4.
