I'm trying to build a simple Teams tab app that lists the members of a team. I've used the Teams Toolkit for VS Code to generate the boilerplate. Running the app using F5 works fine. So far, so good.
Next I'm trying to read the team members from Graph. I've read about RSC (resource-specific consent) which seems to be exactly what I need. Because I want to prevent the app from opening any permission popups or login popups (other than the one appearing during the installation).
So I've followed the guide https://learn.microsoft.com/en-us/microsoftteams/platform/graph-api/rsc/resource-specific-consent and I've also made the check. Works fine.
However, as soon as I'm running the code locally (using F5), I'm getting this error:
AUTHMSAL: Event: adal:tokenRenewFailure, code: invalid_resource|AADSTS500011: The resource principal named api://localhost:53000/MY-CLIENT-ID-UUID was not found in the tenant named MYTESTTENANT. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
I've double checked: the tenant does exist (and there's only this one). The app id is also correct, it resembles the one I've added using the "App registration". I've also added a "Client secret", however I'm not exactly sure where to add it to my code. This is probably the root cause of my problem.
The relevant parts of my code are:
// manifest.template.json
{
...
"webApplicationInfo": {
"id": "MY-CLIENT-ID-UUID",
"resource": "api://localhost:53000/MY-CLIENT-ID-UUID"
},
"authorization": {
"permissions": {
"resourceSpecific": [
{
"name": "TeamSettings.Read.Group",
"type": "Application"
},
{
"name": "TeamMember.Read.Group",
"type": "Application"
},
// ... (I've added *all* other permissions, too, just for testing...)
]
}
}
}
// Tab.tsx
// ...
// I've read on NPM that useTeamsFx() is deprecated and switched to useTeamsUserCredential()
const all = useTeamsUserCredential({
clientId: "MY-CLIENT-ID-UUID",
initiateLoginEndpoint: "fooBarInitiateLoginEndpoint",
// ...don't I have to add my clientSecret somewhere?
});
// Welcome.tsx
// ...
const { loading, error, data, reload } = useGraphWithCredential(
async (graph, credential, scope) => {
await graph.api(`/teams/MY-GROUP-ID/members`).get() // leads to the error
},
{
scope: [
"TeamMember.Read.Group"
],
credential
}
)
I've also tried to follow the AddSSO guide, but it led to permission popups which I want to prevent. Plus it added a lot of AAD specific stuff (like aad.template.json), which I don't need, because RSC suffices in my case (...right?).
Also, my app doesn't need SSO (...right?) because it only lists the members of the team and it runs inside Teams only...
EDIT
I've also tried new TeamsFx(IdentityType.App) instead of useTeamsUserCredential() (because RSC permissions have "type": "Application" in the manifest.template.json), however this ended up in an error:
Uncaught ErrorWithCode.IdentityTypeNotSupported: Application identity is not supported in TeamsFx
Somebody else may be able to provide you a full Teams example, but the main error in your case is this one:
AUTHMSAL: Event: adal:tokenRenewFailure, code: invalid_resource|AADSTS500011: The resource principal named api://localhost:53000/MY-CLIENT-ID-UUID was not found in the tenant named MYTESTTENANT. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You might have sent your authentication request to the wrong tenant.
The reason for this is here:
"webApplicationInfo": {
"id": "MY-CLIENT-ID-UUID",
"resource": "api://localhost:53000/MY-CLIENT-ID-UUID"
},
"api://localhost:53000/MY-CLIENT-ID-UUID" is not a valid resource identifier. Please note that this is not the API url, but rather its identifier. Please look for the App Id URI in your application registration in Azure Portal.
The correct value is most likely api://<your-client-id>, but you need to set it first.
See also Does the resource URL in webApplicationInfo need to contain the Microsoft Teams App ID?
https://portal.azure.com/#view/Microsoft_AAD_RegisteredApps/ApplicationMenuBlade/~/Overview/appId/<your-client-id>/
Related
I have two applications registered in Azure, a REST API and daemon console application that calls the REST API.
The work flow is shown
Daemon console app --call for token using secret--> Azure AD
Daemon console app use the return token to call --> REST API.
I do get the token and correct AppRole is defined in REST API, with Admin consent.
however, when I try to call the REST API Daemon console app throws this error
Failed to call the web API: InternalServerError
Content: System.ArgumentException: Requested value 'ApplicationRole' was not found.
at System.Enum.TryParseByName(RuntimeType enumType, String originalValueString, ReadOnlySpan`1 value, Boolean ignoreCase, Boolean throwOnFailure, UInt64& result)
I followed the official MS documentation here
https://github.com/Azure-Samples/active-directory-dotnetcore-daemon-v2/tree/master/2-Call-OwnApi
I decoded the JWT token it has the role defined:
...
"oid": "xxxxx",
"rh": "xxxx",
"roles": [
"ApplicationRole"
],
...
The Manifest file of REST API
...
appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"description": "Daemon apps in this role can consume the web api.",
"displayName": "ApplicationRole",
"id": "unique uid",
"isEnabled": true,
"lang": null,
"origin": "Application",
"value": "ApplicationRole"
}
],
...
The admin consent is also being granted to Daemon console App
API / Permissions name Type Description Admin consent require Status
IoT-Manager (1)
ApplicationRole Application ApplicationRole Yes Granted for XX
What could be wrong here ?
Update
I debug Daemon application I found out that the access token does not contain roles
...
AuthenticationResult result = null;
try
{
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine("Token acquired \n");
Console.WriteLine(result.AccessToken);
Console.WriteLine("Token ExpireOn \n");
Console.WriteLine(result.ExpiresOn);
Console.ResetColor();
}
...
When I print the access token and decode it shows it has AppRoles, where When I debug the object AuthenticationResult result has no attribute for roles.
I've run the sample you provided and it worked well, I'll show my steps below.
Creating an azure ad app as the service. When registering the app, I only enter the name field and keep others the default.
Next I exposed an api in this application. Expose an API-> Add a scope-> keep the default application id url->save and continue-> edit my scope like below:
Add the role like screenshot below:
Creating another azure ad app as the client, only enter the name field too.
Create client secret and add api permission.
set the sample code, the appsetting.json file of each project are needed to modify, and the controller file in api project. The value needed to be changed in controller depends on the api. In my side, you can see the screenshot in step 5, it shows api://xxx/App.Role, so I set the value as App.Role.
I have an Azure Function with Azure Active Directory authentication enabled (including "Action to take when request is not authenticated" = "Log in with Azure Active Directory"). Additionally the option "User assignment required?" of the Azure Function related service principal (sp_func) is set to "Yes" to avoid everybody in the tenant being able to in the end run the function.
The goal is to have a single security group (that can include users as well as service principals) that is added to "Users and groups" of sp_func so that the assignment to the group decides if the function can be accessed or not. With users this works fine but not with service principals (sp_nonfunc). For them (sp_nonfunc) to work I have to set the permissions for them (sp_nonfunc) what in the end allows them to interact with the Azure Function no matter if they (sp_nonfunc) are assigned to the group or not.
Is it possible that I can just add a service principal (sp_nonfunc) to a group with the group being added to sp_func and then be able to execute the Function by using sp_nonfunc (without giving explicit permissions to sp_nonfunc)?
EDIT: it also does not seem to be possible to add sp_nonfunc to sp_func directly even if I defined an own appRole in the Manifest. The only way currently seems to be to add permissions on sp_nonfunc for sp_func - but that is what I want to avoid.
EDIT2: here how I have defined the role in the sp_func manifest
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "AzureFunctionAccess",
"id": "xxx-xxx-xxx-xxx-xxx",
"isEnabled": true,
"description": "Access Azure Function.",
"value": "AzureFunctionAccess"
}
]
EDIT3: when I don't assign a role directly to sp_nonfunc but just add sp_nonfunc to the security group I get, when making a request to https://login.microsoftonline.com/<tenant id>/oauth2/token with resource = Application ID URI of the registered app of sp_func:
{
"error": "invalid_grant",
"error_description": "AADSTS501051: Application 'xxx-xxx-xx-xx-xx'(xxx) is not assigned to a role for the application 'https://xxx'(xxx).\r\nTrace ID: xxx-xxx-xx-xx-xx\r\nCorrelation ID: xxx-xxx-xx-xx-xx\r\nTimestamp: xx-xx-xx xx:xx:xxZ",
"error_codes": [
501051
],
"timestamp": "xx-xx-xx xx:xx:xxZ",
"trace_id": "5xxx-xxx-xx-xx-xx",
"correlation_id": "xxx-xxx-xx-xx-xx",
"error_uri": "https://login.microsoftonline.com/error?code=501051"
}
This way will not work, to use a service principal(in your case, the sp_nonfunc) get the token for the function app(sp_func), you need to give the API permission for the sp_nonfunc.
Navigate to the App Registration related to the sp_nonfunc in the portal -> API permissions -> add the AzureFunctionAccess you defined, at last click the Grant admin consent for xxx button.
Then get the token with the client credential flow, it will work fine. (I use the v2.0 endpoint, if you use the v1.0, it will also work.)
For more details about the steps, I wrote in this post before, you could refer to it.
I was asked by Azure Support to post this question, just to see if anyone had a useful opinion.
I am stepping through MS Azure training courses. I created the usual free account to go through these. I've gone through a few dozen of them, and am now at this one:
https://learn.microsoft.com/en-us/learn/modules/secure-and-isolate-with-nsg-and-service-endpoints/3-exercise-network-security-groups?source=learn
This attempts to use the Azure PowerShell service. I had some trouble getting to the PowerShell page. It appears that if I'm not already logged into the portal, it goes into a semi-infinite loop, trying to get to the shell page, then trying to login, then the shell page, and finally it gives up and says "We couldn't sign you in. Please try again.".
However, I was able to work around this. If in a separate tab, I log into the Azure Portal, and then go back and follow the link to Azure Cloud Shell, it passes the login gate and sends me to the page where I choose Bash or PowerShell. The course specifies using Bash. When I select that, it then asks me to create a Storage object. When I confirm that, it gives me the following error (subscription id elided):
{
"error": {
"code": "RequestDisallowedByPolicy",
"target": "cs733f82532facdx4f04x95b",
"message": "Resource 'cs733f82532facdx4f04x95b' was disallowed by policy. Policy identifiers: '[{\"policyAssignment\":{\"name\":\"Enforce tag on resource\",\"id\":\"/subscriptions/xxxxx/providers/Microsoft.Authorization/policyAssignments/740514d625684aad84ef8ca0\"},\"policyDefinition\":{\"name\":\"Enforce tag on resource\",\"id\":\"/subscriptions/xxxxx/providers/Microsoft.Authorization/policyDefinitions/be3862a6-ca1e-40b0-a024-0c0c7d1e8b3e\"}}]'.",
"additionalInfo": [
{
"type": "PolicyViolation",
"info": {
"policyDefinitionDisplayName": "Enforce tag on resource",
"evaluationDetails": {
"evaluatedExpressions": [
{
"result": "True",
"expressionKind": "Field",
"expression": "tags[Department]",
"path": "tags[Department]",
"targetValue": "false",
"operator": "Exists"
}
]
},
"policyDefinitionId": "/subscriptions/xxxxx/providers/Microsoft.Authorization/policyDefinitions/be3862a6-ca1e-40b0-a024-0c0c7d1e8b3e",
"policyDefinitionName": "be3862a6-ca1e-40b0-a024-0c0c7d1e8b3e",
"policyDefinitionEffect": "deny",
"policyAssignmentId": "/subscriptions/xxxxx/providers/Microsoft.Authorization/policyAssignments/740514d625684aad84ef8ca0",
"policyAssignmentName": "740514d625684aad84ef8ca0",
"policyAssignmentDisplayName": "Enforce tag on resource",
"policyAssignmentScope": "/subscriptions/xxxxx",
"policyAssignmentParameters": {
"tagName": {
"value": "Department"
}
}
}
}
]
}
}
I think the simple conclusion from this is that my free account doesn't have enough rights to do what is needed here. The documentation I've read seems to imply that I have to get additional rights on the account in order to do this. However, I'm just using a free account that I created to go through the Azure training courses. It doesn't really make sense to ask me to do this. I've seen other Azure courses create a temporary sandbox supposedly because they have particular objects pre-created in the sandbox, but I'm also thinking that the sandbox has particular permissions that are not available in the free account. It seems to me that the only reasonable fix for this problem is for that course to be refactored to use a temporary sandbox with the correct permissions.
I'm just looking for any opinions on this, and confirmations that this is what should be done.
It doesn't look like you are creating resource, cloudshell storage, on your free subscription. Except if you added to a Work/Corporate tenant.
From the information you provide, subscription you are trying to use has a policy to enforce tags Department, mean any resource created should have a tag with Department information.
I am using Azure AD to secure my service to service calls. (Each service having an application identity in Azure AD).
Example: Application A wants to access Application B.
I noticed that when requesting an accesstoken from Application A using Client Credential Flow (with Certificate), an accesstoken is issued without having me to explicitly set the permissions to access Application B.
This seems odd to me because the token returned has its audience set to Application B even thought I haven't explicitly given it access.
If I understand correctly, all registered app have access to each other by default?
Is there a way in Azure AD to explicitly require permissions to be set in order for application to access each other?
Below is a screenshot of Application A required permissions. As you can see, Application B is not listed here.
In the following screenshot, I assigned TodoListService (aka Application B) to the required permissions of Application A
I noticed that when requesting an accesstoken from Application A using Client Credential Flow (with Certificate), an accesstoken is issued without having me to explicitly set the permissions to access Application B.
Yeah, that one can be a bit surprising and I'm not sure why that is the case either.
What you need to do is define application permissions on the API, and then assign it on the client.
Then you need to check the caller has the required app permission in the token.
I have an article on this topic: Defining permission scopes and roles offered by an app in Azure AD.
To define an app permission on the API, you'll have to edit its manifest in Azure AD, and add an app role with member type of Application, something like:
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8d39977-e31e-460b-b92c-9bef51d14f98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
IIRC you have to generate a GUID for the id.
After defining this permission on the API, go to your client app, and add the app permission in the Required permissions.
Then you should press Grant permissions to grant the app permission.
Now then when the client acquires a token with client credentials, the token will contain:
{
"roles": [
"Todo.Read.All"
]
}
So you'll have to check that that is present.
I have a couple of APIs("Group A and B") which call a single, central API ("API X"). All use authentication via Azure AD.
Within "API X", I want to restrict a couple of routes to a subset of the available routes.
/api/stuffForEveryOne/... <-- Group A and B should be able to call this
/api/specialStuff/... <-- Only Group B should be able to call this
What is the proper way to implement this?
My idea was to assign the AAD applications to AAD groups, and validate by querying graph API in "API X". But since there are no users this context, I am not sure if this is the correct way.
Ideally, it would be great if "Group B" would get an additional claim when they request a JWT token for "API X". But I don't know if that is possible. "Group A" should not be able to get this claim.
Use app permissions :)
You can define two app permissions in the API X manifest like this (example showing one, removed other properties for clarity):
{
"appRoles": [
{
"allowedMemberTypes": [
"Application"
],
"displayName": "Read all todo items",
"id": "f8d39977-e31e-460b-b92c-9bef51d14f98",
"isEnabled": true,
"description": "Allow the application to read all todo items as itself.",
"value": "Todo.Read.All"
}
]
}
Set the value to something that makes sense for you, it will be included in the access token.
Example token (with most claims removed):
{
"appid": "28d6c0d7-6017-42f7-8cee-c27d80bb9709",
"roles": [
"Todo.Read.All"
]
}
You can read about them more in my blog: https://joonasw.net/view/defining-permissions-and-roles-in-aad.
Then you go to both of your app's using the API X in AAD, and require both app permissions in one of them, and only one of the permissions in the other.
Don't forget to grant the permissions.
Then, you can add authorization policies that require the existence of a claim with type "roles"/ClaimTypes.Role (I forget which one it is), and value of the value in the app permission.
Example policy definition:
services.AddAuthorization(o =>
{
o.AddPolicy("SpecialStuff", p => p.RequireClaim(ClaimTypes.Role, "SpecialStuff.Read"));
});
Then you specify [Authorize("PolicyName")] on the controller or action where you want to require the permission.
Side note: I really should write a blog on doing this stuff in ASP.NET Core APIs..