App Permissions¶
Allow other applications to call your API with proper authorization.
| Property | Value |
|---|---|
| Goal | Authorize upstream applications to call your API |
| Prerequisites | API deployed, upstream app's Project ID |
| Time Estimate | 15-20 minutes |
| Difficulty | Intermediate |
📋 Overview¶
When your application is an API that other apps need to call, you must configure:
- Scopes - What permissions your API exposes
- Authorized Apps - Which upstream apps can call your API
Configuration must be done in both identity providers.
App Permission Flow¶
Upstream App → Requests Token → With Scopes → Calls Your API → Validates Token → Grants Access
(from Entra/Okta) (User.Read) (checks authorized apps)
Step 1: Define API Scopes¶
Define what permissions your API exposes. Choose scope names that clearly indicate the authorization pattern.
Use when applications call your API on behalf of users (token exchange flow).
Corporate (Entra ID) - infra/api/config.yml:
# Scopes - delegated permissions for user context
scopes:
- value: User.Read
display_name: Read access (Delegated)
description: Allows the app to read data on behalf of the user
- value: User.Write
display_name: Write access (Delegated)
description: Allows the app to write data on behalf of the user
External (Okta) - infra/auth/ext/okta-client/scopes.yml:
Use for direct service-to-service calls with no user context (client credentials flow).
Corporate (Entra ID) - infra/api/config.yml:
# Scopes - application permissions for service-to-service
scopes:
- value: Client.Read
display_name: Read access (Service-to-Service)
description: Allows service-to-service read access
- value: Client.Write
display_name: Write access (Service-to-Service)
description: Allows service-to-service write access
External (Okta) - infra/auth/ext/okta-client/scopes.yml:
Choose Clear Scope Names
- User-delegated: Use
User.*,Profile.*, orAccount.*when user context is required - Service-to-service: Use
Client.*,Service.*, orData.*for background jobs and system integrations - Both files must contain the same scope values
Step 2: Authorize Upstream Apps¶
Specify which upstream applications are allowed to call your API.
File: infra/auth/corp/config.yml
authorized_apps:
- project_id: it-web-frontend
app_roles: [] # Application permissions (service-to-service)
scopes: # Delegated permissions (on-behalf-of user)
- user_impersonation # Required for user-context calls
- User.Read
- project_id: it-api-admin-portal
app_roles:
- App.Read
- App.Write
scopes:
- user_impersonation
- User.Read
- User.Write
User-Context Scope Required
Use user_impersonation for corporate (Entra) and user-groups for external (Okta) when granting user-context access.
Step 3: Deploy Configuration¶
Run the authentication pipeline to apply your changes:
Deployment Order
- Your API must be deployed before upstream apps can be authorized
- Upstream apps must be deployed before they can call your API
Removing Authorized Apps¶
When removing an authorized app and the scopes it uses, you must follow a specific order. Entra ID requires that all pre-authorized applications are removed before the scopes they reference are deleted. Removing scopes first causes Terraform to fail when it later tries to clean up the pre-authorized app entries.
Safe Removal Order¶
flowchart TD
A["1. Remove authorized app from\ninfra/auth/corp/config.yml"] --> B["2. Run azure-pipelines-auth.yml\n(removes pre-authorization)"]
B --> C["3. Remove scope from\ninfra/api/config.yml"]
C --> D["4. Run azure-pipelines-api.yml\n(removes scope)"]
Step 1 — Remove the entry from infra/auth/corp/config.yml:
# Before
authorized_apps:
- project_id: it-api-exp-caller
app_roles: []
scopes:
- read
# After
authorized_apps: []
Step 2 — Run azure-pipelines-auth.yml so the pre-authorization is removed from Entra ID.
Step 3 — Remove the scope from infra/api/config.yml:
# Before
scopes:
- value: read
display_name: Read Access
description: Allows the app to read data on behalf of the signed-in user
# After
scopes: []
Step 4 — Run azure-pipelines-api.yml so the scope is removed from the app registration.
Do Not Remove Scopes First
Removing a scope from infra/api/config.yml and deploying before removing the pre-authorized apps that reference it will cause a Terraform error on the next apply. See Troubleshooting below if you have already done this.
✅ Verification¶
After deploying, verify the configuration:
- Check Entra ID: Verify the upstream app has API permissions granted
- Check Okta: Verify the upstream app is listed in authorized clients
- Test API Call: Have the upstream app call your API and verify success
🔍 Troubleshooting¶
Upstream App Can't Call Your API¶
✅ Check:
- Your API is deployed and accessible
- The upstream app is listed in your authorized apps configuration:
- Corporate:
infra/auth/corp/config.yml→authorized_apps - External:
infra/auth/ext/app/authorized-apps.yml→authorized_apps - Requested scopes are defined in your API configuration
- The upstream app has the correct Project ID configured
- User-context scope is included:
user_impersonation(corp) oruser-groups(ext)
Configuration Out of Sync¶
✅ Check:
- Scopes in
infra/api/config.ymlmatchinfra/auth/ext/okta-client/scopes.yml - Both corp and ext authorized apps are updated
- Auth pipeline has been run after all configuration changes
Terraform Fails When Removing Pre-Authorized Apps¶
Error:
Error: Removing pre-authorized application "..." from Application (Application: "...")
unexpected status 400 (400 Bad Request) with error: InvalidValue: Property
api.preAuthorizedApplications.delegatedPermissionIds has a Permission Id
that cannot be found in the AppPermissions sets.
Cause: A scope was removed from infra/api/config.yml and deployed before the pre-authorized apps referencing it were removed. Entra ID now rejects the pre-authorization removal because it references permission IDs that no longer exist on the application.
Fix: Temporarily restore the missing scope, remove the pre-authorized apps, then remove the scope:
- Re-add the deleted scope back to
infra/api/config.ymland runazure-pipelines-api.yml - Remove the authorized app entry from
infra/auth/corp/config.ymland runazure-pipelines-auth.yml - Remove the scope from
infra/api/config.ymlagain and runazure-pipelines-api.yml
Avoid This in Future
Always remove pre-authorized apps before removing the scopes they use. See Removing Authorized Apps for the correct order.
💡 Common Patterns¶
API-Only Service¶
For APIs that are only called by other services (no direct user access):
# infra/auth/ext/okta-client/scopes.yml
scopes:
Client.Read: Read access for service-to-service calls
Client.Write: Write access for service-to-service calls
# infra/auth/ext/app/authorized-apps.yml
authorized_apps:
- project_id: it-api-caller
scopes:
- Client.Read
No user_groups.yml needed if no human users access directly.
Web Application Backend¶
For APIs that serve a web frontend:
authorized_apps:
- project_id: it-web-frontend
app_roles: []
scopes:
- user_impersonation
- User.Read
- User.Write
📖 Synchronization Checklist¶
When updating security configuration, ensure both identity providers are updated:
| What Changed | Update Corp | Update Ext |
|---|---|---|
| Added new scope | infra/api/config.yml |
infra/auth/ext/okta-client/scopes.yml |
| Authorized new upstream app | infra/auth/corp/config.yml |
infra/auth/ext/app/authorized-apps.yml |
📚 Related Documentation¶
- Configure User Permissions - Set up Business Roles for user access
- Understanding Business Roles - How Business Roles work
- Calling Downstream APIs - Configure your app to call other APIs