Reputation: 8255
I'm working in Postman before I start writing app code so that I can get my head around what I'm actually supposed to be doing - it's educational; in actual app dev, I'll be using MSAL for auth.
In the meantime, I can't even manage to get a token that works.
My app on Azure has the Calendar.Read
and Calendar.ReadWrite
permissions set in the API Permissions blade of the Azure Portal.
I've created a client secret for the app (so using client credentials flow) and I'm providing the /.default
scope per this accepted answer on MS docs.
When I try to query MS graph at https://graph.microsoft.com/v1.0/users/<my user id>/events
I get the error saying:
The token contains no permissions, or permissions can not be understood
Sure enough, the token contains neither the scp
or roles
claims.
I've also referenced csharpcorner to get a token as well as the actual documentation to configure the request in Postman.
I don't understand where I'm going wrong. As far as I can tell I'm doing everything the way I'm supposed to be but I'm just not getting anywhere.
Where am I going wrong? Have I missed something? Is there something I'm doing wrong?
Here's my jquery code from Postman:
var settings = {
"url": "https://login.microsoftonline.com/redacted/oauth2/token",
"method": "POST",
"timeout": 0,
"headers": {
"Content-Type": "application/x-www-form-urlencoded"
},
"data": {
"grant_type": "client_credentials",
"client_id": "redacted",
"client_secret": "redacted",
"resource": "https://graph.microsoft.com",
"scope": "./default"
}
};
$.ajax(settings).done(function (response) {
console.log(response);
});
Your help is greatly appreciated.
Thanks in advance
Upvotes: 2
Views: 1053
Reputation: 9549
Okay, that's the problem, you have not granted Calendars.Read
or Calendars.ReadWrite
application permissions.
Because you are using the client credential flow to get the token and call the /users/{user id}
endpoint to list the events of other users. So you must grant application permissions and grant admin consent for that permission. Then your problem will be solved.
Upvotes: 3
Reputation: 16066
Which language are you using? In my opinion, you can't use jquery to send an ajax request to obtain an access token, that should be a CORS error.
In the meanwhile, client credential flow should be used in daemon applications, but the code you provides are likely to be a single-page application. Here's a document contains many samples for different kind of applications and you may choose one to generate your access token.
Here's my another answer on obtaining access token for calling key vault, you may refer to it. If I misunderstood in some place, pls point it out. You can also provide more details on your further issue.
=========================================================
frontend code, obtain access token from backend and calling graph api:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title></title>
<meta name="viewport" content="width=device-width,initial-scale=1,minimum-scale=1,maximum-scale=1,user-scalable=no" />
</head>
<body>
<div>
<div id="subject"></div>
<div>create time: <span id="cDateTime"></span></div>
</div>
<script src="../js/jquery-3.5.1.min.js"></script>
<script type="text/javascript">
$(function() {
initPage();
});
function initPage(){
$.ajax({
url: "https://localhost:44343/",
type: 'get',
contentType: "application/json;charset=utf-8",
success: function(data) {
callApi(data);
},
error: function(data) {
console.info(data);
}
})
}
function callApi(accesstoken){
$.ajax({
url: "https://graph.microsoft.com/v1.0/users/{user_id_here}/calendar/events",
type: 'get',
headers: {
Authorization: "Bearer " + accesstoken
},
dataType: 'json',
success: function(data) {
var createdDateTime = data.value[0].createdDateTime;
var subject = data.value[0].subject;
$("#subject").html(subject);
$("#cDateTime").html(createdDateTime);
},
error: function(data) {
console.info(data);
}
})
}
</script>
</body>
</html>
backend code(home controller), using client credential flow to generate access token, and send it to front end. Don't forget to deal with cores error if you choose to use frontend-backend-separate.
using Azure.Identity;
using Azure.Security.KeyVault.Secrets;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Logging;
using Microsoft.Identity.Client;
using System;
using System.Threading.Tasks;
namespace crendentailflow_web_mvc.Controllers
{
public class HomeController : Controller
{
private readonly ILogger<HomeController> _logger;
public HomeController(ILogger<HomeController> logger)
{
_logger = logger;
}
public async Task<string> Index()
{
//I saved the client secret in azure key vault, and you can aslo use it directly
const string secretName = "clientsecret";
var kvUri = "https://vaultname.vault.azure.net/";
var a = new DefaultAzureCredential();
var client = new SecretClient(new Uri(kvUri), a);
var secret = await client.GetSecretAsync(secretName);
string secretVaule = secret.Value.Value;
IConfidentialClientApplication app;
app = ConfidentialClientApplicationBuilder.Create("azure_ad_app_client_id")
.WithClientSecret(secretVaule)
.WithAuthority(new Uri("https://login.microsoftonline.com/your_tenant_name.onmicrosoft.com"))
.Build();
AuthenticationResult result = null;
// don't forget to add api permission in azure portal
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
result = await app.AcquireTokenForClient(scopes)
.ExecuteAsync();
string accesstoken = result.AccessToken;
return accesstoken;
}
}
}
dependencies:
<PackageReference Include="Azure.Identity" Version="1.4.0" />
<PackageReference Include="Azure.Security.KeyVault.Secrets" Version="4.1.0" />
Upvotes: 0
Reputation: 18387
Take a look if you're using implicit flow otherwise, there's a max length issue and the content of the token will be truncated.
https://github.com/AzureAD/azure-activedirectory-library-for-js/issues/239
Upvotes: 1