SharePoint csom ADAL Token 401 Unauthorized - sharepoint

I generate a AAD Token that has the following rights.
AllSites.FullControl AllSites.Manage AllSites.Read
AllSites.Write email Mail.Read Mail.Send MyFiles.Read
MyFiles.Write openid profile Sites.Search.All TermStore.Read.All
TermStore.ReadWrite.All User.Read User.Read.All User.ReadBasic.All User.ReadWrite.All
I am getting 401 Unauthorized when using nuget SharePointPnPCoreOnline, I also tried TTCUE.NEtCore.SharepointOnline.CSOM.
I have created an AAD App Registration with various permissions assigned. The permissions are working for Mail.Read, just not SharePoint.
using (var context = new ClientContext("URL"))
{
context.AuthenticationMode = ClientAuthenticationMode.Anonymous;
context.FormDigestHandlingEnabled = false;
context.ExecutingWebRequest +=
delegate (object oSender, WebRequestEventArgs webRequestEventArgs)
{
webRequestEventArgs.WebRequest.Headers["Authorization"] =
"Bearer " + token;
};
context.Load(context.Web);
context.ExecuteQueryAsync().GetAwaiter().GetResult();
Console.WriteLine(context.Web.Title);
Console.ReadLine();
}

I have figured out the answer. SharePoint CSOM access requires an Azure Active Directory V1 token. Ensure you are not trying to access with a V2 token.

Related

Access Microsoft Graph API from Azure B2C

I have an Azure AD B2C tenant with an application running. It is configured to use only Azure AD and Microsoft Accounts to login. This application is used by App Center Auth.
I want to access some Microsoft APIs (Microsoft Graph API, Azure DevOps API) from my mobile application with the same login. Therefore I added the API permissions (Azure DevOps -> user_impersonation and Microsoft Graph -> User.Read) to my application in my Azure AD tenant (not the B2C tenant) to grant these permissions on login.
If I now try to use the access token after the login in my application to access e.g. the user in Microsoft Graph, I get an Unauthorized error.
// Sign-in succeeded, UserInformation is not null.
var userInfo = await Auth.SignInAsync();
// Get tokens. They are not null.
var idToken = userInfo.IdToken;
var accessToken = userInfo.AccessToken;
Within the same method, I try to get the user photo from Microsoft Graph
var graphAPIEndpoint = "https://graph.microsoft.com/v1.0/me";
var scopes = new[] { "user.read" };
var client = new HttpClient();
var request = new HttpRequestMessage(HttpMethod.Get, graphAPIEndpoint + "/photo/$value");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
var response = await client.SendAsync(request);
var image = await response.Content.ReadAsByteArrayAsync();
UserImage.Source = ImageSource.FromStream(() => new MemoryStream(image));
Has anyone an advice how to configure the B2C / Azure AD application to get access to these API with the Access Token? Or am I on the complete wrong way?
Just like #juunas said, as of today,you need to use AAD Graph API to access Azure AD B2C tenant, this is different from the Microsoft Graph API.
Here is the document for this: https://learn.microsoft.com/en-us/azure/active-directory-b2c/active-directory-b2c-devquickstarts-graph-dotnet
Take a look at this documentation published about using the Microsoft Graph API and authenticating against it for B2C instances:
https://learn.microsoft.com/en-us/azure/active-directory-b2c/microsoft-graph-get-started?tabs=applications
With recent migration away from login.microsoftonline.com to *.b2clogin.com, if you're using MSAL to obtain the authentication token from the AAD B2C instance, you need to override the authority config, and turn off authority validation, as per this document:
https://learn.microsoft.com/bs-latn-ba/azure/active-directory/develop/msal-b2c-overview
In JavaScript, this is done like below:
const msalConfig = {
auth: {
clientId: "e760cab2-b9a1-4c0d-86fb-ff7084abd902" //This is your client/application ID
authority: "https://fabrikamb2c.b2clogin.com/fabrikamb2c.onmicrosoft.com/b2c_1_susi", //This is your tenant info
validateAuthority: false
},
};
// create UserAgentApplication instance
const myMSALObj = new Msal.UserAgentApplication(msalConfig);
In C# with the MSAL.NET library, it would be done with the authority URL passed to the PublicClientApplication class constructor.
I was able to consume Graph REST API using the below steps,
Get access token,
POST
URL: https://login.microsoftonline.com/{tenantId}/oauth2/v2.0/token
form-data:
{"client_id": <Registered application clientId>, "client_secret": <Registered application generated secret>, "scope": "https://graph.microsoft.com/.default", "grant_type": "client_credentials"
Response:
{"token_type" : "", "expires_in" : "", "ext_expires_in" : "", "access_token" : ""}
NOTE: Registered application within b2c tenant should have required grants to access Graph API
Pass token to access API resources, ex: to get user details with objectId,
GET
URL: https://graph.microsoft.com/v1.0/users/{objectId}
set header key
"Authorization": "Bearer " ++ {access_token from above step 1.>

Scope needed to get access token to pull user groups from Azure using client id and client secret and graph API

I am trying to get the groups in my azure tenant using my client secret and client id. Problem is I don't know what scope to pass when getting an access token to use the graph api. I used https://graph.microsoft.com/.default to get an access token but this doesn't include permission to pull groups. What's the appropriate scope to use
https://learn.microsoft.com/en-us/graph/api/group-list?view=graph-rest-1.0#permissions
Permissions
One of the following permissions is required to call this API. To learn more, including how to choose permissions, see Permissions.
Permission type Permissions (from least to most privileged)
Delegated (work or school account) Group.Read.All, Group.ReadWrite.All
Application Group.Read.All, Group.ReadWrite.All
You need to configure the API access within AAD, not with the scope. Make sure that you don't forget to click on "grant permissions".
Example assumes that you require application permission. Delegated permission works similarly.
Sample code for getting the data using MSAL for authentication:
IConfidentialClientApplication app = new ConfidentialClientApplication(
"clientId",
"https://login.microsoftonline.com/yourtenant.onmicrosoft.com",
"http://localhost (redirecturi)",
new ClientCredential("secret"),
new TokenCache(), new TokenCache());
string[] scopes = new string[] { "https://graph.microsoft.com/.default" };
try
{
AuthenticationResult result = await app.AcquireTokenForClientAsync(scopes);
System.Console.WriteLine(result.AccessToken);
using (var http = new HttpClient())
{
http.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", result.AccessToken);
var groupResponse = await http.GetAsync("https://graph.microsoft.com/v1.0/groups");
var groupJson = await groupResponse.Content.ReadAsStringAsync();
System.Console.WriteLine(groupJson);
}
}
catch (Exception ex)
{
System.Console.WriteLine(ex.Message);
}

Azure Active Directory Authentication and SharePoint CSOM

I have a web application, which connects to SharePoint (customer tenant) to create sites & various List.
To access the customers Sharepoint environment, I used OpenIdConnectAuthenticationOptions which prompts user for AAD creditinals and then the various options for which user wants to provide access ( This App and various API access needed is configured in AAD - when allowing access to "O365 SharePoint Online"
Using OpenIdConnectAutheticationOption, on AuthorizationCode Received the code is used to get "AccessToken".
Using this "AccessToken" to get clientContext gives error:
"401 - Not authorized"
How one can get the required token which allows CSOM operation?
The code used from -
Active Directory Dot net Webapp Multitenant
In the controller OnboardingController, Processcode function, after getting AcquireTokenByAuthorizationCodeAsync following code is used -
string siteUrl = "https://svtestsite.sharepoint.com/sites/powerapps";
ClientContext ctx = new ClientContext(siteUrl);
ctx.ExecutingWebRequest +=
delegate(object oSender, WebRequestEventArgs webRequestEventArgs)
{
webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =
"Bearer " + result.AccessToken; // accessToken;
};
ctx.Load(ctx.Web, p => p.Title);
ctx.ExecuteQuery();
Console.WriteLine(siteUrl);
Console.WriteLine(ctx.Web.Title);
The code snippet that you've shared, which sets bearer token for request Authorization header looks fine to me.
ctx.ExecutingWebRequest +=
delegate(object oSender, WebRequestEventArgs webRequestEventArgs)
{
webRequestEventArgs.WebRequestExecutor.RequestHeaders["Authorization"] =
"Bearer " + result.AccessToken; // accessToken;
};
So your issue could be with either the token itself or some permissions missing for app/users. Here are a few things that you should check:
Required permissions for your App Registered in Azure AD. Make sure the app has permissions for "Office 365 SharePoint Online". Which exact permissions depends on your requirements/use cases, but at least 1 should be there. Also, you should go through with the consent flow either using "Grant Permissions" button or as part of Onboarding flow for the tenant.
Check which "Resource" has the token been acquired for.
At least the sample code link you mention in your question (Active Directory Dot net Webapp Multitenant) mentions the resource as "https://graph.windows.net".
Make sure you have changed that to your SharePoint site collection URL.. something like "https://yoursite.sharepoint.com/".
Do try with including/excluding the / at the end of this URL. I've seen issues just because of that as well, although not sure if yours is related.
If above points don't work, it would be worthwhile to examine the access token you are sending to SharePoint in a tool like https://jwt.io or https://jwt.ms Especially, look for:
"aud" claim, which tells about the intended audience for which this token was issued. It should be your SharePoint URL. If it's not that can be causing the issue.
"tid" claim, to see that token is from correct Azure AD tenant. It will be GUID.
and other claims in token, to see if anything suspicious pops out.
Obvious one, but if you're using delegated permissions, then check that the user has appropriate permissions in SharePoint site collection.

Can't access the calendars in office365 groups

I got Unauthorized when calling to get Calendar in the Microsoft graph API, I use this verification to retrieve an access token.
I think the problem is that when I want to retrieve the Get HTTP request on Calendar the access token isn't created with credentials which result in the Unauthorized call. How can I change my code to be able to send Credentials with it and get a better Token with more privileges?
In the Azure portal, I granted all permissions to see whether that the application permissions weren't set correctly.
AuthenticationContext authenticationContext = new AuthenticationContext(authString, false);
ClientCredential clientCred = new ClientCredential(_azureAd_ClientId, _azureAd_SecretKey);
string tokenResult;
AuthenticationResult authenticationResult;
try
{
authenticationResult = await authenticationContext.AcquireTokenAsync(_azureAd_GraphResource, clientCred);
var user = authenticationResult.UserInfo;
string_token = authenticationResult.AccessToken;
tokenResult = JsonConvert.SerializeObject(authenticationResult);
}
catch (Exception ex)
{
throw new Exception(ex.Message, ex.InnerException);
}
Reading through the comments it seems that you may need to trigger a new consent prompt in order to grant the application the permissions you have re-configured. If this is a V1 app, you can do so by adding prompt=admin_consent to the end of your request. If this is a V2 app, you will want to use the /adminconsent endpoint. More info on that is here.
There is also some more documentation on admin consent in general here.

B2B users cannot sign in to Tenant using v2.0 endpoint & MSAL Auth flow

I am trying to create a B2B Management portal. I've started off with this sample since it uses MSAL and Graph API.
user#live.se is in the tenant. It's been invited as a "guest user", i.e a B2B user. However, signing in with user#live.se does not work even though it's been added to the tenant. Following error after sign-in:
AADSTS50020: User account 'user#live.se' from external identity provider 'live.com' is not supported for api version '2.0'. Microsoft account pass-thru users and guests are not supported by the tenant-independent endpoint. Trace ID: 2ad8bee0-d00a-4896-9907-b5271a113300 Correlation ID: 0ea84617-4aa1-4830-859f-6f418252765e Timestamp: 2017-10-03 15:35:22Z
I changed the authority (from common) to only allow users from my tenant (requirement):
https://login.microsoftonline.com/tenant.onmicrosoft.com/v2.0
Do guests not count as part of my tenant when using MSAL? that would mean I have to use "old" tech, i.e ADAL and AAD Graph, which is not recommended, and feels kinda lame.
If you pass the specific tenant value in the authority, then
Only users with a work or school account from a specific Azure AD tenant can sign in to the application. Either the friendly domain name of the Azure AD tenant or the tenant's GUID identifier can be used.
That's means the Microsoft Account is not supported in this scenario. Refer here for the Microsoft Account and Work or school accounts. And in this scenario, if you new a user user from other tenant, it should also works.
You can refer the document for tenant from link below:
Fetch the OpenID Connect metadata document
I know this is an old thread but just in case anyone stumbles upon it, here is a solution:
In cases of Personal guest accounts, use Credential Grant Flow (Get access without a user).
To do that, you would first need to grant appropriate permission (of Type Application) for the API you wanted to use on behalf of the signing user. This would let you acquire access token with the application's identity itself rather than the signed in user.
Next get token like this (in this sample, I'm getting access token for Graph API):
public async Task<string> GetAccessToken()
{
using (HttpClient httpClient = new HttpClient())
{
string token = "";
try
{
httpClient.BaseAddress = new Uri($"https://login.microsoftonline.com/{tenantId}");
httpClient.DefaultRequestHeaders.TryAddWithoutValidation("Content-Type", "application/x-www-form-urlencoded; charset=utf-8");
HttpRequestMessage request = new HttpRequestMessage();
List<KeyValuePair<string, string>> body = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string, string>("client_id", clientId),
new KeyValuePair<string, string>("scope", "https://graph.microsoft.com/.default"),
new KeyValuePair<string, string>("client_secret", appSecret),
new KeyValuePair<string, string>("grant_type", "client_credentials")
};
request.Method = HttpMethod.Post;
request.RequestUri = new Uri($"{httpClient.BaseAddress}/oauth2/v2.0/token");
request.Content = new FormUrlEncodedContent(body);
var response = await httpClient.SendAsync(request);
var content = await response.Content.ReadAsAsync<dynamic>();
token = content.access_token;
}
catch (Exception e)
{
}
return token;
}
}
Tip: If your goal is also Graph API, don't try to get logged in user info by using the /me endpoint in this case. Since the token was generated using the application identity rather than the signed in user, /me would be the application not the logged in user. What you want to do is: retrieve logged in user id from the Claim (Type: http://schemas.microsoft.com/identity/claims/objectidentifier) and use the /user/{userid} endpoint.
I found: for personal accounts (Get access without a user) in the body of the request you must to use grant_type = 'client_credentials' and for corporate accounts to use grant_type = 'authorization_code'

Resources