Im having some problems retriving data from sharepoint (Disks) for a dotnet core app. At the moment my app tries to use the app itself, and not the logged in user to retrive disks, but the prefered way would be to use the accesstoken for the logged in user instead.
Maybe authenticating as the app with clientId and secret wont work with drives at all?
The login works fine.
I've set up a dotnet core app with the following startup:
services.AddAuthentication(AzureADDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.ExpireTimeSpan = TimeSpan.FromDays(30);
})
.AddAzureAD(options => Configuration.Bind("AzureAd", options));
I also have the following services registered:
services.AddTransient<IAuthenticationProvider, GraphAuthenticationProvider>();
services.AddTransient<IGraphServiceClient, GraphServiceClient>();
services.AddTransient<IGraphProvider, MicrosoftGraphProvider>();
where i use the this to authenticate:
public class GraphAuthenticationProvider : IAuthenticationProvider
{
public const string GRAPH_URI = "https://graph.microsoft.com/";
private string _tenantId { get; set; }
private string _clientId { get; set; }
private string _clientSecret { get; set; }
public GraphAuthenticationProvider(IConfiguration configuration)
{
_tenantId = configuration.GetValue<string>("AzureAd:TenantId");
_clientId = configuration.GetValue<string>("AzureAd:ClientId");
_clientSecret = configuration.GetValue<string>("AzureAd:ClientSecret");
}
public async Task AuthenticateRequestAsync(HttpRequestMessage request)
{
AuthenticationContext authContext = new AuthenticationContext($"https://login.microsoftonline.com/{_tenantId}");
ClientCredential creds = new ClientCredential(_clientId, _clientSecret);
//I have tried using acquireTokensAsync with scopes, but there is no such method.
AuthenticationResult authResult = await authContext.AcquireTokenAsync(GRAPH_URI, creds);
request.Headers.Add("Authorization", "Bearer " + authResult.AccessToken);
}
}
I have given the app plenty of permissions in the API settings in portal, mostly because im unsure what i need, and at the moment im just eager to make it work first, then refactor some.
The app is able to log in, and retrive the following data with the SDK:
var groups = await _graphServiceClient.Groups[appSettings.AzureAd.GroupId].Request().GetAsync();
however: the following does not work:
var groupDrives = await _graphServiceClient.Groups[appSettings.AzureAd.GroupId].Drives
.Request()
.GetAsync();
and i get the following error: Code: AccessDenied Message: Either scp or roles claim need to be present in the token.
I also have user login in startup, and the app wont be used without logging in towards azure AD: Could i use the accessToken for the user instead?
services.Configure<OpenIdConnectOptions>(AzureADDefaults.OpenIdScheme, options =>
{
options.Authority = options.Authority + "/v2.0/";
options.TokenValidationParameters = new TokenValidationParameters() { NameClaimType = "name" };
options.TokenValidationParameters.ValidateIssuer = false;
options.Events = new OpenIdConnectEvents
{
OnTokenValidated = async ctx =>
{
var roleGroups = new Dictionary<string, string>();
Configuration.Bind("AuthorizationGroups", roleGroups);
var clientApp = ConfidentialClientApplicationBuilder
.Create(Configuration["AzureAD:ClientId"])
.WithTenantId(Configuration["AzureAD:TenantId"])
.WithClientSecret(Configuration["AzureAD:ClientSecret"])
.Build();
var authResult = await clientApp
.AcquireTokenOnBehalfOf(new[] { "User.Read", "Group.Read.All" }, new UserAssertion(ctx.SecurityToken.RawData))
.ExecuteAsync();
var graphClient = new GraphServiceClient(
"https://graph.microsoft.com/v1.0",
new DelegateAuthenticationProvider(async (requestMessage) =>
{
requestMessage.Headers.Authorization = new AuthenticationHeaderValue("bearer", authResult.AccessToken);
}));
//Could i register the graphservice as a singelton with the users accesstoken?
//Fetching drives here with the accessToken from user works.
var graphService = new GraphService(graphClient, Configuration);
var memberGroups = await graphService.CheckMemberGroupsAsync(roleGroups.Keys);
var claims = memberGroups.Select(groupGuid => new Claim(ClaimTypes.Role, roleGroups[groupGuid]));
var appIdentity = new ClaimsIdentity(claims);
ctx.Principal.AddIdentity(appIdentity);
}
};
});
I would actually like to use the users accesstoken to retrive the drives etc, but im not sure on how to store\reuse the accesstoken. I should probably register the service as a singelton with the users accesstoken as mentioned in the comment?
I followed this guide, and it has the same classes\services i have used: http://www.keithmsmith.com/get-started-microsoft-graph-api-calls-net-core-3/
I actually thought the option on top here was just a header. It might be easier now.. https://i.imgur.com/yfZWaoe.png