Portal Community

Azure App Registration

1

Create App Registration

In Azure Portal → Microsoft Entra ID → App Registrations → New Registration.
Name: BizFirstGO API, Supported account types: Single tenant (or multi-tenant if needed).

2

Configure App ID URI

In Expose an API, set the Application ID URI to api://bizfirstgo. This becomes the audience in the access token.

3

Add App Roles

In App Roles, create roles that map to BizFirstGO roles:

Role: BizFirstGO.Admin    — Display name: "BizFirstGO Admin"
Role: BizFirstGO.Manager  — Display name: "BizFirstGO Manager"
Role: BizFirstGO.User     — Display name: "BizFirstGO User"
Role: BizFirstGO.Viewer   — Display name: "BizFirstGO Viewer"
4

Enable Group Claims (Optional)

In Token Configuration → Add groups claim. Choose Security Groups. For the access token, select Group ID (GUIDs). Note: if a user has >200 groups, the overage indicator appears instead of the groups list.

5

Configure BizFirstGO Tenant Claim

Add a Optional Claim for the access token: tid (Azure tenant ID). BizFirstGO can use this to derive the BizFirstGO tenant ID via a static mapping.

Azure AD Token Structure

// Decoded Azure AD access token
{
  "iss": "https://login.microsoftonline.com/{aad-tenant-id}/v2.0",
  "sub": "A1bC2-dEfG3-hIjK4-lMnO5",  // OID (Object ID) — stable user identifier
  "oid": "A1bC2-dEfG3-hIjK4-lMnO5",  // same as sub in v2 tokens
  "tid": "aad-tenant-id",             // Azure AD tenant ID
  "aud": "api://bizfirstgo",
  "upn": "jane.smith@company.onmicrosoft.com",
  "name": "Jane Smith",
  "email": "jane.smith@company.com",
  "roles": ["BizFirstGO.Admin", "BizFirstGO.Manager"],  // App roles
  "groups": [                                              // Security group GUIDs (if <200 groups)
    "a1b2c3d4-...", "e5f6g7h8-..."
  ]
}

BizFirstGO Federation Configuration

POST /passport/admin/tenants/{tenantId}/federation
{
  "providerId":    "azure-ad",
  "displayName":   "Microsoft Entra ID",
  "issuer":        "https://login.microsoftonline.com/{aad-tenant-id}/v2.0",
  "jwksUri":       "https://login.microsoftonline.com/{aad-tenant-id}/discovery/v2.0/keys",
  "audience":      "api://bizfirstgo",
  "tenantIdConfig":{
    "source":     "static",
    "value":      "tenant-abc"   // or use "claim" source with "tid" claim + mapping table
  },
  "userIdClaim":   "oid",        // Object ID — more stable than "sub" for Azure AD
  "emailClaim":    "email",
  "rolesClaim":    "roles",      // App roles preferred over group GUIDs
  "roleMapping": {
    "BizFirstGO.Admin":   "admin",
    "BizFirstGO.Manager": "manager",
    "BizFirstGO.User":    "user",
    "BizFirstGO.Viewer":  "viewer"
  }
}

Groups Overage Handling

Azure AD includes a maximum of 200 groups in JWT tokens. When a user has more than 200 groups, the token includes an overage indicator (_claim_names: { groups: "src1" }) instead of the groups list. BizFirstGO's Azure AD provider handles this automatically by calling Microsoft Graph.

// Overage token structure
{
  "_claim_names": { "groups": "src1" },
  "_claim_sources": {
    "src1": {
      "endpoint": "https://graph.microsoft.com/v1.0/users/{oid}/getMemberObjects"
    }
  }
}

// BizFirstGO Azure AD provider — automatic overage handling
public sealed class AzureADTokenProvider : IExternalTokenProvider
{
    // When groups overage is detected, fall back to Graph API
    private async Task<IList<string>> GetGroupsWithOverageAsync(
        string userId, string accessToken, CancellationToken ct)
    {
        var graphClient = new GraphServiceClient(/* ... */);
        var groups = await graphClient.Users[userId]
            .TransitiveMemberOf
            .GetAsync(ct: ct);

        return groups?.Value?
            .OfType<Group>()
            .Select(g => g.Id ?? "")
            .ToList() ?? [];
    }
}
Prefer App Roles Over Group GUIDs

Azure AD security group claims contain GUIDs which are environment-specific (different in dev/staging/prod). App Roles use human-readable names (like BizFirstGO.Admin) and are portable across environments. Configure App Roles and use roleMapping instead of groupMapping for Azure AD integrations.